001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.apps.search;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.main.CmsException;
033import org.opencms.main.OpenCms;
034import org.opencms.ui.A_CmsUI;
035import org.opencms.ui.CmsVaadinUtils;
036import org.opencms.ui.FontOpenCms;
037import org.opencms.ui.I_CmsDialogContext;
038import org.opencms.ui.I_CmsDialogContext.ContextType;
039import org.opencms.ui.apps.A_CmsWorkplaceApp;
040import org.opencms.ui.apps.CmsAppWorkplaceUi;
041import org.opencms.ui.apps.CmsFileExplorer;
042import org.opencms.ui.apps.I_CmsAppUIContext;
043import org.opencms.ui.apps.I_CmsCachableApp;
044import org.opencms.ui.apps.I_CmsContextProvider;
045import org.opencms.ui.apps.Messages;
046import org.opencms.ui.apps.projects.CmsProjectManagerConfiguration;
047import org.opencms.ui.apps.search.CmsSourceSearchForm.SearchType;
048import org.opencms.ui.components.CmsErrorDialog;
049import org.opencms.ui.components.CmsFileTable;
050import org.opencms.ui.components.CmsFileTableDialogContext;
051import org.opencms.ui.report.CmsReportOverlay;
052import org.opencms.util.CmsStringUtil;
053import org.opencms.util.CmsUUID;
054
055import java.util.Collections;
056import java.util.HashSet;
057import java.util.LinkedHashMap;
058import java.util.List;
059import java.util.Set;
060
061import org.apache.commons.codec.DecoderException;
062import org.apache.commons.codec.net.URLCodec;
063
064import com.vaadin.server.Sizeable.Unit;
065import com.vaadin.ui.Component;
066import com.vaadin.ui.HorizontalSplitPanel;
067import com.vaadin.ui.UI;
068import com.vaadin.ui.themes.ValoTheme;
069import com.vaadin.v7.event.FieldEvents.TextChangeEvent;
070import com.vaadin.v7.event.FieldEvents.TextChangeListener;
071import com.vaadin.v7.ui.TextField;
072import com.vaadin.v7.ui.VerticalLayout;
073
074/**
075 * The source search app.<p>
076 */
077public class CmsSourceSearchApp extends A_CmsWorkplaceApp implements I_CmsCachableApp {
078
079    /** The folder key. */
080    public static final String FOLDER = "f";
081
082    /** The ignore subsites key. */
083    public static final String IGNORE_SUBSITES = "igss";
084
085    /** The index key. */
086    public static final String INDEX = "i";
087
088    /** The locale key. */
089    public static final String LOCALE = "l";
090
091    /** The project ley. */
092    public static final String PROJECT = "p";
093
094    /** The property key. */
095    public static final String PROPERTY = "pr";
096
097    /** The query key. */
098    public static final String QUERY = "q";
099
100    /** The replace pattern key. */
101    public static final String REPLACE_PATTERN = "rp";
102
103    /** The resource type key. */
104    public static final String RESOURCE_TYPE = "rt";
105
106    /** The search pattern key. */
107    public static final String SEARCH_PATTERN = "sp";
108
109    /** The type key. */
110    public static final String SEARCH_TYPE = "t";
111
112    /** The site root key. */
113    public static final String SITE_ROOT = "s";
114
115    /** The XPath key. */
116    public static final String XPATH = "x";
117
118    /** The serial version id. */
119    private static final long serialVersionUID = 4675966043824229258L;
120
121    /** The results file table. */
122    CmsFileTable m_resultTable;
123
124    /** The currently selected result list resources. */
125    private List<CmsResource> m_currentResources;
126
127    /** The current state string. */
128    private String m_currentState;
129
130    /**Layout showing empty result message.*/
131    private VerticalLayout m_infoEmptyResult;
132
133    /**Layout showing introduction message.*/
134    private VerticalLayout m_infoIntroLayout;
135
136    /** The current search report. */
137    private CmsReportOverlay m_report;
138
139    /** The result table filter input. */
140    private TextField m_resultTableFilter;
141
142    /** The search form. */
143    private CmsSourceSearchForm m_searchForm;
144
145    /** The search and replace thread. */
146    private CmsSearchReplaceThread m_thread;
147
148    /**
149     * Generates the state string for the given search settings.<p>
150     *
151     * @param settings the search settings
152     *
153     * @return the state string
154     */
155    public static String generateState(CmsSearchReplaceSettings settings) {
156
157        String state = "";
158        state = A_CmsWorkplaceApp.addParamToState(state, SITE_ROOT, settings.getSiteRoot());
159        state = A_CmsWorkplaceApp.addParamToState(state, SEARCH_TYPE, settings.getType().name());
160        state = A_CmsWorkplaceApp.addParamToState(state, SEARCH_PATTERN, settings.getSearchpattern());
161        if (!settings.getPaths().isEmpty()) {
162            state = A_CmsWorkplaceApp.addParamToState(state, FOLDER, settings.getPaths().get(0));
163        }
164        state = A_CmsWorkplaceApp.addParamToState(state, RESOURCE_TYPE, settings.getTypes());
165        state = A_CmsWorkplaceApp.addParamToState(state, LOCALE, settings.getLocale());
166        state = A_CmsWorkplaceApp.addParamToState(state, QUERY, settings.getQuery());
167        state = A_CmsWorkplaceApp.addParamToState(state, INDEX, settings.getSource());
168        state = A_CmsWorkplaceApp.addParamToState(state, XPATH, settings.getXpath());
169        state = A_CmsWorkplaceApp.addParamToState(state, IGNORE_SUBSITES, String.valueOf(settings.ignoreSubSites()));
170        state = A_CmsWorkplaceApp.addParamToState(state, PROPERTY, settings.getProperty().getName());
171
172        return state;
173    }
174
175    /**
176     * Returns the settings for the given state.<p>
177     *
178     * @param state the state
179     *
180     * @return the search settings
181     */
182    static CmsSearchReplaceSettings getSettingsFromState(String state) {
183
184        try {
185            state = new URLCodec().decode(state);
186        } catch (DecoderException e1) {
187            //
188        }
189        CmsSearchReplaceSettings settings = null;
190        String typeString = A_CmsWorkplaceApp.getParamFromState(state, SEARCH_TYPE);
191        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(typeString)) {
192            SearchType type = SearchType.valueOf(typeString);
193            settings = new CmsSearchReplaceSettings();
194            settings.setType(type);
195            settings.setIgnoreSubSites(
196                Boolean.parseBoolean(A_CmsWorkplaceApp.getParamFromState(state, IGNORE_SUBSITES)));
197            settings.setSiteRoot(A_CmsWorkplaceApp.getParamFromState(state, SITE_ROOT).replace("%2F", "/"));
198            settings.setPaths(
199                Collections.singletonList(A_CmsWorkplaceApp.getParamFromState(state, FOLDER).replace("%2F", "/")));
200            String resType = A_CmsWorkplaceApp.getParamFromState(state, RESOURCE_TYPE);
201            if (resType != null) {
202                settings.setTypes(resType);
203            }
204            String project = A_CmsWorkplaceApp.getParamFromState(state, PROJECT);
205            if (project != null) {
206                settings.setProject(project);
207            }
208            settings.setSearchpattern(A_CmsWorkplaceApp.getParamFromState(state, SEARCH_PATTERN).replace("%2F", "/"));
209            if (type.isContentValuesOnly()) {
210                settings.setOnlyContentValues(true);
211                String locale = A_CmsWorkplaceApp.getParamFromState(state, LOCALE);
212                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(locale)) {
213                    settings.setLocale(locale);
214                }
215                settings.setXpath(A_CmsWorkplaceApp.getParamFromState(state, XPATH).replace("%2F", "/"));
216            }
217            if (type.isSolrSearch()) {
218                settings.setQuery(A_CmsWorkplaceApp.getParamFromState(state, QUERY).replace("%2F", "/"));
219                settings.setSource(A_CmsWorkplaceApp.getParamFromState(state, INDEX));
220            }
221            if (type.isPropertySearch()) {
222                try {
223                    settings.setProperty(
224                        A_CmsUI.getCmsObject().readPropertyDefinition(
225                            A_CmsWorkplaceApp.getParamFromState(state, PROPERTY)));
226                } catch (CmsException e) {
227                    //
228                }
229            }
230        }
231        return settings;
232    }
233
234    /**
235     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#initUI(org.opencms.ui.apps.I_CmsAppUIContext)
236     */
237    @Override
238    public void initUI(I_CmsAppUIContext context) {
239
240        context.addPublishButton(changed -> {/* do nothing */});
241        super.initUI(context);
242    }
243
244    /**
245     * @see org.opencms.ui.apps.I_CmsCachableApp#isCachable()
246     */
247    public boolean isCachable() {
248
249        return true;
250    }
251
252    /**
253     * @see org.opencms.ui.apps.I_CmsCachableApp#onRestoreFromCache()
254     */
255    public void onRestoreFromCache() {
256
257        if (m_resultTable.getItemCount() < CmsFileExplorer.UPDATE_FOLDER_THRESHOLD) {
258            m_resultTable.update(m_resultTable.getAllIds(), false);
259        } else {
260            if (m_currentResources != null) {
261                Set<CmsUUID> ids = new HashSet<CmsUUID>();
262                for (CmsResource res : m_currentResources) {
263                    ids.add(res.getStructureId());
264                }
265                m_resultTable.update(ids, false);
266            }
267        }
268    }
269
270    /**
271     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#onStateChange(java.lang.String)
272     */
273    @Override
274    public void onStateChange(String state) {
275
276        if ((m_currentState == null) || !m_currentState.equals(state)) {
277            super.onStateChange(state);
278        }
279    }
280
281    /**
282     * Displays the search result.<p>
283     */
284    protected void displayResult() {
285
286        if (m_thread.getMatchedResources().isEmpty()) {
287            m_resultTable.setVisible(false);
288            m_infoIntroLayout.setVisible(false);
289            m_infoEmptyResult.setVisible(true);
290        } else {
291            m_resultTable.setVisible(true);
292            m_infoIntroLayout.setVisible(false);
293            m_infoEmptyResult.setVisible(false);
294        }
295        m_resultTable.fillTable(A_CmsUI.getCmsObject(), m_thread.getMatchedResources());
296        m_searchForm.removeComponent(m_report);
297        m_report = null;
298    }
299
300    /**
301     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getBreadCrumbForState(java.lang.String)
302     */
303    @Override
304    protected LinkedHashMap<String, String> getBreadCrumbForState(String state) {
305
306        return null;
307    }
308
309    /**
310     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getComponentForState(java.lang.String)
311     */
312    @Override
313    protected Component getComponentForState(String state) {
314
315        m_rootLayout.setMainHeightFull(true);
316        HorizontalSplitPanel sp = new HorizontalSplitPanel();
317        sp.setSizeFull();
318        m_searchForm = new CmsSourceSearchForm(this);
319        sp.setFirstComponent(m_searchForm);
320        VerticalLayout result = new VerticalLayout();
321        result.setSizeFull();
322        m_infoIntroLayout = CmsVaadinUtils.getInfoLayout(Messages.GUI_SOURCESEARCH_INTRO_0);
323        m_infoEmptyResult = CmsVaadinUtils.getInfoLayout(Messages.GUI_SOURCESEARCH_EMPTY_0);
324        m_resultTable = new CmsFileTable(null);
325
326        result.addComponent(m_resultTable);
327        result.addComponent(m_infoEmptyResult);
328        result.addComponent(m_infoIntroLayout);
329
330        m_resultTable.setVisible(false);
331        m_infoEmptyResult.setVisible(false);
332        m_infoIntroLayout.setVisible(true);
333
334        m_resultTable.applyWorkplaceAppSettings();
335        m_resultTable.setContextProvider(new I_CmsContextProvider() {
336
337            /**
338             * @see org.opencms.ui.apps.I_CmsContextProvider#getDialogContext()
339             */
340            public I_CmsDialogContext getDialogContext() {
341
342                CmsFileTableDialogContext context = new CmsFileTableDialogContext(
343                    CmsProjectManagerConfiguration.APP_ID,
344                    ContextType.fileTable,
345                    m_resultTable,
346                    m_resultTable.getSelectedResources());
347                storeCurrentFileSelection(m_resultTable.getSelectedResources());
348                context.setEditableProperties(CmsFileExplorer.INLINE_EDIT_PROPERTIES);
349                return context;
350            }
351        });
352        m_resultTable.setSizeFull();
353        if (m_resultTableFilter == null) {
354            m_resultTableFilter = new TextField();
355            m_resultTableFilter.setIcon(FontOpenCms.FILTER);
356            m_resultTableFilter.setInputPrompt(
357                Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0));
358            m_resultTableFilter.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON);
359            m_resultTableFilter.setWidth("200px");
360            m_resultTableFilter.addTextChangeListener(new TextChangeListener() {
361
362                private static final long serialVersionUID = 1L;
363
364                public void textChange(TextChangeEvent event) {
365
366                    m_resultTable.filterTable(event.getText());
367
368                }
369            });
370            m_infoLayout.addComponent(m_resultTableFilter);
371        }
372
373        sp.setSecondComponent(result);
374        sp.setSplitPosition(CmsFileExplorer.LAYOUT_SPLIT_POSITION, Unit.PIXELS);
375
376        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(state)) {
377            CmsSearchReplaceSettings settings = getSettingsFromState(state);
378            if (settings != null) {
379                m_currentState = state;
380                m_searchForm.initFormValues(settings);
381                search(settings, false);
382            }
383        }
384        return sp;
385    }
386
387    /**
388     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getSubNavEntries(java.lang.String)
389     */
390    @Override
391    protected List<NavEntry> getSubNavEntries(String state) {
392
393        return null;
394    }
395
396    /**
397     * Executes the search.<p>
398     *
399     * @param settings the search settings
400     * @param updateState <code>true</code> to create a new history entry
401     */
402    protected void search(CmsSearchReplaceSettings settings, boolean updateState) {
403
404        if (updateState) {
405            String state = generateState(settings);
406            CmsAppWorkplaceUi.get().changeCurrentAppState(state);
407            m_currentState = state;
408        }
409
410        CmsObject cms;
411        try {
412            cms = OpenCms.initCmsObject(A_CmsUI.getCmsObject());
413            if (settings.getSiteRoot() != null) {
414                cms.getRequestContext().setSiteRoot(settings.getSiteRoot());
415            }
416
417            m_thread = new CmsSearchReplaceThread(A_CmsUI.get().getHttpSession(), cms, settings);
418            if (m_report != null) {
419                m_searchForm.removeComponent(m_report);
420            }
421            m_report = new CmsReportOverlay(m_thread);
422            m_report.addReportFinishedHandler(new Runnable() {
423
424                public void run() {
425
426                    displayResult();
427                }
428            });
429            m_searchForm.addComponent(m_report);
430            m_report.setTitle(CmsVaadinUtils.getMessageText(Messages.GUI_SOURCESEARCH_REPORT_TITLE_0));
431            m_thread.start();
432            m_resultTableFilter.clear();
433        } catch (CmsException e) {
434            CmsErrorDialog.showErrorDialog(e);
435        }
436    }
437
438    /**
439     * Stores the currently selected resources list.<p>
440     *
441     * @param resources the currently selected resources
442     */
443    void storeCurrentFileSelection(List<CmsResource> resources) {
444
445        m_currentResources = resources;
446    }
447}