Developer Information

Important

The English version of this Visual CMS documentation serves only as a basis and for developers and does not contain all details. More comprehensive documentation can be found in the German documentation.

Widgets are the building blocks of Visual CMS pages — each widget represents a configurable content element such as a text block, image, or carousel. The module ships with a set of built-in widgets and can be extended with custom ones to fit specific project needs.

By default, the module uses the Bootstrap 5 grid system; a custom grid system can be plugged in when needed.

Theme Integration

The Visual CMS shortcode widgets currently supplied are compatible with the APEX theme. For other themes, minor adjustments may be necessary.

The following information refers to the Twig template engine.

Theme Extensions

Visual CMS extends the default shop theme via Twig template extensions located in views/twig/extensions/themes/default/.

base.html.twig extends the shop’s base layout to inject Bootstrap CSS and jQuery/Bootstrap JS. Both can be disabled through module settings — if the theme already provides jQuery or a custom grid framework is used, the corresponding assets can be turned off to avoid conflicts.

page/info/content.html.twig handles CMS page rendering and supports several content-level options:

  • Full-width layout — removes the surrounding container, allowing the entire page to span the full viewport width.

  • Hide title — suppresses the page heading.

  • Landing page mode — injects the content into oxidBlock_pageBody instead of oxidBlock_content and uses the bare base layout, bypassing the standard page wrapper entirely.

More information about Twig in modules can be found here in the developer documentation.

Widget Creation

Note

Internally, widgets are referred to as shortcodes in the codebase and configuration.

To create a custom widget, ensure your shortcode class:

  • Is registered in services.yaml as a separate service

  • Has the tag visualcms.shortcode.tag.twig in its service definition

  • Implements OxidEsales\\VisualCmsModule\\ShortCode\\ShortCodeInterface

  • Defines a unique shortCodeTag in its metadata — the identifier used internally, such as "carousel", "image", or "hero"

A working example (GoogleMap shortcode) can be found in the vcms-examples repository.

OxidEsales\VcmsExamples\ShortCode\GoogleMap:
  class: OxidEsales\VcmsExamples\ShortCode\GoogleMap
  tags: [ 'visualcms.shortcode.tag.twig' ]

BaseShortCode Usage

BaseShortCode is an abstract class that provides a default implementation of common ShortCodeInterface methods, reducing the boilerplate needed when creating a custom shortcode. It is recommended to extend it rather than implementing the interface directly.

ShortCodeInterface

  • public function getMetadata(): ShortCodeMetadataInterface;

    Returns the shortcode metadata. Use the OxidEsales\\VisualCmsModule\\DTO\\ShortCodeMetadata class as the implementation.

  • public function getTemplate(): string;

    The path to the shortcode template. Prefix the path with your module ID, for example: @ddoevisualcms/shortcodes/visualcms_shortcode_tabs

  • public function getOptions(): OptionList;

    Returns the list of configurable options for this shortcode, wrapped in an OxidEsales\\VisualCmsModule\\DTO\\OptionList object. OptionList enforces a consistent structure and prevents misconfiguration.

    Example:

    public function getOptions(): OptionList
    {
        $shopLanguage = Registry::getLang();
    
        return new OptionList([
            'id' => new SelectFromDataOption(
                // Label name
                label: $shopLanguage->translateString('DD_VISUAL_EDITOR_WIDGET_ARTICLE'),
                // Specifies a method which will be used for a live search
                data: 'searchArticle',
                // placeholder name
                placeholder: $shopLanguage->translateString('DD_VISUAL_EDITOR_WIDGET_CHOOSE_ARTICLE'),
                // fields that are also taken into account for a selection (only for type "select")
                dataFields: ['name' => 'label']
            ),
            'articletype' => new SelectOption(
                label: $shopLanguage->translateString('DD_VISUAL_EDITOR_WIDGET_ARTICLE_TYPE'),
                values: [
                    'grid' => $shopLanguage->translateString('DD_VISUAL_EDITOR_WIDGET_ARTICLE_TYPE_GRID'),
                    'line' => $shopLanguage->translateString('DD_VISUAL_EDITOR_WIDGET_ARTICLE_TYPE_LINE'),
                ],
                defaultValue: 'grid'
            ),
            'name' => new HiddenOption()
        ]);
    }
    

    Note

    OptionList accepts an array of OxidEsales\\VisualCmsModule\\Option\\ShortCodeOptionInterface objects. The built-in option types cover most common cases. Custom option types can be implemented and used directly if needed.

  • public function prepareTemplateParams(GridItemInterface $gridItem): array;

    Prepares variables to be passed to the shortcode template. The returned array is extracted as template variables, making each key directly accessible in the template. See the next sections for details.

ShortCodeMetadataInterface

ShortCodeMetadata accepts the following constructor parameters:

  • shortCodeTag

    The unique identifier of the shortcode, e.g. "carousel", "image", "hero". Must be unique across all registered shortcodes.

  • isWidget

    Boolean. Set to true if the shortcode should appear as a selectable widget in the editor. Set to false for internal shortcodes that are not directly placeable from the widget list.

  • title

    The translation string key for the widget title shown in the editor.

  • previewOptionKey

    The key of the option whose value is shown as a preview text in the admin content grid. For example, the Text widget uses "content", so the text content is shown directly in the grid.

  • backgroundColor

    The background color of the widget tile in the admin content grid, in HTML format, e.g. '#ccbbaa'.

  • icon

    The CSS class for the widget icon in the editor. As of Visual CMS 10, Bootstrap Icons are used (see Bootstrap Icons). Earlier versions used Font Awesome.

Example:

public function getMetadata(): ShortCodeMetadataInterface
{
    return new ShortCodeMetadata(
        shortCodeTag: 'text',
        isWidget: true,
        title: 'DD_VISUAL_EDITOR_SHORTCODE_TEXT',
        previewOptionKey: 'content',
        backgroundColor: '#3498db',
        icon: 'bi-text-left' // Bootstrap Icons (VCMS 10+); was 'fa-align-left' in earlier versions
    );
}

prepareTemplateParams() Method

The following describes the general process for all widgets, using the Text widget as an example.

prepareTemplateParams() is called for every widget before rendering. The returned array is extracted as template variables, making each key directly accessible in the shortcode template. When extending BaseShortCode, calling parent::prepareTemplateParams($gridItem) already retrieves the option values via $gridItem->getWidgetConfiguration()->getOptionValues() and returns them as an array, so you only need to add or modify values on top of that.

public function prepareTemplateParams(GridItemInterface $gridItem): array
{
    $params = parent::prepareTemplateParams($gridItem);

    if (!empty($params['background_image'])) {
        $params['background_image'] = $this->mediaFacade->getMediaUrl($params['background_image']);
    }

    if (!empty($params['content'])) {
        $params['content'] = $this->templateRenderer->renderFragment($params['content'], md5($params['content']));
    }

    return $params;
}

The raw option values stored for a widget look like this:

{"content":"some example text","background_color":"#ef9d9dff","background_image":"237-536x354.jpg","background_fixed":"","fullwidth":"","class":""}

After being processed by prepareTemplateParams(), the data is available in the template as an array:

[
    'content' => "some example text",
    'background_color' => "#ef9d9dff",
    'background_image' => "237-536x354.jpg",
    'background_fixed' => "",
    'fullwidth' => "",
    'class' => ""
]

In addition to the option values, the template typically has access to these extra variables:

  • shortcode — provided by BaseShortCode when extending it

  • calculatedClasses — provided by ColumnLayoutTemplateParamsDecorator

  • oView and oViewConf — provided by ViewInformationTemplateParamsDecorator

Additional logic can be added to existing widgets via service decoration.

Services

Since shortcodes are registered as Symfony services, any other service can be injected via the constructor:

public function __construct(
    protected \OxidEsales\VisualCmsModule\Service\Categories $categoriesService
) {
}

Note

All built-in widgets are located in src/ShortCodeCollection/ShortCode/. The vcms-examples repository provides additional examples of creating and extending existing widgets.

Overriding an Existing Shortcode

Overriding an existing shortcode replaces its class in the service container. The example below overrides the Text shortcode to add an additional Heading field, as demonstrated in the vcms-examples repository.

OxidEsales\VisualCmsModule\ShortCode\Text:
  class: OxidEsales\VcmsExamples\ShortCode\Text
  tags: [ 'visualcms.shortcode.tag.twig' ]

Extending an Existing Shortcode

While overriding can technically be used to extend a shortcode, the recommended approach is service decoration — it avoids replacing the original class entirely and is more maintainable.

A concrete example of shortcode decoration can be found in the DecorationExample.php in the vcms-examples repository.

Register the decorator in services.yaml:

OxidEsales\VcmsExamples\DecorationExample:
  decorates: OxidEsales\VisualCmsModule\ShortCodeCollection\ShortCode\Action\Action
  arguments: ['@.inner']

Overriding a Shortcode Template

To override only the template for a shortcode without changing its logic, register a ThemeTemplatesConfiguration service tagged with shortcode.themeTemplates:

vcmsTemplateOverrideForApexTheme:
  class: OxidEsales\VisualCmsModule\DTO\ThemeTemplatesConfiguration
  arguments:
    $themeId: 'apex'
    $templates:
      column: '@@oe_vcmsexamples/shortcodes/vcmsexamples_shortcode_column'
      action: '@@oe_vcmsexamples/some_other_template_for_overwriting_the_original_action_template_for_apex'
  tags: [ 'shortcode.themeTemplates' ]

Frontend Grid

Layout configuration settings for a shortcode are accessible via GridItemInterface using the getLayoutConfiguration() method. It returns a LayoutConfigurationInterface with a dedicated method per device size — getSmallMobileConfiguration(), getMobileConfiguration(), getTabletConfiguration(), getLaptopConfiguration(), getDesktopConfiguration(), and getLargeDesktopConfiguration(). Each returns a DeviceLayoutConfigurationInterface exposing getWidth(), getOffset(), and isHidden().

Custom Grid System

Attention

The custom grid system settings are deprecated and will be removed in a future version. Use the per-device class pattern settings in the Frontend Grid section of the module settings instead.

The deprecated settings — blCustomGridFramework, sGridColPrefix, sGridOffsetPrefix, sGridRow, and blGridWordNumbers — provided a simple prefix-based mechanism for custom grids, where a column class was assembled by concatenating a prefix (e.g. col-sm-) with a numeric size.

The replacement is a set of per-device module settings accessed through FrontendLayoutSettingsInterface. Instead of a single prefix, each device size (small-mobile, mobile, tablet, laptop, desktop, large-desktop) has separate configurable class patterns for column size, offset, and visibility, giving full control over the generated calculatedClasses variable without the limitations of the prefix approach. For even greater control, the interface can be implemented directly and registered as a service to replace the default.

Frontend Architecture

With Visual CMS 10, part of the frontend codebase was converted to TypeScript. The frontend is structured around services — TypeScript classes that encapsulate specific functionality and can be composed together.

Dependency injection between these services is handled by tsyringe, a lightweight IoC container for TypeScript. It allows services to declare their dependencies through constructor injection and have them resolved automatically at runtime.

Extending the WYSIWYG Editor

The WYSIWYG Editor (part of the Content & Media Bundle) can be extended with custom Summernote plugins and toolbar configurations. For detailed documentation, see WYSIWYG Editor.