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.getLocalService(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.getLocalService(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            List<SortInfo> sortInfos = searchContentViewState.getSortInfos();
123            if (sortInfos != null) {
124                ArrayList<Map<String, Serializable>> list = new ArrayList<>();
125                for (SortInfo sortInfo : sortInfos) {
126                    list.add(SortInfo.asMap(sortInfo));
127                }
128                searchDoc.setPropertyValue("cvd:sortInfos", list);
129            }
130            searchDoc.setPropertyValue("cvd:selectedLayoutColumns", (Serializable) searchContentViewState.getResultColumns());
131        } else {
132            log.warn(String.format("Search document type %s is missing %s facet", searchDoc.getType(),
133                CONTENT_VIEW_DISPLAY_FACET));
134        }
135
136        PathSegmentService pathService = Framework.getLocalService(PathSegmentService.class);
137        searchDoc.setPathInfo(uws.getPathAsString(), pathService.generatePathSegment(searchDoc));
138        searchDoc = session.createDocument(searchDoc);
139        session.save();
140
141        return searchDoc;
142    }
143
144    public List<DocumentModel> getCurrentUserSavedSearches(CoreSession session) {
145        return getDocuments(SAVED_SEARCHES_PROVIDER_NAME, session, session.getPrincipal().getName());
146    }
147
148    @SuppressWarnings("unchecked")
149    protected List<DocumentModel> getDocuments(String pageProviderName, CoreSession session, Object... parameters)
150            {
151        PageProviderService pageProviderService = Framework.getService(PageProviderService.class);
152        Map<String, Serializable> properties = new HashMap<String, Serializable>();
153        properties.put("coreSession", (Serializable) session);
154        return ((PageProvider<DocumentModel>) pageProviderService.getPageProvider(pageProviderName, null, null, null,
155                properties, parameters)).getCurrentPage();
156
157    }
158
159    public List<DocumentModel> getSharedSavedSearches(CoreSession session) {
160        return getDocuments(SHARED_SEARCHES_PROVIDER_NAME, session, session.getPrincipal().getName());
161    }
162
163    @Override
164    @SuppressWarnings("unchecked")
165    public ContentViewState loadSearch(DocumentModel savedSearch) {
166        if (!savedSearch.hasFacet(CONTENT_VIEW_DISPLAY_FACET)) {
167            log.warn(String.format("Search document type %s is missing %s facet", savedSearch.getType(),
168                CONTENT_VIEW_DISPLAY_FACET));
169            return null;
170        }
171        ContentViewState state = new ContentViewStateImpl();
172        state.setContentViewName((String) savedSearch.getPropertyValue("cvd:contentViewName"));
173        state.setSearchDocumentModel(savedSearch);
174        state.setSortInfos(getSortInfos(savedSearch));
175        state.setResultColumns((List<String>) savedSearch.getPropertyValue("cvd:selectedLayoutColumns"));
176        return state;
177    }
178
179    @SuppressWarnings("unchecked")
180    List<SortInfo> getSortInfos(DocumentModel savedSearch) {
181        List<Map<String, Serializable>> list = (List<Map<String, Serializable>>) savedSearch.getPropertyValue(
182            "cvd:sortInfos");
183        List<SortInfo> sortInfos = new ArrayList<>();
184        for (Map<String, Serializable> info : list) {
185            sortInfos.add(SortInfo.asSortInfo(info));
186        }
187        return sortInfos;
188    }
189}