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 */
017
018package org.nuxeo.search.ui.seam;
019
020import static org.apache.commons.lang.StringUtils.isNotBlank;
021import static org.apache.commons.logging.LogFactory.getLog;
022import static org.jboss.seam.ScopeType.CONVERSATION;
023import static org.jboss.seam.annotations.Install.FRAMEWORK;
024import static org.nuxeo.ecm.webapp.helpers.EventNames.LOCAL_CONFIGURATION_CHANGED;
025import static org.nuxeo.ecm.webapp.helpers.EventNames.USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED;
026
027import java.io.Serializable;
028import java.io.UnsupportedEncodingException;
029import java.util.ArrayList;
030import java.util.List;
031import java.util.Map;
032
033import javax.faces.application.FacesMessage;
034import javax.faces.component.UIComponent;
035import javax.faces.component.UIViewRoot;
036import javax.faces.context.FacesContext;
037import javax.faces.model.SelectItem;
038import javax.faces.model.SelectItemGroup;
039import javax.faces.validator.ValidatorException;
040
041import org.apache.commons.lang.StringUtils;
042import org.apache.commons.logging.Log;
043import org.jboss.seam.annotations.Begin;
044import org.jboss.seam.annotations.In;
045import org.jboss.seam.annotations.Install;
046import org.jboss.seam.annotations.Name;
047import org.jboss.seam.annotations.Observer;
048import org.jboss.seam.annotations.Scope;
049import org.jboss.seam.annotations.intercept.BypassInterceptors;
050import org.jboss.seam.faces.FacesMessages;
051import org.jboss.seam.international.StatusMessage;
052import org.nuxeo.ecm.core.api.CoreSession;
053import org.nuxeo.ecm.core.api.DocumentModel;
054import org.nuxeo.ecm.core.api.IdRef;
055import org.nuxeo.ecm.core.api.PathRef;
056import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl;
057import org.nuxeo.ecm.platform.contentview.jsf.ContentView;
058import org.nuxeo.ecm.platform.contentview.jsf.ContentViewHeader;
059import org.nuxeo.ecm.platform.contentview.jsf.ContentViewService;
060import org.nuxeo.ecm.platform.contentview.jsf.ContentViewState;
061import org.nuxeo.ecm.platform.contentview.jsf.ContentViewStateImpl;
062import org.nuxeo.ecm.platform.contentview.json.JSONContentViewState;
063import org.nuxeo.ecm.platform.contentview.seam.ContentViewActions;
064import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
065import org.nuxeo.ecm.platform.ui.web.api.WebActions;
066import org.nuxeo.ecm.platform.ui.web.rest.RestHelper;
067import org.nuxeo.ecm.platform.ui.web.util.BaseURL;
068import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils;
069import org.nuxeo.ecm.platform.url.DocumentViewImpl;
070import org.nuxeo.ecm.platform.url.api.DocumentView;
071import org.nuxeo.ecm.platform.url.api.DocumentViewCodecManager;
072import org.nuxeo.ecm.webapp.action.ActionContextProvider;
073import org.nuxeo.ecm.webapp.documentsLists.DocumentsListsManager;
074import org.nuxeo.ecm.webapp.helpers.EventNames;
075import org.nuxeo.runtime.api.Framework;
076import org.nuxeo.search.ui.SearchUIService;
077
078/**
079 * Seam bean handling Search main tab actions.
080 *
081 * @since 6.0
082 */
083@Name("searchUIActions")
084@Scope(CONVERSATION)
085@Install(precedence = FRAMEWORK)
086public class SearchUIActions implements Serializable {
087
088    private static final long serialVersionUID = 1L;
089
090    private static final Log log = getLog(SearchUIActions.class);
091
092    public static final String SAVED_SEARCHES_LABEL = "label.saved.searches";
093
094    public static final String SHARED_SEARCHES_LABEL = "label.shared.searches";
095
096    public static final String SEARCH_FILTERS_LABEL = "label.search.filters";
097
098    public static final String SEARCH_SAVED_LABEL = "label.search.saved";
099
100    public static final String MAIN_TABS_SEARCH = "MAIN_TABS:search";
101
102    public static final String SEARCH_VIEW_ID = "/search/search.xhtml";
103
104    public static final String SEARCH_CODEC = "docpathsearch";
105
106    public static final String SIMPLE_SEARCH_CONTENT_VIEW_NAME = "simple_search";
107
108    public static final String NXQL_SEARCH_CONTENT_VIEW_NAME = "nxql_search";
109
110    public static final String DEFAULT_NXQL_QUERY = "SELECT * FROM Document"
111            + " WHERE ecm:mixinType != 'HiddenInNavigation'" + " AND ecm:isProxy = 0 AND ecm:isCheckedInVersion = 0"
112            + " AND ecm:currentLifeCycleState != 'deleted'";
113
114    public static final String CONTENT_VIEW_NAME_PARAMETER = "contentViewName";
115
116    public static final String CURRENT_PAGE_PARAMETER = "currentPage";
117
118    public static final String PAGE_SIZE_PARAMETER = "pageSize";
119
120    public static final String CONTENT_VIEW_STATE_PARAMETER = "state";
121
122    public static final String SEARCH_TERM_PARAMETER = "searchTerm";
123
124    @In(create = true)
125    protected transient NavigationContext navigationContext;
126
127    @In(create = true, required = false)
128    protected transient CoreSession documentManager;
129
130    @In(create = true, required = false)
131    protected transient ActionContextProvider actionContextProvider;
132
133    @In(create = true)
134    protected transient WebActions webActions;
135
136    @In(create = true)
137    protected RestHelper restHelper;
138
139    @In(create = true)
140    protected ContentViewActions contentViewActions;
141
142    @In(create = true)
143    protected ContentViewService contentViewService;
144
145    @In(create = true)
146    protected DocumentsListsManager documentsListsManager;
147
148    @In(create = true, required = false)
149    protected FacesMessages facesMessages;
150
151    @In(create = true)
152    protected Map<String, String> messages;
153
154    protected String simpleSearchKeywords = "";
155
156    protected String nxqlQuery = DEFAULT_NXQL_QUERY;
157
158    protected List<ContentViewHeader> contentViewHeaders;
159
160    protected String currentContentViewName;
161
162    protected String currentSelectedSavedSearchId;
163
164    protected String currentPage;
165
166    protected String pageSize;
167
168    protected String searchTerm;
169
170    protected String savedSearchTitle;
171
172    public String getSearchMainTab() {
173        return MAIN_TABS_SEARCH;
174    }
175
176    public void setSearchMainTab(String tabs) {
177        webActions.setCurrentTabIds(!StringUtils.isBlank(tabs) ? tabs : MAIN_TABS_SEARCH);
178    }
179
180    public String getSearchViewTitle() {
181        if (currentSelectedSavedSearchId != null) {
182            DocumentModel savedSearch = documentManager.getDocument(new IdRef(currentSelectedSavedSearchId));
183            return savedSearch.getTitle();
184        } else if (currentContentViewName != null) {
185            ContentView cv = contentViewActions.getContentView(currentContentViewName);
186            String title = cv.getTranslateTitle() ? messages.get(cv.getTitle()) : cv.getTitle();
187            return isNotBlank(title) ? title : currentContentViewName;
188        }
189        return null;
190    }
191
192    /**
193     * Returns true if the user is viewing SEARCH.
194     */
195    public boolean isOnSearchView() {
196        if (FacesContext.getCurrentInstance() == null) {
197            return false;
198        }
199
200        UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
201        if (viewRoot != null) {
202            String viewId = viewRoot.getViewId();
203            // FIXME find a better way to update the current document only
204            // if we are on SEARCH
205            if (SEARCH_VIEW_ID.equals(viewId)) {
206                return true;
207            }
208        }
209        return false;
210    }
211
212    public String getJSONContentViewState() throws UnsupportedEncodingException {
213        ContentView contentView = contentViewActions.getContentView(currentContentViewName);
214        ContentViewService contentViewService = Framework.getService(ContentViewService.class);
215        ContentViewState state = contentViewService.saveContentView(contentView);
216        return JSONContentViewState.toJSON(state, true);
217    }
218
219    public String getCurrentContentViewName() {
220        if (currentContentViewName == null) {
221            List<ContentViewHeader> contentViewHeaders = getContentViewHeaders();
222            if (!contentViewHeaders.isEmpty()) {
223                currentContentViewName = contentViewHeaders.get(0).getName();
224            }
225        }
226        return currentContentViewName;
227    }
228
229    public void setCurrentContentViewName(String contentViewName) {
230        this.currentContentViewName = contentViewName;
231    }
232
233    public String getCurrentSelectedSavedSearchId() {
234        return currentSelectedSavedSearchId != null ? currentSelectedSavedSearchId : currentContentViewName;
235    }
236
237    public void setCurrentSelectedSavedSearchId(String selectedSavedSearchId) throws UnsupportedEncodingException {
238        resetCurrentContentViewWorkingList();
239
240        for (ContentViewHeader contentViewHeader : contentViewHeaders) {
241            if (contentViewHeader.getName().equals(selectedSavedSearchId)) {
242                contentViewActions.reset(currentContentViewName);
243                currentContentViewName = selectedSavedSearchId;
244                currentSelectedSavedSearchId = null;
245                return;
246            }
247        }
248        DocumentModel savedSearch = documentManager.getDocument(new IdRef(selectedSavedSearchId));
249        loadSavedSearch(savedSearch);
250    }
251
252    protected void resetCurrentContentViewWorkingList() {
253        if (currentContentViewName != null) {
254            ContentView contentView = contentViewActions.getContentView(currentContentViewName);
255            documentsListsManager.resetWorkingList(contentView.getSelectionListName());
256        }
257    }
258
259    public void loadSavedSearch(DocumentModel searchDocument) throws UnsupportedEncodingException {
260        SearchUIService searchUIService = Framework.getService(SearchUIService.class);
261        ContentViewState contentViewState = searchUIService.loadSearch(searchDocument);
262        if (contentViewState != null) {
263            ContentView contentView = contentViewActions.restoreContentView(contentViewState);
264            currentContentViewName = contentView.getName();
265        }
266        currentSelectedSavedSearchId = searchDocument.getId();
267    }
268
269    public List<ContentViewHeader> getContentViewHeaders() {
270        if (contentViewHeaders == null) {
271            SearchUIService searchUIService = Framework.getService(SearchUIService.class);
272            contentViewHeaders = searchUIService.getContentViewHeaders(actionContextProvider.createActionContext(),
273                    navigationContext.getCurrentDocument());
274        }
275        return contentViewHeaders;
276    }
277
278    public void clearSearch() {
279        if (currentContentViewName != null) {
280            contentViewActions.reset(currentContentViewName);
281            resetCurrentContentViewWorkingList();
282        }
283    }
284
285    public void refreshAndRewind() {
286        String contentViewName = getCurrentContentViewName();
287        if (contentViewName != null) {
288            contentViewActions.refreshAndRewind(contentViewName);
289            resetCurrentContentViewWorkingList();
290        }
291    }
292
293    public void refreshAndRewindAndResetAggregates() {
294        contentViewActions.resetAggregates(getCurrentContentViewName());
295        refreshAndRewind();
296    }
297
298    /*
299     * ----- Load / Save searches -----
300     */
301
302    public List<SelectItem> getAllSavedSearchesSelectItems() {
303        List<SelectItem> items = new ArrayList<>();
304
305        // Add flagged content views
306        SelectItemGroup flaggedGroup = new SelectItemGroup(messages.get(SEARCH_FILTERS_LABEL));
307        List<ContentViewHeader> flaggedSavedSearches = getContentViewHeaders();
308        List<SelectItem> flaggedSavedSearchesItems = convertCVToSelectItems(flaggedSavedSearches);
309        flaggedGroup.setSelectItems(flaggedSavedSearchesItems.toArray(new SelectItem[flaggedSavedSearchesItems.size()]));
310        items.add(flaggedGroup);
311
312        // Add saved searches
313        List<DocumentModel> userSavedSearches = getSavedSearches();
314        if (!userSavedSearches.isEmpty()) {
315            SelectItemGroup userGroup = new SelectItemGroup(messages.get(SAVED_SEARCHES_LABEL));
316
317            List<SelectItem> userSavedSearchesItems = convertToSelectItems(userSavedSearches);
318            userGroup.setSelectItems(userSavedSearchesItems.toArray(new SelectItem[userSavedSearchesItems.size()]));
319            items.add(userGroup);
320        }
321
322        // Add shared searches
323        List<DocumentModel> otherUsersSavedFacetedSearches = getSharedSearches();
324        if (!otherUsersSavedFacetedSearches.isEmpty()) {
325            List<SelectItem> otherUsersSavedSearchesItems = convertToSelectItems(otherUsersSavedFacetedSearches);
326            SelectItemGroup allGroup = new SelectItemGroup(messages.get(SHARED_SEARCHES_LABEL));
327            allGroup.setSelectItems(otherUsersSavedSearchesItems.toArray(new SelectItem[otherUsersSavedSearchesItems.size()]));
328            items.add(allGroup);
329        }
330        return items;
331    }
332
333    protected List<DocumentModel> getSavedSearches() {
334        SearchUIService searchUIService = Framework.getService(SearchUIService.class);
335        return searchUIService.getCurrentUserSavedSearches(documentManager);
336    }
337
338    protected List<DocumentModel> getSharedSearches() {
339        SearchUIService searchUIService = Framework.getService(SearchUIService.class);
340        return searchUIService.getSharedSavedSearches(documentManager);
341    }
342
343    protected List<SelectItem> convertToSelectItems(List<DocumentModel> docs) {
344        List<SelectItem> items = new ArrayList<>();
345        for (DocumentModel doc : docs) {
346            items.add(new SelectItem(doc.getId(), doc.getTitle(), ""));
347        }
348        return items;
349    }
350
351    protected List<SelectItem> convertCVToSelectItems(List<ContentViewHeader> contentViewHeaders) {
352        List<SelectItem> items = new ArrayList<>();
353        for (ContentViewHeader contentViewHeader : contentViewHeaders) {
354            items.add(new SelectItem(contentViewHeader.getName(), messages.get(contentViewHeader.getTitle()), ""));
355        }
356        return items;
357    }
358
359    public String getSavedSearchTitle() {
360        return savedSearchTitle;
361    }
362
363    public void setSavedSearchTitle(String savedSearchTitle) {
364        this.savedSearchTitle = savedSearchTitle;
365    }
366
367    public String saveSearch() {
368        ContentView contentView = contentViewActions.getContentView(getCurrentContentViewName());
369        if (contentView != null) {
370            ContentViewState state = contentViewService.saveContentView(contentView);
371            SearchUIService searchUIService = Framework.getService(SearchUIService.class);
372            DocumentModel savedSearch = searchUIService.saveSearch(documentManager, state, savedSearchTitle);
373            currentSelectedSavedSearchId = savedSearch.getId();
374
375            savedSearchTitle = null;
376            facesMessages.add(StatusMessage.Severity.INFO, messages.get(SEARCH_SAVED_LABEL));
377
378            // Events.instance().raiseEvent(EventNames.DOCUMENT_CHILDREN_CHANGED,
379            // uws);
380
381        }
382        return null;
383    }
384
385    /**
386     * Retsurns true if current search can be saved.
387     * <p>
388     * Returns false if current content view is waiting for a first execution.
389     *
390     * @since 7.4
391     */
392    public boolean getCanSaveSearch() {
393        ContentView contentView = contentViewActions.getContentView(getCurrentContentViewName());
394        if (contentView != null) {
395            boolean res = !contentView.isWaitForExecution() || contentView.isExecuted();
396            return res;
397        }
398        return false;
399    }
400
401    public void cancelSaveSearch() {
402        savedSearchTitle = null;
403    }
404
405    /*
406     * ----- Permanent links -----
407     */
408
409    public void setState(String state) throws UnsupportedEncodingException {
410        if (isNotBlank(state)) {
411            Long finalPageSize = null;
412            if (!StringUtils.isBlank(pageSize)) {
413                try {
414                    finalPageSize = Long.valueOf(pageSize);
415                } catch (NumberFormatException e) {
416                    log.warn(String.format("Unable to parse '%s' parameter with value '%s'", PAGE_SIZE_PARAMETER,
417                            pageSize));
418                }
419            }
420
421            Long finalCurrentPage = null;
422            if (!StringUtils.isBlank(currentPage)) {
423                try {
424                    finalCurrentPage = Long.valueOf(currentPage);
425                } catch (NumberFormatException e) {
426                    log.warn(String.format("Unable to parse '%s' parameter with value '%s'", CURRENT_PAGE_PARAMETER,
427                            currentPage));
428                }
429            }
430
431            String cvName = getCurrentContentViewName();
432            List<ContentViewHeader> contentViewHeaders = getContentViewHeaders();
433            if (cvName != null && contentViewHeaders != null) {
434                boolean canRestore = false;
435                for (ContentViewHeader contentViewHeader : getContentViewHeaders()) {
436                    if (cvName.equals(contentViewHeader.getName())) {
437                        canRestore = true;
438                    }
439                }
440
441                if (canRestore) {
442                    contentViewActions.restoreContentView(getCurrentContentViewName(), finalCurrentPage, finalPageSize,
443                            null, state);
444                } else {
445                    invalidateContentViewsName();
446                }
447            }
448        }
449    }
450
451    public String getCurrentPage() {
452        return currentPage;
453    }
454
455    public void setCurrentPage(String currentPage) {
456        this.currentPage = currentPage;
457    }
458
459    public String getPageSize() {
460        return pageSize;
461    }
462
463    public void setPageSize(String pageSize) {
464        this.pageSize = pageSize;
465    }
466
467    public void setSearchTerm(String searchTerm) throws UnsupportedEncodingException {
468        // If the search term is not defined, we don't do the logic
469        if (!StringUtils.isEmpty(searchTerm)) {
470            // By default, the "simple_search" content view is used
471            currentContentViewName = SIMPLE_SEARCH_CONTENT_VIEW_NAME;
472            // Create a ContentViewState
473            ContentView cv = contentViewService.getContentView(SIMPLE_SEARCH_CONTENT_VIEW_NAME);
474            DocumentModel searchDocumentModel = cv.getSearchDocumentModel();
475            // set the search term
476            searchDocumentModel.setPropertyValue("default_search:ecm_fulltext", searchTerm);
477            ContentViewState state = new ContentViewStateImpl();
478            state.setSearchDocumentModel(searchDocumentModel);
479            state.setContentViewName(getCurrentContentViewName());
480            ContentView ccv = contentViewActions.restoreContentView(state);
481            ccv.setExecuted(true);
482        }
483    }
484
485    /**
486     * Compute a permanent link for the current search.
487     */
488    public String getSearchPermanentLinkUrl() throws UnsupportedEncodingException {
489        // do not try to compute an URL if we don't have any CoreSession
490        if (documentManager == null) {
491            return null;
492        }
493
494        return generateSearchUrl(true);
495    }
496
497    /**
498     * @return the URL of the search tab with the search term defined.
499     */
500    public String getSearchTabUrl(String searchTerm) throws UnsupportedEncodingException {
501        // do not try to compute an URL if we don't have any CoreSession
502        if (documentManager == null) {
503            return null;
504        }
505        // Set the value of the searched term
506        this.searchTerm = searchTerm;
507
508        return generateSearchUrl(false);
509    }
510
511    /**
512     * Create the url to access the Search tab.
513     *
514     * @param withState If set to true, the state is added in the parameters.
515     */
516    protected String generateSearchUrl(boolean withState) throws UnsupportedEncodingException {
517        String currentContentViewName = getCurrentContentViewName();
518        DocumentModel currentDocument = navigationContext.getCurrentDocument();
519        DocumentView docView = computeDocumentView(currentDocument);
520        docView.setViewId("search");
521        docView.addParameter(CONTENT_VIEW_NAME_PARAMETER, currentContentViewName);
522        // Add the state if needed
523        if (withState) {
524            docView.addParameter(CONTENT_VIEW_STATE_PARAMETER, getJSONContentViewState());
525        }
526
527        DocumentViewCodecManager documentViewCodecManager = Framework.getService(DocumentViewCodecManager.class);
528        String url = documentViewCodecManager.getUrlFromDocumentView(SEARCH_CODEC, docView, true, BaseURL.getBaseURL());
529
530        return RestHelper.addCurrentConversationParameters(url);
531    }
532
533    protected DocumentView computeDocumentView(DocumentModel doc) {
534        return new DocumentViewImpl(new DocumentLocationImpl(documentManager.getRepositoryName(),
535                doc != null ? new PathRef(doc.getPathAsString()) : null));
536    }
537
538    /*
539     * ----- Simple Search -----
540     */
541    public String getSimpleSearchKeywords() {
542        return simpleSearchKeywords;
543    }
544
545    public void setSimpleSearchKeywords(String simpleSearchKeywords) {
546        this.simpleSearchKeywords = simpleSearchKeywords;
547    }
548
549    public void validateSimpleSearchKeywords(FacesContext context, UIComponent component, Object value) {
550        if (!(value instanceof String) || StringUtils.isEmpty(((String) value).trim())) {
551            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context,
552                    "feedback.search.noKeywords"), null);
553            // also add global message
554            context.addMessage(null, message);
555            throw new ValidatorException(message);
556        }
557        String[] keywords = ((String) value).trim().split(" ");
558        for (String keyword : keywords) {
559            if (keyword.startsWith("*")) {
560                // Can't begin search with * character
561                FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context,
562                        "feedback.search.star"), null);
563                // also add global message
564                context.addMessage(null, message);
565                throw new ValidatorException(message);
566            }
567        }
568    }
569
570    public String doSimpleSearch() {
571        setSearchMainTab(null);
572        currentContentViewName = SIMPLE_SEARCH_CONTENT_VIEW_NAME;
573        ContentView contentView = contentViewActions.getContentView(SIMPLE_SEARCH_CONTENT_VIEW_NAME);
574        DocumentModel searchDoc = contentView.getSearchDocumentModel();
575        searchDoc.setPropertyValue("defaults:ecm_fulltext", simpleSearchKeywords);
576        refreshAndRewind();
577        return "search";
578    }
579
580    /*
581     * ----- NXQL Search -----
582     */
583    public String getNxqlQuery() {
584        return nxqlQuery;
585    }
586
587    public void setNxqlQuery(String nxqlQuery) {
588        this.nxqlQuery = nxqlQuery;
589    }
590
591    public boolean isNxqlSearchSelected() {
592        return NXQL_SEARCH_CONTENT_VIEW_NAME.equals(currentContentViewName);
593    }
594
595    @Begin(id = "#{conversationIdGenerator.currentOrNewMainConversationId}", join = true)
596    public String loadPermanentLink(DocumentView docView) {
597        restHelper.initContextFromRestRequest(docView);
598        return "search";
599    }
600
601    @Observer(value = LOCAL_CONFIGURATION_CHANGED)
602    public void invalidateContentViewsName() {
603        clearSearch();
604        contentViewHeaders = null;
605        currentContentViewName = null;
606    }
607
608    @Observer(value = USER_ALL_DOCUMENT_TYPES_SELECTION_CHANGED)
609    public void invalidateContentViewsNameIfChanged() {
610        List<ContentViewHeader> temp = new ArrayList<>(
611                Framework.getLocalService(SearchUIService.class).getContentViewHeaders(
612                        actionContextProvider.createActionContext(), navigationContext.getCurrentDocument()));
613        if (temp != null) {
614            if (!temp.equals(contentViewHeaders)) {
615                invalidateContentViewsName();
616            }
617            if (!temp.isEmpty()) {
618                String s = temp.get(0).getName();
619                if (s != null && !s.equals(currentContentViewName)) {
620                    invalidateContentViewsName();
621                }
622            }
623        }
624    }
625
626    /**
627     * Reset attributes.
628     */
629    @Observer(value = { EventNames.FLUSH_EVENT }, create = false)
630    @BypassInterceptors
631    public void resetOnFlush() {
632        contentViewHeaders = null;
633        currentSelectedSavedSearchId = null;
634        currentContentViewName = null;
635        nxqlQuery = DEFAULT_NXQL_QUERY;
636        simpleSearchKeywords = "";
637    }
638
639    public String getSearchTermParameter() {
640        return SEARCH_TERM_PARAMETER;
641    }
642}