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}