001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Thomas Roger
016 *     Nelson Silva
017 */
018
019package org.nuxeo.search.ui;
020
021import static org.nuxeo.search.ui.localconfiguration.Constants.SEARCH_CONFIGURATION_FACET;
022
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.core.api.SortInfo;
034import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService;
035import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService;
036import org.nuxeo.ecm.platform.actions.Action;
037import org.nuxeo.ecm.platform.actions.ActionContext;
038import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
039import org.nuxeo.ecm.platform.contentview.jsf.ContentViewHeader;
040import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService;
041import org.nuxeo.ecm.platform.contentview.jsf.ContentViewState;
042import org.nuxeo.ecm.platform.contentview.jsf.ContentViewStateImpl;
043import org.nuxeo.ecm.platform.query.api.PageProvider;
044import org.nuxeo.ecm.platform.query.api.PageProviderService;
045import org.nuxeo.ecm.platform.userworkspace.api.UserWorkspaceService;
046import org.nuxeo.runtime.api.Framework;
047import org.nuxeo.search.ui.localconfiguration.SearchConfiguration;
048
049/**
050 * @since 6.0
051 */
052public class SearchUIServiceImpl implements SearchUIService {
053
054    private static Log log = LogFactory.getLog(SearchUIServiceImpl.class);
055
056    public static final String SEARCH_CONTENT_VIEWS_CATEGORY = "SEARCH_CONTENT_VIEWS";
057
058    public static final String CONTENT_VIEW_NAME_PROPERTY = "contentViewName";
059
060    public static final String SAVED_SEARCHES_PROVIDER_NAME = "SAVED_SEARCHES";
061
062    public static final String SHARED_SEARCHES_PROVIDER_NAME = "SHARED_SAVED_SEARCHES";
063
064    public static final String CONTENT_VIEW_DISPLAY_FACET = "ContentViewDisplay";
065
066    @Override
067    public List<ContentViewHeader> getContentViewHeaders(ActionContext actionContext) {
068        return getContentViewHeaders(actionContext, null);
069    }
070
071    @Override
072    public List<ContentViewHeader> getContentViewHeaders(ActionContext actionContext, DocumentModel doc) {
073        ActionManager actionService = Framework.getService(ActionManager.class);
074        List<Action> actions = actionService.getActions(SEARCH_CONTENT_VIEWS_CATEGORY, actionContext);
075
076        List<String> contentViewNames = new ArrayList<>();
077        for (Action action : actions) {
078            String contentViewName = (String) action.getProperties().get(CONTENT_VIEW_NAME_PROPERTY);
079            if (contentViewName != null) {
080                contentViewNames.add(contentViewName);
081            }
082        }
083        contentViewNames = filterContentViewNames(contentViewNames, doc);
084
085        ContentViewService contentViewService = Framework.getLocalService(ContentViewService.class);
086        List<ContentViewHeader> contentViewHeaders = new ArrayList<>();
087        for (String contentViewName : contentViewNames) {
088            ContentViewHeader contentViewHeader = contentViewService.getContentViewHeader(contentViewName);
089            if (contentViewHeader != null) {
090                contentViewHeaders.add(contentViewHeader);
091            }
092        }
093        return contentViewHeaders;
094    }
095
096    /**
097     * Returns the filtered content view names based on the local configuration if any.
098     */
099    protected List<String> filterContentViewNames(List<String> contentViewNames, DocumentModel currentDoc) {
100        SearchConfiguration searchConfiguration = getSearchConfiguration(currentDoc);
101        return searchConfiguration == null ? contentViewNames
102                : searchConfiguration.filterAllowedContentViewNames(contentViewNames);
103    }
104
105    protected SearchConfiguration getSearchConfiguration(DocumentModel currentDoc) {
106        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
107        return localConfigurationService.getConfiguration(SearchConfiguration.class, SEARCH_CONFIGURATION_FACET,
108                currentDoc);
109    }
110
111    public DocumentModel saveSearch(CoreSession session, ContentViewState searchContentViewState, String title) {
112        UserWorkspaceService userWorkspaceService = Framework.getLocalService(UserWorkspaceService.class);
113        DocumentModel uws = userWorkspaceService.getCurrentUserPersonalWorkspace(session, null);
114
115        DocumentModel searchDoc = searchContentViewState.getSearchDocumentModel();
116        searchDoc.setPropertyValue("dc:title", title);
117
118        if (searchDoc.hasFacet(CONTENT_VIEW_DISPLAY_FACET)) {
119            searchDoc.setPropertyValue("cvd:contentViewName", searchContentViewState.getContentViewName());
120            List<SortInfo> sortInfos = searchContentViewState.getSortInfos();
121            if (sortInfos != null) {
122                ArrayList<Map<String, Serializable>> list = new ArrayList<>();
123                for (SortInfo sortInfo : sortInfos) {
124                    list.add(SortInfo.asMap(sortInfo));
125                }
126                searchDoc.setPropertyValue("cvd:sortInfos", list);
127            }
128            searchDoc.setPropertyValue("cvd:selectedLayoutColumns", (Serializable) searchContentViewState.getResultColumns());
129        } else {
130            log.warn(String.format("Search document type %s is missing %s facet", searchDoc.getType(),
131                CONTENT_VIEW_DISPLAY_FACET));
132        }
133
134        PathSegmentService pathService = Framework.getLocalService(PathSegmentService.class);
135        searchDoc.setPathInfo(uws.getPathAsString(), pathService.generatePathSegment(searchDoc));
136        searchDoc = session.createDocument(searchDoc);
137        session.save();
138
139        return searchDoc;
140    }
141
142    public List<DocumentModel> getCurrentUserSavedSearches(CoreSession session) {
143        return getDocuments(SAVED_SEARCHES_PROVIDER_NAME, session, session.getPrincipal().getName());
144    }
145
146    @SuppressWarnings("unchecked")
147    protected List<DocumentModel> getDocuments(String pageProviderName, CoreSession session, Object... parameters)
148            {
149        PageProviderService pageProviderService = Framework.getService(PageProviderService.class);
150        Map<String, Serializable> properties = new HashMap<String, Serializable>();
151        properties.put("coreSession", (Serializable) session);
152        return ((PageProvider<DocumentModel>) pageProviderService.getPageProvider(pageProviderName, null, null, null,
153                properties, parameters)).getCurrentPage();
154
155    }
156
157    public List<DocumentModel> getSharedSavedSearches(CoreSession session) {
158        return getDocuments(SHARED_SEARCHES_PROVIDER_NAME, session, session.getPrincipal().getName());
159    }
160
161    @Override
162    @SuppressWarnings("unchecked")
163    public ContentViewState loadSearch(DocumentModel savedSearch) {
164        if (!savedSearch.hasFacet(CONTENT_VIEW_DISPLAY_FACET)) {
165            log.warn(String.format("Search document type %s is missing %s facet", savedSearch.getType(),
166                CONTENT_VIEW_DISPLAY_FACET));
167            return null;
168        }
169        ContentViewState state = new ContentViewStateImpl();
170        state.setContentViewName((String) savedSearch.getPropertyValue("cvd:contentViewName"));
171        state.setSearchDocumentModel(savedSearch);
172        state.setSortInfos(getSortInfos(savedSearch));
173        state.setResultColumns((List<String>) savedSearch.getPropertyValue("cvd:selectedLayoutColumns"));
174        return state;
175    }
176
177    @SuppressWarnings("unchecked")
178    List<SortInfo> getSortInfos(DocumentModel savedSearch) {
179        List<Map<String, Serializable>> list = (List<Map<String, Serializable>>) savedSearch.getPropertyValue(
180            "cvd:sortInfos");
181        List<SortInfo> sortInfos = new ArrayList<>();
182        for (Map<String, Serializable> info : list) {
183            sortInfos.add(SortInfo.asSortInfo(info));
184        }
185        return sortInfos;
186    }
187}