1. Introduction

FirstSpirit is used to create versatile and project-specific content. Thanks to the FirstSpirit Connect for Spryker Commerce OS for Spryker Commerce OS module, it is possible to transfer these contents to the e-commerce shop system Spryker Commerce OS and use it there.

In the remainder of the document the abbreviated form "Spryker" is used instead of "Spryker Commerce OS".

Furthermore, the entire documentation is geared towards connecting to the Spryker B2C Demo Shop in version 202108.0. Mention of the shop therefore refers exclusively to the B2C Demo Shop.

The module combines the functional strengths of both systems, delivering the key advantages and create a successful overall system made up of two areas that work in parallel and are largely decoupled from one another:

Components on the FirstSpirit side

These components are used for creating and maintaining editorial data. The data is transferred in JSON format to the relevant CaaS instance, and queried from there by Spryker.

Components on the Spryker side

These components are used for the integration of the editorial content created in FirstSpirit. Spryker imports this data and integrates it into the shop.

Included in the delivery of the FirstSpirit Connect for Spryker Commerce OS for Spryker Commerce OS module is the reference project FirstSpirit Connect for Spryker Commerce OS Reference Project, which is available as a GitHub repository. This documentation is consistently based on the reference project and provides an explanation of the functions made available by the module using common use cases.

This document is aimed at both Spryker and FirstSpirit developers, who should be able to perform the integration using this document as a guide.
It is not intended as a manual for FirstSpirit editors.

It is assumed that the reader is confident in the use of FirstSpirit and Spryker as well as CaaS and Omnichannel Manager.

1.1. Range of functions

FirstSpirit Connect for Spryker Commerce OS enables editors to do the following:

  • Creating native shop content with FirstSpirit

  • Access to product and category information

  • Display shop elements and editorial content in the FirstSpirit preview simultaneously

  • Transfer of content into the Spryker Commerce OS

The corresponding functions are made available when the module is installed and configured in ContentCreator.

Familiar FirstSpirit tools are used to maintain the content, meaning that editors who are already familiar with FirstSpirit do not require any additional knowledge. The content is made available to Spryker as part of a deployment so that it can be imported. Spryker then integrates the information into the shop.

Consequently there is no difference for Spryker in the delivering of editorial contents to the live state. Even if the FirstSpirit server is shut down due to maintenance work, for example, this does not affect Spryker.

1.2. Architecture

The connection of FirstSpirit and Spryker is realized by an architecture made up of a range of components.

These components are:

  • The modules installed on the FirstSpirit server:

    • FirstSpirit Connect for Spryker Commerce OS

    • Omnichannel Manager

    • Content as a Service

  • Spryker Instance

architecture
Figure 1. Architecture

The individual components always interact according to the following schema:

  1. In FirstSpirit, the creation and editing of editorial content takes place in ContentCreator. With the help of the Omnichannel Manager, the storefront is embedded in it.

  2. The storefront in turn accesses the Preview CaaS and determines the current FirstSpirit content from it. It also integrates the JavaScript required for Omnichannel Manager, which enables the content in ContentCreator to be edited and highlighted.

  3. The transfer of the editorial content into the Preview CaaS takes place automatically with each save action.

  4. The filling of the Online CaaS with the released content is triggered by a deployment. The Online CaaS makes the content available to the importer, who transfers it to Spryker and persists it there.

Spryker thus represents the main component of this architecture. In addition to providing all shop functionality, it imports the content created or maintained in FirstSpirit from the Online CaaS and integrates it into the shop. There is only a loose link between FirstSpirit and Spryker. They primarily work in parallel with one another.

1.3. Concept

The FirstSpirit Connect for Spryker Commerce OS module offers the possibility to maintain editorial contents in FirstSpirit and to transfer them afterwards via deployment to Spryker. For this, content processing in both systems must be coordinated and compatible with each other. The subchapters below describe the underlying concept used to archieve this compatibility.

1.3.1. Pages

Similar to FirstSpirit, pages in Spryker are based on a structure of different components. In order to exchange editorial content between the two systems, these components must be coordinated.

Within the reference project FirstSpirit Connect for Spryker Commerce OS Reference Project included in the delivery, the CMS slots from Spryker are represented by the content areas of a page template in FirstSpirit. Sections corresponding to a CMS block can be added to them.

concept
Figure 2. Page Rendering

With FirstSpirit, CMS blocks can be generated that are displayed in a page via CMS slots.

1.3.2. Preview

The integration realized with the FirstSpirit Connect for Spryker Commerce OS module only allows FirstSpirit to generate and maintain editorial content and to publish it in the form of CMS blocks. However, Spryker still determines the framework of a page whose CMS slots integrate the generated blocks.

To present the preview of a page, FirstSpirit determines its current view in the storefront. The storefront in turn queries the editorial content from the Preview CaaS and replaces the corresponding CMS blocks. FirstSpirit displays the result using the Omnichannel Managers in ContentCreator.

1.3.3. Deployment

The transmission of the content created in FirstSpirit into the live state is done by Spryker. For this, the content must be provided, which happens in form of the Online CaaS. From it Spryker imports the editorial content and persists it in its data system.

1.4. Technical requirements

To use the FirstSpirit Connect for Spryker Commerce OS module, the following technical requirements must be met:

  • the modules FirstSpirit Connect for Spryker Commerce OS, Content as a Service and Omnichannel Manager in their current versions

  • FirstSpirit in version 2022-03 or higher

  • Java 17

  • Spryker Commerce OS 202108.0 with the extensions Category CMS Blocks and Product CMS Blocks

  • php in version 7.4 or higher

When using the supplied reference project FirstSpirit Connect for Spryker Commerce OS Reference Project, the current version of the BasicWorkflows module is also required.

1.5. Important information

This chapter contains information that should be observed when using the FirstSpirit Connect for Spryker Commerce OS module.

1.5.1. Configuration of the glue web server

During deployment, the content maintained in FirstSpirit is retrieved from the Online CaaS using the importer. It transfers the data to Spryker and persists them in the backend in its data system.

Since the Online CaaS potentially contains a large amount of data, the transfer of the contents requires a certain time span. Therefore, early timeouts during the import should be avoided. Due to this reason the web server of the glue API needs to be configured to allow http requests for a duration of at least 60 seconds. Additionally, FirstSpirit-side socket timeout must be adjusted accordingly when communicating with the glue API (see chapter Configuration of the project component).

1.5.2. Configuration of the FirstSpirit project settings page

The CaaS generations assume that a template for the project settings is maintained in the FirstSpirit project. Otherwise, the generation job terminates early and reports the following error in the job logs:

Exception with missing project settings page
error during script execution : java.lang.IllegalStateException:
Invalid value for key 'usedCaasProjects' - Value is not a HashMap (zero).
This can happen when a deployment does not include a single PageRef (blue), which is not supported (e.g. remote-media-project).

For this reason, a template must exist in the project and be configured in the project options in ServerManager, even if it has no content.

p settings
Figure 3. Configuration of the project settings page in the project options

1.5.3. Use of the Spryker B2C Demo Shop

As mentioned before, the entire documentation is geared to the connection of the Spryker B2C Demo Shop. When using it, some difficulties may occur that are neither caused by the FirstSpirit Connect for Spryker Commerce OS module nor by the integration. However, they are listed below and a possible solution is outlined.

Preview page not found in ContentCreator

If the ContentCreator cannot find the preview page, the page certificate is invalid or does not exist. In this case, the page must be opened in a separate browser tab to renew the SSL certificate and allow the page to be displayed. The preview page is then visible again in ContentCreator.

Missing RAM on Composer calls

There is a RAM limit for PHP by default, but it is too low to execute Composer actions. For this reason, the limit defined in the php.ini configuration file in the etc/php/7.2/cli directory must be disabled:

Deactivation of the RAM limit
[...]
set memory_limit=-1
[...]

2. Spryker - Installation and configuration

FirstSpirit is used to create and maintain editorial data that is transferred to and persisted by the CaaS. To integrate the data, Spryker needs access to the CaaS. This is provided by the Spryker modules firstspirit-preview and firstspirit-caas, which require an installation and configuration in Spryker. The firstspirit-preview module enables the storefront to be displayed in ContentCreator. The contents are retrieved from the CaaS using the firstspirit-caas module.

The following chapters describe all steps required for installation and configuration as well as additional extensions in Spryker.

2.1. Spryker-Modules

Both the retrieval of the CaaS contents by Spryker and their display and editing in ContentCreator is done using various Spryker modules. These modules require an installation in Spryker and an extension of the configuration. They also require various JavaScript and CSS files to be included in Spryker.

The following chapters describe how to perform the necessary steps.

2.1.1. Installation of the Spryker modules

The maintenance of shop contents in ContentCreator requires the installation of several Spryker modules, which are included in the delivery in the form of zip files. They have to be stored in any directory below the Spryker project directory, which must be announced to Composer as another repository. This is done via the following command to be executed in the project directory, which extends the composer.json file of the Spryker server accordingly.

Extension of the composer.json file
composer config repo.espirit artifact ./PATH-TO-DIRECTORY

The Spryker modules to be installed use the namespace ESpirit, which must be announced to Spryker. This requires an extension of the KernelConstants in the environment-independent file config_default.php in the directory config/Shared:

Extension of the KernelConstants
$config[KernelConstants::CORE_NAMESPACES] = [
   'SprykerShop',
   'SprykerEco',
   'Spryker',
   'Generated',
   'ESpirit'
];

The session constant for the same site cookies needs to be set after the $config[KernelConstants::CORE_NAMESPACES]:

Session constant
$config[SessionConstants::YVES_SESSION_COOKIE_SAMESITE] = 'None';

In addition, the domain needs to be added to the whitelist configuration:

Extension of the KernelConstants
$config[KernelConstants::DOMAIN_WHITELIST] = array_merge($trustedHosts, [
    ...
    'demo-spryker.e-spirit.hosting',
]);

Enable SSL in the yaml file deploy.dev.yml:

Activation of SSL
docker:

    ssl:
        enabled: true
        redirect: true

The Spryker modules are then installed using the following commands, which must also be executed in the Spryker project directory in the specified order.

Installation commands
composer require e-spirit/firstspirit-caas
composer require e-spirit/firstspirit-preview
composer require e-spirit/firstspirit-preview-navigation
composer require e-spirit/firstspirit-data-state-writer
composer require e-spirit/firstspirit-data-cleanup
composer require e-spirit/firstspirit-data-import
composer require e-spirit/firstspirit-data-inconsistency-check
composer require e-spirit/firstspirit-data-rest-api
composer require e-spirit/firstspirit-cms-data-connector
composer require e-spirit/firstspirit-cms-data-storage

The commands load the modules from the created repository and install them automatically.

The last step of the installation corresponds to the generation of the transfer objects of the Data Import module and the creation of the database schema contained in the CMS Block Data Connector module:

Generation of the transfer objects and creation of the database schema
vendor/bin/console transfer:generate
vendor/bin/console propel:install

2.1.2. Twig Templates

The FirstSpirit Connect for Spryker Commerce OS module provides different ways to access shop content from Spryker and use it in FirstSpirit. These possibilities are described in the following of this documentation on the basis of the supplied reference project in the form of use cases.

If the sections and page templates contained in the reference project should be used, this requires different Twig templates in Spryker.

The sections correspond to different Spryker molecules, atoms and organisms, which are summarized in the folder FirstSpiritReferenceComponents. This is part of the delivery and is provided in the form of another Spryker module, which is to be installed using the following command:

Installation command
composer require e-spirit/firstspirit-reference-components

In order to make the components provided with the module usable in Spryker, the corresponding path must be specified in Spryker. This is done by adapting the files settings.js and tsconfig.json.

Within the settings.js file in the directory Spryker-directory/frontend an extension of the context is necessary:

Adaptation of the settings.js file
[...]

dirs: [
   join(globalSettings.context, paths.core),
   join(globalSettings.context, './vendor/e-spirit'),
   join(globalSettings.context, paths.eco),
   join(globalSettings.context, paths.project)
],

An additional include must be added to the tsconfig.json file in the Spryker directory:

Adaptation of the tsconfig.json file
[...]

"include": [
   "./vendor/spryker-shop/**/*",
   "./vendor/spryker-eco/**/*",
   "./src/Pyz/Yves/**/*",
   "./vendor/e-spirit/**/*"
],

The generic Twig-Template fs_molecule_block.twig controls the output of the supplied molecules. It is a part of the zip file b2c-demo-shop-extensions-<VERSION>.zip included in the delivery and must be stored under the path src/Pyz/Shared/CmsBlock/Theme/default/template in Spryker.

The integration of the Twig templates in Spryker takes place via the following command, which finally is to be executed in the Spryker project directory.

Integration of the Twig templates
npm run yves

The FirstSpirit templates allow the extension of existing static pages, product, and category pages and the creation of dynamic content pages or a magazine. Therefore, the reference project contains the page templates homepage, productpage, categorypage, contentpage, and magazine_detail_page. In addition, the following Twig templates are required in Spryker:

The Twig template home.twig, which can be found under the path src/Pyz/Yves/HomePage/Theme/default/views/home, needs some changes. These are explained in the description of the static pages. The same applies to the Twig templates pdp.twig, page-layout-catalog.twig and catalog-with-cms-slot.twig which are necessary to edit product and category pages.

They are available in Spryker under the following paths:

  • src/Pyz/Yves/ProductDetailPage/Theme/default/views/pdp

  • src/Pyz/Yves/CatalogPage/Theme/default/templates/page-layout-catalog

  • src/Pyz/Yves/CatalogPage/Theme/default/views/catalog-with-cms-slot

The remaining Twig templates are part of the zip file included in the delivery and have to be stored under the following paths:

  • Product pages: src/Pyz/Yves/CmsBlockWidget/Theme/default/components/molecules/product-cms-block

  • Dynamic Content Pages: src/Pyz/Shared/Cms/Theme/default/templates/fs-content-page

2.1.3. Slots import

In both Spryker and FirstSpirit, pages are based on a structure of different components. In Spryker, these include the CMS slots, which are represented in FirstSpirit by the content areas of a page template. The slots bind CMS blocks, each of which corresponds to a FirstSpirit section.

The Twig template home.twig represents a static page on the Spryker side and is mapped by the page template homepage in the provided reference project. It contains the content areas fs_slt_2 and fs_slt_3, for which the corresponding slots must exist in Spryker.

The slots are imported using the file cms_slot.csv, which is stored in Spryker in the directory /data/import/common/common. It must be replaced by the file of the same name from the delivery, which additionally contains the following two lines:

Import file cms_slot.csv
template_path,slot_key,content_provider,name,description,is_active
[...]
@HomePage/views/home/home.twig,fs-slt-2,FirstSpiritCmsSlotBlock,Banner,Banner content section for the homepage.,1
@HomePage/views/home/home.twig,fs-slt-3,FirstSpiritCmsSlotBlock,Homepage Content,Homepage main content section.,1

Afterwards, the new slots are taken over via the following command, which has to be executed in the Spryker project directory.

Import command
vendor/bin/console data:import cms-slot

2.1.4. Extension of the configuration

In FirstSpirit, the creation and editing of editorial content takes place in ContentCreator. The storefront is embedded in it using the Omnichannel Manager.

To enable the display of the shop contents from Spryker in ContentCreator, an extension of the environment-specific configuration is necessary. To do this, a token must be defined in the file config_default-[environment].php in the directory config/Shared as well as the host and port of the FirstSpirit start page. Furthermore, the operation of CaaS requires the specification of some parameters. These parameters are necessary both for production operation and for the use of the CaaS in the preview.

Extension of the configuration
// ----------- FirstSpirit Preview Configurations
$config[FirstSpiritPreviewConstants::FIRSTSPIRIT_PREVIEW_AUTHENTICATION_INIT_TOKEN] = '<ADD TOKEN>';
$config[FirstSpiritPreviewConstants::FIRSTSPIRIT_PREVIEW_WEB_HOST] = '<ADD FS PREVIEW HOST>';
$config[FirstSpiritPreviewConstants::FIRSTSPIRIT_PREVIEW_CAAS_CONTENT_PAGE_COLLECTION] = '<CAAS COLLECTION>';
$config[FirstSpiritPreviewConstants::FIRSTSPIRIT_PREVIEW_CAAS_CATEGORY_PAGE_COLLECTION] = '<CAAS COLLECTION>';
$config[FirstSpiritPreviewConstants::FIRSTSPIRIT_PREVIEW_CAAS_PRODUCT_PAGE_COLLECTION] = '<CAAS COLLECTION>';
$config[FirstSpiritPreviewConstants::FIRSTSPIRIT_PREVIEW_DISPLAY_BLOCK_RENDER_ERRORS] = true;

// ----------- FirstSpirit Data Import Configurations
$config[FirstSpiritDataImportConstants::FIRSTSPIRIT_DATA_IMPORT_BLOCK_DATA_CAAS_PATH] = '/_aggrs/blocks';
$config[FirstSpiritDataImportConstants::FIRSTSPIRIT_CAAS_REQUEST_PAGESIZE] = <VALUE>;
$config[FirstSpiritDataImportConstants::FIRSTSPIRIT_DATA_IMPORT_URL_TEMPLATE] = '/{locale}/{url}';

// ----------- FirstSpirit CaaS Configurations
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_HOSTNAME_PREVIEW] = '<ADD CAAS HOST>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_PORT_PREVIEW] = '<ADD CAAS PORT>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_SCHEME_PREVIEW] = '<ADD CAAS SCHEME>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_APIKEY_PREVIEW] = '<ADD APIKEY>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_DATABASE_PREVIEW] = '<ADD CAAS DATABASE>';

$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_HOSTNAME_ONLINE] = '<ADD CAAS HOST>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_PORT_ONLINE] = '<ADD CAAS PORT>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_SCHEME_ONLINE] = '<ADD CAAS SCHEME>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_APIKEY_ONLINE] = '<ADD APIKEY>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_DATABASE_ONLINE] = '<ADD CAAS DATABASE>';

The config_default-[environment]_[storename].php files can be adjusted in order to support a different configuration for different stores:

Extension of the configuration for a specific store
<?php

use ESpirit\Shared\FirstSpiritCaaS\FirstSpiritCaaSConstants;
use ESpirit\Shared\FirstSpiritDataImport\FirstSpiritDataImportConstants;

$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_DATABASE_PREVIEW] = '<ADD CAAS DATABASE FOR SPECIFIC STORE>';
$config[FirstSpiritCaaSConstants::FIRSTSPIRIT_CAAS_DATABASE_ONLINE] = '<ADD CAAS DATABASE FOR SPECIFIC STORE>';

$config[FirstSpiritDataImportConstants::FIRSTSPIRIT_DATA_IMPORT_URL_TEMPLATE] = '/{locale}/{store}/{url}';

The configuration FirstSpiritDataImportConstants::FIRSTSPIRIT_DATA_IMPORT_URL_TEMPLATE determines how URLs of content pages from FirstSpirit are generated in Spryker. The default value is //, with locale being the language of the content and url being the URL defined by the editor in FirstSpirit. Additionally the variable store can be used, which resolves to the abbreviation of the used store. The changes to the URL structure have to be mirrored within the output channel of the template for content pages.

FirstSpirit Preview Configurations

The token is used for authentication and must be specified in the ContentCreator settings as a query parameter of the external preview URL during the configuration in FirstSpirit. It can be defined arbitrarily.

Specifying the fully qualified host of the FirstSpirit home page (e.g. http://localhost:8000) is a requirement of the FsCookieEventDispatcherPlugin to be installed. It uses the information to enable previewing in ContentCreator.

The mentions of the CaaS collections for content, category and product pages enable access to the editorial content stored in the Preview CaaS and its display in the ContentCreator.

The error parameter enables the display of error outputs in the page. Using the value true therefore only makes sense for Dev and QA instances.

FirstSpirit Data Import Configurations

The path is used to query and persist the block data stored in the Online CaaS. It is required by the FsDataImportConsole command, which triggers the importer in the context of a FirstSpirit deployment.

With the help of the pagesize the number of documents that the CaaS returns on a request can be defined. The parameter is optional and may have a value from 1 to 100. By default, the maximum value of 100 is defined for it.

FirstSpirit CaaS Configurations

The CaaS host, port, and scheme to use (http or https) allow Spryker to connect to the CaaS. Access to the data stored in it requires a valid API Key that needs read access to the corresponding project. The name of the project stored in CaaS must be specified in the configuration file as the value of the database parameter.

For more information on using the CaaS, see the Content as a Service Documentation.

To ensure that the used classes FirstSpiritPreviewConstants, FirstSpiritDataImportConstants and FirstSpiritCaaSConstants can be found, they have to be included in the configuration file via use statements:

Integration of the classes
use ESpirit\Shared\FirstSpiritPreview\FirstSpiritPreviewConstants;
use ESpirit\Shared\FirstSpiritDataImport\FirstSpiritDataImportConstants;
use ESpirit\Shared\FirstSpiritCaaS\FirstSpiritCaaSConstants;

2.1.5. Extension of the basic template

The Omnichannel Manager enables the display and editing of external content in ContentCreator, for which it requires some preview-specific data attributes. In Spryker, it also requires a preview-specific JavaScript file, which is included by the FirstSpiritPreview module.

The JavaScript file must be added to the Twig template, which is responsible for the basic layout of all shop pages. By default this is the page-blank.twig template, which is stored under the path src/Pyz/Yves/ShopUi/Theme/default/templates/page-blank. In the same template, the data attributes must also be determined by the Twig function fsIncludeDataAttributes.

The Twig template is adapted by calling the Twig functions fsIncludeDataAttributes and fsIncludeOcmScript and by extending the template data. The Twig function fsIncludeOcmScript can be passed the URL to a local copy of the JavaScript file as an optional parameter. By default, it retrieves the file from the CDN.

Adaptation of the Twig template
{% extends template('page-blank', '@SprykerShop:ShopUi') %}

{% block attributes %}
    {{ fsIncludeDataAttributes() }}
{% endblock %}

{% block template %}
   {% set isNewFrontendBuildSupported = true %}

[...]

{% block footerScripts %}
   {% include molecule('check-touch') only %}
      {{ fsIncludeOcmScript() }}
   {{ parent() }}
{% endblock %}

2.2. Adjustments

Further adjustments are necessary, which are described in the following paragraph.

All CMS related types need to be added to the DataImportConfig file in the src/Pyz/Zed/DataImport directory to avoid issues with the request aggregation loop.

Extension of the DataImportConfig
[...]
public const IMPORT_TYPE_CMS_BLOCK_STORE = 'cms-block-store';
public const IMPORT_TYPE_CMS_BLOCK_CATEGORY_POSITION = 'cms-block-category-position';
public const IMPORT_TYPE_DISCOUNT = 'discount';
[...]

    $customImportTypes = [
        StockAddressDataImportConfig::IMPORT_TYPE_STOCK_ADDRESS,
        static::IMPORT_TYPE_CMS_PAGE,
        static::IMPORT_TYPE_CMS_BLOCK,
        static::IMPORT_TYPE_NAVIGATION,
    ];

[...]

Disable the append prefix for CMS page URLs by adjusting the CmsConfig file in the src/Pyz/Zed/Cms directory.

Adjustment of the CmsConfig
[...]

public function appendPrefixToCmsPageUrl(): bool
{
    return false;
}

[...]

Create a PHP file called CmsSlotGuiCommunicationFactory in the to be created src/Pyz/Zed/CmsSlotGui/Communication directory and another PHP file called PyzSlotTable in the to be created src/Pyz/Zed/CmsSlotGui/Communication/Table directory with the following content to override the backoffice table.

CmsSlotGuiCommunicationFactory
<?php

/**
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
 */

namespace Pyz\Zed\CmsSlotGui\Communication;

use Pyz\Zed\CmsSlotGui\Communication\Table\PyzSlotTable;
use Spryker\Zed\CmsSlotGui\Communication\Table\SlotTable;
use Spryker\Zed\CmsSlotGui\Communication\CmsSlotGuiCommunicationFactory as SprykerCmsSlotGuiCommunicationFactory;

class CmsSlotGuiCommunicationFactory extends SprykerCmsSlotGuiCommunicationFactory
{
    /**
     * @param int|null $idSlotTemplate
     *
     * @return \Spryker\Zed\CmsSlotGui\Communication\Table\SlotTable
     */
    public function createSlotListTable(?int $idSlotTemplate = null): SlotTable
    {
        return new PyzSlotTable(
            $this->getCmsSlotQuery(),
            $this->getTranslatorFacade(),
            $idSlotTemplate
        );
    }
}
PyzSlotTable
<?php

/**
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
 */

namespace Pyz\Zed\CmsSlotGui\Communication\Table;

use Spryker\Zed\CmsSlotGui\Communication\Table\SlotTable as SprykerSlotTable;

class PyzSlotTable extends SprykerSlotTable
{
    /**
     * @return bool
     */
    protected function isContentProviderColumnVisible(): bool
    {
        if ($this->contentProviderTypesNumber === null) {
            $this->contentProviderTypesNumber = (clone $this->cmsSlotQuery)
                ->select(static::COL_CONTENT_PROVIDER)
                ->distinct()
                ->count();
        }

        return $this->contentProviderTypesNumber > 1;
    }
}

Due to issues with the bar code

PyzSlotTable
<?php

/**
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
 */

namespace Pyz\Zed\CmsSlotGui\Communication\Table;

use Spryker\Zed\CmsSlotGui\Communication\Table\SlotTable as SprykerSlotTable;

class PyzSlotTable extends SprykerSlotTable
{
    /**
     * @return bool
     */
    protected function isContentProviderColumnVisible(): bool
    {
        if ($this->contentProviderTypesNumber === null) {
            $this->contentProviderTypesNumber = (clone $this->cmsSlotQuery)
                ->select(static::COL_CONTENT_PROVIDER)
                ->distinct()
                ->count();
        }

        return $this->contentProviderTypesNumber > 1;
    }
}

2.3. Plugins

In addition to the installation of the Spryker modules, the following plugins must be included:

  • FsCookieEventDispatcherPlugin

  • FsTwigExtensionPlugin

  • FirstSpiritPreviewSlotBlockWidgetCmsSlotContentPlugin

  • FirstSpiritDataImportsResourceRoutePlugin

  • FirstSpiritDataCleanupsResourceRoutePlugin

  • FirstSpiritCmsPagesResourceRoutePlugin

FsCookieEventDispatcherPlugin and FsTwigExtensionPlugin

The FsCookieEventDispatcherPlugin uses the hosts of the FirstSpirit home page defined in the configuration file config_default-[environment].php to enable the storefront to be displayed in ContentCreator. Therefore, it checks if storefront queries contain the Token also defined in the configuration file or if a logged in user exists. If one of the two options applies, the plugin sets the Content Security Policy Header and extends the Cookie header.

The FsTwigExtensionPlugin enables the maintenance of CMS placeholders and CMS blocks in FirstSpirit. Therefore, it accesses information provided by the CmsController to be installed. The information is needed for mapping between the data stored in FirstSpirit and in Spryker.

The integration of both plugins is done with the following code, which has to be added to the EventDispatcherDependencyProvider in the directory src/Pyz/Yves/EventDispatcher/EventDispatcherDependencyProvider and to the ShopApplicationDependencyProvider in the directory src/Pyz/Yves/ShopApplication.

Extension of the EventDispatcherDependencyProvider
use ESpirit\Yves\FirstSpiritPreview\Plugin\EventDispatcher\FsCookieEventDispatcherPlugin;

[...]

protected function getEventDispatcherPlugins(): array
{
   return [
      [...]
      new FsCookieEventDispatcherPlugin(),
   ];
}
Extension of the ShopApplicationDependencyProvider
use ESpirit\Yves\FirstSpiritPreview\Plugin\FsTwigExtensionPlugin;
use ESpirit\Yves\FirstSpiritPreview\Plugin\HeadersSecurityExtensionPlugin;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritCategoryLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritContentLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritExternalLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritProductFlyoutWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritProductLinkWidget;

[...]

protected function getGlobalWidgets(): array
{
   return [
      [...]
      FirstSpiritCategoryLinkWidget::class,
      FirstSpiritProductLinkWidget::class,
      FirstSpiritContentLinkWidget::class,
      FirstSpiritExternalLinkWidget::class,
      FirstSpiritProductFlyoutWidget::class,
   ];
}

[...]

protected function getApplicationPlugins(): array
{
   return [
      [...]
      new FsTwigExtensionPlugin()
   ];
}
FirstSpiritPreviewSlotBlockWidgetCmsSlotContentPlugin

The FirstSpiritPreviewSlotBlockWidgetCmsSlotContentPlugin enables the output of all blocks contained in a slot. In contrast to the CmsSlotBlockWidget included in Spryker, which is limited to the output of the Spryker data, the plugin only considers the information from the CaaS. The inclusion of the plugin requires an extension of the ShopCmsSlotDependencyProvider in the src/Pyz/Yves/ShopCmsSlot directory.

Extension of the ShopCmsSlotDependencyProvider
<?php

namespace Pyz\Yves\ShopCmsSlot;

use ESpirit\Yves\FirstSpiritPreviewSlotBlockWidget\FirstSpiritPreviewSlotBlockWidgetConfig;
use ESpirit\Yves\FirstSpiritPreviewSlotBlockWidget\Plugin\FirstSpiritPreviewSlotBlockWidget\
   FirstSpiritPreviewSlotBlockWidgetCmsSlotContentPlugin;
[...]

class ShopCmsSlotDependencyProvider extends SprykerShopShopCmsSlotDependencyProvider
{
   protected function getCmsSlotContentPlugins(): array
   {
      return [
         CmsSlotBlockConfig::CMS_SLOT_CONTENT_PROVIDER_TYPE
            => new CmsSlotBlockWidgetCmsSlotContentPlugin(),
         FirstSpiritPreviewSlotBlockWidgetConfig::FS_CMS_SLOT_CONTENT_PROVIDER_TYPE
            => new FirstSpiritPreviewSlotBlockWidgetCmsSlotContentPlugin(),
      ];
   }
}
FirstSpiritDataImportsResourceRoutePlugin and FirstSpiritDataCleanupsResourceRoutePlugin

The FirstSpiritDataImportsResourceRoutePlugin and FirstSpiritDataCleanupsResourceRoutePlugin each provide an API endpoint. This endpoint allows the FirstSpirit deployment to address the importer.

The FirstSpiritDataImportsResourceRoutePlugin controls the transfer of editorial content from the Online CaaS to Spryker and its persistence there. In contrast, the FirstSpiritDataCleanupsResourceRoutePlugin is used to find and delete obsolete data in Spryker.

Both plugins and the following FirstSpiritCmsPagesResourceRoutePlugin must be added to the GlueApplicationDependencyProvider.

FirstSpiritCmsPagesResourceRoutePlugin

If a new page is created within the ContentCreator, the creation of a CMS page in Spryker is required in parallel. Because the ContentCreator displays the storefront, the newly created page would otherwise not be visible in the preview. Instead, it would already be available in the live state if it were created in Spryker with a deployment.

The FirstSpiritCmsPagesResourceRoutePlugin is used to create the new page. This plugin and the other two ResourceRoutePlugins described above must be added to the GlueApplicationDependencyProvider in the src/Pyz/Glue/GlueApplication directory:

Extension of the GlueApplicationDependencyProviders
use ESpirit\Glue\FirstSpiritDataRestApi\Plugin\FirstSpiritDataImportsResourceRoutePlugin;
use ESpirit\Glue\FirstSpiritDataRestApi\Plugin\FirstSpiritDataCleanupsResourceRoutePlugin;
use ESpirit\Glue\FirstSpiritDataRestApi\Plugin\FirstSpiritCmsPagesResourceRoutePlugin;

[...]

protected function getResourceRoutePlugins(): array
{
   return [
      [...]
      new FirstSpiritDataImportsResourceRoutePlugin(),
      new FirstSpiritDataCleanupsResourceRoutePlugin(),
      new FirstSpiritCmsPagesResourceRoutePlugin(),
      new FirstSpiritDataStateWriterResourceRoutePlugin(),
      new FirstSpiritDataInconsistencyCheckResourceRoutePlugin(),
   ];
}

2.4. Widgets

In addition to the Spryker modules and the plugins the delivery contains the following widgets. They enable the expandability of the navigations contained in the shop, as well as the usage of the DOM references and the Shoppable Image contained in the reference project. Only if these elements are used a registration of the widgets is required.

  • FirstSpiritPreviewContentNavigationWidget

  • FirstSpiritCategoryLinkWidget

  • FirstSpiritProductLinkWidget

  • FirstSpiritContentLinkWidget

  • FirstSpiritExternalLinkWidget

  • FirstSpiritProductFlyoutWidget

FirstSpiritPreviewContentNavigationWidget
The FirstSpiritPreviewContentNavigationWidget creates the possibility to maintain the navigations contained in the store with FirstSpirit and to add further menu items. It replaces the existing ContentNavigationWidget in Spryker and provides various attributes required for displaying the corresponding navigation in the preview. Therefore the existing ContentNavigationWidget, which is stored under the path /src/Pyz/Yves in Spryker, has to be replaced by the FirstSpiritPreviewContentNavigationWidget included in the delivery. Finally, the use of the FirstSpiritPreviewContentNavigationWidget requires a registration. This is done by modifying the TwigDependencyProvider in the directory /src/Pyz/Yves/Twig/, in which the ContentNavigationTwigPlugin has to be replaced by the FirstSpiritPreviewNavigationWidgetTwigPlugin.

Adaptation of the TwigDependencyProviders
<?php

namespace Pyz\Yves\Twig;

use ESpirit\Yves\FirstSpiritPreviewNavigationWidget\Plugin\Twig\FirstSpiritPreviewNavigationWidgetTwigPlugin;
[...]

class TwigDependencyProvider extends SprykerTwigDependencyProvider
{
   [...]

   protected function getTwigPlugins(): array
   {
      return [
         [...]
         // new ContentNavigationTwigPlugin(),
         new FirstSpiritPreviewNavigationWidgetTwigPlugin(),
      ];
   }

   [...]
}

FirstSpiritCategoryLinkWidget und FirstSpiritProductLinkWidget
The FirstSpiritCategoryLinkWidget and the FirstSpiritProductLinkWidget are used to display editorial DOM links to category or product detail pages. In both cases, the widget determines the corresponding category or product using a unique identifier and passes the result to the Twig template that controls the display of the respective link in Spryker. Each of the two widgets references its corresponding Twig template. The Twig templates are provided by the previously installed FirstSpiritPreview module.

FirstSpiritContentLinkWidget und FirstSpiritExternalLinkWidget
The FirstSpiritContentLinkWidget and the FirstSpiritExternalLinkWidget enable DOM links to editorial content, whereby the type of content differs in each case. The FirstSpiritExternalLinkWidget only allows the referencing of external content. In contrast, the FirstSpiritContentLinkWidget is used to display internal DOM links to dynamic content pages or magazine articles maintained within the FirstSpirit project. An important aspect is that the target URL of the internal links to dynamic content pages depends on the output: While the link in the live state refers to the published CMS page, the corresponding preview URL must be used in the preview. In both cases, the widget controls the determination of the required URL.

FirstSpiritProductFlyoutWidgets
The registration of the FirstSpiritProductFlyoutWidget is only required if the Shoppable Image provided with the delivery is used in the project. The widget controls the display of flyouts for the products linked to the Shoppable Image.

Registration
The four link widgets and the flyout widget are registered using the following code, which must be added to the ShopApplicationDependencyProvider in the folder src/Pyz/Yves/ShopApplication.

Extension of the ShopApplicationDependencyProvider
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritCategoryLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritProductLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritContentLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritExternalLinkWidget;
use ESpirit\Yves\FirstSpiritPreview\Widget\FirstSpiritProductFlyoutWidget;

[...]

protected function getGlobalWidgets(): array
{
   return [
      [...]
      FirstSpiritCategoryLinkWidget::class,
      FirstSpiritProductLinkWidget::class,
      FirstSpiritContentLinkWidget::class,
      FirstSpiritExternalLinkWidget::class,
      FirstSpiritProductFlyoutWidget::class
   ];
}

2.5. Controllers

The previously installed CmsBlockTwigFunctionPlugin allows mapping between the content stored in FirstSpirit and in Spryker. For this, it assumes that the view data of a page contains its ID and type. The controllers of Spryker do not provide this information by default. For this reason, the following controllers must be overwritten both for content and category pages and in the case of the homepage, which corresponds to a static page:

  • IndexController

  • CatalogController

  • CmsController

  • PreviewController

The overwriting is done by creating the corresponding classes, which are all part of the zip file b2c-demo-shop-extensions-<VERSION>.zip included in the delivery. They must be copied into the directory Controller to be created below src/Pyz/Yves/HomePage|CatalogPage|CmsPage.

If the classes already exist under the path specified for them, the controllers are already overwritten project-specifically. In this case, it need to be ensured that the view parameters are extended accordingly.

In contrast to the other pages, the product pages require the adaptation of the ProductController which already exists in Spryker. It is contained in the directory src/Pyz/Yves/ProductDetailPage/Controller/ProductController and must be extended by the definition of the page type and the Product SKU. The following code snippet shows the changes to be made:

Extension of the ProductController
<?php
namespace Pyz\Yves\ProductDetailPage\Controller;

[...]
use ESpirit\Shared\FirstSpiritPreview\FirstSpiritPreviewConstants;

class ProductController extends SprykerShopProductController
{
   protected function executeDetailAction(array $productData, Request $request): array
   {
      [...]

       $viewData['cart'] = $quoteTransfer;
       $viewData[FirstSpiritPreviewConstants::VIEW_DATA_PAGE_TYPE_KEY] = FirstSpiritPreviewConstants::PRODUCT_PAGE_TYPE;
       $viewData[FirstSpiritPreviewConstants::VIEW_DATA_PAGE_ID_KEY] = $productData['sku'];

       return $viewData;
   }
}

In addition to the controllers mentioned above, the delivery includes further render controllers which enable individual CMS elements to be updated in the preview. They are addressed by the FirstSpiritPreviewRoutePlugin which initially does not exist in Spryker and therefore requires registration in the RouterDependencyProvider file in the Pyz/Yves/Router directory. Within the file, the method getRouteProvider has to be extended as follows.

Extension of the method getRouteProvider
use ESpirit\Yves\FirstSpiritPreview\Plugin\Route\FirstSpiritPreviewRoutePlugin;

[...]
protected function getRouteProvider(): array
{
   return [
      [...]
      new FirstSpiritPreviewRoutePlugin()
   ];
}

An adjustment of the RouterConfig file in the src/Pyz/Zed/Router directory is required to enable gateway routing.

RouterConfig
public function getControllerDirectories(): array
{
   [...]
   $controllerDirectories[] = sprintf('%s/e-spirit/*/src/*/Zed/*/Communication/Controller/', APPLICATION_VENDOR_DIR);

   return array_filter($controllerDirectories, 'glob');
}

2.6. Main navigation

By default, the Spryker B2C Demo Shop contains different navigations, which can be created and edited in Spryker. The integration realised with the FirstSpirit Connect for Spryker Commerce OS module offers the possibility to maintain these navigations with FirstSpirit and to add further menu items.

Within the reference project the functionality is implemented exemplarily for the main navigation. Its usage requires the already described referencing of the FirstSpiritPreviewContentNavigationWidgets, the creation of a placeholder, and the addition of a preview ID.

The placeholder to be created corresponds to another menu item. This is required to display the navigation in the preview and must be added to the navigation-header.twig file in the src/Pyz/Yves/ShopUi/Theme/default/components/molecules/navigation-header directory:

Adjustment of the navigation-header.twig file
[..]
<ul class="menu {{ menuClass }} grid grid--center grid--no-wrap">
   {% for node in data.nodes %}
      [..]
   {% endfor %}

   {% if isFsPreview() %}
      <li class="fs-navigation-extension"
         style="padding-left: 1rem; transform: translate(0,15% );">
      </li>
   {% endif %}
</ul>
[..]

In addition, a preview ID is required to display the main navigation in the preview. This preview ID is to be defined in the navigation-multilevel-node.twig file in the directory src/Pyz/Yves/ShopUi/Theme/default/components/molecules/navigation-multilevel-node:

Adjustment of the navigation-multilevel-node.twig file
[...]

{% block attributes %}
   {% if isFsPreview() and data.node.previewId is defined and data.node.previewId is not empty %}
      data-preview-id="{{ data.node.previewId }}" style="min-height: unset;"
   {% endif %}
{% endblock %}

Add the following code to the atom to fix the twig exception in the navigation-list.twig file:

Adjustment of the navigation-list.twig file
{% block body %}
   [...]
   {% import _self as component %}
   {% block url %}
   [...]
{% endblock %}

After the changes have been made, it may be necessary to empty the cache for the twig templates and to rebuild the frontend.

2.7. Query Container

The previously installed <sp_install_module,Cms Block Data Connector module>> creates a Zed table. During the import, the editorial contents determined from the Online CaaS are stored in this table. The CMS blocks are persisted in another table.

Since the CMS blocks do not contain placeholders, they are not published by Spryker by default. For this reason, it is necessary to overwrite the CmsBlockStorageQueryContainer and thus adapt the standard behavior of Spryker for publishing CMS blocks. The query container is overwritten by creating the class CmsBlockStorageQueryContainer.php. It is part of the zip file b2c-demo-shop-extensions-<VERSION>.zip included in the delivery and must be copied into the directory src/Pyz/Zed/CmsBlockStorage/Persistence to be created.

2.8. Technical user

The integration realized with the FirstSpirit Connect for Spryker Commerce OS module only allows FirstSpirit to create and maintain editorial content and to publish it in the form of JSON objects. The integration of these contents and the creation of the preview, however, still takes place in Spryker.

In the preview case, FirstSpirit determines the current view of a page in the storefront and displays it in ContentCreator using the Omnichannel Manager. To integrate the editorial content into the shop, Spryker imports it from the Online CaaS and persists it in its internal data system.

As both the preview and the import process are protected by authentication, a technical user must be specified in the project component of the FirstSpirit Connect for Spryker Commerce OS module. This corresponds to a customer, who in turn is assigned to a Zed user. The assignment can be done in the menu Users Control  User via the button Assign Customers.

For a more detailed description of the steps required for the assignment, see the chapters Assigning Customers and Previewing a Page of the Spryker documentation.

2.9. Import command

The integration of the contents created or maintained in FirstSpirit into the Shop takes place through Spryker. Therefore, Spryker imports the data from the Online CaaS and persists them in its data system. The transfer of the information must take place with each FirstSpirit deployment, which for this reason each time triggers a Spryker sided import command.

The command instructs the importer to import the block data stored in the Online CaaS. To do this, it uses the FirstSpirit Data Import Block CaaS Path stored in the configuration file config_default-[environment].php. Using this path, the importer accesses the CaaS aggregation that is created by the FirstSpirit deployment.

The import commands must be registered in Spryker in the getConsoleCommands. This requires the following extension to the ConsoleDependencyProvider.php file in the src/Pyz/Zed/Console directory:

Erweiterung der getConsoleCommands
use ESpirit\Zed\FirstSpiritDataImport\Communication\Console\FsDataImportConsole;
use ESpirit\Zed\FirstSpiritDataImport\Communication\Console\FsStoreDataImportConsole;

[...]

$commands = [
   // Default commands
   [...]

   new DataImportConsole(DataImportConsole::DEFAULT_NAME . ':' . DataImportConfig::IMPORT_TYPE_CMS_BLOCK_STORE),
   new DataImportConsole(DataImportConsole::DEFAULT_NAME . ':' . DataImportConfig::IMPORT_TYPE_CMS_BLOCK_CATEGORY_POSITION),
   new DataImportConsole(DataImportConsole::DEFAULT_NAME . ':' . DataImportConfig::IMPORT_TYPE_DISCOUNT),

   [...]

   // FirstSpirit Data importers
   new FsDataImportConsole(FsDataImportConsole::DEFAULT_NAME),
   new FsStoreDataImportConsole(FsStoreDataImportConsole::DEFAULT_NAME),
];

To import blocks for categories, the contents of the the DataImportBusinessFactory.php file in the src/Pyz/Zed/DataImport/Business directory have to be adjusted:

Extension of the DataImportBusinessFactory
[...]
use Pyz\Zed\DataImport\Business\Model\CmsBlock\CmsBlockWriterStep;
use Pyz\Zed\DataImport\Business\Model\CmsBlockCategoryPosition\CmsBlockCategoryPositionWriterStep;
use Pyz\Zed\DataImport\Business\Model\CmsBlockStore\CmsBlockStoreWriterStep;
[...]

   public function getDataImporterByType(DataImportConfigurationActionTransfer $dataImportConfigurationActionTransfer): ?DataImporterInterface
   {
      switch ($dataImportConfigurationActionTransfer->getDataEntity()) {
         [...]
         case DataImportConfig::IMPORT_TYPE_CMS_BLOCK_STORE:
               return $this->createCmsBlockStoreImporter($dataImportConfigurationActionTransfer);
         case DataImportConfig::IMPORT_TYPE_CMS_BLOCK_CATEGORY_POSITION:
               return $this->createCmsBlockCategoryPositionImporter($dataImportConfigurationActionTransfer);
         case DataImportConfig::IMPORT_TYPE_DISCOUNT_AMOUNT:
               return $this->createDiscountAmountImporter($dataImportConfigurationActionTransfer);
         [...]
      }
   }


   [...]

   /**
    * @param \Generated\Shared\Transfer\DataImportConfigurationActionTransfer $dataImportConfigurationActionTransfer
    *
    * @return \Spryker\Zed\DataImport\Business\Model\DataImporterInterface|\Spryker\Zed\DataImport\Business\Model\DataSet\DataSetStepBrokerAwareInterface
    */
   protected function createCmsBlockCategoryPositionImporter(DataImportConfigurationActionTransfer $dataImportConfigurationActionTransfer)
      {
      $dataImporter = $this->getCsvDataImporterFromConfig(
      $this->getConfig()->buildImporterConfigurationByDataImportConfigAction($dataImportConfigurationActionTransfer)
      );

      $dataSetStepBroker = $this->createTransactionAwareDataSetStepBroker(CmsBlockCategoryPositionWriterStep::BULK_SIZE);
      $dataSetStepBroker->addStep(new CmsBlockCategoryPositionWriterStep());
      $dataImporter->addDataSetStepBroker($dataSetStepBroker);

      return $dataImporter;
   }

   [...]

2.10. Synchronization

During deployment, the content maintained in FirstSpirit is retrieved from the Online CaaS using the importer. It transfers the data to Spryker and persists them in the backend in its data system. The display of the data in the live state additionally requires its transmission to Yves. The transmission of the data requires the registration of an event subscriber, several queues and a message processor.

See the Publish and Synchronization chapter of the Spryker documentation for more information.

The registration of the EventSubscriber is done with the following code to be added to the EventDependencyProvider in the directory Pyz/Zed/Event.

EventDependencyProvider
use ESpirit\Zed\FirstSpiritCmsDataStorage\Communication\Plugin\Event\Subscriber\FirstSpiritCmsDataStorageEventSubscriber;

[...]

class EventDependencyProvider extends SprykerEventDependencyProvider
{
   [...]

   public function getEventSubscriberCollection()
   {
      $eventSubscriberCollection = parent::getEventSubscriberCollection();

      /**
      * Storage Events
      */
      [...]
      $eventSubscriberCollection->add(new FirstSpiritCmsDataStorageEventSubscriber());

      /**
      * Search Events
      */
      [...]

      return $eventSubscriberCollection;
   }
}

Registration of the queues requires the following adaption of the RabbitMqConfig file in the src/Pyz/Client/RabbitMq directory:

RabbitMqConfig
<?php
namespace Pyz\Client\RabbitMq;

use ESpirit\Shared\FirstSpiritCmsDataStorage\FirstSpiritCmsDataStorageConfig;
[...]

class RabbitMqConfig extends SprykerRabbitMqConfig
{
   [...]

   protected function getPublishQueueConfiguration(): array
   {
      return [
         PublisherConfig::PUBLISH_QUEUE => [
            [...]
         ],
         [...]
         FirstSpiritCmsDataStorageConfig::PUBLISH_CMS_PAGE_DATA_CONNECTOR,
         FirstSpiritCmsDataStorageConfig::PUBLISH_CMS_BLOCK_DATA_CONNECTOR
      ];
   }

   protected function getSynchronizationQueueConfiguration(): array
   {
      return [
         [...]
         FirstSpiritCmsDataStorageConfig::CMS_PAGE_DATA_CONNECTOR_SYNC_STORAGE_QUEUE,
         FirstSpiritCmsDataStorageConfig::CMS_BLOCK_DATA_CONNECTOR_SYNC_STORAGE_QUEUE
      ];
   }

   [...]
}

The trigger plugin needs to be added to the EventBehaviorDependencyProvider file in the src/Pyz/Zed/EventBehavior directory.

EventBehaviorDependencyProvider
<?php

namespace Pyz\Zed\EventBehavior;

use ESpirit\Zed\FirstSpiritCmsDataStorage\Communication\Plugin\Event\FirstSpiritCmsBlockDataEventResourceQueryContainerPlugin;
use ESpirit\Zed\FirstSpiritCmsDataStorage\Communication\Plugin\Event\FirstSpiritCmsDataEventResourceQueryContainerPlugin;
[...]

class EventBehaviorDependencyProvider extends SprykerEventBehaviorDependencyProvider
{
   protected function getEventTriggerResourcePlugins()
   {
      return [
         [...]
         new FirstSpiritCmsDataEventResourceQueryContainerPlugin(),
         new FirstSpiritCmsBlockDataEventResourceQueryContainerPlugin(),
      ];
   }
}

In order to enable trigger events from the GLUE application, the dispatcher plugin needs to be added to the EventDispatcherDependencyProvider in the src/Pyz/Zed/EventDispatcher directory.

EventDispatcherDependencyProvider
[...]

protected function getBackendGatewayEventDispatcherPlugins(): array
{
   return [
      [...]
      new EventBehaviorEventDispatcherPlugin(),
   ];
}

The sync plugin needs to be added to the SynchronizationDependencyProvider file in the src/Pyz/Zed/Synchronization directory.

SynchronizationDependencyProvider
<?php

namespace Pyz\Zed\Synchronization;

use ESpirit\Zed\FirstSpiritCmsDataStorage\Communication\Plugin\Synchronization\FirstSpiritCmsBlockDataSynchronizationDataBulkPlugin;
use ESpirit\Zed\FirstSpiritCmsDataStorage\Communication\Plugin\Synchronization\FirstSpiritCmsDataSynchronizationDataBulkPlugin;
[...]

class SynchronizationDependencyProvider extends SprykerSynchronizationDependencyProvider
{
   protected function getSynchronizationDataPlugins(): array
   {
      return [
         [...]
         new FirstSpiritCmsDataSynchronizationDataBulkPlugin(),
         new FirstSpiritCmsBlockDataSynchronizationDataBulkPlugin(),
      ];
   }

   [...]
}

The registration of the event subscriber necessary for the queues requires an adjustment of the QueueDependencyProvider in the directory Pyz/Zed/Queue:

QueueDependencyProvider
<?php
namespace Pyz\Zed\Queue;

use ESpirit\Shared\FirstSpiritCmsDataStorage\FirstSpiritCmsDataStorageConfig;

[...]

class QueueDependencyProvider extends SprykerDependencyProvider
{
   protected function getProcessorMessagePlugins(Container $container)
   {
      return [
         [...]
         FirstSpiritCmsDataStorageConfig::PUBLISH_CMS_PAGE_DATA_CONNECTOR => new EventQueueMessageProcessorPlugin(),
         FirstSpiritCmsDataStorageConfig::PUBLISH_CMS_BLOCK_DATA_CONNECTOR => new EventQueueMessageProcessorPlugin(),
         FirstSpiritCmsDataStorageConfig::CMS_PAGE_DATA_CONNECTOR_SYNC_STORAGE_QUEUE => new SynchronizationStorageQueueMessageProcessorPlugin(),
         FirstSpiritCmsDataStorageConfig::CMS_BLOCK_DATA_CONNECTOR_SYNC_STORAGE_QUEUE => new SynchronizationStorageQueueMessageProcessorPlugin(),
      ];
   }
}

3. FirstSpirit - Installation and Configuration

Various components must be installed and configured in order to use the functions of the FirstSpirit Connect for Spryker Commerce OS module. The following subchapters explain the steps required.

3.1. Configuration of the project component

A project-specific configuration is necessary for the use of the FirstSpirit Connect for Spryker Commerce OS module. This is done via the project component, which is already added to the supplied reference project.

To use the FirstSpirit Connect for Spryker Commerce OS module, the Content as a Service module must also be configured. The necessary steps are described in the Content as a Service Documentation.

For the media usage, the generation of media into the CaaS has been defined in the CaaS project component. This configuration is explicitly not to be used in productive operation. For productive operation, the media usage must be converted to the use of a CDN.

To configure the project component, open the ServerManager and select Project properties  Project components.

projectcomponents
Figure 4. Server properties - Project components

The main panel displays a list of all existing project components. Select FirstSpirit Connect for Spryker Commerce OS for FirstSpirit Commerce OS Project Configuration and open the corresponding configuration dialog via Configure.

The dialog is divided into the tabs General, Preview, Publication, and YouTube:

General

Within the tab General, the Storefront Base URL and the Glue-API Base URL have to be specified. They have to be assigned to the FirstSpirit server to connect to Spryker and retrieve data.

It is also possible to specify user-defined values for the connection timeout and the socket timeout. If no values are specified, the default values of 60 seconds for the socket timeout and 10 seconds for the connection timeout are used for communication with the glue API.

pcomp general
Figure 5. Project component - General

Preview

Within the Preview tab, the login path is required first. The corresponding field contains the default value /firstspirit_login_check, which can be adjusted if necessary.

Furthermore, the login data of a technical user must be entered in this tab. This must correspond to the Customer defined during the steps to be performed on the Spryker side. In Spryker, it is used to query the storefront, which is included in the ContentCreator using the Omnichannel Manager.

pcomp preview
Figure 6. Project component - Preview

Publication

The Publication tab contains a list of all groups and full deployment schedules available in the project. It allows the selected deployment schedule to be provided in the Actions menu in the ContentCreators for the groups selected in the tab. The Actions menu contains the Publication menu item for executing the schedule. For groups that are not allowed to execute the schedule, the entry is visible but disabled.

Within the supplied reference project, the ChiefEditors can be enabled to execute the full deployment in this way, for example.

Furthermore, the release and the delete workflow use the selected schedule if the publication of the entire project should be triggered additionally to the release or deletion of a page.

Server and project administrators are allowed to execute the full deployment by default, regardless of the configuration within the tab.

pcomp deployment
Figure 7. Project component - Publication

YouTube

The reference project provided contains a Shoppable video paragraph. It allows to display and reference products at defined times in a YouTube video. The usage of the paragraph requires a Google API Key, which has to be entered here.

In addition, one or more Channel Ids can be specified, separated by commas. As soon as the field contains an Id, the video selection will show a dropdown. This allows to reference a YouTube video from a specific channel.

pcomp youtube
Figure 8. Projekt-Komponente - YouTube

3.2. Adding the web component

To prepare the ContentCreator, a web component is needed, which is already added to the reference project. By default it is installed globally in the ServerManager in the area Server Properties  Web applications. In this case, all installation or configuration steps required for the web component have already been performed by the Crownpeak Technology GmbH.

Alternatively, you may install the web component in the Project properties. Only in this case it still needs to be installed on an active web server. Therefore, open the ServerManager and select Project properties  Web components.

Within the main panel there are several tabs, each with a list of existing web components. This list contains the FirstSpirit Connect for Spryker Commerce OS for Spryker Commerce OS Web App for the Preview and ContentCreator. The ContentCreator tab also contains the FirstSpirit ThirdPartyPreview WebApp and optionally the BasicWorkflows_ContentCreator_Library (see figure Web components in the project properties). The web component of the BasicWorkflows is only required when using the workflows.

In both tabs, select a web server via the selection box and start the installation with the Install button. After the successful installation, a dialog opens, in which the activation of the web server is to be confirmed.

For detailed information about adding web components, see the FirstSpirit Documentation for Administrators.

webcomponents
Figure 9. Web components in the project properties

3.3. Definition of the external preview URL

By using the Omnichannel Manager, the ContentCreator displays external content from Spryker to which the Omnichannel Manager needs access. The preview URL of the Spryker storefront must therefore be specified in the ContentCreator properties. Since the specification is always project-specific, there is no default configuration for it within the reference project.

Open the Project properties  ContentCreator area within the ServerManager and enter the URL of the storefront with the following syntax in the External preview URL field:

https://[STOREFRONT_HOST_DOMAIN]/en/?firstSpiritPreview=[PREVIEW_INIT_TOKEN]

The token to be specified for the firstSpiritPreview query parameter must match the authentication token defined during the configuration on Spryker side. It is stored in the file config_default-[environment].php in the directory config/Shared.

previewurl
Figure 10. External Preview URL

3.4. Conversion rule

The data to be transferred to CaaS is defined in the template set still to be created. The use of certain special characters could lead to invalid JSON objects. A conversion rule must therefore be created in order to prevent potential errors.

It is advisable to create a new conversion rule instead of editing an existing rule. To do this, create a text file in your file system and add the following content to this file:

Conversion rule
0x22="\""
[convert]
0x3c="&lt;"
0x3e="&gt;"
0x22="&#34;"
0x26="&amp;"
0x27="&#39;"

Then open ServerManager and select Server properties  Conversion rules. A list of the existing conversion rules is displayed in the main panel. After clicking Add, select the text file created earlier from your file system and click Open to confirm your selection. The new rule is then added to the list in the main panel and is to be assigned to the template set to be created.

More information is available in the FirstSpirit Documentation for Administrators.

3.5. Template set

In addition to the existing template sets of a project, another XML channel is required. This must be created manually for empty projects, but is already contained within the supplied reference project FirstSpirit Connect for Spryker Commerce OS Reference Project.

The template set was created in the ServerManager under Project properties  Template sets and has the following configuration:

templateset
Figure 11. Template set

In the selection box of the same name, the conversion rule created previously must be selected.

The template set is activated and therefore available in the project. It is used to define the contents to be transmitted, which are summarized in messages during generation and transmitted to the CaaS.

3.6. Resolutions

The reference project FirstSpirit Connect for Spryker Commerce OS Reference Project has different sections, which can be used to include images on the various pages. The editor should be able to cut the images to a certain detail. This functionality is activated by specifying a resolution.

For the reference project the resolutions CONTENT_IMAGE, BANNER_IMAGE and MAGAZINE_ARTICLE_BANNER are required. They are already created within the ServerManagers in the Project properties  Resolutions area and specified at the corresponding places.

resolutions
Figure 12. Resolutions

3.7. Generation schedule - full generation

By default, releasing content does not update the Online CaaS and does not transfer data to Spryker. Instead, it only includes the FirstSpirit release process, which can be mapped using the BasicWorkflows release workflow, for example. An update of the Online CaaS as well as a data transfer to Spryker takes place only in the context of a deployment.

The deployment of released editorial content is executed via a CaaS schedule. Therefore the reference project has the schedule Spryker Deployment, which is created within the ServerManager in the Project properties  Schedule management area. It can be started using the Publication action available in ContentCreator and contains the following actions:

deploy actions
Figure 13. Actions of the full generation

The schedule executes a full generation and transfers the data to the Online CaaS, which provides it to Spryker for import. The actions Initialize CaaS Generation, CaaS Generate, CaaS Cleanup and Finalize CaaS Generation are used to fill the Online CaaS. They are described in the Content as a Service-Dokumentation.

In case of an error, the action Send Result Mail sends an e-mail with all relevant information to a recipient to be defined. The recipient’s e-mail address must be specified in the field e-mail distribution list in the properties of the schedule.

In addition, the ChiefEditors group within the reference project must be enabled to execute a full generation. To do so, the group must be allowed to execute the Interactive execution in the properties of the schedule. This setting has already been made in the schedule supplied.

deploy groups
Figure 14. Properties of the schedule

3.7.1. Execute Publish Media to CDN

Unlike the content created with FirstSpirit, the released media is not transferred to the CaaS, but to the CDN provided by the Crownpeak Technology GmbH. Therefore the schedule contains the script action Execute Publish Media to CDN.

Execute Publish Media to CDN
import de.espirit.firstspirit.access.AdminService;

String SCHEDULER_NAME = "Media Deployment";

connection = context.getConnection();
adminService = connection.getService(AdminService.class);

scheduleStorage = adminService.getScheduleStorage();
scheduleEntry = scheduleStorage.getScheduleEntry(context.getProject(), SCHEDULER_NAME);

if(scheduleEntry != null) {
   control = scheduleEntry.execute();
   control.awaitTermination();
   isSuccessful =
      control.state.state.equals(de.espirit.firstspirit.access.schedule.RunState.SUCCESS);
   if(!isSuccessful){
      context.logWarning(SCHEDULER_NAME + " Completed with errors!");
   }
   context.logInfo("Schedule Entry executed...");
} else {
   context.logError("Could not find schedule entry with name" + SCHEDULER_NAME);
}

The script triggers the media generation schedule, which is also included in the supplied reference project. The script contains the variable SCHEDULER_NAME, which has the name of the schedule as its value. If the name changes, it must also be adjusted accordingly here.

3.7.2. Setup CaaS Blocks Aggregations

By default, CaaS stores and delivers the content transferred from FirstSpirit page-based. However, the processing and persistence of editorial content in Spryker is done on the basis of CMS blocks, each corresponding to a FirstSpirit section. In order to resolve this discrepancy, the data stored in the Online CaaS must be prepared accordingly, before importing. For this reason, an aggregation must only be added to all collections of the Online CaaS. This adjustment is not necessary for the Preview CaaS, since the information in the preview is obtained directly from it and no processing takes place in Spryker.

The schedule contains the script action Setup CaaS Blocks Aggregations to create the aggregations.

Setup CaaS Blocks Aggregations
#!executable-class
com.espirit.ecom.contentconnect.spryker.module.caas.BlocksAggregationSetupExecutable

The script executes a PUT request for the configured collections of the Online CaaS. This generates the aggregations that are used to make all CMS blocks contained in the extended collections available for Spryker to import. For this, the script requires the list of the relevant collections, which can be passed to it using the optional parameter caas_collections.

By default, the parameter has the value contentpages;categorypages;productpages;technical and therefore does not need to be configured in the reference project. To overwrite the default value, the parameter caas_collections needs to be added in the script’s properties dialog. Its value must correspond to a list of all collections, separated by semicolons, for which an aggregation is to be created.

deployparams
Figure 15. Configuration parameter

3.7.3. Trigger Spryker Import

In FirstSpirit, the creation and editing of editorial content takes place in the ContentCreator. After release, they are transferred by deployment to the Online CaaS and imported from there by Spryker to be integrated into the shop. In order to keep the contents always up-to-date in Spryker, the import process must be triggered by the deployment. Therefore, the schedule contains the script action Trigger Spryker Import. The script activates a import job on Spryker side, which in turn triggers the import and persistence of the contents stored in the Online CaaS.

Trigger Spryker Import
#!executable-class
com.espirit.ecom.contentconnect.spryker.module.trigger.triggerimport.TriggerImportExecutable

When using the Spryker B2C Demo Shop, the import may fail when accessing the access-tokens endpoint with a Response Code 500. In this case, the key files dev_only_private.key and dev_only_public.key within the directory data/shop/development/current/config/Zed must receive the authorization 600 or 660 respectively.

References to categories or products that have been deleted or are inactive in Spryker will result in errors and warnings during import. Errors occurring in this context and recorded in the Spryker log are transferred to the FirstSpirit log by the following action.

3.7.4. Trigger Fetch Spryker Logs

Deleting or deactivating categories or products in Spryker that are referenced in the FirstSpirit project leads to errors and warnings during the import. However, by default these are only recorded in the Spryker log. To provide all information about a release in one place, the errors and warnings must also be recorded in the FirstSpirit log. Therefore, the schedule contains the script action Trigger Fetch Spryker Logs. It determines all errors and warnings for missing categories and products from the Spryker log and transfers them as warnings to the FirstSpirit log. Operational managers are thus given the opportunity to react quickly to the outdated references in the FirstSpirit project.

Trigger Fetch Spryker Logs
#!executable-class
com.espirit.ecom.contentconnect.spryker.module.trigger.triggerfetchlogs.TriggerFetchLogsExecutable

3.7.5. Trigger Spryker Cleanup

The creation and editing of editorial content takes place in FirstSpirit before it is transferred to the Online CaaS via deployment and imported from there by Spryker. In order to keep the data in both systems up-to-date, deleted content from the FirstSpirit project must also be removed on the Spryker side. Therefore, the schedule contains the script action Trigger Spryker Cleanup. The script triggers a comparison between the contents stored in the Online CaaS and persisted in the Spryker data system. In this way, the obsolete data can be determined on the Spryker side and then removed.

Trigger Spryker Cleanup
#!executable-class
com.espirit.ecom.contentconnect.spryker.module.trigger.triggercleanup.TriggerCleanupExecutable

3.7.6. Send Result Mail

In most cases, the deployment of the content created or edited in FirstSpirit is performed via the ContentCreator. However, the ContentCreator does not inform the editor whether a publication was successful or failed. Therefore, the schedule contains the script action Send Result Mail. In case of an error, this action sends an e-mail with all relevant information to the recipient defined in the e-mail distribution list field in the schedule’s properties. Additionally the e-mail contains the possibility to forward these information to the Technical Support of the Crownpeak Technology GmbH.

Trigger Spryker Cleanup
#! executable-class
com.espirit.ecom.contentconnect.spryker.module.schedule.ReviewScheduleResultExecutable

3.8. Generation schedule - Media generation

Unlike the content created with FirstSpirit, the released media is not transferred to the CaaS, but to the CDN provided by the Crownpeak Technology GmbH. Therefore, the reference project has the schedule Media Deployment, which is created within the ServerManager in the Project properties  Schedule management area. It is triggered by the Execute Publish Media to CDN action contained in the full generation and has the actions Media Generation and Publish to CDN. These are described in the following subchapters.

mediadeploy actions
Figure 16. Actions of the Media generation

If it is desired to use a local CDN or a MAM instead of the CDN provided by the Crownpeak Technology GmbH, the Technical Support will assist with the configuration.

3.8.1. Media Generation

To transfer the media to the CDN provided by the Crownpeak Technology GmbH, they must first be generated. Therefore the schedule contains the generation action Media Generation. It represents a partial generation, which refers to the root node of the Media Store.

To avoid inconsistencies in the live state, the options Clear generation directory beforehand and Generate release version must be activated in the Properties of the action. Furthermore, it is necessary that the URL Factory selected at this point for PathGeneration and the URL-Factory for media use configured in the project component of the CaaS are identical.

mediageneration settings
Figure 17. Properties of the Media Generation action

3.8.2. Publish to CDN

Once the media have been generated, they are transferred to the CDN. Therefore, the schedule contains the action Publish to CDN. In the Cloud environment, this action is added by the Cloud department of the Crownpeak Technology GmbH and is thus already preconfigured. No further configuration is required.

In the On Premises environment, a manual creation and configuration of this action is required. The necessary information is provided by the Crownpeak Technology GmbH.

mediageneration publication
Figure 18. Publish to CDN

3.9. Generation schedule - preview generation

Under certain circumstances it might happen that the datasets in the FirstSpirit project, the CaaS and in Spryker differ. In this case the contents from the Online CaaS and in Spryker can be updated by a full generation. In contrast, updating the Preview CaaS would require all pages within the FirstSpirit project to be saved again separately.

For this reason, the reference project has the schedule Spryker Preview Deployment, which is created within the ServerManager in the Project properties  Schedule management area and can only be executed by project administrators. This schedule has the same actions as the full generation with exception of the Execute Publish Media to CDN, Setup CaaS Blocks Aggregations, Trigger Fetch Spryker Logs, and Send Result Mail actions. Since the information in the preview is obtained directly from the FirstSpirit project or the Preview CaaS and no processing takes place in Spryker, it is not necessary to create aggregations, publish media or determine Spryker logs for the preview generation. Furthermore, the preview generation is started from the ServerManager, so sending an e-mail to an administrator is also not required.

After a project import a content exchange is mandatory. For this, however, all configuration steps on both the FirstSpirit and the Spryker side must be completed.

previewdeploy actions
Figure 19. Actions of the preview generation

Within the actions Initialize CaaS Generation, CaaS Generate, Trigger Spryker Import, and Trigger Spryker Cleanup different settings are necessary.

Initialize CaaS Generation

Among other things, the action Initialize CaaS Generation requires the definition of a API Key and a CaaS URL. In the case of the preview generation, the Preview CaaS data must be specified for both parameters.

The API Key requires both read and write permissions.

A description of the action and the parameters needed for it is contained in the Content as a Service Documentation.

CaaS Generate

During preview generation, this action may only deal with the current state of the content. For this reason, the checkbox Generate release status in the Properties of the action must be deactivated.

A description of the action is contained in the Content as a Service Documentation.

deploy caas generate
Figure 20. Properties of the action CaaS Generate
Trigger Spryker Import

In the preview, the import may only consider the contents of the Preview CaaS. For this purpose, the parameter triggerStagedContentImport must be added to the action’s Properties and the value true must be specified for it. By default, the parameter has the value false and is therefore not required in the action of the same name in the full generation.

This setting is already included in the schedule of the reference project.

Trigger Spryker Cleanup

As the import action, the cleanup action may only consider the contents of the Preview CaaS. For this purpose, the parameter triggerStagedContentCleanup must be added to the action’s Properties and the value true must be specified for it. By default, this parameter also has the value false and is therefore optional in the full generation.

This setting is also already included in the schedule of the reference project.

CMS Pages deleted within the FirstSpirit project are only deleted in Spryker if they have not been published before. Otherwise, due to technical restrictions, they are kept in Spryker and are only removed when a full generation is executed.

3.10. Workflows

Content is released, deleted and published within the provided reference project via workflows. These are already added to the project and are described in the following subchapters.

3.10.1. Release workflow for navigation

The reference project provides the possibility to extend an existing navigation or to create a brand new navigation. As with content changes, navigation changes must be released in order to publish them to the live state. The reference project therefore contains the workflow Release Navigation, which requires the release right for its execution. The workflow merely releases a global page for the navigation and is therefore only activated in the Global Settings. It does not provide conflict handling, nor does it follow the principle of dual control.

3.10.2. Delete workflow for automated deletion

The reference project contains the report Lost and Found, which shows an overview of all orphaned product and category pages. These are pages that contain references to deleted or inactive products or categories in Spryker.

Each entry of this report has a delete button, which enables the removal of the corresponding page. The reference project therefore contains the workflow Direct delete, which is triggered by this button. With the exception of the dual control principle, its functionality corresponds to that of the delete BasicWorkflow described in the following chapter. In addition to the deletion of the page, the workflow releases the parent structure and finally performs a full deployment.

3.10.3. BasicWorkflows

Content is released, deleted, and published by editors within the supplied reference project FirstSpirit Connect for Spryker Commerce OS Reference Project via the BasicWorkflows. These were adapted project-specifically and extended by the possibility to execute a full generation. Just like the direct release or delete option, this option is only available to roles that have the release or delete right. In the reference project, this is the group ChiefEditors.

In addition, both workflows send an e-mail to the user who has performed (and not just requested) the release or deletion. In case of success, this e-mail only informs that the workflow has been successfully executed. In case of an error, it indicates that the recipient specified in the deployment schedule has received all necessary information and should be contacted.

The following subchapter explains the adjustments made.

Installation of the BasicWorkflows module

Before using the workflows, the BasicWorkflows module must first be installed on the FirstSpirit server and the web component activated. The necessary steps are the same as the installation of the other modules and activation of the corresponding web components. By default, these steps are already performed.

The use of BasicWorkflows in ContentCreator also requires the selection of the provided BasicWorkflows Status Provider in the Project properties  ContentCreator area within the ServerManager. This setting has as well already been made in the reference project FirstSpirit Connect for Spryker Commerce OS Reference Project.

basicwfs
Figure 21. Element Status Provider

Templates

The BasicWorkflows need different templates. These usually have to be imported into the FirstSpirit project via the context menu. However, they are already included in the reference project and an import of the templates is therefore not necessary.

The templates contained in the reference project were adapted to the specific project and supplemented with additional templates. If the workflows contained in the reference project should be used in another project, these templates and not those of the BasicWorkflows module must be transferred to the other project.

Permission assignment

In the final step, the workflows must be allowed in each store to run on FirstSpirit elements. To do this, you can open the permission assignment on the root nodes of the stores via the context menu entry Extras  Change Permissions. This step has already been performed in the reference project and is therefore omitted.

The rights set on the stores' root nodes to execute the workflows refer to the Everyone group. If this is not desired, the rights must be adjusted manually.

More information about the installation, configuration and functionality of the workflows can be found in the BasicWorkflows Documentation.

3.10.4. Project-specific adjustments

By default, the BasicWorkflows workflows has two ways to release or to delete an item:

  • Direct release | Direct delete

  • Request

In both cases, the direct option is only available to roles that have the release or delete right. In the reference project, this is the group ChiefEditors. Otherwise, a release or deletion can only be requested and assigned to the next person. This person either rejects the request by manually triggering a conflict, or executes the release or deletion of the element by continuing the workflow.

Both workflows provided in the reference project were adapted project-specific and extended by the possibility, to execute a full generation in addition to the release or deletion of a page. Therefore, an additional button has been added to the start dialog and the check dialog.

In the release workflow this is the button Page Publication. It corresponds to the transition trigger_release_and_deploy, which, like direct release, can only be executed with the release right.
The delete workflow has been extended by the button Delete with publication, which represents the transition trigger_delete_and_deploy and requires both release and delete rights.

wf dialogs
Figure 22. Extension of the release workflow

Both buttons execute the set_value_for_deployment script, which sets the deploymentTriggered variable. This variable is evaluated in the last step of the respective workflow in the script trigger_full_deployment and defines whether the workflow executes a full generation in addition to the release or deletion.

In order to execute the publication, the Executable triggered by the trigger_full_deployment script must know the name of the corresponding full deployment schedule. Therefore the Executable accesses the combo box Schedule Entry for publication, which is included in the Publication tab of the project component, and queries the schedule selected in it. Within the reference project, the full generation Spryker Deployment is defined here.

After execution, both workflows also send an e-mail to the user who has performed (and not just requested) the release or deletion. This e-mail either just informs about the successful execution of the workflow or points out that an error occurred during the publication. In this case, the e-mail also indicates that the recipient specified in the Deployment schedule has received all needed information and should be contacted. This is necessary because only project administrators can resolve errors in the deployment schedule.

Both in the case of success and in the case of error, the executable called in the last step of the respective workflow accesses the technical page template html_mail_template to send the corresponding e-mail. This template includes the texts of all e-mails sent by the workflows or deployment schedules.

The notification of the recipient defined in the deployment schedule is done via the action Send Result Mail. This e-mail is sent for all errors that occur during a generation and is not linked to the execution of a workflow.

Since the delete workflow can only be used for deleting dynamic content pages as well as product and category pages, its display logic has also been extended. The new statement hides the workflow for all pages that do not match a content, product, or category page.

For more information on the functionality of the workflows, see the BasicWorkflows Documentation.

3.11. Project settings

There is some essential project-specific information that needs to be entered for the connection between FirstSpirit and Spryker. It is entered using the project settings form and must be specified within the project being used.

Within the supplied reference project, the project settings page with the required configuration already exists. In this case, there is no need to create the template or the required components. Even defining the settings page in the Options of the project in the ServerManager is no longer required.

The form of the project settings in the reference project is divided into the tabs General and Static Pages, in which different information must be entered:

Aktive Store

This input component provides the option to select the store in which the created content is to be published. The selection is read out at different points within the FirstSpirit templates.

Page Reference

The feature associated with this field is not yet available in the current version. It therefore has no function at this time.

psettings general
Figure 23. Projekteinstellungen - General
Static Page URLs

This input component provides the option of assigning a relative URL to static pages. This is needed because static pages do not have a URL by default and content page links to them would therefore not be possible.

The definition of the URLs first requires a determination of all static pages. For this purpose, the Static Page Templates Import button within the reference project reads the contents of the static_pages template folder, which contains all page templates for static pages. For each template, an entry is automatically created that contains a reference to the template and a language-dependent field for the relative URL to be defined.

psettings staticpages
Figure 24. Projekteinstellungen - Static Pages

A future modification of the defined URLs requires the execution of both the full generation and the preview generation.

4. Reference project

The FirstSpirit Connect for Spryker Commerce OS module provides different ways to access shop content from Spryker and use it in FirstSpirit. This requires different templates both within the FirstSpirit project and on the Spryker side. These templates and the dependencies between them are described in the following subchapters using the reference project provided.

The section and page templates contained in the reference project require different Twig templates for their use in Spryker. The descriptions in the following subchapters assume the existence of these templates.

4.1. Extension of existing static pages

The FirstSpirit Connect for Spryker Commerce OS module allows editors to enrich static pages that already exist on Spryker side with FirstSpirit content. The pages are displayed within the ContentCreator using the Omnichannel Manager and thus integrate seamlessly into the editorial process familiar from FirstSpirit.

Besides the integration of the Omnichannel Manager, which is a step of the installation, the extension of static pages in FirstSpirit requires the filling of the output channel of the page and section templates. In addition, an adaptation of the corresponding Twig templates is necessary on Spryker side.

4.1.1. Page template

Editorial content in the FirstSpirit project is always edited on the basis of a page reference. It and the page on which it is based must first be created in the FirstSpirit project. Within the reference project FirstSpirit Connect for Spryker Commerce OS Reference Project, the creation of the page reference and its page runs automatically in the background and invisible to the editor. The infer_page_storage_information script uses the page ID defined in the IndexController to determine the corresponding FirstSpirit page template.

The page template homepage contained in the reference project is an example of a static page. The following code fragment shows the output channel of this page template:

Output channel of the page template homepage
$CMS_TRIM(level:1)$
   $CMS_SET(json, {:})$
   $CMS_SET(previewId, previewId(element:#global.node))$
   $CMS_SET(void, json.put("previewId", "" + previewId))$
   $CMS_SET(void, json.put("pagetype", "static"))$
   $CMS_SET(void, json.put("identifier", #global.node.uid))$

   $CMS_SET(blocks, [])$
   $CMS_FOR(bodyName, #global.page.template.bodies.map(body -> body.name))$
      $CMS_VALUE(#global.page.body(bodyName))$
   $CMS_END_FOR$

   $CMS_SET(void, json.put("blocks", blocks))$

   $CMS_VALUE(json.toJSON)$
$CMS_END_TRIM$

The page template maps the Twig template home.twig and contains the content areas fs_slt_2, and fs_slt_3. The content areas enable the addition of editorial content, for which various section templates are available in the reference project. Their content is added to the json object created in the template.

In addition to the editorial content, the previewId, the identifier and the pagetype are passed to the JSON object: The previewId serves to identify the page reference and allows its decoration in the ContentCreator as well as its mapping to the Spryker Twig template. The page reference for the preview is identified by the JavaScript of the Omnichannel Manager. The generation of the page in Spryker is done using the identifier. It ensures the unique identification in Spryker. It must also be known whether the underlying page is static or dynamic. This information is indicated by the pagetype.

The generated JSON object is part of the CaaS item to be generated, which is persisted on the Spryker side by the importer.

4.1.2. Section template

The page template homepage contained in the reference project is an example of a static page. It contains the content areas fs_slt_2, and fs_slt_3. The content areas allow the addition of editorial content. Different section templates are available for this, all based on the same principle. One of them is the shoppable_video section template, which is described here as an example.

Within the output channel of the Shoppable Video section, various general information is first defined and stored in the block array: The previewId serves the identification of the section and enables it to be edited in the ContentCreator. The fields active and store_names indicate that the CMS block represented by the section is active on the Spryker side and visible in the store selected in the project settings.

The output of the section on Spryker side is handled by the Twig template fs_molecule_block. This corresponds to a generic template that controls the output of all molecules. In order to ensure the output of the section both in the preview and in the live state, the name and path of this Twig template must be stored within the CaaS item. Both details are therefore included in the general information of the section.

Definition of general information in the output channel of the section
$CMS_SET(block,{:})$

$-- Defining general block data --$
$CMS_SET(void, block.put("previewId", "" + previewId()))$
$CMS_SET(void, block.put("active", "1"))$
$CMS_SET(void, block.put("store_names",[ps_activeStore]))$
$CMS_SET(void, block.put("template_name", "fs_molecule_block"))$
$CMS_SET(void, block.put("template_path", "@CmsBlock/template/fs_molecule_block.twig"))$

The block_name_render render template then determines the block name. This is composed of the name of the parent content area and the ID of the section.

The block_key_render render template behaves in the same way as the block_name_render render template. In contrast to this, however, it does not determine the block name, but the unique key of the corresponding block.

Using the render template additional_block_attributes_render, additional attributes are added to the block. These attributes are the slot key and the position of the block within the slot, as well as the page type and a unique ID.

The importer requires the placeholders field to be in the generated CaaS item. Therefore, it has to be created here, even though it does not have any data.

Determination of specific information in the output channel of the section
$CMS_SET(set_pageType, json.get("pagetype"))$

$-- Add block name attribute to block object (page type dependent) --$
$CMS_RENDER(template: "block_name_render", rt_pageType: set_pageType, rt_blockObject: block)$

$-- Add block key attribute to block object (page type dependent) --$
$CMS_RENDER(template: "block_key_render", rt_pageType: set_pageType, rt_blockObject: block)$

$-- Add additional block attributes to block object (page type dependent) --$
$CMS_RENDER(template: "additional_block_attributes_render", rt_pageType: set_pageType, rt_blockObject: block)$

$-- Defining placeholder data of the block --$
$CMS_SET(placeholders,{:})$
$CMS_SET(void, block.put("placeholder",placeholders))$

The array data contains the editorial contents maintained in the form of the section. These correspond to the video ID of the selected YouTube video and a list of video items based on the shoppable_video_item section template described below. They are displayed at defined times of the video. To display the video, the player needs the previewId specified here. On Spryker side, the section is represented by the molecule fs-shoppable-video, whose name is also contained in the data array.

The importer expects the information in the context of the current language. For this reason, the corresponding language abbreviation to which the array data is assigned is determined.

In the preview, the contents of the section are output directly. Otherwise, the last step adds the section to the blocks array defined in the page template, which contains all sections of a page and integrates them into the CaaS item to be generated.

Processing of editorial content in the output channel of the section
$CMS_SET(data,{:})$
$CMS_SET(void, data.put("moleculename", "fs-shoppable-video"))$
$CMS_SET(void, data.put("videoId", if(!st_video.empty,st_video.values.first.id)))$
$CMS_SET(void, data.put("previewId", previewId()))$

$CMS_SET(items,[])$
$CMS_VALUE(st_items)$
$CMS_SET(void, data.put("items", items))$

$CMS_SET(locale, #global.language.getLocale())$
$CMS_SET(localeAbbr, locale.getLanguage() + "_" + locale.getCountry())$
$CMS_SET(localizedData,{:})$
$CMS_SET(void, localizedData.put(localeAbbr, data))$
$CMS_SET(void, block.put("data", localizedData))$

$CMS_IF(#global.is("WEBEDIT") && !isSet(caas_preview_generation))$
   $CMS_VALUE(block.toJSON)$
$CMS_ELSE$
   $CMS_SET(void, blocks.add(block))$
$CMS_END_IF$

As mentioned before, the various video items are based on the shoppable_video_item section template. Within the output channel of this template, the editorial content for a single video item is determined and stored in the item array. The editorial content corresponds to the time at which the video item is displayed, an image, a text and a reference to the detail page of a product to be selected. The last step adds the single video item to the items array defined in the shoppable_video section template, which contains all video items and integrates them into the CaaS item to be generated.

Output channel of the video item
$CMS_SET(item, {"time": st_time})$

$CMS_IF(!st_picture.empty)$
        $CMS_SET(void, item.put("picture", {
                "imageUrl": ref(st_picture, res:"CONTENT_IMAGE", abs:1).url,
                "ambilight": !st_ambilight.empty && st_ambilight
        }))$
        $CMS_IF(!#global.release)$
                $CMS_SET(void, item.picture.put("previewId", previewId(element: st_picture)))$
        $CMS_END_IF$
$CMS_END_IF$

$CMS_IF(!st_text.empty)$
        $CMS_SET(void, item.put("text", st_text.normalize.toText(true)))$
$CMS_END_IF$

$CMS_IF(!st_product.empty)$
        $CMS_SET(void, item.put("productId", st_product.identifiers.get(0)))$
$CMS_END_IF$

$CMS_SET(void, items.add(item))$

The edit dialog in the ContentCreator has the Open Video button for entering the display time, the image, the text and the reference of a video item. This button opens another dialog in which the selected video is visible and playable. With the help of a timeline, the video items can be intuitively created, edited or deleted within this dialog.

videoitem
Figure 25. Editing dialog for video items

4.1.3. Twig templates

In FirstSpirit, the creation and editing of editorial content takes place in ContentCreator. The storefront is embedded in it using the Omnichannel Manager. This in turn accesses the Preview CaaS and determines the current FirstSpirit contents from there. These contents are transferred to the Online CaaS via a FirstSpirit deployment. The latter makes them available to the importer, who transfers them to Spryker and persists them there.

The Twig template home.twig represents a static page on the Spryker side and can be found under the path src/Pyz/Yves/HomePage/Theme/default/views/home. To make this page editable in ContentCreator, it is necessary to replace the CMS slots contained in it.

The slots represent the content areas of the respective FirstSpirit page template. For this reason, the key of the CMS slot and the name of the content area must match and start with the prefix fs-slt and fs_slt respectively. It is important to note that hyphens in the key of the slot are replaced by underscores within FirstSpirit. The slot fs-slt-2 thus corresponds to the content area fs_slt_2.

The inclusion of a slot is done via the Spryker standard command cms_slot, which in turn calls the FirstSpiritPreviewSlotBlockWidgetCmsSlotContentPlugin. This is part of the delivery and requires an extension of the ShopCmsSlotDependencyProvider. Unlike the CmsSlotBlockWidget included in Spryker, it only takes into account the data stored in CaaS.

The Twig template home.twig is represented by the page template homepage contained in the reference project. It is an example for a static page and has the content areas fs_slt_2 and fs_slt_3. The following code example shows the integration of the slots of the same name within the Twig template to make the static page editable in ContentCreator.

Within the Twig template the ID idCmsPage has to be defined and passed to each block. It is composed of the page type and the name of the FirstSpirit page.

Extension of the Twig template home.twig
{% extends template('page-layout-main') %}

{% define data = {
   idCmsPage: 'static_homepage',
} %}

{% block pageInfo %}{% endblock %}

{% block container %}
   {% cms_slot 'fs-slt-2' with{
      idCmsPage: 'static_homepage',
   }%}

   <div class="container container--home-page">
      <main>
         {% block content %}
            {% cms_slot 'fs-slt-3' with{
               idCmsPage: 'static_homepage',
            }%}
         {% endblock %}
      </main>
   </div>
{% endblock %}

The content areas fs_slt_2 and fs_slt_3 contained in the page template homepage allow the addition of editorial content. For this purpose, various section templates are available within the reference project, for which a corresponding block Twig template must exist on the Spryker side. One of these templates is the section template shoppable_video, which is represented by the molecule fs-shoppable-video on the Spryker side. This molecule is part of the Spryker module firstspirit-reference-components included in the delivery and is stored in the directory FirstSpiritReferenceComponents in Spryker.

The following code extract shows the content of the molecule fs-shoppable-video in a highly abbreviated form:

Molecule fs-shoppable-video
{% extends model('component') %}

{% define config = {
   name: 'fs-shoppable-video',
   tag: 'fs-shoppable-video'
} %}

{% define data = {
   fsBlockData: [],
} %}

{% block body %}
   [...]
   <fs-youtube-player video-id="{{ data.fsBlockData.videoId }}" nocookie muted>
      {% for item in data.fsBlockData.items %}
         <div data-time="{{ item.time }}">
            {% set url = item.productId is defined ? getProductPageUrl(item.productId):null %}
            <a href="{{url}}">
               {% set ambilight = item.picture.ambilight is defined and item.picture.ambilight %}
               <span class="picture{{ambilight ? 'with-ambilight':''}}"
                  style="background-image: url('{{ item.picture.imageUrl }}');">
               </span>
               [...]
               {{ item.text | raw }}
               [...]
            </a>
            [...]
         </div>
      {% endfor %}
   </fs-youtube-player>
   <script type="module" src="https://www.unpkg.com/fs-youtube-player"></script>
{% endblock %}

Within the template, the name and tag of the molecule are defined first. The fsBlockData object is then created. It provides access to the structured data defined in the output channel of the FirstSpirit section template. In the preview case, this data is obtained directly from the Preview CaaS. In contrast, the data for the live state comes from Spryker. Therefore, they are imported during a FirstSpirit generation from the Online CaaS and persisted in Spryker. The fsBlockData object and the JSON object created in FirstSpirit thus have the same structure in both cases.

The block body describes the output of the editorial content. These correspond to the time at which the video item is displayed, an image, a text and a reference to the detail page of a product to be selected.

The following figure shows the representation of the CMS block.

section
Figure 26. Representation of the section

4.2. Extension of existing category pages

Just like the static pages described above, category pages that already exist on the Spryker side can also be displayed in ContentCreator using the Omnichannel Manager and enriched with editorial content. This also requires the filling of the output channel of the page and section template within the FirstSpirit project as well as a adaptation of the corresponding Twig templates in Spryker.

If there are references to categories or products within the FirstSpirit project which have been deleted or are inactive in Spryker, so-called orphaned pages occur. These can be removed from the project via the report Lost and Found.

4.2.1. Page template

Just like static pages, the extension of a category page in the FirstSpirit project is based on a page reference. It and its associated page are also automatically created in the background and invisible to the editor.

The reference project contains the page template categorypage, which is an example of a category page and maps the Twig template catalog-with-cms-slot.twig. The following code fragment shows the output channel of this page template:

Output channel of the page template categorypage
$CMS_TRIM(level:1)$
   $CMS_SET(json, {:})$
   $CMS_SET(previewId, previewId(element:#global.node))$
   $CMS_SET(void, json.put("previewId", "" + previewId))$
   $CMS_SET(void, json.put("pageRevisionId", #global.page.revision.id))$
   $CMS_SET(void, json.put("pagetype", "category"))$
   $CMS_SET(blocks, [])$

   $CMS_VALUE(#global.page.body("banner"))$
   $CMS_VALUE(#global.page.body("top"))$
   $CMS_VALUE(#global.page.body("bottom"))$
   $CMS_SET(void, json.put("blocks", blocks))$

   $CMS_VALUE(json.toJSON)$
$CMS_END_TRIM$

The output channel of the category page is almost identical to that of the homepage, which corresponds to a static page. It differs only in the page type, the revision id and in the number of content areas. The page type is used to determine the block name and must have the value category at this point. The block name is determined depending on the page type using the render template block_name_render, which is referenced in the output channel of the respective section. The content areas of the page template correspond to the CMS block positions defined on the Spryker side. They must therefore be named identically.

4.2.2. Section template

In addition to the already described Shoppable Video section and the Text-Image section, the section template carousel_section is available for adding editorial content to category pages. The carousel section in turn embeds one or more banners.

The banner allows the selection of an image that can be cut to size as well as the definition of a link, a title and a subtitle. For both the heading and the subtitle, a text color can be selected. Unlike the other sections, which can be used only on subcategory pages, the banner can be embedded on all category pages.

The banner corresponds to the Spryker molecule fs-banner.twig, whose output is also controlled by the generic Twig template fs_molecule_block.twig. This corresponds to a generic template that regulates the output of all molecules.

The output channel of the banner is almost identical to that of the Shoppable Video section and only differs in the evaluation of the available input components. For this reason, no additional extensions are required that go beyond the requirements already described for the Shoppable Video section.

4.2.3. Twig Templates

The FirstSpirit page template categorypage contained in the reference project is an example of a category page. It has the content areas banner, top and bottom and maps the Twig template catalog-with-cms-slot.twig. This is stored in Spryker under the path src/Pyz/Yves/CatalogPage/Theme/default/views/catalog-with-cms-slot and extends the parent template page-layout-catalog.twig, which defines the base layout of all category pages.

To make category pages editable in ContentCreator, the CMS blocks within both templates must be integrated using the Twig function fsSpyCmsBlock.

Unlike the CMS block banner, which is available on all category pages, the CMS blocks top and bottom can only be used on subcategory pages. If they should also be available on all category pages, the template Catalog + CMS Slot must be selected for them in Spryker.

The following code example shows the Twig template catalog-with-cms-slot.twig including the adjustments necessary for editing in FirstSpirit. Since only for the positions top and bottom content areas exist in the FirstSpirit page template, the integration of the CMS blocks takes place exclusively in these two cases via the Twig function fsSpyCmsBlock.

Adaptation of the Twig template catalog-with-cms-slot.twig
{% extends template('page-layout-catalog', 'CatalogPage') %}

[...]

{% block catalogContent %}
   {% if data.categoryId %}
      <div class="catalog-cms-block catalog-cms-block--top">
         {{ fsSpyCmsBlock({category: data.categoryId, position: 'top'}) }}
         {% cms_slot 'slt-4' required ['idCategory'] with {
            idCategory: data.categoryId,
         } %}
      </div>
   {% endif %}

   {{ parent() }}

   {% if data.categoryId %}
      <div class="catalog-cms-block catalog-cms-block--bottom">
         {{ fsSpyCmsBlock({category: data.categoryId, position: 'bottom'}) }}
         {% cms_slot 'slt-6' required ['idCategory'] with {
            idCategory: data.categoryId,
         } %}
      </div>
   {% endif %}
{% endblock %}

Each CMS block corresponds to a FirstSpirit section placed at a specific position on the category page. The use of positions in category pages is enabled Spryker sided by the CMS Block Category Connector feature, which is already configured within the demo shop. The feature provides by default the positions top, middle and bottom. In FirstSpirit, the positions are represented by the content areas of the corresponding page template. For this reason, the names of the content areas and the names of the positions defined for category pages must be identical.

For the integration of the banner the feature must be additionally extended by the position banner. For this the file cms_block_category_position.csv within the directory data/import has to be adapted accordingly.

To make the positions of the blocks for category pages known, the YAML-files within data/import/local/ need to be changed:

Extension of full_EU.yaml
[...]
  - data_entity: cms-block
    source: data/import/common/common/cms_block.csv
  - data_entity: cms-block-category-position
    source: data/import/common/common/cms_block_category_position.csv
  - data_entity: cms-page
    source: data/import/common/common/cms_page.csv
[...]
Extension of full_US.yaml
[...]
  - data_entity: cms-block
    source: data/import/common/common/cms_block.csv
  - data_entity: cms-block-category-position
    source: data/import/common/common/cms_block_category_position.csv
  - data_entity: cms-page
    source: data/import/common/common/cms_page.csv
[...]

The transfer of the new position is then done via the following command, which has to be executed in the Spryker project directory.

Update command
console data:import cms-block-category-position

A more detailed description of the configuration of positions for category pages can be found in the chapter Usage for Demoshop of the Spryker documentation.

Furthermore, the CMS block banner has to be added to the superior Twig template page-layout-catalog.twig, which is stored in the directory src/Pyz/Yves/CatalogPage/Theme/default/templates/page-layout-catalog. Therefore, the existing block title section within the template must be replaced by the following code.

Integration of the banner
{% block title %}
   {% if data.category %}
      {{ fsSpyCmsBlock({category:data.category.id_category, position: 'banner'}) }}
   {% endif %}
   [...]
{% endblock %}

In Spryker, the banner corresponds to the molecule fs-banner. The following code fragment shows the content of the molecule in a highly abbreviated form:

Molecule fs-banner
{% extends model('component') %}

{% define config = {
   name: 'fs-banner',
   tag: 'fs-banner'
} %}

{% define data = {
   fsBlockData: []
} %}

{% block body %}
   {% set url -%}
      {%- if data.fsBlockData.link is defined and data.fsBlockData.link is not empty -%}
         {%- include template('link', 'FirstSpiritPreview') with { link: data.fsBlockData.link } -%}
      {%- endif -%}
   {%- endset %}
   <figure class="fs-banner"
      {% if data.fsBlockData.variantEditorName is defined %}
         data-variant-editor-name="{{ data.fsBlockData.variantEditorName }}"
      {% endif %}
      {% if data.fsBlockData.variant is defined and data.fsBlockData.variant is not empty %}
         data-variant="{{data.fsBlockData.variant}}"
      {% endif %}
      {% if data.fsBlockData.previewId is defined and data.fsBlockData.previewId is not empty %}
         data-preview-id="{{data.fsBlockData.previewId}}"
      {% endif %}>

      [...]

      <img src="{{ data.fsBlockData.picture.imageUrl }}"
         {% if data.fsBlockData.picture.previewId is defined %}
            data-preview-id="{{ data.fsBlockData.picture.previewId }}"
            data-tpp-context-image-resolution="BANNER_IMAGE"
         {% endif %}>

      [...]

   </figure>
{% endblock %}

Within the template, the name and the tag of the molecule are defined first and then the fsBlockData object is created. It provides access to the structured data defined in the output channel of the FirstSpirit section template. In the preview case, this data is obtained directly from the Preview CaaS. In contrast, the data for the live state comes from Spryker. For this they are imported during the FirstSpirit generation from the Online CaaS and persisted in Spryker. The fsBlockData object and the JSON object created in FirstSpirit thus have the same structure.

The block body describes the output of the editorial content. These correspond to a cropable image and a link as well as a title and a subtitle, for each of which a text color is defined.

The following figure shows the representation of a carousel with an embedded banner in the ContentCreator.

banner
Figure 27. Carousel with a single element

The output of all molecules is controlled by the generic Twig template fs_molecule_block.twig. The following code fragment shows the content of this template:

Generic Twig template for the output of all molecules
{% define data = {
   fsBlockData: fsBlockData()
} %}

{% block content %}
   {% include molecule( data.fsBlockData.moleculename, 'FirstSpiritReferenceComponents') with{
      data: {
         fsBlockData: data.fsBlockData
      }
      attributes: {
         'data-attributes': data.fsBlockData.attributes | default('') | json_encode
      }
   }only %}
{% endblock %}

As in the Twig template of the molecule, the fsBlockData object is first created in the generic template. The block content then includes the molecule specified in the FirstSpirit section template for the parameter moleculename. In Spryker, the molecule is stored in the directory FirstSpiritReferenceComponents. In this way, the Twig template generically controls the output of all molecules and creates the mapping between the FirstSpirit section template and the respective Spryker block Twig template.

The attributes attribute provides additional configuration attributes that can be defined in the output channel of a section template. They are provided in this way directly in the tag of the corresponding molecule, so that they can be easily accessed via TypeScript. If no configuration attributes are defined within the output channel of a section, the attributes attribute remains empty.

4.3. Extension of existing product pages

Product pages that already exist in Spryker are equivalent to the category pages described in the previous chapter. They can also be displayed and edited in the ContentCreator using the Omnichannel Manager. This also requires the filling of the output channel of the FirstSpirit page template and an adaptation of the corresponding Twig templates in Spryker.

If there are references to categories or products within the FirstSpirit project which have been deleted or are inactive in Spryker, so-called orphaned pages occur. These can be removed from the project via the report Lost and Found.

4.3.1. Page template

Editing a product page is equivalent to extending a category page and is, in the FirstSpirit project, also based on a page reference. This and its associated page are automatically created in the background and invisible to the editor.

The page template productpage provided with the reference project represents a product page. It maps the Twig template pdp.twig and comes with the output channel displayed in the following code snippet.

Output channel of the page template productpage
$CMS_TRIM(level:1)$
   $CMS_SET(json, {:})$
   $CMS_SET(previewId, previewId(element:#global.node))$
	$CMS_SET(void, json.put("previewId", "" + previewId))$
	$CMS_SET(void, json.put("pageRevisionId", #global.page.revision.id))$
	$CMS_SET(void, json.put("pagetype", "product"))$
	$-- Page uid is needed for later identification when setting the SKU of the page --$
	$CMS_SET(void, json.put("page_uid", #global.page.uid))$
	$CMS_SET(void, json.put("product_sku", pt_productSku))$
	$CMS_SET(blocks, [])$

	$CMS_VALUE(#global.page.body("content"))$
	$CMS_SET(void, json.put("blocks", blocks))$

	$CMS_VALUE(json.toJSON)$
$CMS_END_TRIM$

The output channel of the product page and the category page are almost identical. They only differ in the specification of the Page UID, the Product SKU and in the number of content areas. The Product SKU is a unique product ID which, together with the Page UID, is used to uniquely identify the product page in Spryker. By using the script set_product_sku, the Product SKU is automatically saved in the form of the page when a product page is created in the FirstSpirit project.

4.3.2. Twig template

The Twig template product-cms-block.twig represents a product section on the Spryker side and extends the parent template pdp.twig, in which the basic layout of all product pages is defined. It is a part of the zip file b2c-demo-shop-extensions-<VERSION>.zip included in the delivery and must be stored under the path`src/Pyz/Yves/CmsBlockWidget/Theme/default/components/molecules/product-cms-block` in Spryker.

The template pdp.twig requires the following adaptation to enable the integration of product sections.

Adaptation of the Twig template pdp.twig
[...]
<div class="container__inner">
   {% include molecule('product-detail', 'ProductDetailPage') with {
      [...]
   } only %}

   {% include molecule('product-cms-block', 'CmsBlockWidget') ignore missing with {
      data: {
         idProductAbstract: data.product.idProductAbstract,
      }
   } only %}
</div>
[...]

The following code example shows the content of the Twig template product-cms-block.twig. It first contains the name of the molecule and specifies that the specification of a Product SKU is required. This is passed to the Twig template via the parent Twig template pdp.twig. The block body is used to output the contents that are maintained for the corresponding product page.

Twig template product-cms-block.twig
{% extends model('component') %}

{% define config = {
   name: 'product-cms-block'
} %}

{% define data = {
   idProductAbstract: required
} %}

{% block body %}
   {{ fsSpyCmsBlock({ product: data.idProductAbstract }) }}
{% endblock %}

4.4. Creation of dynamic content pages

In addition to the extension of existing static pages or category pages, the FirstSpirit Connect for Spryker Commerce OS module allows the creation of dynamic content pages. These correspond, for example, to the imprint, the data security explanation or the general terms and conditions. Just like the other pages, the dynamic content pages can also be displayed in ContentCreator using the Omnichannel Manager and enriched with editorial content. However, since the dynamic pages are newly created and not only extended, they are initially empty, unlike the other pages.

To create the dynamic content pages, it is also necessary to fill the output channel of the page template within the FirstSpirit project. In addition, the existence of the Twig template fs-content-page.twig is assumed in Spryker. The template is part of the zip file b2c-demo-shop-extensions-<VERSION>.zip included in the delivery.

The section templates contained in the reference project are allowed for all pages. For this reason, no additional extensions are required that go beyond the requirements already described for the Shoppable Video section.

4.4.1. Page template

Just like static pages and category pages, the maintenance of dynamic content pages in the FirstSpirit project is based on a page reference. However, in contrast to the other pages that already exist in the project, dynamic content pages are created using the familiar editing process. For this reason, they are initially empty.

The creation of the page reference in the ContentCreator triggers an event of the Omnichannel Manager, which triggers the creation of the corresponding CMS page in Spryker. This is necessary for displaying the CMS page in the preview.

New dynamic content pages must be created in the Content Pages structure folder. This ensures their storage in the corresponding CaaS collection and thereby their automatic creation in Spryker.

Under certain circumstances it is possible that the FirstSpirit project contains a dynamic content page for which there is no CMS page on the Spryker side. In this case, the first time the content page is called in the ContentCreator, the Omnichannel Manager event is also triggered, which triggers the creation of the corresponding CMS page in Spryker.

The reference project contains the page template contentpage. It is an example for dynamic content pages and maps the Twig template fs-content-page.twig, which has to be created on the Spryker side.

Within the output channel of the page template, various general information is first defined and stored in the JSON object of the CaaS item to be generated: The previewId serves to identify the page reference and allows its decoration in the ContentCreator as well as its mapping to the Spryker Twig template. The automatically determined name and path of this template are specified using the fields template_name and template_path. The page reference is identified using the JavaScript of the Omnichannel Manager.

In addition to the name of the Twig template, the ID of the corresponding CMS page is also determined automatically. It is stored in a hidden input component of the FirstSpirit page template. Since the ID is only required for the preview and is read directly from the page form, it is not required within the CaaS item.

In addition, the pagetype parameter is used to define the page type. In the case of dynamic content pages, the field must have the value cmspage.

The is_active and store_names fields indicate that the Twig template represented by the page is active on the Spryker side and visible in the store selected in the Project settings. The is_searchable field can also be used to configure whether the page can be searched by its name in Spryker. In contrast to the previewId, which only serves for the preview, the unique identifier also allows the referencing of the CaaS item on Spryker side.

Definition of general information in the output channel of the page template
$CMS_SET(json, {:})$
$CMS_SET(previewId, previewId(element:#global.node))$
$CMS_SET(void, json.put("previewId", "" + previewId))$
$CMS_SET(void, json.put("template_name", pt_cmsPageTemplateName))$
$CMS_SET(void, json.put("template_path", "@Cms/templates/fs-content-page/fs-content-page.twig"))$
$CMS_SET(void, json.put("pagetype", "cmspage"))$
$CMS_SET(void, json.put("is_active", "1"))$
$CMS_SET(void, json.put("store_names", [ps_activeStore]))$
$CMS_SET(void, json.put("is_searchable", "1"))$
$CMS_SET(void, json.put("identifier", #global.node.uid))$

In addition to the general information, the localizedAttributes array is used to pass specific information to the JSON object, which Spryker requires for each content page. They are used to define various metadata, a page name, and the URL at which the page will be accessible in Spryker. While the various metadata can optionally be entered by the editor in the form of the page, the URL is a language-dependent mandatory field. It must have the structure /language abbreviation/subpath, with the language abbreviation automatically prefixed to the URL during generation. The importer expects this information in the context of the current language. For this reason, the corresponding language abbreviation, which is passed to each attribute, is determined.

Definition of specific information
$CMS_SET(locale, #global.language.getLocale())$
$CMS_SET(localeAbbr, locale.getLanguage() + "_" + locale.getCountry())$

$CMS_SET(attributes, {:})$
$CMS_IF(pt_url != null && !pt_url.isEmpty())$
   $CMS_SET(url, "/" + locale.getLanguage() + if(!pt_url.startsWith("/"), "/") + pt_url.convert2)$
   $CMS_SET(void, attributes.put("url", {localeAbbr: url}))$
$CMS_ELSE$
   $CMS_SET(void,#global.logError(
      "Missing url for contentpage " + #global.node.uid + ". Contentpages without url can't create and generate."))$
   $CMS_SET(#global.stopGenerate,true)$
$CMS_END_IF$
$CMS_SET(pageName, #global.page.getDisplayName(#global.language))$
$CMS_SET(void, attributes.put("name", {localeAbbr: if(pageName.toString() != null, pageName, "")}))$
$CMS_SET(void, attributes.put("meta_title", {localeAbbr: if(!pt_metaTitle.isEmpty, pt_metaTitle.convert2, "")}))$
$CMS_SET(void, attributes.put("meta_description", {localeAbbr: if(!pt_metaDescription.isEmpty, pt_metaDescription.convert2, "")}))$
$CMS_SET(void, attributes.put("meta_keywords", {localeAbbr: if(!pt_metaKeywords.isEmpty, pt_metaKeywords.convert2, "")}))$
$CMS_SET(void, json.put("localizedAttributes", attributes))$

The page template for dynamic content pages contains the two content areas content_top and content_body. They enable the creation of editorial content, for which different section templates are available in the reference project. Their contents are added one after the other to the JSON object json created in the template.

Determination of editorial content
$CMS_SET(blocks, [])$
$CMS_VALUE(#global.page.body("content_top"))$
$CMS_SET(contentTopBlocks, [])$
$CMS_SET(void, contentTopBlocks.addAll(blocks))$

$CMS_SET(blocks, [])$
$CMS_VALUE(#global.page.body("content_body"))$
$CMS_SET(contentBodyBlocks, [])$
$CMS_SET(void, contentBodyBlocks.addAll(blocks))$

$CMS_SET(resultingBlocks, [])$
$CMS_SET(void, resultingBlocks.addAll(contentTopBlocks))$
$CMS_SET(void, resultingBlocks.addAll(contentBodyBlocks))$
$CMS_SET(void, json.put("blocks", resultingBlocks))$

Unlike static pages and category pages, dynamic content pages have so-called placeholders. These correspond to the content areas of the FirstSpirit page template and contain only references to the CMS blocks assigned to them. After the editorial content has been entered, these references are created for each content area.

The placeholders must also be created in the case of empty content pages. This is especially necessary for the initial creation of the pages.

Creation of the block references
$CMS_SET(placeholder, {:})$
$CMS_SET(contentTopPlaceholder, "")$

$CMS_IF(!contentTopBlocks.isEmpty())$
   $CMS_FOR(contentTopBlock, contentTopBlocks)$
      $CMS_SET(contentTopPlaceholder, contentTopPlaceholder + "{{ fsSpyCmsBlock({name: '" + contentTopBlock.get("block_name") + "'}) }}\n")$
   $CMS_END_FOR$
$CMS_END_IF$

$CMS_SET(contentBodyPlaceholder, "")$
$CMS_IF(!contentBodyBlocks.isEmpty())$
   $CMS_FOR(contentBodyBlock, contentBodyBlocks)$
      $CMS_SET(contentBodyPlaceholder, contentBodyPlaceholder + "{{ fsSpyCmsBlock({name: '" + contentBodyBlock.get("block_name") + "'}) }}\n")$
   $CMS_END_FOR$
$CMS_END_IF$

$-- Placeholders values have to exist in Spryker for synchronization purposes, therefore allowing even empty placeholder values for online CaaS --$
$CMS_IF(!contentTopPlaceholder.isEmpty() || #global.isRelease())$
   $CMS_SET(void, placeholder.put("content_top", {localeAbbr: contentTopPlaceholder}))$
$CMS_END_IF$
$CMS_IF(!contentBodyPlaceholder.isEmpty() || #global.isRelease())$
   $CMS_SET(void, placeholder.put("content_body", {localeAbbr: contentBodyPlaceholder}))$
$CMS_END_IF$

$CMS_SET(void, json.put("placeholder", placeholder))$
$CMS_VALUE(json.toJSON)$

4.4.2. Twig template

In Spryker, the Twig template fs-content-page.twig represents a dynamic content page. Unlike static pages and category pages, dynamic content pages have placeholders. To make the placeholders editable in ContentCreator, it is necessary to use the Twig function fsSpyCms to include them.

Each placeholder represents a content area of the corresponding FirstSpirit page template. For this reason, the name of the placeholder and the name of the content area must be identical. Each of the content areas can contain any number of sections, for which a Twig template must also exist in Spryker.

The FirstSpirit page template contentpage contained in the reference project is an example for dynamic content pages. It has the content areas content_top and content_body and maps the Twig template fs-content-page.twig.

Within the Twig template, specific metadata and the page title are first defined. Then the output of the two placeholders content_top and content_body is handled within the blocks title and content. They reference the CMS blocks assigned to them, which in turn control the output of the editorial content.

The following code example shows the content of the template.

Twig template fs-content-page.twig
{% extends template('page-layout-main') %}

{% define data = {
   title: _view.pageTitle | default('global.spryker.shop' | trans),
   metaTitle: _view.pageTitle | default('global.spryker.shop' | trans),
   metaDescription: _view.pageDescription | default(''),
   metaKeywords: _view.pageKeywords | default('')
} %}

{% block breadcrumbs %}{% endblock %}

{% block title %}
   <!-- CMS_PLACEHOLDER : "content-top" -->
   <div class="cms-page__title">
      {{ fsSpyCms('content_top') | raw }}
   </div>
{% endblock %}

{% block content %}
   <!-- CMS_PLACEHOLDER : "content-body" -->
   <div class="cms-page__content">
      {{ fsSpyCms('content_body') | raw }}
   </div>
{% endblock %}

4.5. Creation of a magazine

Based on the previously described dynamic content pages, the FirstSpirit Connect for Spryker Commerce OS module allows the creation of a magazine. This consists of any number of magazine articles and an overview page. Both the magazine articles and the overview page can be maintained in the ContentCreator just like the other pages. In contrast to them, the magazine articles do not represent different pages, but individual data sets. Within the reference project the data source Magazine Articles exists for their editorial creation and maintenance. For the output of the magazine articles, the corresponding table template is referenced in a dynamic content page. The data for the overview page is referenced via ContentSelects in a section template, which is also integrated in a dynamic content page.

The creation of a new magazine article in ContentCreator triggers an event of the Omnichannel Manager, which triggers the creation of the corresponding CMS page in Spryker. This is necessary for displaying the CMS page in the preview.

For the creation of the magazine articles, it is necessary to fill the output channel of the corresponding page and table template. The overview page requires the filling of the output channel of the corresponding section template. Furthermore, the Twig templates fs-magazine-intro.twig, fs-magazine-overview.twig, fs-magazine-teaser.twig and fs-content-page.twig are required in Spryker. The templates are part of the Spryker module firstspirit-reference-components included in the delivery.

The other section templates contained in the reference project for maintaining editorial content are permitted for all pages. For this reason, they do not require any additional extensions that go beyond the requirements already described for the Shoppable Video section.

4.5.1. Page template

Both the magazine articles and the overview page are based on dynamic content pages. Since they differ in their structure, the magazine articles need their own page template in contrast to the overview page. In the reference project this corresponds to the page template magazine_detail_page and is a copy of a contentpage. However, in comparison to the contentpage, the magazine_detail_page has only one content area, which only allows the integration of the table template of the magazine articles.

The following code fragment shows the output channel of the page template magazine_detail_page:

Output channel of the page template magazine_detail_page
$CMS_SET(json, {:})$
$CMS_IF(#global.pageParams.data.size > 0)$
   $CMS_SET(previewId, previewId(element:#global.node))$
   $CMS_SET(void, json.put("previewId", "" + previewId))$
   $CMS_SET(void, json.put("template_name", pt_cmsPageTemplateName))$
   $CMS_SET(void, json.put("template_path", "@Cms/templates/fs-content-page/fs-content-page.twig"))$
   $CMS_SET(void, json.put("pagetype", "cmspage"))$
   $CMS_SET(void, json.put("is_active", "1"))$
   $CMS_SET(void, json.put("store_names", [ps_activeStore]))$
   $CMS_SET(void, json.put("is_searchable", "1"))$

   $CMS_IF(#global.dataset.formData.tt_page_identifier != null && !#global.dataset.formData.tt_page_identifier.isEmpty())$
      $CMS_SET(void, json.put("identifier", #global.dataset.formData.tt_page_identifier))$
   $CMS_ELSE$
      $CMS_SET(void, #global.logError(
         "Missing page identifier for magazine detail page with entity id '" + #row.id + "'. Magazine detail pages without a page identifier can't be generated."))$
      $CMS_SET(#global.stopGenerate, true)$
   $CMS_END_IF$

   $CMS_SET(locale, #global.language.getLocale())$
   $CMS_SET(localeAbbr, locale.getLanguage() + "_" + locale.getCountry())$

   $CMS_SET(attributes, {:})$
   $CMS_SET(set_entityUrl, #global.dataset.formData.tt_url.convert2)$
   $CMS_IF(set_entityUrl != null && !set_entityUrl.isEmpty())$
      $CMS_SET(url, "/" + locale.getLanguage() + if(!set_entityUrl.startsWith("/"), "/") + set_entityUrl)$
      $CMS_SET(void, attributes.put("url", {localeAbbr: url}))$
   $CMS_ELSE$
      $CMS_SET(void,#global.logError(
         "Missing url for contentpage " + #global.node.uid + "_" + global.dataset.entity.fs_id + ". Contentpages without url can't create and generate."))$
      $CMS_SET(#global.stopGenerate,true)$
   $CMS_END_IF$

   $CMS_SET(pageName, #global.dataset.formData.tt_title.convert2)$
   $CMS_SET(void, attributes.put("name", {localeAbbr: if(pageName.toString() != null, pageName, "")}))$
   $CMS_SET(void, attributes.put("meta_title", {localeAbbr: ""}))$
   $CMS_SET(void, attributes.put("meta_description", {localeAbbr: ""}))$
   $CMS_SET(void, attributes.put("meta_keywords", {localeAbbr: ""}))$
   $CMS_SET(void, json.put("localizedAttributes", attributes))$

   $CMS_VALUE(#global.page.body("container"))$
$CMS_END_IF$

$CMS_VALUE(json.toJSON)$

The output channel of the magazine articles is almost identical to the output channel of dynamic content pages. It differs only in the definitions of the identifier, some attributes and the number of content areas.

The identifier allows the referencing of the CaaS item on Spryker side and must therefore be unique. However, if it were specified within the output channel, all magazine articles would have the same identifier. Due to this it is stored in the form of the corresponding data set.

In contrast to dynamic content pages, the URL is not specified in the page form, but, like the identifier, within the data set. For this reason, it is determined at this point from the input component of the data set. For the page name, the title defined for the magazine article is used. This ensures that each magazine article has a unique page name.

The enclosing If query prevents generation errors in case of an empty data source.

In contrast to dynamic content pages, the editorial content and the block references for the magazine articles are not determined within the page template, but in the output channel of the associated table template. This represents the only permissible content for the container content area.

The overview page corresponds to a dynamic content page. It includes the data required for it using a section template and also allows the addition of further sections. For this reason, it does not need its own page template.

4.5.2. Table template

In contrast to the other pages, the magazine articles represent individual data sets, which are integrated into a content page via content projection. Their maintenance and processing is therefore based on a table template that provides the corresponding form and forms the basis for the corresponding data source.

The table template magazine_articles contained in the reference project consists of two thematic content blocks:
The first block maps the Twig template fs-magazine-intro.twig in Spryker and consists of superordinate intro data, such as a title and various teaser information. For their determination, the render template magazine_intro_block_render_template is referenced within the output channel of the table template, the result of which is stored in the array contentTopBlocks. The output channel of this render template is almost identical to the output channel of the Shoppable Video section and differs only in the evaluation of the available input components.
The second block corresponds to the various sections of the magazine page, which respectively represent the Twig templates belonging to them in Spryker. The sections are combined in a FS_CATALOG, which is stored in the output channel of the table template in the contentBodyBlocks array.

Both arrays are successively added to the JSON object json created in the page template.

Determination of editorial content
$CMS_SET(blocks, [])$

$CMS_RENDER(template: "magazine_intro_block_render_template")$
$CMS_SET(contentTopBlocks, [])$
$CMS_SET(void, contentTopBlocks.addAll(blocks))$

$CMS_SET(blocks, [])$
$CMS_VALUE(tt_sections)$
$CMS_SET(contentBodyBlocks, [])$
$CMS_SET(void, contentBodyBlocks.addAll(blocks))$

$CMS_SET(resultingBlocks, [])$
$CMS_SET(void, resultingBlocks.addAll(contentTopBlocks))$
$CMS_SET(void, resultingBlocks.addAll(contentBodyBlocks))$
$CMS_SET(void, json.put("blocks", resultingBlocks))$

Since the magazine articles are based on dynamic content pages, they also have so-called placeholders. The output channels of the magazine articles and the content pages are therefore almost identical from this point on. They differ only in the output of the placeholder for the body area and in the specification of the previewId. In contrast to the content pages, the body area must also exist in the preview for the magazine articles in order to avoid the prohibited addition of further sections. The previewId specified in this step overwrites the ID of the parent page, which references all magazine articles. This is necessary because the previewId of the page is identical for all magazine articles, while the article IDs are unique.

Creation of the block references
$CMS_SET(placeholder, {:})$
$CMS_SET(contentTopPlaceholder, "")$

$CMS_IF(!contentTopBlocks.isEmpty())$
   $CMS_FOR(contentTopBlock, contentTopBlocks)$
      $CMS_SET(contentTopPlaceholder, contentTopPlaceholder + "{{ fsSpyCmsBlock({name: '" + contentTopBlock.get("block_name") + "'}) }}\n")$
   $CMS_END_FOR$
$CMS_END_IF$

$CMS_SET(contentBodyPlaceholder, "")$
$CMS_IF(!contentBodyBlocks.isEmpty())$
   $CMS_FOR(contentBodyBlock, contentBodyBlocks)$
      CMS_SET(contentBodyPlaceholder, contentBodyPlaceholder + "{{ fsSpyCmsBlock({name: '" + contentBodyBlock.get("block_name") + "'}, isContentEditable = false) }}\n")$
   $CMS_END_FOR$
$CMS_END_IF$

$-- Placeholders values have to exist in Spryker for synchronization purposes, therefore allowing even empty placeholder values for online CaaS --$
$CMS_IF(!contentTopPlaceholder.isEmpty() || #global.isRelease())$
   $CMS_SET(void, placeholder.put("content_top", {localeAbbr: contentTopPlaceholder}))$
$CMS_END_IF$
$CMS_SET(void, placeholder.put("content_body", {localeAbbr: contentBodyPlaceholder}))$

$CMS_SET(void, json.put("placeholder", placeholder))$
$CMS_SET(void, json.put("previewId", "" + previewId()))$

4.5.3. Section template

In addition to the magazine articles, there is an overview page on which the articles are listed in the form of teasers sorted by date in ascending or descending order. In contrast to magazine articles, the editorial content is not rendered via a content projection, but via two ContentSelects. The reference project therefore contains the section template magazine_overview, which can only be used in dynamic content pages.

Editing, deleting or creating a magazine article does not update the overview page in the preview by default. Within the reference project, the section template magazine_overview therefore contains the hidden FS_REFERENCE input component st_dataSource. The data source magazine_articles, which contains all magazine articles, is entered as the default value in this component. With the help of this specification, a service running in the background recognizes the dependency between the corresponding magazine article and the overview page and triggers its update in the preview.

Publishing a magazine article always includes the publication of the overview page. This ensures that the overview page is always up-to-date and contains all published magazine articles in the live status at any time.

The output channel of the section template contains the ContentSelects in the first place. These query all data sets of the data source magazine and stores them in different order in the variables fr_st_teasers_descending or fr_st_teasers_ascending.

ContentSelects in the output channel of the section
<CMS_HEADER>
   <CMS_FUNCTION name="contentSelect" resultname="fr_st_teasers_descending">
      <CMS_PARAM name="schema" value="magazine" />
      <QUERY entityType="magazine_articles">
         <ORDERCRITERIA attribute="date" descending="1" />
      </QUERY>
   </CMS_FUNCTION>

   <CMS_FUNCTION name="contentSelect" resultname="fr_st_teasers_ascending">
      <CMS_PARAM name="schema" value="magazine" />
      <QUERY entityType="magazine_articles">
         <ORDERCRITERIA attribute="date" descending="0" />
      </QUERY>
   </CMS_FUNCTION>
</CMS_HEADER>

The next step is to define various information, which is also contained in all other sections of the reference project. At this point, the output channel is therefore identical to that of the Shoppable Video section.

Definition of various information in the output channel of the section
$CMS_SET(block,{:})$

$-- Defining general block data --$
$CMS_SET(void, block.put("previewId", "" + previewId()))$
$CMS_SET(void, block.put("active", "1"))$
$CMS_SET(void, block.put("store_names",[ps_activeStore]))$
$CMS_SET(void, block.put("template_name", "fs_molecule_block"))$
$CMS_SET(void, block.put("template_path", "@CmsBlock/template/fs_molecule_block.twig"))$

$CMS_SET(set_pageType, json.get("pagetype"))$

$-- Add block name attribute to block object (page type dependent) --$
$CMS_RENDER(template: "block_name_render", rt_pageType: set_pageType, rt_blockObject: block)$

$-- Add additional block attributes to block object (page type dependent) --$
$CMS_RENDER(template: "additional_block_attributes_render", rt_pageType: set_pageType, rt_blockObject: block)$

$-- Add block key attribute to block object (page type dependent) --$
$CMS_RENDER(template: "block_key_render", rt_pageType: set_pageType, rt_blockObject: block)$

$-- Defining placeholder data of the block --$
$CMS_SET(placeholders,{:})$
$CMS_SET(void, block.put("placeholder",placeholders))$

Subsequently, the teaser information is determined in two steps. They consist of a title, an image, a text and a reference and are stored in the teaser array for each data set.

If for a teaser no title is specified or no image is selected, the title and image of the corresponding magazine article will be used for the display on the magazine overview page. If the fields are maintained in both places, the overview page always shows the contents of the teaser.

The first step only considers the information of the Featured Articles, which can be defined in the form of the overview page. The second step is based on the result set of the corresponding ContentSelect and thus includes all magazine articles. In both cases, the render template magazine_teaser_render is referenced, whose output channel only contains the evaluation of the input components available for the teasers. The information determined in the two steps is stored in the arrays featuredTeasers and teasers.

Determination of the teaser data in the output channel of the section
$CMS_SET(featuredTeasers, [])$
$CMS_FOR(for_featuredArticle, st_featuredArticles.values)$
   $CMS_SET(teaser,{:})$
   $CMS_RENDER(template: "magazine_teaser_render", rt_entity: for_featuredArticle.entity, rt_teaser: teaser)$
   $CMS_SET(void, featuredTeasers.add(teaser))$
$CMS_END_FOR$

$CMS_SET(teasers, [])$
$CMS_FOR(for_teaser, #global.context.getVariableValue("fr_st_teasers_"+st_sortingOrder.key))$
   $CMS_SET(teaser,{:})$
   $CMS_RENDER(template: "magazine_teaser_render", rt_entity: for_teaser, rt_teaser: teaser)$
   $CMS_SET(void, teasers.add(teaser))$
$CMS_END_FOR$

The data array contains the editorial content. The previously determined teaser data is added to it. In Spryker, the section is represented by the molecule fs-magazine-overview, whose name is also contained in the data array.

The importer expects the information in the context of the current language. For this reason, the corresponding language abbreviation to which the array data is assigned is determined.

In the preview, the contents of the section are output directly. Otherwise, the last step adds the section to the blocks array defined in the page template, which contains all sections of a page and integrates them into the CaaS item to be generated. Since the overview page is based on a dynamic content page, the corresponding page template is the contentpage.

Processing of editorial content in the output channel of the sectionsection.
$CMS_SET(data, {:})$
$CMS_SET(void, data.put("moleculename", "fs-magazine-overview"))$
$CMS_SET(void, data.put("featuredTeasers", featuredTeasers))$
$CMS_SET(void, data.put("teasers", teasers))$

$CMS_SET(locale, #global.language.getLocale())$
$CMS_SET(localeAbbr, locale.getLanguage() + "_" + locale.getCountry())$
$CMS_SET(localizedData, {:})$
$CMS_SET(void, localizedData.put(localeAbbr, data))$
$CMS_SET(void, block.put("data", localizedData))$

$CMS_IF(#global.is("WEBEDIT") && !isSet(caas_preview_generation))$
   $CMS_VALUE(block.toJSON)$
$CMS_ELSE$
   $CMS_SET(void, blocks.add(block))$
$CMS_END_IF$

4.5.4. Twig Templates

Both the magazine articles and the overview page are based on dynamic content pages and are therefore represented by the Twig template fs-content-page.twig on Spryker side. This contains the placeholders content_top and content_body, which reference the CMS blocks assigned to them. In this way, the molecule fs-magazine-intro.twig is called for the intro data of the magazine articles and the molecule fs-magazine-overview.twig for the teaser information of the overview page. The molecule fs-magazine-overview.twig in turn references the molecule fs-magazine-teaser.twig, which provides the teaser information of a single magazine article. All three molecules control the output of the editorial content.

The output of the remaining sections of the magazine articles takes place via the Twig Templates belonging to them. Both the templates of these sections as well as the molecules for the magazine articles and the overview page are part of the Spryker module firstspirit-reference-components included in the delivery.

As with the molecules of the Shoppable Video section and the Banner, the names of the molecules are defined within the three Twig Templates for the magazine articles and the overview page and then the fsBlockData object is created. It provides access to the structured data defined in the output channel of the FirstSpirit table template. The fsBlockData object and the JSON object created in FirstSpirit thus each have the same structure.

The block body describes the output of the editorial contents in the molecules. In the template for the magazine articles, these correspond to their intro data, which consist of a title, a sub-title, and a banner. The template for the overview page contains the teaser data of the magazine articles, which consist of an image, a title, a text and a reference. The teaser data is provided using the molecule for the teasers.

The following code examples show the contents of the molecules.

Twig template fs-magazine-intro.twig
{% extends model('component') %}

{% define config = {
    name: 'fs-magazine-intro'
} %}

{% define data = {
    fsBlockData: [],
} %}

{% block body %}
   <article class="magazine">
      <header>
         <h1>{{ data.fsBlockData.title }}</h1>
         {% if data.fsBlockData.subtitle is defined %}
            <h2>{{ data.fsBlockData.subtitle }}</h2>
         {% endif %}
         {% if data.fsBlockData.banner.imageUrl is defined %}
            <figure class="picture picture-responsive">
               <img class="img-fluid" src="{{ data.fsBlockData.banner.imageUrl | raw }}"
                  {% if  data.fsBlockData.banner.previewId is defined %}
                     data-preview-id="{{ data.fsBlockData.banner.previewId }}"
                  {% endif %}
                  data-tpp-context-image-resolution="MAGAZINE_ARTICLE_BANNER">
            </figure>
         {% endif %}
      </header>
   </article>
{% endblock %}
Twig template fs-magazine-overview.twig
{% extends model('component') %}

{% define config = {
    name: 'fs-magazine-overview'
} %}

{% define data = {
    fsBlockData: [],
} %}

{% block body %}
   {% if isFsPreview() %}
      <div style="padding-top: 24px;"></div>
   {% endif %}
   {% if data.fsBlockData.featuredTeasers is not empty %}
      <h2 style="text-align: left;">Featured</h2>
   {% endif %}

   <div class="featured-magazine-teasers">
      {% for teaser in data.fsBlockData.featuredTeasers %}
         {% include molecule('fs-magazine-teaser', 'FirstSpiritReferenceComponents') with {
            data: {
               fsBlockData: teaser
            }
         } only %}
      {% endfor %}
   </div>

   {% if data.fsBlockData.featuredTeasers is not empty %}
      <hr class="magazine-featured-divider">
   {% endif %}

   <div class="magazine-teasers">
      {% for teaser in data.fsBlockData.teasers %}
         {% include molecule('fs-magazine-teaser', 'FirstSpiritReferenceComponents') with {
            data: {
               fsBlockData: teaser
            }
         } only %}
      {% endfor %}
   </div>
{% endblock %}
Twig template fs-magazine-teaser.twig
{% extends model('component') %}

{% define config = {
   name: 'fs-magazine-teaser'
} %}

{% define data = {
   fsBlockData: [],
} %}

{% block body %}
   <article class="magazine-teaser"
      {% if data.fsBlockData.previewId is defined %}
         data-preview-id="{{ data.fsBlockData.previewId }}"
      {% endif %}>
      {% set cmsPageUrl = getCmsPageUrl(data.fsBlockData.page_identifier) %}

      {% if cmsPageUrl is not null %}
         <a href="{{ cmsPageUrl }}">
      {% endif %}

         {% if data.fsBlockData.picture.imageUrl is defined %}
            <figure class="picture picture-responsive">
               <img src="{{ data.fsBlockData.picture.imageUrl }}"
                  {% if  data.fsBlockData.picture.previewId is defined %}
                     data-preview-id="{{ data.fsBlockData.picture.previewId }}"
                  {% endif %}
                  data-tpp-context-image-resolution="CONTENT_IMAGE">
            </figure>
         {% endif %}
         <h3>{{ data.fsBlockData.title | raw }}</h3>
         <p>{{ data.fsBlockData.text | raw }}</p>

      {% if cmsPageUrl is not null %}
         </a>
      {% endif %}
   </article>
{% endblock %}

4.6. Extension of the main navigation

In addition to the different page types, the main navigation of the Spryker B2C Demo Shop can be edited in the ContentCreator. In FirstSpirit the navigation is represented by a global page, a section template and a technical page reference. These enable the creation of further menu items in the first level of the navigation and their transfer to the Preview CaaS and to the Online CaaS.

In order to enable the main navigation to be extended, the following Twig templates must also be adapted:

  • navigation-header.twig

  • navigation-multilevel-node.twig

4.6.1. Global page

The navigations of the Spryker B2C Demo Shop are represented in FirstSpirit via the page template navigation_extension, which is used to automatically create a global page for each navigation. It includes an FS_CATALOG input component, which uses the section template navigation_node_reference to refer all start nodes of the menu items maintained in FirstSpirit. Each of these start nodes corresponds to a structure folder, which is to be selected within an editing dialog in the ContentCreator. Therefore the main navigation in the ContentCreator has a button which allows the addition of new menu items in the first level of the navigation. If a referenced structure folder contains further subfolders, these are automatically interpreted as sub-items of the new menu item.

The button for adding further menu items is only visible for those roles that have the right to edit and delete. Otherwise the button is hidden.

img navigation
Figure 28. Button to extend the navigation

4.6.2. Section template

The global page navigation_extension contained in the reference project enables the extension of the main navigation in the ContentCreator. The navigation can be enhanced by adding menu items on the first level, each of which corresponds to a structure folder and serves as a start node. Subfolders contained in such a structure folder are automatically interpreted as sub-items of the new menu item.

For all new start nodes, the FS_CATALOG input component of the global page is enhanced with an entry based on the navigation_node_reference section template. This section template in turn contains an FS_REFERENCE input component, which is used to reference the respective menu item.

In its output channel, the section template additionally contains the navigation function fr_mainNav. Starting from the selected structure folder, this function generates the sub-navigation of the new menu item for this folder and the subfolders contained in it. All these sub-navigations of the individual menu items are combined using the technical page template navigations and transferred to the CaaS.

4.6.3. Page template

In addition to the global page the reference project contains the technical page reference navigations. This is used to transfer all navigation changes made in FirstSpirit to the CaaS.

Using the script get_navigation_gca_pages, the page reference determines all global pages, which are based on the page template navigation_extension. The global pages represent the navigations in FirstSpirit that can be extended using the ContentCreator. Within the reference project, this is the main navigation of the Spryker B2C Demo Shop.

Each of these global pages contains an FS_CATALOG input component, which uses the section template navigation_node_reference to reference the start nodes of all menu items maintained in FirstSpirit. The section template in turn generates a sub-navigation for each of these start nodes. The technical page reference combines all these sub-navigations and creates a JSON document containing all navigations that are extendable in FirstSpirit.

For the release of the navigation changes made, the reference project provides the workflow Release Navigation, which requires the release right for its execution. This workflow only performs the release, but not the publication of the corresponding global page. At the same time, it triggers a script that ensures that the technical page reference is transferred to the Preview CaaS. The publication of the adaptations and their transfer to the Online CaaS only takes place in the context of a full generation.

4.6.4. Twig templates

In addition to the described FirstSpirit templates, the adaptation of the following Twig templates is required in Spryker, to enable the extension of the main navigation of the Spryker B2C Demo Shop in the ContentCreator.

  • navigation-header.twig

  • navigation-multilevel-node.twig

The necessary adjustments are part of the installation and are therefore not described here.

In Spryker, the Twig templates represent the main navigation. They reference the menu items and control their display in the preview and in the live state. The adaptations provide the main navigation with a button in the ContentCreator, which allows the addition of further menu items.

If other navigations should be editable in the ContentCreator, only an adjustment of the corresponding Twig templates is necessary. The chapter navigation maintenance explains the required steps for this.

The editorial contents maintained in FirstSpirit can contain the following references, which link them with other contents. They are based on various link templates that are already included in the reference project.

All link templates exist twice because they differ in the way they are integrated: In contrast to the general references, the DOM references can only be used within DOM Editors - according to their name.

Category and product link

The FirstSpirit Connect for Spryker Commerce OS module provides a report for products and categories in ContentCreator. They serve to display the category and product information coming from Spryker, which can be used for links to category or product detail pages.

Therefore, the reference project contains the link templates category_link and product_link as well as dom_category_link and dom_product_link, which correspond to a category or product link. They enable category or product detail pages to be referenced and are equivalent to each other.

In contrast to a category, a referenced product is identified by its sku and not by the node ID.

Content page link

The link templates content_link and dom_content_link contained in the reference project correspond to a content page link. This allows referencing static pages and dynamic content pages maintained within the FirstSpirit project. Within the reference project, the selection of the link target is therefore restricted to page references in the structure folder content_pages.

For editors it is not obvious whether a link target is a dynamic content page or a static page. However, since static pages do not have a URL by default, links to them would generally not be possible. The project settings therefore provide a way of assigning a URL to them. The technical adjustments made in the reference project are described in the following subchapter.

Magazine article link

The reference project contains the link templates magazine_article_link and dom_magazine_article_link, which correspond to a magazine article link. Like the content page link, it enables the referencing of editorial content maintained within the FirstSpirit project. In contrast to this, the magazine article link refers to magazine articles according to its name.

External link

Just like the content page link and the magazine article link described above, the external link enables the referencing of editorial content. However, in contrast to them it refers exclusively to external content.

Search link

The reference project contains the link templates search_link and dom_search_link, which correspond to a search link. It enables the linking of a search result page to a defined search term.

All of the link templates for the DOM references contain in their output channel a call to the corresponding widget which controls the Spryker-side output of the reference. This way, the HTML for the reference is generated automatically. A project-specific adaptation of the HTML is not possible.

Widget call within the DOM category reference
{% widget 'FirstSpiritCategoryLinkWidget' args [$CMS_VALUE(lt_category.values[0].getNodeId)$, "$CMS_VALUE(lt_linkText.convert2)$"] only %}{% endwidget %}

In contrast, the link templates for general references only contain the definition of two parameters. They thus follow the same concept as the page and section templates. In this way, only the URL of the reference is generated and the HTML is freely definable on the Spryker side.

Parameter definition within the category reference
$CMS_SET(set_id)$
   $CMS_VALUE(lt_category.values[0].getNodeId)$
$CMS_END_SET$
$CMS_SET(void, set_link.put("id", set_id.toString()))$
$CMS_SET(void, set_link.put("type","category"))$

Processing the general references is controlled by the link.twig Twig template, which is part of the FirstSpiritPreview module and determines the corresponding URL based on the reference type.

Twig template link.twig
{% define data = {link: link} %}
{%- if data.link.type is defined -%}
   {%- set type = data.link.type -%}
   {%- if data.link.id is defined and data.link.id is not empty -%}
      {%- set id = data.link.id -%}
      {%- if  type == "cmspage" -%}
         {%- set url = getCmsPageUrl(id) -%}
      {%- elseif type == "category" -%}
         {%- set url = getCategoryPageUrl(id) -%}
      {%- elseif type == "product" -%}
         {%- set url = getProductPageUrl(id) -%}
      {%- endif -%}
   {%- else -%}
      {%- if type == "search" -%}
         {%- set url = path('search') ~ '?q=' ~ data.link.query -%}
      {%- elseif type == "external" -%}
         {%- set url = data.link.url -%}
      {%- elseif type == "static" -%}
         {%- set url = data.link.url -%}
      {%- endif -%}
   {%- endif -%}
{%- endif -%}

{%- if url is defined and url is not null -%}
   {{- url -}
}{%- endif -%}

The Twig template is called in the individual components that represent the FirstSpirit sections in Spryker:

Example call for the Twig template link.twig
 <a href="{% include template('link', 'FirstSpiritPreview') with { link: data.fsBlockData.link } %}">

Alternatively, the Twig functions for determining the general references can also be called directly:

calls of the Twig functions to determine the general references
{% set url = getCmsPageUrl(cms_page_id) %}
{% set url = getProductPageUrl(productId) %}
{% set url = getCategoryPageUrl(categoryId) %}

When creating content page links, editors are not able to see any difference between static and dynamic pages. However, since static pages do not have a URL by default, links to them would generally not be possible. The project settings therefore provide a way of assigning a URL to them. For this purpose, the project settings contain the FS_CATALOG ps_staticLinks, which is filled via the Static Page Templates Import button.

A future modification of the URLs defined in the project settings requires the execution of both the full generation and the preview generation.

The button Static Page Templates Import calls the executable ProjectPropertiesStaticPageUrlListExecutable which determines the content of the template folder static_pages. For each template contained in this folder it adds an entry to the FS_CATALOG based on the technical section template static_page_url. Every entry contains a reference to the respective template and a language-dependent field for the relative URL to be defined.

For creating the entries and referencing the templates automatically within these entries, the button has the following parameters, which are predefined within the reference project:

  • staticPageCatalog: This parameter defines the FS_CATALOG component in the project settings.

  • staticPageUrlTemplateUid: This parameter specifies the reference name of the technical section template on which the entries in the FS_CATALOG are created.

  • staticPageTemplateFolder: This parameter defines the reference name of the template folder that contains all templates for static pages.

  • pageTemplateFsReference: The name of the FS_REFERENCE component from the technical section template is assigned to this parameter for referencing the static page templates automatically.

To enable content page links to static pages, the URLs defined in the project settings must be available in the link templates. Therefore, the output channel of the project settings contains the following code that stores the URL of a static page template in the variable set_ps_staticLinks.

output channel of the project settings
$CMS_SET(set_ps_staticLinks,{:})$
$CMS_FOR(for_staticLink,ps_staticLinks.filter(x->!x.item.st_pageTemplate.isEmpty))$
   $CMS_SET(void, set_ps_staticLinks
      .put(
         for_staticLink.item.st_pageTemplate.get.uid,
         for_staticLink.item.st_url.convert2
      )
   )$
$CMS_END_FOR$
$CMS_SET(set_ps_contentPageList,["contentpage"])$

Within the reference project the variable is queried in the output channel of the content page link. This way editors do not have to distinguish between static and dynamic content pages when selecting a link target.

Code snippet of the content page link
$CMS_SET(set_pageUid,lt_pageRef.get().page.template.uid)$
$CMS_IF(set_pageUid == "contentpage")$
[...]
$CMS_ELSIF(!set_ps_staticLinks.isEmpty)$
   $CMS_IF(set_ps_staticLinks.containsKey(set_pageUid) && !set_ps_staticLinks.get(set_pageUid).isEmpty)$
      $CMS_SET(void, set_link.put("url", set_ps_staticLinks.get(set_pageUid)))$
      $CMS_SET(void, set_link.put("type","static"))$
      [...]
   $CMS_END_IF$
   [...]
$CMS_END_IF$

5. Use cases

Within the progress of a project, different situations can arise that require certain steps to be taken. The following subchapters describe situations that can typically occur in integration projects, and describe the necessary steps for resolving them by using the reference project supplied as a reference.

5.1. Content Exchange

The integration requires that the data within the FirstSpirit project, the corresponding CaaS instance and in Spryker are consistent to each other. However, this consistency will be violated by the following situations that may occur during the progress of a project:

Project transfer to a new deployment environment (Dev, Prod, QA)

Creating a new deployment environment always involves importing an existing FirstSpirit project. The import creates a constellation in which contents already exist in FirstSpirit, whereas the CaaS instances and Spryker are empty or have different contents.

ContentTransport

In contrast to the previous point, which involves the transfer of entire projects to another deployment environment, the ContentTransport is used to transfer editorial content between two projects. During the progress of a project, for example, the contents of the production environment can be transferred back to the test and development environment in this way, to exchange existing test data with real information.

Contrary to the project transfer, the target project therefore already exists and will not be created. This implies that the corresponding CaaS instances and Spryker also already contain content. After the ContentTransport these contents differ from those of the FirstSpirit project.

The transfer of content to the Preview CaaS and its generation in Spryker is done automatically each time new content is saved. In contrast, existing content - as in the situations described - would have to be saved again separately for their persistence. For this reason, the reference project has the schedule Spryker Preview Deployment, which handles both the filling of the Preview CaaS and the updating of the preview contents in Spryker. It is created within the ServerManager in the Project properties  Schedule management area and can only be executed by project administrators.

The schedule requires all configuration steps to have been completed beforehand - on both the FirstSpirit and the Spryker side.

Once the schedule has been performed, the Preview CaaS is filled with the preview contents of the FirstSpirit project. Furthermore, on the spryker side, all content pages and magazine pages included in the project were created in addition to the existing static pages and category pages. Afterwards the contents are available for the editors in the preview.

The schedule Spryker Preview Deployment can be executed at any time. If required, the consistency of the preview data can thus be ensured whenever needed.

If, in addition to the consolidation of the preview content, a transfer to the storefront is required, the content needs to be released and published. The reference project provides a workflow for the release, which also enables the publication of single pages. In addition, it contains the Spryker Deployment schedule, which transfers all released contents of the project to the Online CaaS and triggers their import to Spryker. The schedule can be started by ChiefEditors using the action Publication available in ContentCreator.

5.2. Navigation maintenance

In addition to a main navigation, a shop generally has numerous other navigations. The integration realised with the FirstSpirit Connect for Spryker Commerce OS module offers the possibility to maintain these navigations with FirstSpirit and to add further menu items. This requires the following steps to be carried out first.

The steps described refer to the enhancement of an existing navigation. If instead an existing navigation should be managed completely in FirstSpirit, its menu items must be deleted in Spryker.

Alternatively, a new navigation can be maintained in FirstSpirit. For this, it must first be created in Spryker.

Replacement of the ContentNavigationWidget

The FirstSpiritPreviewContentNavigationWidget contained in the delivery replaces the ContentNavigationWidget existing in Spryker. In Spryker, therefore, a replacement must be done by deleting the ContentNavigationWidget and adding the FirstSpiritPreviewContentNavigationWidget instead.

Addition of a further menu item

In the preview, an additional menu item is required to display the navigation. It serves as a placeholder and must be added within the twig template that represents the first level menu items:

Addition of a further menu item
[..]
<ul class="menu {{ menuClass }} grid grid--center grid--no-wrap">
   {% for node in data.nodes %}
      [..]
   {% endfor %}

   {% if isFsPreview() %}
      <li class="fs-navigation-extension"
         style="padding-left: 1rem; transform: translate(0,15% );">
      </li>
   {% endif %}
</ul>
[..]
Definition of the Preview ID

In addition to the placeholder, a preview ID is required for the preview. It must be defined in the twig template that controls the output of the individual menu items:

Definition of the Preview ID
{% block attributes %}
   {% if isFsPreview() and data.node.previewId is defined and data.node.previewId is not empty %}
      data-preview-id="{{ data.node.previewId }}" style="min-height: unset;"
   {% endif %}
{% endblock %}

After the changes have been made, it may be necessary to empty the cache for the twig templates and to rebuild the frontend.

After a successful completion of the described steps, the corresponding navigation in ContentCreator is extended by a button, which allows the addition of further menu items. The addition of further menu items requires the right to edit and delete. If a user does not have these rights, the button is hidden.

img navigation
Figure 29. Button for extending the navigation

A click on the button opens an editing dialog, in which a structure folder has to be selected as target of the new menu item. If the referenced structure folder contains further subfolders, these are automatically displayed as sub-items of the new menu item.

Mixing menu items maintained in FirstSpirit and those originating from Spryker is not possible. For this reason, menu items created using the ContentCreator are only appended to the existing navigation.

The changes done to the navigation finally require a release. Therefore, the workflow Release Navigation can be executed via via a button that appears by hovering over the navigation. This workflow only performs a release, but not a publication. The publication of the adaptations only takes place in the context of a full generation.

The button to execute the workflow requires the release right. If a user does not have this right, hovering over the navigation has no effect.

5.3. Maintenance of page attributes

In contrast to the sections, the content pages do not have an EasyEdit frame, and so neither the buttons provided with it. Therefore, no direct editing options are available for content pages in the ContentCreator. Because of that, the Actions menu contains the menu item Edit Page, which is only visible for content pages. It opens the edit dialog of the page and allows the maintenance of page attributes and metadata. The attributes are required both for creating the corresponding page in Spryker and for SEO configuration.

actions editpage
Figure 30. Edit Page

5.4. Removal of orphaned product and category pages

In the course of a project, a Spryker-sided deletion or deactivation of no longer needed products or categories can occur at any time. However, these are possibly still referenced within the FirstSpirit project. The deletion or deactivation results in orphaned product and category pages, which can cause errors and warnings during the import. These orphaned pages can no longer be accessed and therefore cannot be identified by editors.

The ContentCreator therefore provides the report Lost and Found. It shows an overview of all orphaned pages within the FirstSpirit project and allows filtering for product or category pages. Each entry has a delete button that appears when hovering over it and, after confirming a security question, triggers a delete workflow. The workflow removes the corresponding page from the FirstSpirit project, releases the parent structure, and finally performs a full deployment.

The execution of the delete workflow requires both delete and release rights. If the editor is missing one of these rights, the delete button is hidden for him and the removal of orphaned pages is therefore not possible.

img lostandfound
Figure 31. Report Lost and Found

FirstSpirit Connect for Spryker Commerce OS is a product of Crownpeak Technology GmbH, Dortmund, Germany. Only a license agreed upon with Crownpeak Technology GmbH is valid for using the module.

Details regarding any third-party software products in use but not created by Crownpeak Technology GmbH, as well as the third-party licenses and, if applicable, update information can be found in the file THIRD-PARTY.txt included with the module.

All pictures contained in the delivered reference project FirstSpirit Connect for Spryker Commerce OS Reference Project are taken from the provider pixabay.

7. Help

The Technical Support of the Crownpeak Technology GmbH provides expert technical support covering any topic related to the FirstSpirit™ product. You can get and find more help concerning relevant topics in our community.

8. Disclaimer

This document is provided for information purposes only. Crownpeak Technology GmbH may change the contents hereof without notice. This document is not warranted to be error-free, nor subject to any other warranties or conditions, whether expressed orally or implied in law, including implied warranties and conditions of merchantability or fitness for a particular purpose. Crownpeak Technology GmbH specifically disclaims any liability with respect to this document and no contractual obligations are formed either directly or indirectly by this document. The technologies, functionality, services, and processes described herein are subject to change without notice.