Customizing pickers in Content Editor 4.1

web-development.jpg
The full documentation about picker customization is available in a dedicated page on the Academy.

 

Introduction

Content Editor 4.1 comes with new and improved fully extensible content pickers. By default it is preloaded with standard picker configurations for selection of pages, editorial content, files, images, categories, users and more. However, while default configurations should be enough for your content management needs there may be a time when you require additional functionality and features.

In this article I walk you through an example of creating a custom Event picker and talk about:

  • Picker overview (brief overview of layout options)
  • Registering UI component in Jahia
  • Picker configuration anatomy
    • Configuration
    • Accordion Item

Picker anatomy

Each picker has a table section, which features a table, optional table view selector, search field and current selections at the bottom.

user-picker.png

Simple user picker

The picker might also come with an accordion section which allows the user to optionally switch site and navigate content under specified paths where each path is represented by an accordion tab.

editorial-picker.png

Editorial picker

These main picker parts are identical in design to what you can find in jContent but may differ in functionality depending on their intended use.

Registering UI Components

UI components are easily registered with the help of a javascript file which contains code to inject custom components into the registry. You can find information about the registration process on our Academy page for custom UI extensions. Normally when you create a module in Jahia 8 you will already have some example components registered, which should be available in registerExtensions.js file found in /resources/javascript/apps folder.

extension-location.png

Extension location

 

Picker Configuration Anatomy

Every picker consists of two parts: picker configuration and accordion item(s) specification. You can specify labels, visibility options for various picker parts and selectable and searchable types in picker configuration. Accordion item specification allows you to define root path, query, additional fragments, column visibility as well as labels for accordion itself. All of this is best demonstrated with an example, so let’s create a simple event picker. Let’s assume that we want a picker for selection of summer time events for a given year. The following approach may not be the most optimal but it should give you a good idea of what is possible and at the same time expose you to most of the available functionality.

Configuration

Configuration requires that I have a component with my unique name for the picker. This can be done as a cnd definition:

[jnt:summerEventPicker] > jnt:content, jmix:droppableContent, jmix:siteComponent
- event (weakreference, picker[type='summerEvent']) < jnt:event

Now I can create configuration for the summerEvent picker, which is the name of my picker. I use registry to register pickerConfiguration like so:

registry.add('pickerConfiguration', 'summerEvent', {
            pickerInput: {
                emptyLabel: 'picker-example:label.picker.empty',
                notFoundLabel: 'picker-example:label.picker.notFound',
            },
            pickerDialog: {
                view: 'List',
                dialogTitle: 'picker-example:label.picker.title',
                displayTree: false,
                displaySiteSwitcher: false,
                displaySearch: false
            },
            searchContentType: 'jnt:event',
            selectableTypesTable: ['jnt:event'],
            accordions: ['picker-summerEvent']
        });

This indicates that I want to be able to select and be able to search for all nodes of type jnt:event. The pickerDialog section hides accordion tree, site switcher and search, provides a label and preselects list view mode. The pickerInput section provides some labels for when nothing is found. Note that the labels can be internationalized using desired locale files stored in javascript/locales folder.

Accordion Item

Note that picker configuration also defines an accordion named picker-summerEvent. By default Jahia comes with three predefined accordions: picker-pages, picker-content-folders and picker-media. As the name suggests they are designed to retrieve content from pages, content folder and files respectively. You can access these accordions via registry and extend them as you see fit.

In this example I will extend picker-pages accordion, override its query handler and specify a custom fragment to retrieve additional properties from the node. I use SQL2 query and restrict date to roughly match the summer months of 2023:

registry.add('accordionItem', 'picker-summerEvent', jcontent.jcontentUtils.mergeDeep({}, registry.get('accordionItem', 'picker-pages'), {
            targets: ['summerEvent:50'],
            label: 'Custom accordion',
            rootPath: '/sites/{site}',
            tableConfig: {
                viewSelector: false,
                queryHandler: {
                    ...window.jahia.jcontent.Sql2SearchQueryHandler,
                    getQueryVariables: p => {
                        return {
                        ...window.jahia.jcontent.Sql2SearchQueryHandler.getQueryVariables(p),
                        selectableTypesTable: p.selectableTypesTable,
                        query: `SELECT * FROM ['jnt:event'] as e where e.['startDate'] >= CAST('2023-06-21T00:00:00.000Z' AS DATE) and e.['startDate'] <= CAST('2023-09-21T00:00:00.000Z' AS DATE) and ISDESCENDANTNODE('${p.path}')`,
                    }},
                    getFragments: () => [
                        {
                            gql: window.jahia.graphqlTag(`fragment IsSelectable on JCRNode {
                                isSelectable: isNodeType(type: {types: $selectableTypesTable})
                                startDate: property(name: "startDate") { value },
                                endDate: property(name: "endDate") { value }
                            }`),
                            variables: {
                                selectableTypesTable: '[String!]!'
                            },
                            applyFor: 'node'
                        }
                    ]
                },
                columns: ["publicationStatus", "name",
                    {
                        id: 'start-date',
                        accessor: row => row.startDate && new Date(row.startDate.value).toLocaleDateString(),
                        label: 'picker-example:label.startDate',
                        width: '100px',
                        sortable: true,
                        property: 'startDate.value'
                    },
                    {
                        id: 'end-date',
                        accessor: row => row.endDate && new Date(row.endDate.value).toLocaleDateString(),
                        label: 'picker-example:label.endDate',
                        width: '100px',
                        sortable: true,
                        property: 'endDate.value'
                    }
                ]
            }
        }));

Once again this is by no means an ideal approach for this kind of problem but it is used to demonstrate how far you can go when it comes to extending and customizing Jahia’s pickers.

What the specification does is it specifies the rootPath which in this case is the root of the site, it hides table view selector, extends query handler to perform a custom SQL2 query, adds custom properties with a fragment and specifies which rows to use. For more details on row configuration you can refer to the react-table library as it is used to render the table. In the end the picker looks like this:

custom-event-picker.png

Custom event picker

You may, however, have a simpler use case which preserves left side navigation and does not require query modification. In this case you can simply add fragments to the query defined by picker-pages accordion by putting it directly under tableConfig.

registry.add('accordionItem', 'picker-summerEvent', jcontent.jcontentUtils.mergeDeep({}, registry.get('accordionItem', 'picker-pages'),{
            targets: ['summerEvent:50'],
            label: 'Custom accordion',
            icon: window.jahia.moonstone.toIconComponent('Page', {size: 'default'}),
            rootPath: '/sites/{site}',
            tableConfig: {
                viewSelector: false,
                fragments: [
                    {
                        gql: window.jahia.graphqlTag(`fragment CustomFragment on JCRNode {
                                startDate: property(name: "startDate") { value }
                                endDate: property(name: "endDate") { value }
                            }`),
                        applyFor: 'node'
                    }
                ],
                columns: ["publicationStatus", "name",
                    {
                        id: 'start-date',
                        accessor: row => row.startDate && new Date(row.startDate.value).toLocaleDateString(),
                        label: 'picker-example:label.startDate',
                        width: '100px',
                        sortable: true,
                        property: 'startDate.value'
                    },
                    {
                        id: 'end-date',
                        accessor: row => row.endDate && new Date(row.endDate.value).toLocaleDateString(),
                        label: 'picker-example:label.endDate',
                        width: '100px',
                        sortable: true,
                        property: 'endDate.value'
                    }
                ]
            }
        }));

Now you should be able to navigate pages and see nodes of type jnt:event if they exists on a given page:

picker-with-accordion.png

Custom event picker with navigation

 

Conclusion

With Content Editor 4.1 you can have fully customizable pickers for all your needs. Whether you need to change labels, hide certain layout components or perform a different query altogether it can be done. Small blogpost is not an ideal format for a detailed and thorough overview of every feature and functionality but it is my hope that I’ve given you enough food for thought and a good starting point when it comes to picker extensions.

Alexander Karmanov

Software developer at Jahia

Retour