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