Developing the Bulk Editor developing-the-bulk-editor

CAUTION
AEM 6.4 has reached the end of extended support and this documentation is no longer updated. For further details, see our technical support periods. Find the supported versions here.

This section describes how to develop the bulk editor tool and how to extend the Product List component, which is based on the bulk editor.

Bulk Editor Query Parameters bulk-editor-query-parameters

When working with the bulk editor, there are several query parameters that you can add to the URL to call the bulk editor with a specific configuration. If you want the bulk editor to always be used with a certain configuration, for example, as in the Product List component, then you need to modify bulkeditor.jsp (located in /libs/wcm/core/components/bulkeditor) or create a component with the specific configuration. Changes made using query parameters are not permanent.

For example if you type the following in your browser’s URL:

https://<servername><port_number>/etc/importers/bulkeditor.html?rootPath=/content/geometrixx/en&queryParams=geometrixx&initialSearch=true&hrp=true

the bulk editor displays without the Root Path field as hrp=true hides the field. With the parameter hrp=false, the field is displayed (the default value).

The following is a list of the bulk editor query parameters:

NOTE
Each parameter can have a long and a short name. For example the long name for the search root path is rootPath, the short one is rp. If the long name is not defined, the short one is read from the request.

Parameter

(long name / short name)

Type
Description
rootPath / rp
String
search root path
queryParams / qp
String
search query
contentMode / cm
Boolean
when true, the content mode is enabled
colsValue / cv
String[]
searched properties (checked values from colsSelection displayed as checkboxes)
extraCols / ec
String[]
extra searched properties (displayed in a comma-separated text field)
initialSearch / is
Boolean
when true, the query is performed on page load
colsSelection / cs
String[]
searched properties selection (displayed as checkboxes)
showGridOnly / sgo
Boolean
when true, shows only the grid and not the search panel
searchPanelCollapsed / spc
Boolean
when true, search panel is collapsed on load
hideRootPath / hrp
Boolean
when true, hides the root path field
hideQueryParams / hqp
Boolean
when true, hides the query field
hideContentMode / hcm
Boolean
when true, hides the content mode field
hideColsSelection / hcs
Boolean
when true, hides the columns selection field
hideExtraCols / hec
Boolean
when true, hides the extra columns field
hideSearchButton
Boolean
when true, hides the search button
hideSaveButton / hsavep
Boolean
when true, hides the save button
hideExportButton / hexpb
Boolean
when true, hides the export button
hideImportButton / hib
Boolean
when true, hides the import button
hideResultNumber / hrn
Boolean
when true, hides the grid search result number text
hideInsertButton / hinsertb
Boolean
when true, hides the grid insert button
hideDeleteButton / hdelb
Boolean
when true, hides the grid delete button
hidePathCol / hpc
Boolean
when true, hides the grid "path" column

Developing a Bulk Editor based Component: the Product List Component developing-a-bulk-editor-based-component-the-product-list-component

This section provides an overview of how to use the bulk editor and gives a description of the existing Geometrixx component based on the bulk editor: the Product List component.

The Product List component lets users display and edit a table of data. For example, you can use the Product List component to represent products in a catalog. The information is presented in a standard HTML table and any editing is performed in the Edit dialog, which contains a BulkEditor widget. (This Bulk Editor is exactly the same as the one accessible at /etc/importers/bulkeditor.html or through the Tools menu). The Product List component has been configured for specific, limited bulk editor functionality. Every part of the bulk editor (or components derived from the bulk editor) can be configured.

With the bulk editor, you can add, modify, delete, filter, and export the rows, save modifications, and import a set of rows. Every row is stored as a node under the Product List component instance itself. Every cell is a property of each node. This is a design choice and it can easily be changed, for example, you could store nodes somewhere else in the repository. The query servlet’s role is to return the list of the nodes to display; the search path is defined as a Product List instance.

The source code of the Product List component is available in the repository at /apps/geometrixx/components/productlist and is composed of several parts like all AEM components:

  • HTML rendering: the rendering is done in a JSP file (https://experienceleague.adobe.com/apps/geometrixx/components/productlist/productlist.jsp?lang=en). The JSP reads the subnodes of the current Product List component and displays each of them as a row of an HTML table.
  • Edit dialog, which is where you define the Bulk Editor configuration. Configure the dialog to match the needs of the component: columns available and possible actions performed on the grid or on the search. See bulk editor configuration properties for information on all of the configuration properties.

Here is an XML representation of the dialog sub nodes:

        <editor
            jcr:primaryType="cq:Widget"
            colsSelection="[ProductId,ProductName,Color,CatalogCode,SellingSku]"
            colsValue="[ProductId,ProductName,Color,CatalogCode,SellingSku]"
            contentMode="false"
            exportURL="/etc/importers/bulkeditor/export.tsv"
            extraCols="Selection"
            hideColsSelection="false"
            hideContentMode="true"
            hideDeleteButton="false"
            hideExportButton="false"
            hideExtraCols="true"
            hideImportButton="false"
            hideInsertButton="false"
            hideMoveButtons="false"
            hidePathCol="true"
            hideRootPath="true"
            hideSaveButton="false"
            hideSearchButton="false"
            importURL="/etc/importers/bulkeditor/import"
            initialSearch="true"
            insertedResourceType="geometrixx/components/productlist/sku"
            queryParams=""
            queryURL="/etc/importers/bulkeditor/query.json"
            saveURL="/etc/importers/bulkeditor/save"
            xtype="bulkeditor">
            <saveButton
                jcr:primaryType="nt:unstructured"
                text="Save modifications"/>
            <searchButton
                jcr:primaryType="nt:unstructured"
                text="Apply filter"/>
            <queryParamsInput
                jcr:primaryType="nt:unstructured"
                fieldDescription="Enter here your filters"
                fieldLabel="Filters"/>
            <searchPanel
                jcr:primaryType="nt:unstructured"
                height="200">
                <defaults
                    jcr:primaryType="nt:unstructured"
                    labelWidth="150"/>
            </searchPanel>
            <grid
                jcr:primaryType="nt:unstructured"
                height="275"/>
            <store jcr:primaryType="nt:unstructured">
                <sortInfo
                    jcr:primaryType="nt:unstructured"
                    direction="ASC"
                    field="CatalogCode"/>
            </store>
            <colModel
                jcr:primaryType="nt:unstructured"
                width="150"/>
            <colsMetadata jcr:primaryType="nt:unstructured">
                <Selection
                    jcr:primaryType="nt:unstructured"
                    checkbox="true"
                    forcedPosition="0"
                    headerText=""/>
                <ProductId
                    jcr:primaryType="nt:unstructured"
                    cellCls="productlist-cell-productid"
                    headerText="Product Id"/>
                <ProductName
                    jcr:primaryType="nt:unstructured"
                    cellStyle="background-color: #FFCC99;"
                    headerText="Product Name"/>
                <CatalogCode
                    jcr:primaryType="nt:unstructured"
                    cellStyle="background-color: #EDEDED;"
                    headerText="Catalog Code"/>
                <Color jcr:primaryType="nt:unstructured">
                    <editor
                        jcr:primaryType="nt:unstructured"
                        store="[Blue,Red,Yellow]"
                        triggerAction="all"
                        typeAhead="true"
                        xtype="combo"/>
                </Color>
                <SellingSku
                    jcr:primaryType="nt:unstructured"
                    headerText="Sku Id"/>
            </colsMetadata>
        </editor>

Bulk Editor Configuration Properties bulk-editor-configuration-properties

Every part of the bulk editor can be configured. The following table lists all the configuration properties for the bulk editor.

Property name
Definition
rootPath
Search root path
queryParams
Search query
contentMode
True to enable content mode: properties are read on jcr:content node and not on search result node
colsValue
Searched properties (checked values from colsSelection displayed as checkboxes)
extraCols
Extra searched properties (displayed in a textfield comma separated)
initialSearch
True to perform query on page load
colsSelection
Searched properties selection (displayed as checkboxes)
showGridOnly
True to show only the grid and not the search panel (do not forget to set the initialSearch to true)
searchPanelCollapsed
True to collapse search panel by default
hideRootPath
Hide root path field
hideQueryParams
Hide query field
hideContentMode
Hide content mode field
hideColsSelection
Hide cols selection field
hideExtraCols
Hide extra cols field
hideSearchButton
Hide search button
hideSaveButton
Hide save button
hideExportButton
Hide export button
hideImportButton
Hide import button
hideResultNumber
Hide grid search result number text
hideInsertButton
Hide grid insert button
hideDeleteButton
Hide grid delete button
hidePathCol
Hide grid "path" column
queryURL
Path to query servlet
exportURL
Path to export servlet
importURL
Path to import servlet
insertedResourceType
Resource type added to node when a row is inserted
saveButton
Save button widget config
searchButton
Search button widget config
exportButton
Export button widget config
importButton
Import button widget config
searchPanel
Search panel widget config
grid
Grid widget config
store
Store config
colModel
Grid column model config
rootPathInput
rootPath widget config
queryParamsInput
queryParams widget config
contentModeInput
contentMode widget config
colsSelectionInput
colsSelection widget config
extraColsInput
extraCols widget config
colsMetadata

Column metadata config. Possible properties are (applied to all cells of the column):

  • cellStyle: html style

  • cellCls: css class

  • readOnly: true to not being able to change value

  • checkbox: true to define all cells of the column as checkboxes (true/false values)

  • forcedPosition: integer value to specify where column must be placed in grid (between 0 and number of colums-1)

Columns Metadata Configuration columns-metadata-configuration

You can configure for each column:

  • display properties: html style, CSS class and read-only

  • a checkbox

  • a forced position

CSS and read-only columns

The bulk editor has three column configurations:

  • Cell CSS class name (cellCls): a CSS class name that is added to each cell of the configured column.
  • Cell style (cellStyle): an HTML style that is added to each cell of the configured column.
  • Read only (readOnly): read only is set for each cell of the configured column.

The configuration must be defined as the following one:

"colsMetadata": {
"Column name": {
     "cellStyle": "html style",
     "cellCls": "CSS class",
     "readOnly": true/false
}
}

The following example can be found in the productlist component (https://experienceleague.adobe.com/apps/geometrixx/components/productlist/dialog/items/editor/colsMetadata?lang=en):

            <colsMetadata jcr:primaryType="nt:unstructured">
                <Selection
                    jcr:primaryType="nt:unstructured"
                    checkbox="true"
                    forcedPosition="0"
                    headerText=""/>
                <ProductId
                    jcr:primaryType="nt:unstructured"
                    cellCls="productlist-cell-productid"
                    headerText="Product Id"/>
                <ProductName
                    jcr:primaryType="nt:unstructured"
                    cellStyle="background-color: #FFCC99;"
                    headerText="Product Name"/>
                <CatalogCode
                    jcr:primaryType="nt:unstructured"
                    cellStyle="background-color: #EDEDED;"
                    headerText="Catalog Code"/>
                <Color jcr:primaryType="nt:unstructured">
                    <editor
                        jcr:primaryType="nt:unstructured"
                        store="[Blue,Red,Yellow]"
                        triggerAction="all"
                        typeAhead="true"
                        xtype="combo"/>
                </Color>
                <SellingSku
                    jcr:primaryType="nt:unstructured"
                    headerText="Sku Id"/>
            </colsMetadata>

Checkbox

If the checkbox configuration property is set to true, all the cells of the column are rendered as checkboxes. A checked box sends true to the server Save servlet, false otherwise. In the header menu, you can also select all or select none. These options are enabled if the selected header is the header of a checkbox column.

In the former example the selection column contains only checkboxes as checkbox=“true”.

Forced position

The forced position metadata forcedPosition lets you specify where the column is placed within the grid: 0 is the first place and <number of columns>-1 is the last position. Any other value is ignored.

In the former example the selection column is the first column as forcedPosition=“0”.

Query Servlet query-servlet

By default, the Query servlet can be found at /libs/wcm/core/components/bulkeditor/json.java. You can configure another path to retrieve the data.

The Query servlet works as follows: it receives a GQL query and the columns to return, computes the results, and sends the results back to the bulk editor as a JSON stream.

In the Product List component case, the two parameters sent to the Query servlet are as follows:

  • query: “path:/content/geometrixx/en/customers/jcr:content/par/productlist Cube”
  • cols: “Selection,ProductId,ProductName,Color,CatalogCode,SellingSku”

and the JSON stream returned is as follows:

{
  "hits": [{
      "jcr:path": "/content/geometrixx/en/products/jcr:content/par/productlist/1258674828905",
      "ProductId": "21",
      "ProductName": "Cube",
      "Color": "Blue",
      "CatalogCode": "43244",
      "SellingSku": "32131"
    }
  ],
  "results": 1
}

Each hit corresponds to one node and its properties, and is displayed as a row in the grid.

You can extend the Query servlet to return a complex inheritance model or return nodes stored at a specific logic place. The Query servlet can be used to do any kind of complex computation. The grid can then display rows that are an aggregate of several nodes in the repository. The modification and the saving of these rows must in that case be managed by the Save Servlet.

Save Servlet save-servlet

In the default configuration of the bulk editor each row is a node and the path of this node is stored in the row record. The bulk editor keeps the link between the row and the node through the jcr path. When a user edits the grid, a list of all of the modifications is built. When a user clicks Save, a POST query is sent to each path with the updated properties values. This is the basis of the Sling concept and it works well if each cell is a property of the node. But if the Query servlet is implemented to do inheritance computation, this model cannot work as a property returned by the Query servlet can be inherited from another node.

The Save servlet concept is that the modifications are not directly posted to each node but that they are posted to one servlet that does the saving job. This gives this servlet the possibility to analyze the modifications and save the properties on the right node.

Each updated property is sent to the servlet in the following format:

  • Parameter name: <jcr path>/<property name>

    Example: /content/geometrixx/en/products/jcr:content/par/productlist/1258674859000/SellingSku

  • Value: <value>

    Example: 12123

The servlet needs to know where the catalogCode property is stored.

A default Save servlet implementation is available at /libs/wcm/bulkeditor/save/POST.jsp and is used in the Product List component. It takes all the parameters from the request (with a <jcr path>/<property name> format) and writes properties on nodes using the JCR API. It also creates node if they do not exist (grid inserted rows).

The default code should not be used as is as it re-implements what the server natively does (a POST on <jcr path>/<property name>) and is therefore only a good starting point for building a Save servlet that will manage a property inheritance model.

recommendation-more-help
2315f3f5-cb4a-4530-9999-30c8319c520e