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.workplace.tools.content; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsProperty; 033import org.opencms.file.CmsResource; 034import org.opencms.file.CmsResourceFilter; 035import org.opencms.file.CmsUser; 036import org.opencms.i18n.CmsMessageContainer; 037import org.opencms.lock.CmsLock; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.report.A_CmsReportThread; 042import org.opencms.report.I_CmsReport; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.xml.CmsXmlException; 045import org.opencms.xml.content.CmsXmlContent; 046import org.opencms.xml.content.CmsXmlContentFactory; 047import org.opencms.xml.types.I_CmsXmlContentValue; 048 049import java.util.Iterator; 050import java.util.List; 051import java.util.Locale; 052 053import org.apache.commons.logging.Log; 054 055import org.htmlparser.util.ParserException; 056 057/** 058 * Replaces HTML tags of xmlpage resources using the corresponding settings object. 059 * <p> 060 * 061 * @since 6.1.8 062 */ 063public class CmsTagReplaceThread extends A_CmsReportThread { 064 065 /** The log object for this class. */ 066 private static final Log LOG = CmsLog.getLog(CmsTagReplaceThread.class); 067 068 private CmsProperty m_markerProperty; 069 070 private CmsTagReplaceSettings m_settings; 071 072 /** 073 * Creates a replace html tag Thread.<p> 074 * 075 * @param cms the current cms context. 076 * 077 * @param settings the settings needed to perform the operation. 078 */ 079 public CmsTagReplaceThread(CmsObject cms, CmsTagReplaceSettings settings) { 080 081 super(cms, Messages.get().getBundle().key(Messages.GUI_TAGREPLACE_THREAD_NAME_0)); 082 initHtmlReport(cms.getRequestContext().getLocale()); 083 m_settings = settings; 084 m_markerProperty = new CmsProperty( 085 CmsTagReplaceSettings.PROPERTY_CONTENTOOLS_TAGREPLACE, 086 null, 087 m_settings.getPropertyValueTagReplaceID(), 088 true); 089 } 090 091 /** 092 * @see org.opencms.report.A_CmsReportThread#getReportUpdate() 093 */ 094 @Override 095 public String getReportUpdate() { 096 097 return getReport().getReportUpdate(); 098 } 099 100 /** 101 * @see java.lang.Runnable#run() 102 */ 103 @Override 104 public void run() { 105 106 getReport().println( 107 Messages.get().container(Messages.RPT_TAGREPLACE_BEGIN_1, m_settings.getWorkPath()), 108 I_CmsReport.FORMAT_HEADLINE); 109 try { 110 // change the element locales 111 replaceTags(); 112 } catch (CmsException e) { 113 getReport().println( 114 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 115 I_CmsReport.FORMAT_ERROR); 116 getReport().println(e.getMessageContainer(), I_CmsReport.FORMAT_ERROR); 117 if (LOG.isErrorEnabled()) { 118 LOG.error(e.getMessageContainer(), e); 119 } 120 } catch (Throwable f) { 121 getReport().println( 122 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0), 123 I_CmsReport.FORMAT_ERROR); 124 getReport().println(f); 125 if (LOG.isErrorEnabled()) { 126 LOG.error(f); 127 } 128 } 129 130 // append runtime statistics to report 131 getReport().print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_STAT_0)); 132 getReport().println( 133 org.opencms.report.Messages.get().container( 134 org.opencms.report.Messages.RPT_STAT_DURATION_1, 135 getReport().formatRuntime())); 136 getReport().println(Messages.get().container(Messages.RPT_TAGREPLACE_END_0), I_CmsReport.FORMAT_HEADLINE); 137 } 138 139 /** 140 * Checks the shared property {@link CmsTagReplaceSettings#PROPERTY_CONTENTOOLS_TAGREPLACE} if 141 * it has the value of this configuration ({@link CmsTagReplaceSettings#getPropertyValueTagReplaceID()}).<p> 142 * 143 * @param resource the resource to test. 144 * 145 * @return true if the property with the value was found. 146 * 147 * @throws CmsException if reading a property fails. 148 */ 149 private boolean isProcessedBefore(CmsResource resource) throws CmsException { 150 151 CmsProperty testProp = getCms().readPropertyObject( 152 resource, 153 CmsTagReplaceSettings.PROPERTY_CONTENTOOLS_TAGREPLACE, 154 false); 155 if (testProp.isNullProperty()) { 156 return false; 157 } else { 158 String testValue = testProp.getResourceValue(); 159 if (CmsStringUtil.isEmptyOrWhitespaceOnly(testValue)) { 160 return false; 161 } else { 162 return testValue.equals(m_settings.getPropertyValueTagReplaceID()); 163 } 164 } 165 } 166 167 private void replaceTags() throws CmsException { 168 169 I_CmsReport report = getReport(); 170 report.print(Messages.get().container(Messages.RPT_TAGREPLACE_READ_RESOURCES_1, m_settings.getWorkPath())); 171 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 172 if (LOG.isDebugEnabled()) { 173 LOG.debug( 174 Messages.get().getBundle().key(Messages.RPT_TAGREPLACE_READ_RESOURCES_1, m_settings.getWorkPath())); 175 } 176 CmsResourceFilter filter = CmsResourceFilter.ALL.addRequireType( 177 OpenCms.getResourceManager().getResourceType("xmlpage").getTypeId()); 178 List resources = getCms().readResources(m_settings.getWorkPath(), filter, true); 179 if (LOG.isDebugEnabled()) { 180 LOG.debug( 181 Messages.get().getBundle().key(Messages.LOG_TAGREPLACE_READ_RESOURCES_OK_1, m_settings.getWorkPath())); 182 } 183 report.println( 184 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 185 I_CmsReport.FORMAT_OK); 186 Integer size = Integer.valueOf(resources.size()); 187 Iterator itResources = resources.iterator(); 188 CmsResource resource; 189 int count = 1; 190 while (itResources.hasNext()) { 191 resource = (CmsResource)itResources.next(); 192 replaceTags(resource, size, Integer.valueOf(count)); 193 count++; 194 } 195 } 196 197 /** 198 * Replaces all replacement mappings configured in the internal {@link CmsTagReplaceSettings} 199 * instance in the content of the given resource.<p> 200 * 201 * No modifications will be done: 202 * <ol> 203 * <li>the resource is locked by another user.</li> 204 * <li>the special marker property with the value that stands for the replacement configuration 205 * is set on the resource (shared).</li> 206 * <li>locking of the non-locked resource fails.</li> 207 * <li>Loading of the content fails.</li> 208 * <li>Unmarshalling fails.</li> 209 * <li>Unexpected exception while replacing occur.</li> 210 * <li>Marshalling of XML fails.</li> 211 * <li>Writing of the marker property fails.</li> 212 * <li>Writing of the file fails.</li> 213 * </ol> 214 * <p> 215 * 216 * @param resource denotes the content to process. 217 * 218 * @param totalJobCount for fancy report writing. 219 * 220 * @param actualJobCount for even fancier report writing. 221 * 222 * @throws CmsException if sth. goes wrong. 223 */ 224 private void replaceTags(CmsResource resource, Integer totalJobCount, Integer actualJobCount) throws CmsException { 225 226 I_CmsReport report = getReport(); 227 report.print(org.opencms.report.Messages.get().container( 228 org.opencms.report.Messages.RPT_SUCCESSION_2, 229 actualJobCount, 230 totalJobCount)); 231 report.print( 232 Messages.get().container( 233 Messages.RPT_TAGREPLACE_PROCESS_FILE_1, 234 getCms().getRequestContext().removeSiteRoot(resource.getRootPath()))); 235 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 236 237 if (isProcessedBefore(resource)) { 238 report.print( 239 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_SKIPPED_0), 240 I_CmsReport.FORMAT_OK); 241 report.println( 242 Messages.get().container(Messages.RPT_TAGREPLACE_SKIP_REASON_PROPERTY_0), 243 I_CmsReport.FORMAT_OK); 244 return; 245 } 246 247 if (LOG.isDebugEnabled()) { 248 LOG.debug( 249 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_LOCK_RESOURCE_1, resource.getRootPath())); 250 } 251 try { 252 // checking the lock: 253 if (LOG.isDebugEnabled()) { 254 LOG.debug( 255 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_LOCK_READ_1, resource.getRootPath())); 256 } 257 258 CmsLock lock = getCms().getLock(resource); 259 260 if (LOG.isDebugEnabled()) { 261 LOG.debug( 262 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_LOCK_READ_1, resource.getRootPath())); 263 } 264 265 boolean myLock = !lock.isNullLock() && lock.isOwnedBy(getCms().getRequestContext().getCurrentUser()); 266 if (lock.isNullLock() || myLock) { 267 if (!myLock) { 268 if (LOG.isDebugEnabled()) { 269 LOG.debug( 270 Messages.get().getBundle().key( 271 Messages.LOG_DEBUG_TAGREPLACE_LOCK_RESOURCE_1, 272 resource.getRootPath())); 273 } 274 // obtaining the lock: 275 getCms().lockResource(getCms().getRequestContext().removeSiteRoot(resource.getRootPath())); 276 if (LOG.isDebugEnabled()) { 277 LOG.debug( 278 Messages.get().getBundle().key( 279 Messages.LOG_DEBUG_TAGREPLACE_LOCK_RESOURCE_OK_1, 280 resource.getRootPath())); 281 } 282 } 283 } else { 284 // locked by another user: 285 if (LOG.isDebugEnabled()) { 286 LOG.debug( 287 Messages.get().getBundle().key( 288 Messages.LOG_DEBUG_TAGREPLACE_RESOURCE_SKIPPED_1, 289 resource.getRootPath())); 290 LOG.debug(Messages.get().getBundle().key(Messages.RPT_TAGREPLACE_SKIP_REASON_LOCKED_0)); 291 } 292 report.print( 293 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_SKIPPED_0), 294 I_CmsReport.FORMAT_WARNING); 295 try { 296 CmsUser locker = getCms().readUser(lock.getUserId()); 297 report.println( 298 Messages.get().container(Messages.RPT_TAGREPLACE_SKIP_REASON_LOCKED_1, locker.getName()), 299 I_CmsReport.FORMAT_WARNING); 300 } catch (Throwable f) { 301 report.println( 302 Messages.get().container(Messages.RPT_TAGREPLACE_SKIP_REASON_ERR_LOCK_0), 303 I_CmsReport.FORMAT_WARNING); 304 if (LOG.isDebugEnabled()) { 305 LOG.debug( 306 Messages.get().getBundle().key( 307 Messages.LOG_DEBUG_TAGREPLACE_RESOURCE_SKIPPED_1, 308 resource.getRootPath())); 309 LOG.debug(Messages.get().getBundle().key(Messages.RPT_TAGREPLACE_SKIP_REASON_ERR_LOCK_0)); 310 } 311 } 312 return; 313 } 314 } catch (CmsException e) { 315 if (LOG.isErrorEnabled()) { 316 LOG.error( 317 Messages.get().getBundle().key( 318 Messages.LOG_WARN_TAGREPLACE_LOCK_RESOURCE_FAILED_1, 319 resource.getRootPath()), 320 e); 321 } 322 report.print( 323 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_SKIPPED_0), 324 I_CmsReport.FORMAT_WARNING); 325 report.println( 326 Messages.get().container(Messages.RPT_TAGREPLACE_SKIP_REASON_LOCKED_0), 327 I_CmsReport.FORMAT_WARNING); 328 return; 329 } 330 331 if (LOG.isDebugEnabled()) { 332 LOG.debug( 333 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_LOAD_FILE_1, resource.getRootPath())); 334 } 335 336 CmsFile file = getCms().readFile(resource); 337 338 if (LOG.isDebugEnabled()) { 339 LOG.debug( 340 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_LOAD_FILE_OK_1, resource.getRootPath())); 341 LOG.debug( 342 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_UNMARSHAL_1, resource.getRootPath())); 343 } 344 345 CmsXmlContent xmlcontent = CmsXmlContentFactory.unmarshal(getCms(), file); 346 347 if (LOG.isDebugEnabled()) { 348 LOG.debug( 349 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_UNMARSHAL_OK_1, resource.getRootPath())); 350 } 351 352 List locales = xmlcontent.getLocales(); 353 Iterator itLocales = locales.iterator(); 354 List elements; 355 Iterator itElements; 356 Locale locale; 357 CmsTagReplaceParser parser = new CmsTagReplaceParser(m_settings); 358 I_CmsXmlContentValue value; 359 int count = 1; 360 while (itLocales.hasNext()) { 361 locale = (Locale)itLocales.next(); 362 if (LOG.isDebugEnabled()) { 363 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_LOCALE_1, locale.getLanguage())); 364 } 365 366 elements = xmlcontent.getValues(locale); 367 itElements = elements.iterator(); 368 while (itElements.hasNext()) { 369 value = (I_CmsXmlContentValue)itElements.next(); 370 String content = value.getStringValue(getCms()); 371 if (LOG.isDebugEnabled()) { 372 LOG.debug( 373 Messages.get().getBundle().key( 374 Messages.LOG_DEBUG_TAGREPLACE_ELEMENT_2, 375 value.getPath(), 376 content)); 377 } 378 try { 379 380 parser.process(content, xmlcontent.getEncoding()); 381 value.setStringValue(getCms(), parser.getResult()); 382 } catch (ParserException e) { 383 CmsMessageContainer container = Messages.get().container( 384 Messages.ERR_TAGREPLACE_PARSE_4, 385 new Object[] { 386 getCms().getRequestContext().removeSiteRoot(resource.getRootPath()), 387 locale.getLanguage(), 388 value.getPath(), 389 parser.getResult()}); 390 throw new CmsXmlException(container, e); 391 } 392 } 393 count++; 394 } 395 396 if (parser.isChangedContent()) { 397 398 if (LOG.isDebugEnabled()) { 399 LOG.debug( 400 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_MARSHAL_1, resource.getRootPath())); 401 } 402 byte[] content = xmlcontent.marshal(); 403 if (LOG.isDebugEnabled()) { 404 LOG.debug( 405 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_MARSHAL_OK_1, resource.getRootPath())); 406 } 407 408 // write back the modified xmlcontent: 409 file.setContents(content); 410 411 if (LOG.isDebugEnabled()) { 412 LOG.debug( 413 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_WRITE_1, resource.getRootPath())); 414 } 415 416 getCms().writeFile(file); 417 418 if (LOG.isDebugEnabled()) { 419 LOG.debug( 420 Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_WRITE_OK_1, resource.getRootPath())); 421 } 422 423 try { 424 // set the marker property: 425 426 if (LOG.isDebugEnabled()) { 427 LOG.debug( 428 Messages.get().getBundle().key( 429 Messages.LOG_DEBUG_TAGREPLACE_PROPERTY_WRITE_3, 430 new Object[] { 431 m_markerProperty.getName(), 432 m_markerProperty.getResourceValue(), 433 resource.getRootPath()})); 434 } 435 getCms().writePropertyObject( 436 getCms().getRequestContext().removeSiteRoot(resource.getRootPath()), 437 m_markerProperty); 438 if (LOG.isDebugEnabled()) { 439 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEBUG_TAGREPLACE_PROPERTY_WRITE_OK_0)); 440 } 441 report.println( 442 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 443 I_CmsReport.FORMAT_OK); 444 } catch (CmsException e) { 445 CmsMessageContainer container = Messages.get().container( 446 Messages.LOG_ERROR_TAGREPLACE_PROPERTY_WRITE_3, 447 new Object[] { 448 m_markerProperty.getName(), 449 m_markerProperty.getResourceValue(), 450 resource.getRootPath()}); 451 throw new CmsXmlException(container, e); 452 } 453 454 } else { 455 if (LOG.isDebugEnabled()) { 456 LOG.debug( 457 Messages.get().container(Messages.LOG_DEBUG_TAGREPLACE_UNLOCK_FILE_1, resource.getRootPath())); 458 } 459 getCms().unlockResource(getCms().getRequestContext().removeSiteRoot(resource.getRootPath())); 460 if (LOG.isDebugEnabled()) { 461 LOG.debug(Messages.get().container(Messages.LOG_DEBUG_TAGREPLACE_UNLOCK_FILE_OK_0)); 462 } 463 report.print( 464 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_SKIPPED_0), 465 I_CmsReport.FORMAT_OK); 466 report.println( 467 Messages.get().container(Messages.RPT_TAGREPLACE_SKIP_REASON_UNMODIFIED_0), 468 I_CmsReport.FORMAT_OK); 469 470 } 471 } 472}