001/*
002 * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
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.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 *     Olivier Grisel
016 *
017 */
018package org.nuxeo.ecm.platform.suggestbox.jsf;
019
020import static org.jboss.seam.ScopeType.CONVERSATION;
021
022import java.io.Serializable;
023import java.util.Collections;
024import java.util.List;
025import java.util.Locale;
026import java.util.Map;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.jboss.seam.annotations.In;
031import org.jboss.seam.annotations.Name;
032import org.jboss.seam.annotations.Scope;
033import org.jboss.seam.annotations.web.RequestParameter;
034import org.nuxeo.ecm.core.api.CoreSession;
035import org.nuxeo.ecm.core.api.DocumentModel;
036import org.nuxeo.ecm.platform.contentview.seam.ContentViewActions;
037import org.nuxeo.ecm.platform.suggestbox.service.Suggestion;
038import org.nuxeo.ecm.platform.suggestbox.service.SuggestionContext;
039import org.nuxeo.ecm.platform.suggestbox.service.SuggestionException;
040import org.nuxeo.ecm.platform.suggestbox.service.SuggestionService;
041import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
042import org.nuxeo.ecm.platform.ui.web.invalidations.AutomaticDocumentBasedInvalidation;
043import org.nuxeo.ecm.platform.ui.web.invalidations.DocumentContextBoundActionBean;
044import org.nuxeo.ecm.webapp.tree.nav.MultiNavTreeManager;
045import org.nuxeo.runtime.api.Framework;
046
047/**
048 * Back seam component for the top right search box using the suggestion service to help decode the user intent and
049 * minimize the number of clicks to find the relevant information.
050 */
051@Name("suggestboxActions")
052@Scope(CONVERSATION)
053@AutomaticDocumentBasedInvalidation
054public class SuggestboxActions extends DocumentContextBoundActionBean implements Serializable {
055
056    private static final Log log = LogFactory.getLog(SuggestboxActions.class);
057
058    private static final long serialVersionUID = 1L;
059
060    @In(create = true, required = false)
061    protected transient CoreSession documentManager;
062
063    @In(create = true)
064    protected transient NavigationContext navigationContext;
065
066    @In(create = true)
067    protected Map<String, String> messages;
068
069    @In(create = true)
070    protected Locale locale;
071
072    @In(create = true)
073    protected MultiNavTreeManager multiNavTreeManager;
074
075    /*
076     * @In(create = true) protected FacetedSearchActions facetedSearchActions;
077     */
078
079    @In(create = true)
080    protected ContentViewActions contentViewActions;
081
082    // keep suggestions in cache for maximum 10 seconds to avoid useless and
083    // costly re-computation of the suggestions by rich:suggestionbox at
084    // selection time
085    protected Cached<List<Suggestion>> cachedSuggestions = new Cached<List<Suggestion>>(10000);
086
087    protected String searchKeywords = "";
088
089    protected String suggesterGroup;
090
091    public String getSearchKeywords() {
092        return searchKeywords;
093    }
094
095    public void setSearchKeywords(String searchKeywords) {
096        this.searchKeywords = searchKeywords;
097    }
098
099    public String getSuggesterGroup() {
100        return suggesterGroup;
101    }
102
103    @RequestParameter
104    public void setSuggesterGroup(String suggesterGroup) {
105        this.suggesterGroup = suggesterGroup;
106    }
107
108    protected SuggestionContext getSuggestionContext() {
109        SuggestionContext ctx = new SuggestionContext(suggesterGroup, documentManager.getPrincipal()).withSession(
110                documentManager).withCurrentDocument(navigationContext.getCurrentDocument()).withLocale(locale).withMessages(
111                messages);
112        return ctx;
113    }
114
115    /**
116     * Callback for the ajax keypress event that triggers the generation of context sensitive action suggestions. The
117     * most specific actions (e.g. direct navigation to a document with matching titles) should be suggested in the
118     * first position and more generic (traditional free-text search for documents) last.
119     */
120    public List<Suggestion> getSuggestions(Object input) {
121        if (cachedSuggestions.hasExpired(input, locale)) {
122            SuggestionService service = Framework.getLocalService(SuggestionService.class);
123            SuggestionContext ctx = getSuggestionContext();
124            try {
125                List<Suggestion> suggestions = service.suggest(input.toString(), ctx);
126                cachedSuggestions.cache(suggestions, input, locale);
127            } catch (SuggestionException e) {
128                // log the exception rather than trying to display it since
129                // this
130                // method is called by ajax events when typing in the
131                // searchbox.
132                log.error(e, e);
133                return Collections.emptyList();
134            }
135        }
136        return cachedSuggestions.value;
137    }
138
139    @Override
140    protected void resetBeanCache(DocumentModel newCurrentDocumentModel) {
141        cachedSuggestions.expire();
142    }
143
144}