<%-- ADOBE CONFIDENTIAL ___________________ Copyright 2016 Adobe All Rights Reserved. NOTICE: All information contained herein is, and remains the property of Adobe and its suppliers, if any. The intellectual and technical concepts contained herein are proprietary to Adobe and its suppliers and are protected by all applicable intellectual property laws, including trade secret and copyright laws. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from Adobe. --%><% %><%@ include file="/libs/granite/ui/global.jsp" %><% %><%@ page import="java.util.ArrayList, java.util.Iterator, java.util.List, java.util.UUID, org.apache.commons.lang3.StringUtils, org.apache.commons.collections4.IteratorUtils, org.apache.jackrabbit.util.Text, org.apache.sling.commons.json.JSONObject, com.adobe.granite.ui.components.AttrBuilder, com.adobe.granite.ui.components.Config, com.adobe.granite.ui.components.ExpressionHelper, com.adobe.granite.toggle.api.ToggleRouter, com.adobe.granite.ui.components.Tag" %><%--### Picker ====== .. granite:servercomponent:: /libs/granite/ui/components/coral/foundation/picker The component to render the picker dialog. It implements :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/clientlibs/foundation/vocabulary/picker` vocabulary. It has the following content structure: .. gnd:gnd:: [granite:Picker] /** * The title of the picker. * * e.g. "Select Path" */ - jcr:title (String) /** * The value of :doc:`[data-foundation-mode-group] ` * that the collection is part of. * * This property is required to be set when ``actions/selection`` is set. */ - modeGroup (String) /** * The selector to the collection. */ - targetCollection (String) /** * The path representing the current location of the picker. * * This path will be used as the starting location to generate the breadcrumbs up to ``rootPath``. * * If this property is not specified or ``null`` then the breadcrumbs is not generated. */ - path (StringEL) /** * The path representing the root location of the picker. * * This path will be used as the root location to generate the breadcrumbs. */ - rootPath (StringEL) = '/' /** * The count of item to be selected: * * single * Only maximum single selection allowed * multiple * Zero or more selection allowed */ - selectionCount (StringEL) = 'multiple' /** * The folder for the available views (i.e. the rendering) of the collection, * where each can be any component implementing :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/clientlibs/foundation/vocabulary/collection`. * * At least one view needs to be provided. * If there are at least two views, the following properties are needed to be able to switch the view: * * icon (String) * The icon of the view. * jcr:title (String) * The title of the view. * src (StringEL) * The URI Template that is returning the HTML response of the new view. * It supports the following variables: * * offset * The item offset of the current request. * limit * The item limit of the pagination. * id * The id of the collection (:doc:`[data-foundation-collection-id] `). */ + views mandatory /** * A folder to configure search feature. */ + search (granite:PickerSearch) /** * The folder for the actions applicable in the context of the collection. */ + actions (granite:PickerActions) [granite:PickerSearch] /** * The ``name`` attribute for the main text field of the search. * * e.g. "fulltext" */ - name (String) /** * The selector to the search result collection. */ - targetCollection (String) /** * Allowed nodeTypes during picker search. This can be used to provide dynamic nodeTypes. * For multiple types, please use comma to separate like "cq:Tag, dam:Asset". * Added to remove generic fulltext search dependency. */ - nodeTypes (StringEl) = 'nt:base' /** * This value should be true, if nodeType is added as search form child resource OR * search datasource does not depend upon generic lucene index. * * This will be given preference over nodeTypes configuration * If searchFormContainsNodeTypes is false and nodeTypes is null/empty, search will be disabled. * Added to remove generic fulltext search dependency. */ - searchFormContainsNodeTypes (Boolean) /** * The folder containing the form fields. * These fields are in addition to the main text field of the search. * * The HTML form will be setup if this resource is specified. * The form's ``action`` is set to ``Text.escapePath(resource.getPath()) + ".result." + selectionCount + ".html"``, * which means that the search handler are given two request selectors: ``result`` and the value of ``selectionCount``. * The form values would be sent as form request body as usual. */ + form /** * The folder for the available views (i.e. the rendering) of the search result collection, * where each can be any component implementing :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/clientlibs/foundation/vocabulary/collection`. * * At least one view needs to be provided. * If there are at least two views, the following properties are needed to be able to switch the view: * * icon (String) * The icon of the view. * jcr:title (String) * The title of the view. * src (StringEL) * The URI Template that is returning the HTML response of the new view. * It supports the following variables: * * offset * The item offset of the current request. * limit * The item limit of the pagination. */ + views mandatory [granite:PickerActions] /** * The folder for actions applicable during selection mode. (e.g. when one of the collection item is selected) * * The action can be any action component such as :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/button/index`, * :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/anchorbutton/index`, * :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/pulldown/index`, * :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/collection/index`. * * The ``actionBar`` variant of the components above SHOULD be used, unless ``primary`` variant is used. * * Usually the action is implementing :doc:`/jcr_root/libs/granite/ui/components/coral/foundation/clientlibs/foundation/js/collection/action/index`, * with ``relScope`` = ``item``. * * The actions are wrapped inside :doc:`.foundation-collection-actionbar ` * element where ``[data-foundation-collection-actionbar-target]`` is set as the value of ``targetCollection`` property. * This way setting the ``target`` property at individual action is not required. */ + selection Example:: + /apps/my/picker - sling:resourceType = "granite/ui/components/coral/foundation/picker" - jcr:title = "Select Path" - targetCollection = "#example-pathfield-collection" - path = "${requestPathInfo.suffix}" - nodeTypes = "cq:Page" - searchFormContainsNodeTypes = false - rootPath = "/content" - selectionCount = "single" + views + column - sling:resourceType = "granite/ui/components/coral/foundation/columnview" - granite:id = "example-pathfield-collection" - jcr:title="Column View" - icon = "viewColumn" - limit = 40 - size = '${empty requestPathInfo.selectors[1] ? "40" : requestPathInfo.selectors[1]}' - loadAncestors = true - path = "${requestPathInfo.suffix}" - rootPath = "/content" - selectionCount = "single" - src = "/apps/my/picker/views/column{.offset,limit}.html{+id}"> + datasource - sling:resourceType = "example/datasources/children" - itemResourceType = "example/columnitem" - offset = "${requestPathInfo.selectors[0]}" - limit = '${empty requestPathInfo.selectors[1] ? "41" : requestPathInfo.selectors[1] + 1}' - path = "${requestPathInfo.suffix}" + search - name = "fulltext" - targetCollection = "#example-pathfield-search-collection" + form + rootpath - sling:resourceType = "granite/ui/components/coral/foundation/form/textfield" - fieldLabel = "Path" - name = "path" - value = "/content" + orderby - sling:resourceType = "granite/ui/components/coral/foundation/form/hidden" - name = "orderby" - value = "@jcr:score" + orderby.sort - sling:resourceType = "granite/ui/components/coral/foundation/form/hidden" - name = "orderby.sort" - value = "desc" + views + card - sling:resourceType = "granite/ui/components/coral/foundation/masonry" - granite:id = "example-pathfield-search-collection" - jcr:title = "Card View" - icon = "viewCard" - limit = 20 - selectionCount = "single" - selectionMode = true - size = '${empty requestPathInfo.selectors[3] ? "20" : requestPathInfo.selectors[3]}' - src = "/apps/my/picker/search/views/card.result.${requestPathInfo.selectors[1]}{.offset,limit}.html?${querystring}" + datasource - sling:resourceType = "granite/ui/components/coral/foundation/querybuilder/datasource" - itemResourceType = "example/card" - offset = "${requestPathInfo.selectors[2]}" - limit = '${empty requestPathInfo.selectors[3] ? "21" : requestPathInfo.selectors[3] + 1}' + actions + selection + button1 - sling:resourceType = "granite/ui/components/coral/foundation/button" - variant = "actionBar" - text = "Button 1" + button2 - sling:resourceType = "granite/ui/components/coral/foundation/button" - variant = "actionBar" - text = "Button 2" ###--%><% final Config cfg = cmp.getConfig(); final ExpressionHelper ex = cmp.getExpressionHelper(); final ToggleRouter toggleRouter = sling.getService(ToggleRouter.class); final String rootPath = StringUtils.trimToNull(ex.getString(cfg.get("rootPath", "/"))); String path = StringUtils.trimToNull(ex.getString(cfg.get("path", String.class))); String nodeTypes; if (toggleRouter != null && toggleRouter.isEnabled("FT_GRANITE_36952")) { nodeTypes = StringUtils.trimToNull(ex.getString(cfg.get("nodeTypes", ""))); } else { nodeTypes = StringUtils.trimToNull(ex.getString(cfg.get("nodeTypes", "nt:base"))); } String[] nodeTypeArray = null == nodeTypes ? null : StringUtils.split(nodeTypes, ","); boolean searchFormContainsNodeTypes = cfg.get("searchFormContainsNodeTypes", false); Resource currentResource = null; if (path != null) { currentResource = resourceResolver.getResource(path); if (currentResource == null) { path = rootPath; currentResource = resourceResolver.getResource(path); if (currentResource == null) { response.sendError(404); return; } log.warn("Picker path value is invalid, opening picker at rootPath"); } } final Resource selection = resource.getChild("actions/selection"); final String targetCollection = cfg.get("targetCollection", String.class); final boolean isMultipleSelection = "multiple".equals(ex.getString(cfg.get("selectionCount", "multiple"))); final String modeGroup = cfg.get("modeGroup", String.class); String dialogTitle = i18n.getVar(cfg.get("jcr:title", String.class)); if (dialogTitle == null) { dialogTitle = i18n.get("Select Path"); } final Resource search = resource.getChild("search"); final Resource searchForm = search == null ? null : search.getChild("form"); final boolean isSearchFormEnabled = searchForm != null && (null != nodeTypeArray || searchFormContainsNodeTypes); // the below statement handles the case in which form is null // but search is present and view datasource does not depend upon lucene index final boolean showSearchField = isSearchFormEnabled || (search != null && searchFormContainsNodeTypes); final List viewCache = IteratorUtils.toList(resource.getChild("views").listChildren()); final Resource currentView = viewCache.get(0); // At least one view is needed final String formPanelId = generateId("granite-pickerdialog"); final String formAction; if (isMultipleSelection) { formAction = Text.escapePath(resource.getPath()) + ".result.multiple.html"; } else { formAction = Text.escapePath(resource.getPath()) + ".result.single.html"; } AttrBuilder dialogAttrs = new AttrBuilder(request, xssAPI); dialogAttrs.addClass("foundation-picker-collection granite-pickerdialog"); dialogAttrs.add("data-granite-pickerdialog-collection", targetCollection); AttrBuilder formAttrs = new AttrBuilder(request, xssAPI); formAttrs.addClass("granite-pickerdialog-content foundation-layout-panel"); formAttrs.addHref("action", formAction); formAttrs.add("method", "post"); formAttrs.addClass("foundation-form"); formAttrs.add("data-foundation-form-ajax", true); formAttrs.add("data-foundation-form-loadingmask", true); formAttrs.add("data-foundation-form-response-ui-success", new JSONObject().put("name", "granite.pickerdialog.search.result").toString()); %>>
>
<% if (showSearchField) { Config searchCfg = new Config(search); String labelId = generateId("label"); AttrBuilder autocompleteAttrs = new AttrBuilder(request, xssAPI); autocompleteAttrs.addClass("granite-pickerdialog-searchfield"); autocompleteAttrs.add("name", searchCfg.get("name", String.class)); autocompleteAttrs.add("placeholder", i18n.get("Type to search ...")); autocompleteAttrs.add("variant", "omnisearch"); autocompleteAttrs.add("labelledby", labelId); AttrBuilder autocompleteTagsAttrs = new AttrBuilder(request, xssAPI); autocompleteTagsAttrs.add("foundation-autocomplete-value", ""); autocompleteTagsAttrs.add("name", searchCfg.get("name", String.class)); %> > > <% } else { %>

<%= xssAPI.encodeForHTML(dialogTitle) %>

<% } %>
<% if (currentResource != null && rootPath != null) { List crumbs = getCrumbs(currentResource, rootPath); if (!crumbs.isEmpty()) { Resource firstCrumb = (Resource) crumbs.get(0); AttrBuilder navigatorAttrs = new AttrBuilder(request, xssAPI); navigatorAttrs.addClass("granite-collection-navigator"); navigatorAttrs.add("data-granite-collection-navigator-target", targetCollection); %>><% for (int j = 0; j < crumbs.size(); j++) { Resource item = (Resource) crumbs.get(j); String title = item.getName(); if (title.isEmpty() && item.getPath().equals("/")) { title = i18n.get("Root", "The root of the tree structure"); } AttrBuilder itemAttrs = new AttrBuilder(request, xssAPI); itemAttrs.add("data-granite-collection-navigator-collectionid", item.getPath()); %>><%= xssAPI.encodeForHTML(title) %><% } %><% } } %> <% if (isSearchFormEnabled) { String panelId = xssAPI.encodeForHTMLAttr("#" + formPanelId); %> <%= xssAPI.encodeForHTML(i18n.get("Content Only")) %> <%= xssAPI.encodeForHTML(i18n.get("Filters")) %> <% } Resource primary = resource.getChild("actions/primary"); if (primary != null) { for (Iterator it = primary.listChildren(); it.hasNext();) { %><% } } %> <% Resource sortResource = resource.getChild("sort"); if (sortResource != null) { AttrBuilder attrs = new AttrBuilder(request, xssAPI); attrs.add("data-shell-collection-target", targetCollection); cmp.include(sortResource, new Tag(attrs)); } if (isMultipleSelection && selection == null) { AttrBuilder deselectAttrs = new AttrBuilder(request, xssAPI); deselectAttrs.add("is", "coral-button"); deselectAttrs.add("type", "button"); deselectAttrs.add("icon", "close"); deselectAttrs.add("iconposition", "right"); deselectAttrs.add("iconsize", "S"); deselectAttrs.addClass("granite-collection-deselect"); deselectAttrs.add("data-granite-collection-deselect-target", targetCollection); deselectAttrs.add("data-granite-collection-deselect-template", i18n.get("Remove {0} selected items", null, "{{count}}")); AttrBuilder counterAttrs = new AttrBuilder(request, xssAPI); counterAttrs.addClass("foundation-admin-selectionstatus"); counterAttrs.add("data-foundation-admin-selectionstatus-template", i18n.get("{0} selected", "The current selection count", "{{count}}")); counterAttrs.add("data-foundation-admin-selectionstatus-target", targetCollection); %><% } if (viewCache.size() > 1) { AttrBuilder switcherAttrs = new AttrBuilder(request, xssAPI); switcherAttrs.addClass("granite-collection-switcher"); switcherAttrs.add("data-granite-collection-switcher-target", targetCollection); %>><% for (Resource item : viewCache) { Config itemCfg = new Config(item); String src = ex.getString(itemCfg.get("src", String.class)); AttrBuilder itemAttrs = new AttrBuilder(request, xssAPI); itemAttrs.add("data-granite-collection-switcher-src", handleURITemplate(src, request)); itemAttrs.add("icon", itemCfg.get("icon", String.class)); itemAttrs.addSelected(item.getName().equals(currentView.getName())); %>><%= outVar(xssAPI, i18n, itemCfg.get("jcr:title", String.class)) %><% } %><% } %>
<% if (isSearchFormEnabled) { AttrBuilder formPanelAttrs = new AttrBuilder(request, xssAPI); formPanelAttrs.add("id", formPanelId); formPanelAttrs.addClass("foundation-toggleable"); formPanelAttrs.addClass("foundation-layout-panel-rail granite-rail"); %>
>
<% for (Iterator it = searchForm.listChildren(); it.hasNext();) { %><% } // Fall back to nodeTypes if searchFormContainsNodeTypes is false if(!searchFormContainsNodeTypes && null != nodeTypeArray) { %><% for(int i = 0; i < nodeTypeArray.length; i++) { String nodeTypeName = "51_group." + (i + 1) + "_type"; String nodeTypeValue = nodeTypeArray[i].trim(); %><% } } %>
<% } %>
<% if (selection != null) { AttrBuilder selectionAttrs = new AttrBuilder(request, xssAPI); selectionAttrs.addClass("foundation-layout-panel-footer"); selectionAttrs.addClass("foundation-mode-switcher"); selectionAttrs.add("data-foundation-mode-switcher-group", modeGroup); %>
>
<% AttrBuilder selectionBarAttrs = new AttrBuilder(request, xssAPI); selectionBarAttrs.addClass("betty-ActionBar betty-ActionBar--large betty-ActionBar--bottom"); selectionBarAttrs.addClass("foundation-collection-actionbar"); selectionBarAttrs.add("data-foundation-collection-actionbar-target", targetCollection); %>> <% for (Iterator it = selection.listChildren(); it.hasNext();) { Resource item = it.next(); if (!cmp.getRenderCondition(item, true).check()) { continue; } %><% AttrBuilder selectionItemAttrs = new AttrBuilder(request, xssAPI); selectionItemAttrs.addClass("betty-ActionBar-item"); cmp.include(item, new Tag(selectionItemAttrs)); %><% } %> <% AttrBuilder deselectAttrs = new AttrBuilder(request, xssAPI); deselectAttrs.add("type", "button"); deselectAttrs.addClass("betty-ActionBar-item"); deselectAttrs.addClass("granite-collection-deselect"); deselectAttrs.add("data-granite-collection-deselect-target", targetCollection); deselectAttrs.addClass("coral-Button coral-Button--quiet"); AttrBuilder counterAttrs = new AttrBuilder(request, xssAPI); counterAttrs.addClass("foundation-admin-selectionstatus"); counterAttrs.add("data-foundation-admin-selectionstatus-template", i18n.get("{0} selected", null, "{{count}}")); counterAttrs.add("data-foundation-admin-selectionstatus-target", targetCollection); %>
<% } %>
<%! private String generateId(String prefix) { return prefix + "-" + UUID.randomUUID(); } private String handleURITemplate(String src, HttpServletRequest request) { if (src != null && src.startsWith("/")) { return request.getContextPath() + src; } return src; } private List getCrumbs(Resource r, String rootPath) { final List crumbs = new ArrayList(); Resource current = r; while (current != null) { crumbs.add(current); if (current.getPath().equals(rootPath)) { break; } current = current.getParent(); } return crumbs; } %>