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 GmbH & Co. KG, 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.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProject; 033import org.opencms.file.CmsProperty; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.file.types.CmsResourceTypeXmlContent; 037import org.opencms.i18n.CmsLocaleManager; 038import org.opencms.jsp.CmsJspTagContainer; 039import org.opencms.loader.CmsLoaderException; 040import org.opencms.lock.CmsLock; 041import org.opencms.main.CmsException; 042import org.opencms.main.CmsLog; 043import org.opencms.main.OpenCms; 044import org.opencms.report.A_CmsReportThread; 045import org.opencms.report.I_CmsReport; 046import org.opencms.search.CmsSearchException; 047import org.opencms.search.solr.CmsSolrIndex; 048import org.opencms.search.solr.CmsSolrQuery; 049import org.opencms.ui.apps.Messages; 050import org.opencms.ui.apps.search.CmsSourceSearchForm.SearchType; 051import org.opencms.util.CmsRequestUtil; 052import org.opencms.util.CmsStringUtil; 053import org.opencms.xml.CmsXmlUtils; 054import org.opencms.xml.containerpage.CmsContainerElementBean; 055import org.opencms.xml.containerpage.CmsXmlContainerPage; 056import org.opencms.xml.containerpage.CmsXmlContainerPageFactory; 057import org.opencms.xml.content.CmsXmlContent; 058import org.opencms.xml.content.CmsXmlContentFactory; 059import org.opencms.xml.types.I_CmsXmlContentValue; 060 061import java.util.ArrayList; 062import java.util.Collections; 063import java.util.HashSet; 064import java.util.Iterator; 065import java.util.LinkedHashSet; 066import java.util.List; 067import java.util.Locale; 068import java.util.Set; 069import java.util.regex.Matcher; 070import java.util.regex.Pattern; 071 072import javax.servlet.http.HttpSession; 073 074import org.apache.commons.logging.Log; 075 076/** 077 * Searches in sources. 078 * <p> 079 * 080 * @since 7.5.3 081 */ 082public class CmsSearchReplaceThread extends A_CmsReportThread { 083 084 /** The log object for this class. */ 085 private static final Log LOG = CmsLog.getLog(CmsSearchReplaceThread.class); 086 087 /** The number of Solr search results to be processed at maximum. */ 088 private static final int MAX_PROCESSED_SOLR_RESULTS = 10000; 089 090 /** Number of errors while searching. */ 091 private int m_errorSearch; 092 093 /** Number of errors while updating. */ 094 private int m_errorUpdate; 095 096 /** Number of locked files during updating. */ 097 private int m_lockedFiles; 098 099 /** The found resources. */ 100 private Set<CmsResource> m_matchedResources = new LinkedHashSet<CmsResource>(); 101 102 /** The replace flag. */ 103 private boolean m_replace; 104 105 /** Settings. */ 106 private CmsSearchReplaceSettings m_settings; 107 108 /** 109 * Creates a replace html tag Thread.<p> 110 * 111 * @param session the current session 112 * @param cms the current cms object 113 * @param settings the settings needed to perform the operation. 114 */ 115 public CmsSearchReplaceThread(HttpSession session, CmsObject cms, CmsSearchReplaceSettings settings) { 116 117 super(cms, "searchAndReplace"); 118 initHtmlReport(cms.getRequestContext().getLocale()); 119 m_settings = settings; 120 } 121 122 /** 123 * Creates a replace html tag Thread.<p> 124 * 125 * @param session the current session 126 * @param cms the current cms object 127 * @param settings the settings needed to perform the operation. 128 */ 129 public CmsSearchReplaceThread( 130 HttpSession session, 131 CmsObject cms, 132 CmsSearchReplaceSettings settings, 133 I_CmsReport report) { 134 135 super(cms, "searchAndReplace"); 136 m_report = report; 137 m_settings = settings; 138 } 139 140 /** 141 * Returns the matched resources.<p> 142 * 143 * @return the matched resources 144 */ 145 public List<CmsResource> getMatchedResources() { 146 147 if (m_replace) { 148 // re-read the resources to include changes 149 List<CmsResource> result = new ArrayList<CmsResource>(); 150 for (CmsResource resource : m_matchedResources) { 151 try { 152 result.add(getCms().readResource(resource.getStructureId())); 153 } catch (CmsException e) { 154 LOG.error(e.getLocalizedMessage(), e); 155 } 156 } 157 return result; 158 } else { 159 return new ArrayList<CmsResource>(m_matchedResources); 160 } 161 } 162 163 /** 164 * @see org.opencms.report.A_CmsReportThread#getReportUpdate() 165 */ 166 @Override 167 public String getReportUpdate() { 168 169 return getReport().getReportUpdate(); 170 } 171 172 /** 173 * @see java.lang.Runnable#run() 174 */ 175 @Override 176 public void run() { 177 178 // get the report 179 I_CmsReport report = getReport(); 180 boolean isError = false; 181 report.println( 182 Messages.get().container(Messages.RPT_SOURCESEARCH_BEGIN_SEARCH_THREAD_0), 183 I_CmsReport.FORMAT_HEADLINE); 184 // write parameters to report 185 report.println(Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_0), I_CmsReport.FORMAT_HEADLINE); 186 // the paths 187 if (!m_settings.getPaths().isEmpty()) { 188 // iterate over the paths 189 Iterator<String> iter = m_settings.getPaths().iterator(); 190 while (iter.hasNext()) { 191 String path = iter.next(); 192 report.println( 193 Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_RESOURCE_PATH_1, path), 194 I_CmsReport.FORMAT_NOTE); 195 } 196 } else { 197 // no paths selected 198 isError = true; 199 report.println( 200 Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_EMPTY_RESOURCE_PATHS_0), 201 I_CmsReport.FORMAT_ERROR); 202 } 203 // the search pattern 204 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(m_settings.getSearchpattern())) { 205 // there is a search pattern 206 report.println( 207 Messages.get().container( 208 Messages.RPT_SOURCESEARCH_PARAMETERS_SEARCHPATTERN_1, 209 CmsStringUtil.escapeHtml(m_settings.getSearchpattern())), 210 I_CmsReport.FORMAT_NOTE); 211 } else { 212 // empty search pattern 213 isError = true; 214 report.println( 215 Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_EMPTY_SEARCHPATTERN_0), 216 I_CmsReport.FORMAT_ERROR); 217 } 218 // the replace pattern 219 report.println( 220 Messages.get().container( 221 Messages.RPT_SOURCESEARCH_PARAMETERS_REPLACEPATTERN_1, 222 CmsStringUtil.escapeHtml(m_settings.getReplacepattern())), 223 I_CmsReport.FORMAT_NOTE); 224 // the project 225 report.println( 226 Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_PROJECT_1, m_settings.getProject()), 227 I_CmsReport.FORMAT_NOTE); 228 // remarks for search/replace dependent od the replace pattern and the selected project 229 // in the online project search is possible only 230 // in other projects there is replaced, if the replace pattern is not empty 231 if (CmsStringUtil.isEmpty(m_settings.getReplacepattern()) && !m_settings.isForceReplace()) { 232 // empty replace pattern, search only 233 report.println( 234 Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_EMPTY_REPLACEPATTERN_0), 235 I_CmsReport.FORMAT_NOTE); 236 } else { 237 // not empty replace pattern, search and replace 238 m_replace = true; 239 report.println( 240 Messages.get().container(Messages.RPT_SOURCESEARCH_PARAMETERS_NOTEMPTY_REPLACEPATTERN_0), 241 I_CmsReport.FORMAT_NOTE); 242 } 243 244 // make an OpenCms object copy if replace is active 245 CmsObject cmsObject = getCms(); 246 if (m_replace && !m_settings.getProject().equals(cmsObject.getRequestContext().getCurrentProject().getName())) { 247 try { 248 cmsObject = OpenCms.initCmsObject(getCms()); 249 CmsProject cmsProject = getCms().readProject(m_settings.getProject()); 250 cmsObject.getRequestContext().setCurrentProject(cmsProject); 251 } catch (CmsException e) { 252 report.println( 253 Messages.get().container(Messages.RPT_SOURCESEARCH_INIT_CMS_OBJECT_FAILED_0), 254 I_CmsReport.FORMAT_NOTE); 255 m_replace = false; 256 } 257 } 258 259 // search the resources and replace the patterns 260 if (!isError) { 261 List<CmsResource> resources = searchResources(); 262 263 if (resources.isEmpty()) { 264 // no resources found, so search is not possible 265 report.println( 266 Messages.get().container(Messages.RPT_SOURCESEARCH_NO_FILES_TO_SEARCH_IN_0), 267 I_CmsReport.FORMAT_NOTE); 268 } else { 269 270 report.println( 271 Messages.get().container( 272 Messages.RPT_SOURCESEARCH_NR_OF_FILES_TO_SEARCH_IN_1, 273 new Integer(resources.size())), 274 I_CmsReport.FORMAT_NOTE); 275 if (m_replace) { 276 // start searching and replacing 277 report.println( 278 Messages.get().container(Messages.RPT_SOURCESEARCH_START_SEARCHING_REPLACING_0), 279 I_CmsReport.FORMAT_HEADLINE); 280 } else { 281 // start searching 282 report.println( 283 Messages.get().container(Messages.RPT_SOURCESEARCH_START_SEARCHING_0), 284 I_CmsReport.FORMAT_HEADLINE); 285 } 286 287 if (m_settings.getType().isPropertySearch()) { 288 searchProperties(resources); 289 } else { 290 searchAndReplace(resources); 291 } 292 } 293 294 } else { 295 // do not show the resources, because there were errors while searching 296 } 297 298 report.println( 299 Messages.get().container(Messages.RPT_SOURCESEARCH_END_SEARCH_THREAD_0), 300 I_CmsReport.FORMAT_HEADLINE); 301 } 302 303 /** 304 * Search the resources.<p> 305 * 306 * @param resources the relevant resources 307 */ 308 protected void searchAndReplace(List<CmsResource> resources) { 309 310 // the file counter 311 int counter = 0; 312 int resCount = resources.size(); 313 I_CmsReport report = getReport(); 314 // iterate over the files in the selected path 315 for (CmsResource resource : resources) { 316 317 try { 318 319 // get the content 320 CmsFile file = getCms().readFile(resource); 321 byte[] contents = file.getContents(); 322 323 // report the current resource 324 ++counter; 325 report(report, counter, resCount, resource); 326 327 // search and replace 328 byte[] result = null; 329 boolean xpath = false; 330 if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_settings.getXpath()) 331 || m_settings.isOnlyContentValues()) && CmsResourceTypeXmlContent.isXmlContent(resource)) { 332 xpath = true; 333 } 334 if (!xpath) { 335 result = replaceInContent(file, contents); 336 } else { 337 result = replaceInXml(file); 338 } 339 340 if ((result != null) && (contents != null) && !contents.equals(result)) { 341 // rewrite the content 342 writeContent(file, result); 343 } else { 344 getReport().println(); 345 } 346 347 } catch (Exception e) { 348 report.print( 349 org.opencms.report.Messages.get().container(Messages.RPT_SOURCESEARCH_COULD_NOT_READ_FILE_0), 350 I_CmsReport.FORMAT_ERROR); 351 report.addError(e); 352 report.println( 353 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 354 I_CmsReport.FORMAT_ERROR); 355 m_errorSearch += 1; 356 LOG.error( 357 org.opencms.report.Messages.get().container(Messages.RPT_SOURCESEARCH_COULD_NOT_READ_FILE_0), 358 e); 359 continue; 360 } 361 } 362 363 // report results 364 reportResults(resources.size()); 365 } 366 367 /** 368 * Locks the current resource.<p> 369 * 370 * @param cms the current CmsObject 371 * @param cmsResource the resource to lock 372 * @param report the report 373 * 374 * @return <code>true</code> if the given resource was locked was successfully 375 * 376 * @throws CmsException if some goes wrong 377 */ 378 private boolean lockResource(CmsObject cms, CmsResource cmsResource, I_CmsReport report) throws CmsException { 379 380 CmsLock lock = cms.getLock(cms.getSitePath(cmsResource)); 381 // check the lock 382 if ((lock != null) 383 && lock.isOwnedBy(cms.getRequestContext().getCurrentUser()) 384 && lock.isOwnedInProjectBy( 385 cms.getRequestContext().getCurrentUser(), 386 cms.getRequestContext().getCurrentProject())) { 387 // prove is current lock from current user in current project 388 return true; 389 } else if ((lock != null) && !lock.isUnlocked() && !lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) { 390 // the resource is not locked by the current user, so can not lock it 391 m_lockedFiles += 1; 392 return false; 393 } else if ((lock != null) 394 && !lock.isUnlocked() 395 && lock.isOwnedBy(cms.getRequestContext().getCurrentUser()) 396 && !lock.isOwnedInProjectBy( 397 cms.getRequestContext().getCurrentUser(), 398 cms.getRequestContext().getCurrentProject())) { 399 // prove is current lock from current user but not in current project 400 // file is locked by current user but not in current project 401 // change the lock 402 cms.changeLock(cms.getSitePath(cmsResource)); 403 } else if ((lock != null) && lock.isUnlocked()) { 404 // lock resource from current user in current project 405 cms.lockResource(cms.getSitePath(cmsResource)); 406 } 407 lock = cms.getLock(cms.getSitePath(cmsResource)); 408 if ((lock != null) 409 && lock.isOwnedBy(cms.getRequestContext().getCurrentUser()) 410 && !lock.isOwnedInProjectBy( 411 cms.getRequestContext().getCurrentUser(), 412 cms.getRequestContext().getCurrentProject())) { 413 // resource could not be locked 414 m_lockedFiles += 1; 415 416 return false; 417 } 418 // resource is locked successfully 419 return true; 420 } 421 422 /** 423 * Renames a nested container within a container page XML.<p> 424 * 425 * @param targetContainerPage the target container page 426 * @param layoutResource the container element resource generating the nested container 427 * @param oldName the old container name 428 * @param newName the new container name 429 * 430 * @return the changed content bytes 431 * 432 * @throws Exception in case unmarshalling of the container page fails 433 */ 434 private byte[] renameNestedContainers( 435 CmsFile targetContainerPage, 436 CmsResource layoutResource, 437 String oldName, 438 String newName) 439 throws Exception { 440 441 byte[] contents = targetContainerPage.getContents(); 442 Set<String> replaceElementIds = new HashSet<String>(); 443 try { 444 CmsXmlContainerPage page = CmsXmlContainerPageFactory.unmarshal(getCms(), targetContainerPage); 445 for (CmsContainerElementBean element : page.getContainerPage(getCms()).getElements()) { 446 if (element.getId().equals(layoutResource.getStructureId()) && (element.getInstanceId() != null)) { 447 replaceElementIds.add(element.getInstanceId()); 448 } 449 } 450 if (replaceElementIds.size() > 0) { 451 String encoding = CmsLocaleManager.getResourceEncoding(getCms(), targetContainerPage); 452 String content = new String(contents, encoding); 453 for (String instanceId : replaceElementIds) { 454 Pattern patt = Pattern.compile( 455 CmsJspTagContainer.getNestedContainerName(oldName, instanceId, null)); 456 Matcher m = patt.matcher(content); 457 StringBuffer sb = new StringBuffer(content.length()); 458 while (m.find()) { 459 m.appendReplacement( 460 sb, 461 Matcher.quoteReplacement( 462 CmsJspTagContainer.getNestedContainerName(newName, instanceId, null))); 463 } 464 m.appendTail(sb); 465 content = sb.toString(); 466 } 467 contents = content.getBytes(encoding); 468 } 469 } catch (Exception e) { 470 LOG.error(e.getLocalizedMessage(), e); 471 throw e; 472 } 473 return contents; 474 } 475 476 /** 477 * Performs the replacement in content.<p> 478 * 479 * @param file the file object 480 * @param contents the byte content 481 * 482 * @return the new content if a replacement has been performed 483 * 484 * @throws Exception if something goes wrong 485 */ 486 private byte[] replaceInContent(CmsFile file, byte[] contents) throws Exception { 487 488 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_settings.getLocale())) { 489 Locale contentLocale = CmsLocaleManager.getMainLocale(getCms(), file); 490 if (!contentLocale.toString().equalsIgnoreCase(m_settings.getLocale())) { 491 // content does not match the requested locale, skip it 492 getReport().println( 493 Messages.get().container(Messages.RPT_SOURCESEARCH_NOT_MATCHED_0), 494 I_CmsReport.FORMAT_NOTE); 495 return null; 496 } 497 } 498 499 String encoding = CmsLocaleManager.getResourceEncoding(getCms(), file); 500 String content = new String(contents, encoding); 501 502 if (CmsSourceSearchForm.REGEX_ALL.equals(m_settings.getSearchpattern()) & !m_replace) { 503 m_matchedResources.add(file); 504 getReport().print(Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0), I_CmsReport.FORMAT_OK); 505 return null; 506 } 507 508 Matcher matcher = Pattern.compile(m_settings.getSearchpattern()).matcher(content); 509 510 if (matcher.find()) { 511 // search pattern did match here, so take this file in the list with matches resources 512 m_matchedResources.add(file); 513 getReport().print(Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0), I_CmsReport.FORMAT_OK); 514 if (m_replace) { 515 if (m_settings.getType().equals(SearchType.renameContainer)) { 516 517 return renameNestedContainers( 518 file, 519 m_settings.getElementResource(), 520 m_settings.getReplacepattern().split(";")[0], 521 m_settings.getReplacepattern().split(";")[1]); 522 } 523 return matcher.replaceAll(m_settings.getReplacepattern()).getBytes(encoding); 524 } 525 } else { 526 // search pattern did not match 527 getReport().print( 528 Messages.get().container(Messages.RPT_SOURCESEARCH_NOT_MATCHED_0), 529 I_CmsReport.FORMAT_NOTE); 530 } 531 return null; 532 } 533 534 /** 535 * Performs a replacement for XML contents.<p> 536 * 537 * @param cmsFile the file to operate on 538 * 539 * @return the marshaled content 540 * @throws Exception if something goes wrong 541 */ 542 private byte[] replaceInXml(CmsFile cmsFile) throws Exception { 543 544 Exception e = null; 545 CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), cmsFile); 546 Pattern pattern = Pattern.compile(m_settings.getSearchpattern()); 547 // loop over the locales of the content 548 boolean modified = false; 549 boolean matched = false; 550 String requestedLocale = m_settings.getLocale(); 551 for (Locale locale : xmlContent.getLocales()) { 552 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(requestedLocale) 553 && !locale.toString().equalsIgnoreCase(requestedLocale)) { 554 // does not match the requested locale, skip it 555 continue; 556 } 557 // loop over the available element paths of the current content locale 558 List<String> paths = xmlContent.getNames(locale); 559 for (String xpath : paths) { 560 // try to get the value extraction for the current element path 561 I_CmsXmlContentValue value = xmlContent.getValue(xpath, locale); 562 if (value.isSimpleType()) { 563 try { 564 String currPath = value.getPath(); 565 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_settings.getXpath()) 566 || currPath.equals(m_settings.getXpath()) 567 || (CmsXmlUtils.removeXpath(currPath).equals(m_settings.getXpath()))) { 568 // xpath match 569 String oldVal = value.getStringValue(getCms()); 570 Matcher matcher = pattern.matcher(oldVal); 571 matcher = Pattern.compile(m_settings.getSearchpattern()).matcher(oldVal); 572 if (matcher.find()) { 573 matched = true; 574 m_matchedResources.add(cmsFile); 575 if (m_replace) { 576 String newVal = matcher.replaceAll(m_settings.getReplacepattern()); 577 if (!oldVal.equals(newVal)) { 578 value.setStringValue(getCms(), newVal); 579 modified = true; 580 } 581 } 582 } 583 } 584 } catch (Exception ex) { 585 // log and go on 586 LOG.error(ex.getMessage(), ex); 587 e = ex; 588 } 589 } 590 } 591 } 592 if (e != null) { 593 throw e; 594 } 595 if (matched) { 596 getReport().println(Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0), I_CmsReport.FORMAT_OK); 597 } else { 598 getReport().println( 599 Messages.get().container(Messages.RPT_SOURCESEARCH_NOT_MATCHED_0), 600 I_CmsReport.FORMAT_NOTE); 601 } 602 if (modified) { 603 return xmlContent.marshal(); 604 } 605 return null; 606 } 607 608 /** 609 * Replace properties of given resources.<p> 610 * 611 * @param matchedResources to replace properties 612 */ 613 private void replaceProperties(Set<CmsResource> matchedResources) { 614 615 for (CmsResource resource : matchedResources) { 616 try { 617 CmsProperty prop = getCms().readPropertyObject(resource, m_settings.getProperty().getName(), false); 618 Matcher matcher = Pattern.compile(m_settings.getSearchpattern()).matcher(prop.getValue()); 619 if (m_settings.getReplacepattern().isEmpty()) { 620 prop.setValue("", ""); 621 } else { 622 prop.setValue(matcher.replaceAll(m_settings.getReplacepattern()), CmsProperty.TYPE_INDIVIDUAL); 623 } 624 getCms().lockResource(resource); 625 getCms().writePropertyObjects(resource, Collections.singletonList(prop)); 626 getCms().unlockResource(resource); 627 } catch (CmsException e) { 628 LOG.error("Ubable to change property", e); 629 } 630 } 631 } 632 633 /** 634 * Reads the content as byte array of the given resource and prints a message to the report.<p> 635 * 636 * @param report the report 637 * @param counter the counter 638 * @param resCount the total resource count 639 * @param resource the file to get the content for 640 */ 641 private void report(I_CmsReport report, int counter, int resCount, CmsResource resource) { 642 643 // report entries 644 report.print( 645 org.opencms.report.Messages.get().container( 646 org.opencms.report.Messages.RPT_SUCCESSION_2, 647 String.valueOf(counter), 648 String.valueOf(resCount)), 649 I_CmsReport.FORMAT_NOTE); 650 report.print( 651 org.opencms.report.Messages.get().container( 652 org.opencms.report.Messages.RPT_ARGUMENT_1, 653 report.removeSiteRoot(resource.getRootPath()))); 654 report.print( 655 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0), 656 I_CmsReport.FORMAT_DEFAULT); 657 } 658 659 /** 660 * Prints the result messages into the report.<p> 661 * 662 * @param nrOfFiles the total number of files 663 */ 664 private void reportResults(int nrOfFiles) { 665 666 I_CmsReport report = getReport(); 667 // report entries 668 if (m_replace) { 669 // finish searching and replacing 670 report.println( 671 Messages.get().container(Messages.RPT_SOURCESEARCH_END_SEARCHING_REPLACING_0), 672 I_CmsReport.FORMAT_HEADLINE); 673 } else { 674 // finish searching 675 report.println( 676 Messages.get().container(Messages.RPT_SOURCESEARCH_END_SEARCHING_0), 677 I_CmsReport.FORMAT_HEADLINE); 678 } 679 // the results are written in the report 680 report.println(Messages.get().container(Messages.RPT_SOURCESEARCH_RESULT_0), I_CmsReport.FORMAT_HEADLINE); 681 report.println( 682 Messages.get().container( 683 Messages.RPT_SOURCESEARCH_NR_OF_FILES_TO_SEARCH_IN_1, 684 new Integer(nrOfFiles).toString()), 685 I_CmsReport.FORMAT_NOTE); 686 report.println( 687 Messages.get().container( 688 Messages.RPT_SOURCESEARCH_NR_OF_FILES_MATCHED_1, 689 new Integer(m_matchedResources.size()).toString()), 690 I_CmsReport.FORMAT_NOTE); 691 report.println( 692 Messages.get().container( 693 Messages.RPT_SOURCESEARCH_SEARCH_ERROR_COUNT_1, 694 new Integer(m_errorSearch).toString()), 695 I_CmsReport.FORMAT_NOTE); 696 if (m_replace) { 697 // replace report entries 698 report.println( 699 Messages.get().container( 700 Messages.RPT_SOURCESEARCH_REPLACE_ERROR_COUNT_1, 701 new Integer(m_errorUpdate).toString()), 702 I_CmsReport.FORMAT_NOTE); 703 report.println( 704 Messages.get().container( 705 Messages.RPT_SOURCESEARCH_LOCKED_FILES_1, 706 new Integer(m_lockedFiles).toString()), 707 I_CmsReport.FORMAT_NOTE); 708 if (m_matchedResources.size() == 0) { 709 report.println( 710 Messages.get().container(Messages.RPT_SOURCESEARCH_NO_FILES_FOUND_0), 711 I_CmsReport.FORMAT_OK); 712 } else { 713 report.println( 714 Messages.get().container(Messages.RPT_SOURCESEARCH_CLICK_OK_TO_GET_LIST_0), 715 I_CmsReport.FORMAT_OK); 716 } 717 if (m_lockedFiles > 0) { 718 report.println( 719 Messages.get().container(Messages.RPT_SOURCESEARCH_REPLACE_FAILED_0), 720 I_CmsReport.FORMAT_ERROR); 721 } else { 722 report.println( 723 Messages.get().container(Messages.RPT_SOURCESEARCH_REPLACE_SUCCESS_0), 724 I_CmsReport.FORMAT_OK); 725 } 726 } else { 727 // search report entries 728 if (m_matchedResources.size() == 0) { 729 report.println( 730 Messages.get().container(Messages.RPT_SOURCESEARCH_NO_FILES_FOUND_0), 731 I_CmsReport.FORMAT_OK); 732 } else { 733 report.println( 734 Messages.get().container(Messages.RPT_SOURCESEARCH_CLICK_OK_TO_GET_LIST_0), 735 I_CmsReport.FORMAT_OK); 736 } 737 if (m_errorSearch > 0) { 738 // only searching failed 739 report.println( 740 Messages.get().container(Messages.RPT_SOURCESEARCH_SEARCH_FAILED_0), 741 I_CmsReport.FORMAT_ERROR); 742 } else { 743 // only searching was successful 744 report.println( 745 Messages.get().container(Messages.RPT_SOURCESEARCH_SEARCH_SUCCESS_0), 746 I_CmsReport.FORMAT_OK); 747 } 748 } 749 } 750 751 /** 752 * Search and replace function for properties.<p> 753 * 754 * @param resources to be considered 755 */ 756 private void searchProperties(List<CmsResource> resources) { 757 758 if (CmsSourceSearchForm.REGEX_ALL.equals(m_settings.getSearchpattern())) { 759 for (CmsResource resource : resources) { 760 m_matchedResources.add(resource); 761 getReport().println( 762 Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0), 763 I_CmsReport.FORMAT_OK); 764 } 765 766 } else { 767 for (CmsResource resource : resources) { 768 Matcher matcher; 769 try { 770 CmsProperty prop = getCms().readPropertyObject(resource, m_settings.getProperty().getName(), false); 771 matcher = Pattern.compile(m_settings.getSearchpattern()).matcher(prop.getValue()); 772 if (matcher.find()) { 773 m_matchedResources.add(resource); 774 getReport().println( 775 Messages.get().container(Messages.RPT_SOURCESEARCH_MATCHED_0), 776 I_CmsReport.FORMAT_OK); 777 } else { 778 getReport().println( 779 Messages.get().container(Messages.RPT_SOURCESEARCH_NOT_MATCHED_0), 780 I_CmsReport.FORMAT_NOTE); 781 } 782 783 } catch (CmsException e) { 784 LOG.error("Ubable to read property", e); 785 } 786 } 787 } 788 if (m_replace) { 789 replaceProperties(m_matchedResources); 790 } 791 // report results 792 reportResults(resources.size()); 793 } 794 795 /** 796 * Searches/reads all resources that are relevant.<p> 797 * 798 * @return the relevant resources 799 */ 800 @SuppressWarnings("deprecation") 801 private List<CmsResource> searchResources() { 802 803 getReport().println( 804 Messages.get().container(Messages.RPT_SOURCESEARCH_START_COLLECTING_FILES_TO_SEARCH_IN_0), 805 I_CmsReport.FORMAT_HEADLINE); 806 807 List<CmsResource> resources = new ArrayList<CmsResource>(); 808 if (m_settings.isSolrSearch()) { 809 CmsSolrIndex index = OpenCms.getSearchManager().getIndexSolr(m_settings.getSource()); 810 if (index != null) { 811 CmsSolrQuery query = new CmsSolrQuery( 812 null, 813 CmsRequestUtil.createParameterMap(m_settings.getQuery() + "&fl=path,type")); 814 List<String> rootPaths = new ArrayList<>(m_settings.getPaths().size()); 815 String siteRoot = getCms().getRequestContext().getSiteRoot(); 816 for (String path : m_settings.getPaths()) { 817 rootPaths.add(path.startsWith(siteRoot) ? path : getCms().addSiteRoot(path)); 818 } 819 query.setSearchRoots(rootPaths); 820 if ((m_settings.getTypesArray() != null) && (m_settings.getTypesArray().length > 0)) { 821 query.setResourceTypes(m_settings.getTypesArray()); 822 } 823 query.setRows(new Integer(MAX_PROCESSED_SOLR_RESULTS)); 824 query.ensureParameters(); 825 try { 826 resources.addAll( 827 index.search(getCms(), query, true, null, false, null, MAX_PROCESSED_SOLR_RESULTS)); 828 } catch (CmsSearchException e) { 829 LOG.error(e.getMessage(), e); 830 } 831 } 832 } else { 833 CmsResourceFilter filter = CmsResourceFilter.ALL.addExcludeState( 834 CmsResource.STATE_DELETED).addRequireVisible(); 835 List<CmsResourceFilter> filterList = new ArrayList<CmsResourceFilter>(); 836 if ((m_settings.getTypesArray() != null) && (m_settings.getTypesArray().length > 0)) { 837 for (String resTypeName : m_settings.getTypesArray()) { 838 try { 839 int typeId = OpenCms.getResourceManager().getResourceType(resTypeName).getTypeId(); 840 filterList.add(((CmsResourceFilter)filter.clone()).addRequireType(typeId)); 841 } catch (CmsLoaderException e) { 842 // noop 843 } catch (NullPointerException e) { 844 // noop 845 } 846 } 847 } 848 if (filterList.size() == 1) { 849 filter = filterList.get(0); 850 } 851 852 // iterate over all selected paths 853 Iterator<String> iterPaths = m_settings.getPaths().iterator(); 854 855 if (!m_settings.getType().isPropertySearch()) { 856 filter = filter.addRequireFile(); 857 } 858 while (iterPaths.hasNext()) { 859 String path = iterPaths.next(); 860 try { 861 if (m_settings.getType().isPropertySearch()) { 862 resources.addAll( 863 getCms().readResourcesWithProperty(path, m_settings.getProperty().getName(), null, filter)); 864 } else { 865 // only read resources which are files and not deleted, which are in the current time range window and where the current 866 // user has the sufficient permissions to read them 867 868 List<CmsResource> tmpResources = getCms().readResources(path, filter); 869 List<String> subsites = null; 870 if (m_settings.ignoreSubSites()) { 871 subsites = OpenCms.getADEManager().getSubSitePaths(getCms(), path); 872 subsites.remove( 873 OpenCms.getADEManager().getSubSiteRoot( 874 getCms(), 875 getCms().readResource(path).getRootPath())); 876 } 877 Iterator<CmsResource> iterator = tmpResources.iterator(); 878 while (iterator.hasNext()) { 879 CmsResource r = iterator.next(); 880 boolean remove = true; 881 if (filterList.size() > 1) { 882 for (CmsResourceFilter f : filterList) { 883 if (f.isValid(getCms().getRequestContext(), r)) { 884 remove = false; 885 } 886 } 887 } else { 888 remove = false; 889 } 890 if ((subsites != null) & !remove) { 891 if (subsites.contains( 892 OpenCms.getADEManager().getSubSiteRoot(getCms(), r.getRootPath()))) { 893 remove = true; 894 } 895 } 896 if (remove) { 897 iterator.remove(); 898 } 899 } 900 901 if ((tmpResources != null) && !tmpResources.isEmpty()) { 902 resources.addAll(tmpResources); 903 } 904 } 905 } catch (CmsException e) { 906 // an error occured 907 LOG.error(Messages.get().container(Messages.RPT_SOURCESEARCH_ERROR_READING_RESOURCES_1, path), e); 908 getReport().println( 909 Messages.get().container(Messages.RPT_SOURCESEARCH_ERROR_READING_RESOURCES_1, path), 910 I_CmsReport.FORMAT_ERROR); 911 } 912 } 913 } 914 return resources; 915 } 916 917 /** 918 * Writes the file contents.<p> 919 * 920 * @param file the file to write 921 * @param content the file content 922 * 923 * @return success flag 924 */ 925 private boolean writeContent(CmsFile file, byte[] content) { 926 927 boolean success = true; 928 I_CmsReport report = getReport(); 929 CmsObject cmsObject = getCms(); 930 // get current lock from file 931 try { 932 // try to lock the resource 933 if (!lockResource(cmsObject, file, report)) { 934 report.println( 935 Messages.get().container(Messages.RPT_SOURCESEARCH_LOCKED_FILE_0, cmsObject.getSitePath(file)), 936 I_CmsReport.FORMAT_ERROR); 937 success = false; 938 } 939 } catch (CmsException e) { 940 report.println( 941 Messages.get().container(Messages.RPT_SOURCESEARCH_LOCKED_FILE_0, cmsObject.getSitePath(file)), 942 I_CmsReport.FORMAT_ERROR); 943 if (LOG.isErrorEnabled()) { 944 LOG.error(e.getMessageContainer(), e); 945 } 946 success = false; 947 } 948 949 // write the file content 950 try { 951 file.setContents(content); 952 cmsObject.writeFile(file); 953 } catch (Exception e) { 954 m_errorUpdate += 1; 955 report.println( 956 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 957 I_CmsReport.FORMAT_ERROR); 958 if (LOG.isErrorEnabled()) { 959 LOG.error(e.toString()); 960 } 961 success = false; 962 } 963 964 // unlock the resource 965 try { 966 cmsObject.unlockResource(cmsObject.getSitePath(file)); 967 } catch (CmsException e) { 968 m_errorUpdate += 1; 969 report.println( 970 Messages.get().container(Messages.RPT_SOURCESEARCH_UNLOCK_FILE_0), 971 I_CmsReport.FORMAT_WARNING); 972 if (LOG.isErrorEnabled()) { 973 LOG.error(e.getMessageContainer(), e); 974 } 975 success = false; 976 } 977 978 if (success) { 979 // successfully updated 980 report.println( 981 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 982 I_CmsReport.FORMAT_OK); 983 } 984 985 return success; 986 } 987}