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.CmsObject; 031import org.opencms.file.CmsProperty; 032import org.opencms.file.CmsPropertyDefinition; 033import org.opencms.file.CmsResource; 034import org.opencms.file.CmsResourceFilter; 035import org.opencms.file.CmsVfsException; 036import org.opencms.i18n.CmsEncoder; 037import org.opencms.i18n.CmsMessages; 038import org.opencms.jsp.CmsJspActionElement; 039import org.opencms.main.CmsException; 040import org.opencms.main.CmsLog; 041import org.opencms.util.CmsStringUtil; 042import org.opencms.workplace.CmsDialog; 043import org.opencms.workplace.CmsWorkplaceSettings; 044 045import java.util.ArrayList; 046import java.util.List; 047import java.util.regex.Pattern; 048import java.util.regex.PatternSyntaxException; 049 050import javax.servlet.http.HttpServletRequest; 051import javax.servlet.http.HttpServletResponse; 052import javax.servlet.jsp.JspException; 053import javax.servlet.jsp.PageContext; 054 055import org.apache.commons.logging.Log; 056 057/** 058 * Provides methods for the change property values dialog.<p> 059 * 060 * @since 6.0.0 061 */ 062public class CmsPropertyChange extends CmsDialog { 063 064 /** Value for the action: show result. */ 065 public static final int ACTION_SHOWRESULT = 100; 066 067 /** Request parameter value for the action: show result. */ 068 public static final String DIALOG_SHOWRESULT = "showresult"; 069 070 /** The dialog type. */ 071 public static final String DIALOG_TYPE = "propertychange"; 072 /** Request parameter name for the property name. */ 073 public static final String PARAM_NEWVALUE = "newvalue"; 074 /** Request parameter name for the property name. */ 075 public static final String PARAM_OLDVALUE = "oldvalue"; 076 /** Request parameter name for the property name. */ 077 public static final String PARAM_PROPERTYNAME = "propertyname"; 078 /** Request parameter name for the property name. */ 079 public static final String PARAM_RECURSIVE = "recursive"; 080 081 /** The log object for this class. */ 082 private static final Log LOG = CmsLog.getLog(CmsPropertyChange.class); 083 084 private List m_changedResources; 085 086 /** The error message. */ 087 private String m_errorMessage; 088 089 private String m_paramNewValue; 090 private String m_paramOldValue; 091 private String m_paramPropertyName; 092 private String m_paramRecursive; 093 094 private boolean m_validationErrors; 095 096 /** 097 * Public constructor with JSP action element.<p> 098 * 099 * @param jsp an initialized JSP action element 100 */ 101 public CmsPropertyChange(CmsJspActionElement jsp) { 102 103 super(jsp); 104 } 105 106 /** 107 * Public constructor with JSP variables.<p> 108 * 109 * @param context the JSP page context 110 * @param req the JSP request 111 * @param res the JSP response 112 */ 113 public CmsPropertyChange(PageContext context, HttpServletRequest req, HttpServletResponse res) { 114 115 this(new CmsJspActionElement(context, req, res)); 116 } 117 118 /** 119 * Builds the html for the property definition select box.<p> 120 * 121 * @param cms the CmsObject 122 * @param selectValue the localized value for the "Please select" option 123 * @param attributes optional attributes for the <select> tag 124 * @param selectedValue the value that is currently selected 125 * @return the html for the property definition select box 126 */ 127 public static String buildSelectProperty( 128 CmsObject cms, 129 String selectValue, 130 String attributes, 131 String selectedValue) { 132 133 List propertyDef = new ArrayList(); 134 try { 135 // get all property definitions 136 propertyDef = cms.readAllPropertyDefinitions(); 137 } catch (CmsException e) { 138 // should usually never happen 139 if (LOG.isInfoEnabled()) { 140 LOG.info(e.getLocalizedMessage(), e); 141 } 142 } 143 144 int propertyCount = propertyDef.size(); 145 List options = new ArrayList(propertyCount + 1); 146 List values = new ArrayList(propertyCount + 1); 147 options.add(CmsEncoder.escapeXml(selectValue)); 148 values.add(""); 149 int selectedIndex = 0; 150 int count = 1; 151 152 for (int i = 0; i < propertyCount; i++) { 153 // loop property definitions and get definition name 154 CmsPropertyDefinition currDef = (CmsPropertyDefinition)propertyDef.get(i); 155 if (currDef.getName().equals(selectedValue)) { 156 selectedIndex = count; 157 } 158 options.add(CmsEncoder.escapeXml(currDef.getName())); 159 values.add(CmsEncoder.escapeXml(currDef.getName())); 160 count += 1; 161 } 162 163 CmsDialog wp = new CmsDialog(null); 164 return wp.buildSelect(attributes, options, values, selectedIndex); 165 } 166 167 /** 168 * Changes the property values on the specified resources.<p> 169 * 170 * @throws JspException if problems including sub-elements occur 171 */ 172 public void actionChange() throws JspException { 173 174 // save initialized instance of this class in request attribute for included sub-elements 175 getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this); 176 try { 177 boolean recursive = Boolean.valueOf(getParamRecursive()).booleanValue(); 178 if (performChangeOperation(recursive)) { 179 // if no exception is caused and "true" is returned change property operation was successful 180 setAction(ACTION_SHOWRESULT); 181 } else { 182 // "false" returned, display "please wait" screen 183 getJsp().include(FILE_DIALOG_SCREEN_WAIT); 184 } 185 } catch (Throwable e) { 186 // error while changing property values, show error dialog 187 includeErrorpage(this, e); 188 189 } 190 } 191 192 /** 193 * Builds the html for the result list of resources where the property was changed.<p> 194 * 195 * @return the html for the result list 196 */ 197 public String buildResultList() { 198 199 StringBuffer result = new StringBuffer(16); 200 if ((getChangedResources() != null) && (getChangedResources().size() > 0)) { 201 // at least one resource property value has been changed, show list 202 for (int i = 0; i < getChangedResources().size(); i++) { 203 CmsResource res = (CmsResource)getChangedResources().get(i); 204 String resName = getCms().getSitePath(res); 205 result.append(resName); 206 result.append("<br>\n"); 207 } 208 } else { 209 // nothing was changed, show message 210 result.append(Messages.get().getBundle(getLocale()).key(Messages.GUI_INPUT_PROPERTYCHANGE_RESULT_NONE_0)); 211 } 212 return result.toString(); 213 } 214 215 /** 216 * Builds the html for the property definition select box.<p> 217 * 218 * @param attributes optional attributes for the <select> tag 219 * @return the html for the property definition select box 220 */ 221 public String buildSelectProperty(String attributes) { 222 223 return buildSelectProperty( 224 getCms(), 225 Messages.get().getBundle(getLocale()).key(Messages.GUI_PLEASE_SELECT_0), 226 attributes, 227 getParamPropertyName()); 228 } 229 230 /** 231 * Returns the error message.<p> 232 * 233 * @return the error message 234 */ 235 public String getErrorMessage() { 236 237 if (CmsStringUtil.isEmpty(m_errorMessage)) { 238 return ""; 239 } 240 241 return m_errorMessage; 242 } 243 244 /** 245 * Returns the value of the newvalue parameter.<p> 246 * 247 * @return the value of the newvalue parameter 248 */ 249 public String getParamNewValue() { 250 251 if (m_paramNewValue != null) { 252 return m_paramNewValue; 253 } else { 254 return CmsProperty.DELETE_VALUE; 255 } 256 } 257 258 /** 259 * Returns the value of the oldvalue parametere.<p> 260 * 261 * @return the value of the oldvalue parameter 262 */ 263 public String getParamOldValue() { 264 265 return m_paramOldValue; 266 } 267 268 /** 269 * Returns the value of the propertyname parameter.<p> 270 * 271 * @return the value of the propertyname parameter 272 */ 273 public String getParamPropertyName() { 274 275 return m_paramPropertyName; 276 } 277 278 /** 279 * Returns the value of the recursive parameter.<p> 280 * 281 * @return the value of the recursive parameter 282 */ 283 public String getParamRecursive() { 284 285 return m_paramRecursive; 286 } 287 288 /** 289 * Returns the height for the result list of changed resources.<p> 290 * 291 * @return the height for the result list of changed resources 292 */ 293 public String getResultListHeight() { 294 295 if ((getChangedResources() != null) && (getChangedResources().size() > 0)) { 296 int height = getChangedResources().size() * 14; 297 if (height > 300) { 298 height = 300; 299 } 300 return "" + height; 301 } else { 302 return "14"; 303 } 304 } 305 306 /** 307 * Returns if validation errors were found.<p> 308 * 309 * @return true if validation errors were found, otherwise false 310 */ 311 public boolean hasValidationErrors() { 312 313 return m_validationErrors; 314 } 315 316 /** 317 * Sets the value of the newvalue parameter.<p> 318 * 319 * @param paramNewValue the value of the newvalue parameter 320 */ 321 public void setParamNewValue(String paramNewValue) { 322 323 m_paramNewValue = paramNewValue; 324 } 325 326 /** 327 * Sets the value of the oldvalue parameter.<p> 328 * 329 * @param paramOldValue the value of the oldvalue parameter 330 */ 331 public void setParamOldValue(String paramOldValue) { 332 333 m_paramOldValue = paramOldValue; 334 } 335 336 /** 337 * Sets the value of the propertyname parameter.<p> 338 * 339 * @param paramPropertyName the value of the propertyname parameter 340 */ 341 public void setParamPropertyName(String paramPropertyName) { 342 343 m_paramPropertyName = paramPropertyName; 344 } 345 346 /** 347 * Sets the value of the recursive parameter.<p> 348 * 349 * @param paramRecursive the value of the recursive parameter 350 */ 351 public void setParamRecursive(String paramRecursive) { 352 353 m_paramRecursive = paramRecursive; 354 } 355 356 /** 357 * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest) 358 */ 359 @Override 360 protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) { 361 362 // fill the parameter values in the get/set methods 363 fillParamValues(request); 364 // set the dialog type 365 setParamDialogtype(DIALOG_TYPE); 366 // set the action for the JSP switch 367 if (DIALOG_OK.equals(getParamAction())) { 368 if (validateParameters()) { 369 // all parameters are valid, proceed 370 setAction(ACTION_OK); 371 } else { 372 // validation error(s), redisplay form 373 setAction(ACTION_DEFAULT); 374 } 375 } else if (DIALOG_WAIT.equals(getParamAction())) { 376 setAction(ACTION_WAIT); 377 } else if (DIALOG_CANCEL.equals(getParamAction())) { 378 setAction(ACTION_CANCEL); 379 } else { 380 setAction(ACTION_DEFAULT); 381 // build title for change property value dialog 382 setParamTitle(Messages.get().getBundle(getLocale()).key(Messages.GUI_TITLE_PROPERTYCHANGE_0)); 383 } 384 } 385 386 /** 387 * Sets the error message.<p> 388 * 389 * @param errorMessage the error message to set 390 */ 391 protected void setErrorMessage(String errorMessage) { 392 393 m_errorMessage = errorMessage; 394 } 395 396 /** 397 * Sets the validation error flag.<p> 398 * 399 * @param validationErrors the validation error flag, true if validation errors were found 400 */ 401 protected void setValidationErrors(boolean validationErrors) { 402 403 m_validationErrors = validationErrors; 404 } 405 406 /** 407 * Returns the changed resources that were affected by the property change action.<p> 408 * 409 * @return the changed resources that were affected by the property change action 410 */ 411 private List getChangedResources() { 412 413 return m_changedResources; 414 } 415 416 /** 417 * Performs the main property change value operation on the resource property.<p> 418 * 419 * @param recursive true, if the property value has to be changed recursively, otherwise false 420 * @return true, if the property values are changed successfully, otherwise false 421 * @throws CmsException if changing is not successful 422 */ 423 private boolean performChangeOperation(boolean recursive) throws CmsException { 424 425 // on recursive property changes display "please wait" screen 426 if (recursive && !DIALOG_WAIT.equals(getParamAction())) { 427 // return false, this will trigger the "please wait" screen 428 return false; 429 } 430 431 // lock the selected resource 432 checkLock(getParamResource()); 433 // change the property values 434 List changedResources = null; 435 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getParamOldValue())) { 436 changedResources = getCms().changeResourcesInFolderWithProperty( 437 getParamResource(), 438 getParamPropertyName(), 439 getParamOldValue(), 440 getParamNewValue(), 441 recursive); 442 } else { 443 changedResources = setPropertyInFolder( 444 getParamResource(), 445 getParamPropertyName(), 446 getParamNewValue(), 447 recursive); 448 } 449 setChangedResources(changedResources); 450 return true; 451 } 452 453 /** 454 * Sets the given property with the given value to the given resource 455 * (potentially recursiv) if it has not been set before.<p> 456 * 457 * Returns a list with all sub resources that have been modified this way.<p> 458 * 459 * @param resourceRootPath the resource on which property definition values are changed 460 * @param propertyDefinition the name of the propertydefinition to change the value 461 * @param newValue the new value of the propertydefinition 462 * @param recursive if true, change recursively all property values on sub-resources (only for folders) 463 * 464 * @return a list with the <code>{@link CmsResource}</code>'s where the property value has been changed 465 * 466 * @throws CmsVfsException for now only when the search for the oldvalue failed. 467 * @throws CmsException if operation was not successful 468 */ 469 private List setPropertyInFolder( 470 String resourceRootPath, 471 String propertyDefinition, 472 String newValue, 473 boolean recursive) throws CmsException, CmsVfsException { 474 475 CmsObject cms = getCms(); 476 477 // collect the resources to look up 478 List resources = new ArrayList(); 479 if (recursive) { 480 resources = cms.readResources(resourceRootPath, CmsResourceFilter.IGNORE_EXPIRATION); 481 } else { 482 resources.add(resourceRootPath); 483 } 484 485 List changedResources = new ArrayList(resources.size()); 486 CmsProperty newProperty = new CmsProperty(propertyDefinition, null, null); 487 // create permission set and filter to check each resource 488 for (int i = 0; i < resources.size(); i++) { 489 // loop through found resources and check property values 490 CmsResource res = (CmsResource)resources.get(i); 491 CmsProperty property = cms.readPropertyObject(res, propertyDefinition, false); 492 if (property.isNullProperty()) { 493 // change structure value 494 newProperty.setStructureValue(newValue); 495 newProperty.setName(propertyDefinition); 496 cms.writePropertyObject(cms.getRequestContext().removeSiteRoot(res.getRootPath()), newProperty); 497 changedResources.add(res); 498 } else { 499 // nop 500 } 501 } 502 return changedResources; 503 } 504 505 /** 506 * Sets the changed resources that were affected by the property change action.<p> 507 * 508 * @param changedResources the changed resources that were affected by the property change action 509 */ 510 private void setChangedResources(List changedResources) { 511 512 m_changedResources = changedResources; 513 } 514 515 /** 516 * Validates the submitted form parameters.<p> 517 * 518 * If parameters are missing, a localized error message String is created.<p> 519 * 520 * @return true if all parameters are correct, otherwise false 521 * 522 */ 523 private boolean validateParameters() { 524 525 boolean allOk = true; 526 527 StringBuffer validationErrors = new StringBuffer(32); 528 CmsMessages messages = Messages.get().getBundle(getLocale()); 529 530 // check resource parameter presence 531 if (CmsStringUtil.isEmptyOrWhitespaceOnly(getParamResource()) || !getCms().existsResource(getParamResource())) { 532 allOk = false; 533 validationErrors.append(messages.key(Messages.GUI_PROP_CHANGE_VALIDATE_VFS_RESOURCE_0)).append("<br>"); 534 } 535 536 // check selected property name 537 if (CmsStringUtil.isEmptyOrWhitespaceOnly(getParamPropertyName())) { 538 allOk = false; 539 validationErrors.append(messages.key(Messages.GUI_PROP_CHANGE_VALIDATE_SELECT_PROPERTY_0)).append("<br>"); 540 } 541 542 // check old property value to look up 543 if (CmsStringUtil.isEmptyOrWhitespaceOnly(getParamOldValue())) { 544 allOk = false; 545 validationErrors.append(messages.key(Messages.GUI_PROP_CHANGE_VALIDATE_OLD_PROP_VALUE_0)).append("<br>"); 546 } else { 547 try { 548 // check if there is a place holder in the expression pattern 549 // remove it here, because otherwise this is no valid expression pattern 550 String oldValue = getParamOldValue(); 551 if (oldValue.contains(CmsStringUtil.PLACEHOLDER_START) 552 && oldValue.contains(CmsStringUtil.PLACEHOLDER_END)) { 553 oldValue = oldValue.replace(CmsStringUtil.PLACEHOLDER_START, ""); 554 oldValue = oldValue.replace(CmsStringUtil.PLACEHOLDER_END, ""); 555 } 556 // compile regular expression pattern 557 Pattern.compile(oldValue); 558 } catch (PatternSyntaxException e) { 559 allOk = false; 560 validationErrors.append(messages.key(Messages.GUI_PROP_CHANGE_VALIDATE_OLD_PROP_PATTERN_0)).append( 561 "<br>"); 562 } 563 } 564 565 // check new property value 566 if (CmsStringUtil.isEmptyOrWhitespaceOnly(getParamNewValue())) { 567 // if no new value was given, set it to the delete value 568 setParamNewValue(CmsProperty.DELETE_VALUE); 569 } 570 571 setErrorMessage(validationErrors.toString()); 572 setValidationErrors(!allOk); 573 return allOk; 574 } 575}