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.io.ByteArrayInputStream; 056import java.text.SimpleDateFormat; 057import java.util.Collections; 058import java.util.Date; 059import java.util.HashSet; 060import java.util.LinkedHashMap; 061import java.util.List; 062import java.util.Set; 063 064import org.apache.commons.codec.DecoderException; 065import org.apache.commons.codec.net.URLCodec; 066 067import com.vaadin.server.Sizeable.Unit; 068import com.vaadin.server.StreamResource; 069import com.vaadin.ui.Component; 070import com.vaadin.ui.HorizontalSplitPanel; 071import com.vaadin.ui.UI; 072import com.vaadin.ui.themes.ValoTheme; 073import com.vaadin.v7.event.FieldEvents.TextChangeEvent; 074import com.vaadin.v7.event.FieldEvents.TextChangeListener; 075import com.vaadin.v7.ui.TextField; 076import com.vaadin.v7.ui.VerticalLayout; 077 078/** 079 * The source search app.<p> 080 */ 081public class CmsSourceSearchApp extends A_CmsWorkplaceApp implements I_CmsCachableApp { 082 083 /** The folder key. */ 084 public static final String FOLDER = "f"; 085 086 /** The ignore subsites key. */ 087 public static final String IGNORE_SUBSITES = "igss"; 088 089 /** The index key. */ 090 public static final String INDEX = "i"; 091 092 /** The locale key. */ 093 public static final String LOCALE = "l"; 094 095 /** The project ley. */ 096 public static final String PROJECT = "p"; 097 098 /** The property key. */ 099 public static final String PROPERTY = "pr"; 100 101 /** The query key. */ 102 public static final String QUERY = "q"; 103 104 /** The replace pattern key. */ 105 public static final String REPLACE_PATTERN = "rp"; 106 107 /** The resource type key. */ 108 public static final String RESOURCE_TYPE = "rt"; 109 110 /** The search pattern key. */ 111 public static final String SEARCH_PATTERN = "sp"; 112 113 /** The type key. */ 114 public static final String SEARCH_TYPE = "t"; 115 116 /** The site root key. */ 117 public static final String SITE_ROOT = "s"; 118 119 /** The XPath key. */ 120 public static final String XPATH = "x"; 121 122 /** The serial version id. */ 123 private static final long serialVersionUID = 4675966043824229258L; 124 125 /** The results file table. */ 126 CmsFileTable m_resultTable; 127 128 /** The currently selected result list resources. */ 129 private List<CmsResource> m_currentResources; 130 131 /** The current state string. */ 132 private String m_currentState; 133 134 /**Layout showing empty result message.*/ 135 private VerticalLayout m_infoEmptyResult; 136 137 /**Layout showing introduction message.*/ 138 private VerticalLayout m_infoIntroLayout; 139 140 /** The current search report. */ 141 private CmsReportOverlay m_report; 142 143 /** The result table filter input. */ 144 private TextField m_resultTableFilter; 145 146 /** The search form. */ 147 private CmsSourceSearchForm m_searchForm; 148 149 /** The search and replace thread. */ 150 private CmsSearchReplaceThread m_thread; 151 152 /** 153 * Generates the state string for the given search settings.<p> 154 * 155 * @param settings the search settings 156 * 157 * @return the state string 158 */ 159 public static String generateState(CmsSearchReplaceSettings settings) { 160 161 String state = ""; 162 state = A_CmsWorkplaceApp.addParamToState(state, SITE_ROOT, settings.getSiteRoot()); 163 state = A_CmsWorkplaceApp.addParamToState(state, SEARCH_TYPE, settings.getType().name()); 164 state = A_CmsWorkplaceApp.addParamToState(state, SEARCH_PATTERN, settings.getSearchpattern()); 165 if (!settings.getPaths().isEmpty()) { 166 state = A_CmsWorkplaceApp.addParamToState(state, FOLDER, settings.getPaths().get(0)); 167 } 168 state = A_CmsWorkplaceApp.addParamToState(state, RESOURCE_TYPE, settings.getTypes()); 169 state = A_CmsWorkplaceApp.addParamToState(state, LOCALE, settings.getLocale()); 170 state = A_CmsWorkplaceApp.addParamToState(state, QUERY, settings.getQuery()); 171 state = A_CmsWorkplaceApp.addParamToState(state, INDEX, settings.getSource()); 172 state = A_CmsWorkplaceApp.addParamToState(state, XPATH, settings.getXpath()); 173 state = A_CmsWorkplaceApp.addParamToState(state, IGNORE_SUBSITES, String.valueOf(settings.ignoreSubSites())); 174 state = A_CmsWorkplaceApp.addParamToState(state, PROPERTY, settings.getProperty().getName()); 175 176 return state; 177 } 178 179 /** 180 * Returns the settings for the given state.<p> 181 * 182 * @param state the state 183 * 184 * @return the search settings 185 */ 186 static CmsSearchReplaceSettings getSettingsFromState(String state) { 187 188 try { 189 state = new URLCodec().decode(state); 190 } catch (DecoderException e1) { 191 // 192 } 193 CmsSearchReplaceSettings settings = null; 194 String typeString = A_CmsWorkplaceApp.getParamFromState(state, SEARCH_TYPE); 195 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(typeString)) { 196 SearchType type = SearchType.valueOf(typeString); 197 settings = new CmsSearchReplaceSettings(); 198 settings.setType(type); 199 settings.setIgnoreSubSites( 200 Boolean.parseBoolean(A_CmsWorkplaceApp.getParamFromState(state, IGNORE_SUBSITES))); 201 settings.setSiteRoot(A_CmsWorkplaceApp.getParamFromState(state, SITE_ROOT).replace("%2F", "/")); 202 settings.setPaths( 203 Collections.singletonList(A_CmsWorkplaceApp.getParamFromState(state, FOLDER).replace("%2F", "/"))); 204 String resType = A_CmsWorkplaceApp.getParamFromState(state, RESOURCE_TYPE); 205 if (resType != null) { 206 settings.setTypes(resType); 207 } 208 String project = A_CmsWorkplaceApp.getParamFromState(state, PROJECT); 209 if (project != null) { 210 settings.setProject(project); 211 } 212 settings.setSearchpattern(A_CmsWorkplaceApp.getParamFromState(state, SEARCH_PATTERN).replace("%2F", "/")); 213 if (type.isContentValuesOnly()) { 214 settings.setOnlyContentValues(true); 215 String locale = A_CmsWorkplaceApp.getParamFromState(state, LOCALE); 216 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(locale)) { 217 settings.setLocale(locale); 218 } 219 settings.setXpath(A_CmsWorkplaceApp.getParamFromState(state, XPATH).replace("%2F", "/")); 220 } 221 if (type.isSolrSearch()) { 222 settings.setQuery(A_CmsWorkplaceApp.getParamFromState(state, QUERY).replace("%2F", "/")); 223 settings.setSource(A_CmsWorkplaceApp.getParamFromState(state, INDEX)); 224 } 225 if (type.isPropertySearch()) { 226 try { 227 settings.setProperty( 228 A_CmsUI.getCmsObject().readPropertyDefinition( 229 A_CmsWorkplaceApp.getParamFromState(state, PROPERTY))); 230 } catch (CmsException e) { 231 // 232 } 233 } 234 } 235 return settings; 236 } 237 238 /** 239 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#initUI(org.opencms.ui.apps.I_CmsAppUIContext) 240 */ 241 @Override 242 public void initUI(I_CmsAppUIContext context) { 243 244 context.addPublishButton(changed -> {/* do nothing */}); 245 super.initUI(context); 246 } 247 248 /** 249 * @see org.opencms.ui.apps.I_CmsCachableApp#isCachable() 250 */ 251 public boolean isCachable() { 252 253 return true; 254 } 255 256 /** 257 * @see org.opencms.ui.apps.I_CmsCachableApp#onRestoreFromCache() 258 */ 259 public void onRestoreFromCache() { 260 261 if (m_resultTable.getItemCount() < CmsFileExplorer.UPDATE_FOLDER_THRESHOLD) { 262 m_resultTable.update(m_resultTable.getAllIds(), false); 263 } else { 264 if (m_currentResources != null) { 265 Set<CmsUUID> ids = new HashSet<CmsUUID>(); 266 for (CmsResource res : m_currentResources) { 267 ids.add(res.getStructureId()); 268 } 269 m_resultTable.update(ids, false); 270 } 271 } 272 } 273 274 /** 275 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#onStateChange(java.lang.String) 276 */ 277 @Override 278 public void onStateChange(String state) { 279 280 if ((m_currentState == null) || !m_currentState.equals(state)) { 281 super.onStateChange(state); 282 } 283 } 284 285 /** 286 * Displays the search result.<p> 287 */ 288 protected void displayResult() { 289 290 if (m_thread.getMatchedResources().isEmpty()) { 291 m_resultTable.setVisible(false); 292 m_infoIntroLayout.setVisible(false); 293 m_infoEmptyResult.setVisible(true); 294 m_resultTable.fillTable(A_CmsUI.getCmsObject(), m_thread.getMatchedResources()); 295 m_searchForm.setDownload(null); 296 } else { 297 m_resultTable.setVisible(true); 298 m_infoIntroLayout.setVisible(false); 299 m_infoEmptyResult.setVisible(false); 300 m_resultTable.fillTable(A_CmsUI.getCmsObject(), m_thread.getMatchedResources()); 301 SimpleDateFormat fmt = new SimpleDateFormat("hhmmss"); 302 String timeStr = fmt.format(new Date()); 303 String filename = "opencms_sourcesearch_" + timeStr + ".csv"; 304 StreamResource downloadResource = new StreamResource( 305 () -> new ByteArrayInputStream(m_resultTable.generateCsv()), 306 filename); 307 m_searchForm.setDownload(downloadResource); 308 } 309 m_searchForm.removeComponent(m_report); 310 m_report = null; 311 } 312 313 /** 314 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getBreadCrumbForState(java.lang.String) 315 */ 316 @Override 317 protected LinkedHashMap<String, String> getBreadCrumbForState(String state) { 318 319 return null; 320 } 321 322 /** 323 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getComponentForState(java.lang.String) 324 */ 325 @Override 326 protected Component getComponentForState(String state) { 327 328 m_rootLayout.setMainHeightFull(true); 329 HorizontalSplitPanel sp = new HorizontalSplitPanel(); 330 sp.setSizeFull(); 331 m_searchForm = new CmsSourceSearchForm(this); 332 sp.setFirstComponent(m_searchForm); 333 VerticalLayout result = new VerticalLayout(); 334 result.setSizeFull(); 335 m_infoIntroLayout = CmsVaadinUtils.getInfoLayout(Messages.GUI_SOURCESEARCH_INTRO_0); 336 m_infoEmptyResult = CmsVaadinUtils.getInfoLayout(Messages.GUI_SOURCESEARCH_EMPTY_0); 337 m_resultTable = new CmsFileTable(null); 338 339 result.addComponent(m_resultTable); 340 result.addComponent(m_infoEmptyResult); 341 result.addComponent(m_infoIntroLayout); 342 343 m_resultTable.setVisible(false); 344 m_infoEmptyResult.setVisible(false); 345 m_infoIntroLayout.setVisible(true); 346 347 m_resultTable.applyWorkplaceAppSettings(); 348 m_resultTable.setContextProvider(new I_CmsContextProvider() { 349 350 /** 351 * @see org.opencms.ui.apps.I_CmsContextProvider#getDialogContext() 352 */ 353 public I_CmsDialogContext getDialogContext() { 354 355 CmsFileTableDialogContext context = new CmsFileTableDialogContext( 356 CmsProjectManagerConfiguration.APP_ID, 357 ContextType.fileTable, 358 m_resultTable, 359 m_resultTable.getSelectedResources()); 360 storeCurrentFileSelection(m_resultTable.getSelectedResources()); 361 context.setEditableProperties(CmsFileExplorer.INLINE_EDIT_PROPERTIES); 362 return context; 363 } 364 }); 365 m_resultTable.setSizeFull(); 366 if (m_resultTableFilter == null) { 367 m_resultTableFilter = new TextField(); 368 m_resultTableFilter.setIcon(FontOpenCms.FILTER); 369 m_resultTableFilter.setInputPrompt( 370 Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0)); 371 m_resultTableFilter.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON); 372 m_resultTableFilter.setWidth("200px"); 373 m_resultTableFilter.addTextChangeListener(new TextChangeListener() { 374 375 private static final long serialVersionUID = 1L; 376 377 public void textChange(TextChangeEvent event) { 378 379 m_resultTable.filterTable(event.getText()); 380 381 } 382 }); 383 m_infoLayout.addComponent(m_resultTableFilter); 384 } 385 386 sp.setSecondComponent(result); 387 sp.setSplitPosition(CmsFileExplorer.LAYOUT_SPLIT_POSITION, Unit.PIXELS); 388 389 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(state)) { 390 CmsSearchReplaceSettings settings = getSettingsFromState(state); 391 if (settings != null) { 392 m_currentState = state; 393 m_searchForm.initFormValues(settings); 394 search(settings, false); 395 } 396 } 397 return sp; 398 } 399 400 /** 401 * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getSubNavEntries(java.lang.String) 402 */ 403 @Override 404 protected List<NavEntry> getSubNavEntries(String state) { 405 406 return null; 407 } 408 409 /** 410 * Executes the search.<p> 411 * 412 * @param settings the search settings 413 * @param updateState <code>true</code> to create a new history entry 414 */ 415 protected void search(CmsSearchReplaceSettings settings, boolean updateState) { 416 417 if (updateState) { 418 String state = generateState(settings); 419 CmsAppWorkplaceUi.get().changeCurrentAppState(state); 420 m_currentState = state; 421 } 422 423 CmsObject cms; 424 try { 425 cms = OpenCms.initCmsObject(A_CmsUI.getCmsObject()); 426 if (settings.getSiteRoot() != null) { 427 cms.getRequestContext().setSiteRoot(settings.getSiteRoot()); 428 } 429 430 m_thread = new CmsSearchReplaceThread(A_CmsUI.get().getHttpSession(), cms, settings); 431 if (m_report != null) { 432 m_searchForm.removeComponent(m_report); 433 } 434 m_report = new CmsReportOverlay(m_thread); 435 m_report.addReportFinishedHandler(new Runnable() { 436 437 public void run() { 438 439 displayResult(); 440 } 441 }); 442 m_searchForm.addComponent(m_report); 443 m_report.setTitle(CmsVaadinUtils.getMessageText(Messages.GUI_SOURCESEARCH_REPORT_TITLE_0)); 444 m_thread.start(); 445 m_resultTableFilter.clear(); 446 } catch (CmsException e) { 447 CmsErrorDialog.showErrorDialog(e); 448 } 449 } 450 451 /** 452 * Stores the currently selected resources list.<p> 453 * 454 * @param resources the currently selected resources 455 */ 456 void storeCurrentFileSelection(List<CmsResource> resources) { 457 458 m_currentResources = resources; 459 } 460}