diff --git a/source/_static/images/developer/content-processor-script-sample-result.webp b/source/_static/images/developer/content-processor-script-sample-result.webp new file mode 100644 index 000000000..bdb4bc20d Binary files /dev/null and b/source/_static/images/developer/content-processor-script-sample-result.webp differ diff --git a/source/by-role/developer/common/content-modeling/content-modeling.rst b/source/by-role/developer/common/content-modeling/content-modeling.rst index 99abf05a9..d047a88b1 100644 --- a/source/by-role/developer/common/content-modeling/content-modeling.rst +++ b/source/by-role/developer/common/content-modeling/content-modeling.rst @@ -1943,6 +1943,40 @@ To access the ``controller.groovy`` script from Studio: - In the ``Properties Explorer`` on the right side, look for the ``Controller`` field. This field contains the ``controller.groovy`` file. Click on the field then click on the pencil icon in the field to edit the script. +The following variables are available for server-side controllers in addition to the :ref:`global groovy variables `: + +.. list-table:: + :header-rows: 1 + + * - Name + - Description + * - site + - The site to use + * - user + - The user running the script + * - path + - The path of the content + * - contentType + - The content type the script is being executed from + * - contentLifecycleOperation + - The content operations, e.g. COPY, RENAME, etc. + * - contentLoader + - Used for loading content from the repository + * - lifecycleContent + - Provides access to the content being written + * - logger + - The GroovyUtils SLF4J logger. For the controller scripts, the logger name contains the site and the content type + id (``[Lifecycle-{Site Id}-{Content Type Id}]``) e.g. + + .. code-block:: text + + [DEBUG] 2025-09-27T16:30:36,626 [http-nio-8080-exec-4] [Lifecycle-ed1-/page/category-landing] | Debug message + [ERROR] 2025-09-27T16:30:36,626 [http-nio-8080-exec-4] [Lifecycle-ed1-/page/category-landing] | Error message + + +If you'd like to access the ``applicationContext`` variable or add to the list of available beans in the controller script, +see :ref:`content-processors-configuration` for more information. + ~~~~~~~ Example ~~~~~~~ @@ -1962,7 +1996,7 @@ screen under ``Properties Explorer``, click on the ``Controller`` field then cli | -We'll now edit the controller to log lifecycle events by adding +We'll now edit the controller to log lifecycle events by adding the following in the ``controller.groovy`` file: .. raw:: html @@ -1971,23 +2005,8 @@ We'll now edit the controller to log lifecycle events by adding .. code-block:: groovy :caption: *Example controller.groovy* - :emphasize-lines: 15 - - import scripts.libs.CommonLifecycleApi - - def contentLifecycleParams =[:] - contentLifecycleParams.site = site - contentLifecycleParams.path = path - contentLifecycleParams.user = user - contentLifecycleParams.contentType = contentType - contentLifecycleParams.contentLifecycleOperation = contentLifecycleOperation - contentLifecycleParams.contentLoader = contentLoader - contentLifecycleParams.applicationContext = applicationContext - - def controller = new CommonLifecycleApi(contentLifecycleParams) - controller.execute() - System.out.println("Server side content lifecycle event : " + contentLifecycleOperation) + logger.info('Server side content lifecycle event: ' + contentLifecycleOperation) .. raw:: html @@ -2006,11 +2025,9 @@ Save your changes to the ``controller.groovy`` file, then edit some content in y just edited. Once your changes have been saved, watch your logs and notice the entry made when we updated content: .. code-block:: text - :emphasize-lines: 2 :caption: *Tomcat log* - [INFO] 2025-08-20T10:32:35,265 [pool-23-thread-1] [ed] [context.SiteContext] | GraphQL schema build completed for site 'hello' in 0 secs - Server side content lifecycle event : UPDATE + [INFO] 2025-10-17T17:05:09,311 [http-nio-8080-exec-5] [Lifecycle-hello-/page/entry] | Server side content lifecycle event: UPDATE | diff --git a/source/reference/modules/studio.rst b/source/reference/modules/studio.rst index 52fc2a2cb..47c54c316 100644 --- a/source/reference/modules/studio.rst +++ b/source/reference/modules/studio.rst @@ -111,6 +111,8 @@ In this section, we will highlight some of the more commonly used properties in - Configure the maximum length of configuration content * - :ref:`Git Configuration ` - Configure Git properties + * - :ref:`Content Processors Configuration ` + - Configure content processors (content lifecycle) properties * - :ref:`Content Type Editor Configuration ` - Configure the content types * - :ref:`Dependency Resolver Configuration ` @@ -3512,6 +3514,157 @@ Remember that you need to restart Studio for the changes you make to the above p |hr| +.. _content-processors-configuration: + +"""""""""""""""""""""""""""""""" +Content Processors Configuration +"""""""""""""""""""""""""""""""" +.. version_tag:: + :label: Since + :version: 5.0.0 + +| + +Here are the default values for the configurable properties of content processors: + +.. code-block:: yaml + :caption: *Default values for content processors properties* + + ############################################################ + ## Content Processors ## + ############################################################ + # Location where groovy script for content lifecycle processor is stored. + studio.contentProcessor.contentLifecycle.scriptLocation: /config/studio/content-types/{content-type}/controller.groovy + # Indicates if application context should be available in the content lifecycle controller script. + studio.contentProcessor.contentLifecycle.includeApplicationContext: false + # List of beans that should be available in the content lifecycle controller script. + studio.contentProcessor.contentLifecycle.includedBeans: [] + +| + +The ``applicationContext`` variable provides access to Crafter Engine's Spring beans and site beans defined in +``config/spring/application-context.xml``. The ``applicationContext`` is disabled by default and can be enabled +by setting the property ``studio.contentProcessor.contentLifecycle.includeApplicationContext`` to ``true``: + +.. code-block:: yaml + :caption: *CRAFTER_HOME/bin/apache-tomcat/shared/classes/crafter/studio/extension/studio-config-override.yaml* + + # Indicates if application context should be available in the content lifecycle controller script. + studio.contentProcessor.contentLifecycle.includeApplicationContext: true + +| + +When ``studio.contentProcessor.contentLifecycle.includeApplicationContext`` is set to false, administrators might +decide to include some specific beans from the context, e.g.: contentService, searchService. Use the +``studio.contentProcessor.contentLifecycle.includedBeans`` property to list the beans to be included: + +.. code-block:: yaml + :caption: *CRAFTER_HOME/bin/apache-tomcat/shared/classes/crafter/studio/extension/studio-config-override.yaml* + + # List of beans that should be available in the content lifecycle controller script. + studio.contentProcessor.contentLifecycle.includedBeans: [beanA, beanB] + +| + +To learn more about content lifecycle controllers, see :ref:`server-side-form-controllers`. + +~~~~~~~ +Example +~~~~~~~ +Let's take a look at an example script that will read the content and create a copy with a new ``internal-name`` value set to +"This is a test". It will also amend the "main content item" to append "-revised" to its ``internal-name``. We'll use a site +created using the empty blueprint and add the script to the ``/page/entry`` content type ``controller.groovy`` file: + +.. code-block:: groovy + + /* + * Copyright (C) 2007-2025 Crafter Software Corporation. All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + import org.dom4j.Document + import org.apache.commons.lang3.StringUtils + + Document doc = lifecycleContent.get(path).contentAsDocument() + doc.getRootElement().element('internal-name').setText('This is a test') + lifecycleContent.write(StringUtils.removeEnd(path, '/index.xml') + '/bk/index.xml', doc) + + doc = lifecycleContent.get(path).contentAsDocument() + def internalNameElem = doc.getRootElement().element('internal-name') + internalNameElem.setText(internalNameElem.getText() + '-revised') + lifecycleContent.write(path, doc) + +We'll edit content on the page by using the :base_url:`write content<_static/api/studio.html#tag/content/operation/contentWrite>` API, +which triggers the script above. Here's how the API response will look like: + +.. code-block:: json + :caption: *Content write API response with script added in controller.groovy* + + { + "response": { + "code": 0, + "message": "OK", + "remedialAction": "", + "documentationUrl": "" + }, + "items": [ + { + "path": "/site/website/bk/index.xml", + "operation": "UPDATE", + "amended": false + }, + { + "path": "/site/website/index.xml", + "operation": "UPDATE", + "amended": true + } + ], + "commitId": "7f9c36d7d99fa7754198fe7944cad730e7a5b04d" + } + +| + +Notice that the ``internal-name`` of the page we edited now has ``-revised`` appended to it: + +.. figure:: /_static/images/developer/content-processor-script-sample-result.webp + :alt: Page internal name edited via controller.groovy script + :width: 75% + :align: center + +When we look at the ``/site/website/bk/index.xml`` file, notice the internal name has been changed to ``This is a test``: + +.. code-block:: xml + :caption: */site/website/bk/index.xml* + :emphasize-lines: 8 + + + + /page/entry + /templates/web/entry.ftl + inherit-levels + false + index.xml + This is a test + -1 + ... + false + + +| + +|hr| + .. _content-type-editor-configuration: """"""""""""""""""""""""""