001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Thomas Roger
018 *     Nelson Silva
019 */
020
021package org.nuxeo.search.ui;
022
023import static org.nuxeo.search.ui.localconfiguration.Constants.SEARCH_CONFIGURATION_FACET;
024
025import java.io.Serializable;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.nuxeo.ecm.core.api.CoreSession;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.SortInfo;
036import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService;
037import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService;
038import org.nuxeo.ecm.platform.actions.Action;
039import org.nuxeo.ecm.platform.actions.ActionContext;
040import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
041import org.nuxeo.ecm.platform.contentview.jsf.ContentViewHeader;
042import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService;
043import org.nuxeo.ecm.platform.contentview.jsf.ContentViewState;
044import org.nuxeo.ecm.platform.contentview.jsf.ContentViewStateImpl;
045import org.nuxeo.ecm.platform.query.api.PageProvider;
046import org.nuxeo.ecm.platform.query.api.PageProviderService;
047import org.nuxeo.ecm.platform.userworkspace.api.UserWorkspaceService;
048import org.nuxeo.runtime.api.Framework;
049import org.nuxeo.search.ui.localconfiguration.SearchConfiguration;
050
051/**
052 * @since 6.0
053 */
054public class SearchUIServiceImpl implements SearchUIService {
055
056    private static Log log = LogFactory.getLog(SearchUIServiceImpl.class);
057
058    public static final String SEARCH_CONTENT_VIEWS_CATEGORY = "SEARCH_CONTENT_VIEWS";
059
060    public static final String CONTENT_VIEW_NAME_PROPERTY = "contentViewName";
061
062    public static final String SAVED_SEARCHES_PROVIDER_NAME = "SAVED_SEARCHES";
063
064    public static final String SHARED_SEARCHES_PROVIDER_NAME = "SHARED_SAVED_SEARCHES";
065
066    public static final String CONTENT_VIEW_DISPLAY_FACET = "ContentViewDisplay";
067
068    @Override
069    public List<ContentViewHeader> getContentViewHeaders(ActionContext actionContext) {
070        return getContentViewHeaders(actionContext, null);
071    }
072
073    @Override
074    public List<ContentViewHeader> getContentViewHeaders(ActionContext actionContext, DocumentModel doc) {
075        ActionManager actionService = Framework.getService(ActionManager.class);
076        List<Action> actions = actionService.getActions(SEARCH_CONTENT_VIEWS_CATEGORY, actionContext);
077
078        List<String> contentViewNames = new ArrayList<>();
079        for (Action action : actions) {
080            String contentViewName = (String) action.getProperties().get(CONTENT_VIEW_NAME_PROPERTY);
081            if (contentViewName != null) {
082                contentViewNames.add(contentViewName);
083            }
084        }
085        contentViewNames = filterContentViewNames(contentViewNames, doc);
086
087        ContentViewService contentViewService = Framework.getService(ContentViewService.class);
088        List<ContentViewHeader> contentViewHeaders = new ArrayList<>();
089        for (String contentViewName : contentViewNames) {
090            ContentViewHeader contentViewHeader = contentViewService.getContentViewHeader(contentViewName);
091            if (contentViewHeader != null) {
092                contentViewHeaders.add(contentViewHeader);
093            }
094        }
095        return contentViewHeaders;
096    }
097
098    /**
099     * Returns the filtered content view names based on the local configuration if any.
100     */
101    protected List<String> filterContentViewNames(List<String> contentViewNames, DocumentModel currentDoc) {
102        SearchConfiguration searchConfiguration = getSearchConfiguration(currentDoc);
103        return searchConfiguration == null ? contentViewNames
104                : searchConfiguration.filterAllowedContentViewNames(contentViewNames);
105    }
106
107    protected SearchConfiguration getSearchConfiguration(DocumentModel currentDoc) {
108        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
109        return localConfigurationService.getConfiguration(SearchConfiguration.class, SEARCH_CONFIGURATION_FACET,
110                currentDoc);
111    }
112
113    public DocumentModel saveSearch(CoreSession session, ContentViewState searchContentViewState, String title) {
114        UserWorkspaceService userWorkspaceService = Framework.getService(UserWorkspaceService.class);
115        DocumentModel uws = userWorkspaceService.getCurrentUserPersonalWorkspace(session, null);
116
117        DocumentModel searchDoc = searchContentViewState.getSearchDocumentModel();
118        searchDoc.setPropertyValue("dc:title", title);
119
120        if (searchDoc.hasFacet(CONTENT_VIEW_DISPLAY_FACET)) {
121            searchDoc.setPropertyValue("cvd:contentViewName", searchContentViewState.getContentViewName());
122            searchDoc.setPropertyValue("saved:providerName", searchContentViewState.getPageProviderName());
123            searchDoc.setPropertyValue("saved:pageSize", searchContentViewState.getPageSize());
124            searchContentViewState.getPageSize();
125            List<SortInfo> sortInfos = searchContentViewState.getSortInfos();
126            if (sortInfos != null) {
127                ArrayList<Map<String, Serializable>> list = new ArrayList<>();
128                String sortBy = "", sortOrder = "";
129                for (SortInfo sortInfo : sortInfos) {
130                    if (!sortBy.isEmpty()) {
131                        sortBy += ",";
132                        sortOrder += ",";
133                    }
134                    sortBy += sortInfo.getSortColumn();
135                    sortOrder += sortInfo.getSortAscending() ? "ASC" : "DESC";
136                    list.add(SortInfo.asMap(sortInfo));
137                }
138                searchDoc.setPropertyValue("cvd:sortInfos", list);
139                searchDoc.setPropertyValue("saved:sortBy", sortBy);
140                searchDoc.setPropertyValue("saved:sortOrder", sortOrder);
141            }
142            searchDoc.setPropertyValue("cvd:selectedLayoutColumns", (Serializable) searchContentViewState.getResultColumns());
143        } else {
144            log.warn(String.format("Search document type %s is missing %s facet", searchDoc.getType(),
145                CONTENT_VIEW_DISPLAY_FACET));
146        }
147
148        PathSegmentService pathService = Framework.getService(PathSegmentService.class);
149        searchDoc.setPathInfo(uws.getPathAsString(), pathService.generatePathSegment(searchDoc));
150        searchDoc = session.createDocument(searchDoc);
151        session.save();
152
153        return searchDoc;
154    }
155
156    public List<DocumentModel> getCurrentUserSavedSearches(CoreSession session) {
157        return getDocuments(SAVED_SEARCHES_PROVIDER_NAME, session, session.getPrincipal().getName());
158    }
159
160    @SuppressWarnings("unchecked")
161    protected List<DocumentModel> getDocuments(String pageProviderName, CoreSession session, Object... parameters)
162            {
163        PageProviderService pageProviderService = Framework.getService(PageProviderService.class);
164        Map<String, Serializable> properties = new HashMap<String, Serializable>();
165        properties.put("coreSession", (Serializable) session);
166        return ((PageProvider<DocumentModel>) pageProviderService.getPageProvider(pageProviderName, null, null, null,
167                properties, parameters)).getCurrentPage();
168
169    }
170
171    public List<DocumentModel> getSharedSavedSearches(CoreSession session) {
172        return getDocuments(SHARED_SEARCHES_PROVIDER_NAME, session, session.getPrincipal().getName());
173    }
174
175    @Override
176    @SuppressWarnings("unchecked")
177    public ContentViewState loadSearch(DocumentModel savedSearch) {
178        if (!savedSearch.hasFacet(CONTENT_VIEW_DISPLAY_FACET)) {
179            log.warn(String.format("Search document type %s is missing %s facet", savedSearch.getType(),
180                CONTENT_VIEW_DISPLAY_FACET));
181            return null;
182        }
183        ContentViewState state = new ContentViewStateImpl();
184        state.setContentViewName((String) savedSearch.getPropertyValue("cvd:contentViewName"));
185        state.setSearchDocumentModel(savedSearch);
186        state.setSortInfos(getSortInfos(savedSearch));
187        state.setResultColumns((List<String>) savedSearch.getPropertyValue("cvd:selectedLayoutColumns"));
188        return state;
189    }
190
191    @SuppressWarnings("unchecked")
192    List<SortInfo> getSortInfos(DocumentModel savedSearch) {
193        List<Map<String, Serializable>> list = (List<Map<String, Serializable>>) savedSearch.getPropertyValue(
194            "cvd:sortInfos");
195        List<SortInfo> sortInfos = new ArrayList<>();
196        for (Map<String, Serializable> info : list) {
197            sortInfos.add(SortInfo.asSortInfo(info));
198        }
199        return sortInfos;
200    }
201}