001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.commons; 029 030import org.opencms.file.CmsResource; 031import org.opencms.file.CmsResource.CmsResourceCopyMode; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.file.CmsVfsException; 034import org.opencms.file.CmsVfsResourceAlreadyExistsException; 035import org.opencms.file.CmsVfsResourceNotFoundException; 036import org.opencms.jsp.CmsJspActionElement; 037import org.opencms.lock.CmsLockException; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.security.CmsPermissionSet; 042import org.opencms.staticexport.CmsLinkManager; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.workplace.CmsMultiDialog; 045import org.opencms.workplace.CmsWorkplaceSettings; 046 047import java.util.ArrayList; 048import java.util.Iterator; 049import java.util.List; 050 051import org.apache.commons.logging.Log; 052 053import jakarta.servlet.http.HttpServletRequest; 054import jakarta.servlet.http.HttpServletResponse; 055import jakarta.servlet.jsp.JspException; 056import jakarta.servlet.jsp.PageContext; 057 058/** 059 * Provides methods for the copy resources dialog.<p> 060 * 061 * The following files use this class: 062 * <ul> 063 * <li>/commons/copy.jsp 064 * </ul> 065 * <p> 066 * 067 * @since 6.0.0 068 */ 069public class CmsCopy extends CmsMultiDialog { 070 071 /** Value for the action: copy the resource. */ 072 public static final int ACTION_COPY = 100; 073 074 /** The dialog type. */ 075 public static final String DIALOG_TYPE = "copy"; 076 077 /** Request parameter name for the keep rights flag. */ 078 public static final String PARAM_KEEPRIGHTS = "keeprights"; 079 080 /** Request parameter name for the overwrite flag. */ 081 public static final String PARAM_OVERWRITE = "overwrite"; 082 083 /** The log object for this class. */ 084 private static final Log LOG = CmsLog.getLog(CmsCopy.class); 085 086 /** A parameter of this dialog. */ 087 private String m_paramCopymode; 088 089 /** A parameter of this dialog. */ 090 private String m_paramKeeprights; 091 092 /** A parameter of this dialog. */ 093 private String m_paramOverwrite; 094 095 /** A parameter of this dialog. */ 096 private String m_paramTarget; 097 098 /** 099 * Public constructor with JSP action element.<p> 100 * 101 * @param jsp an initialized JSP action element 102 */ 103 public CmsCopy(CmsJspActionElement jsp) { 104 105 super(jsp); 106 } 107 108 /** 109 * Public constructor with JSP variables.<p> 110 * 111 * @param context the JSP page context 112 * @param req the JSP request 113 * @param res the JSP response 114 */ 115 public CmsCopy(PageContext context, HttpServletRequest req, HttpServletResponse res) { 116 117 this(new CmsJspActionElement(context, req, res)); 118 } 119 120 /** 121 * Performs the copy action, will be called by the JSP page.<p> 122 * 123 * @throws JspException if problems including sub-elements occur 124 */ 125 public void actionCopy() throws JspException { 126 127 // save initialized instance of this class in request attribute for included sub-elements 128 getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this); 129 CmsResource resource = null; 130 try { 131 boolean isFolder = false; 132 String source = getResourceList().get(0); 133 String target = CmsLinkManager.getAbsoluteUri(getParamTarget(), CmsResource.getParentFolder(source)); 134 if (!isMultiOperation()) { 135 resource = getCms().readResource(source, CmsResourceFilter.ALL); 136 isFolder = resource.isFolder(); 137 } else { 138 String siteRootFolder = null; 139 try { 140 // check if a site root was added to the target name 141 if (OpenCms.getSiteManager().getSiteRoot(target) != null) { 142 siteRootFolder = getCms().getRequestContext().getSiteRoot(); 143 if (siteRootFolder.endsWith("/")) { 144 siteRootFolder = siteRootFolder.substring(0, siteRootFolder.length() - 1); 145 } 146 getCms().getRequestContext().setSiteRoot("/"); 147 } 148 resource = getCms().readResource(target, CmsResourceFilter.ALL); 149 } finally { 150 if (siteRootFolder != null) { 151 getCms().getRequestContext().setSiteRoot(siteRootFolder); 152 } 153 } 154 if (!resource.isFolder()) { 155 // no folder selected for multi operation, throw exception 156 throw new CmsVfsException( 157 Messages.get().container(Messages.ERR_COPY_MULTI_TARGET_NOFOLDER_1, target)); 158 } 159 } 160 if (performDialogOperation()) { 161 // if no exception is caused and "true" is returned copy operation was successful 162 if (isMultiOperation() || isFolder) { 163 // set request attribute to reload the explorer tree view 164 List<String> folderList = new ArrayList<String>(1); 165 String targetParent = CmsResource.getParentFolder(target); 166 folderList.add(targetParent); 167 getJsp().getRequest().setAttribute(REQUEST_ATTRIBUTE_RELOADTREE, folderList); 168 } 169 actionCloseDialog(); 170 } else { 171 // "false" returned, display "please wait" screen 172 getJsp().include(FILE_DIALOG_SCREEN_WAIT); 173 } 174 } catch (Throwable e) { 175 // check if this exception requires a confirmation or error screen for single resource operations 176 if (!isMultiOperation() 177 && ((e instanceof CmsVfsResourceAlreadyExistsException) || (e instanceof CmsLockException)) 178 && (resource != null) 179 && !(resource.isFolder())) { 180 // file copy but file already exists, now check target file type 181 int targetType = -1; 182 String storedSiteRoot = null; 183 try { 184 if (OpenCms.getSiteManager().getSiteRoot(getParamTarget()) != null) { 185 storedSiteRoot = getCms().getRequestContext().getSiteRoot(); 186 getCms().getRequestContext().setSiteRoot("/"); 187 } 188 CmsResource targetRes = getCms().readResource(getParamTarget()); 189 targetType = targetRes.getTypeId(); 190 } catch (CmsException e2) { 191 // can usually be ignored 192 if (LOG.isInfoEnabled()) { 193 LOG.info(e2.getLocalizedMessage()); 194 } 195 } finally { 196 if (storedSiteRoot != null) { 197 getCms().getRequestContext().setSiteRoot(storedSiteRoot); 198 } 199 } 200 if (resource.getTypeId() == targetType) { 201 // file type of target is the same as source, show confirmation dialog 202 setParamMessage( 203 CmsStringUtil.escapeHtml( 204 key( 205 Messages.GUI_COPY_CONFIRM_OVERWRITE_2, 206 new Object[] {getParamResource(), getParamTarget()}))); 207 getJsp().include(FILE_DIALOG_SCREEN_CONFIRM); 208 } else { 209 // file type is different, create error message 210 includeErrorpage(this, e); 211 } 212 } else { 213 // error during copy, show error dialog 214 includeErrorpage(this, e); 215 } 216 } 217 } 218 219 /** 220 * Builds the input radio buttons to select between preserving links or creating new resources when copying.<p> 221 * 222 * @return the HTML code for the radio buttons 223 */ 224 public String buildRadioCopyMode() { 225 226 StringBuffer retValue = new StringBuffer(256); 227 228 // check if the current resource is a folder for single operation 229 boolean isFolder = isOperationOnFolder(); 230 String checkedAttr = " checked=\"checked\""; 231 232 if (isMultiOperation() || isFolder) { 233 // for multi resource operations or folders, show an additional option "preserve links" 234 CmsResourceCopyMode defaultMode = getSettings().getUserSettings().getDialogCopyFolderMode(); 235 retValue.append("<input type=\"radio\" name=\"copymode\" value=\""); 236 retValue.append(CmsResource.COPY_AS_SIBLING.getMode()); 237 retValue.append("\""); 238 if (defaultMode == CmsResource.COPY_AS_SIBLING) { 239 retValue.append(checkedAttr); 240 } 241 retValue.append("> "); 242 String msgKey; 243 if (isMultiOperation()) { 244 msgKey = Messages.GUI_COPY_MULTI_CREATE_SIBLINGS_0; 245 } else { 246 msgKey = Messages.GUI_COPY_CREATE_SIBLINGS_0; 247 } 248 retValue.append(key(msgKey)); 249 retValue.append("<br>\n"); 250 retValue.append("<input type=\"radio\" name=\"copymode\" value=\""); 251 retValue.append(CmsResource.COPY_PRESERVE_SIBLING.getMode()); 252 retValue.append("\""); 253 if (defaultMode == CmsResource.COPY_PRESERVE_SIBLING) { 254 retValue.append(checkedAttr); 255 } 256 retValue.append("> "); 257 retValue.append(key(Messages.GUI_COPY_ALL_NO_SIBLINGS_0)); 258 retValue.append("<br>\n"); 259 retValue.append("<input type=\"radio\" name=\"copymode\" value=\""); 260 retValue.append(CmsResource.COPY_AS_NEW.getMode()); 261 retValue.append("\""); 262 if (defaultMode == CmsResource.COPY_AS_NEW) { 263 retValue.append(checkedAttr); 264 } 265 retValue.append("> "); 266 retValue.append(key(Messages.GUI_COPY_ALL_0)); 267 retValue.append("<br>\n"); 268 269 if (isMultiOperation()) { 270 // show overwrite option for multi resource copy 271 retValue.append(dialogSpacer()); 272 retValue.append("<input type=\"checkbox\" name=\""); 273 retValue.append(PARAM_OVERWRITE); 274 retValue.append("\" value=\"true\"> "); 275 retValue.append(key(Messages.GUI_COPY_MULTI_OVERWRITE_0)); 276 retValue.append("<br>\n"); 277 } 278 } else { 279 // for files, show copy option "copy as sibling" and "copy as new resource" 280 CmsResourceCopyMode defaultMode = getSettings().getUserSettings().getDialogCopyFileMode(); 281 retValue.append("<input type=\"radio\" name=\"copymode\" value=\""); 282 retValue.append(CmsResource.COPY_AS_SIBLING.getMode()); 283 retValue.append("\""); 284 if (defaultMode == CmsResource.COPY_AS_SIBLING) { 285 retValue.append(checkedAttr); 286 } 287 retValue.append("> "); 288 retValue.append(key(Messages.GUI_CREATE_SIBLING_0)); 289 retValue.append("<br>\n"); 290 retValue.append("<input type=\"radio\" name=\"copymode\" value=\""); 291 retValue.append(CmsResource.COPY_AS_NEW.getMode()); 292 retValue.append("\""); 293 if (defaultMode == CmsResource.COPY_AS_NEW) { 294 retValue.append(checkedAttr); 295 } 296 retValue.append("> "); 297 retValue.append(key(Messages.GUI_COPY_AS_NEW_0)); 298 retValue.append("<br>\n"); 299 } 300 301 return retValue.toString(); 302 } 303 304 /** 305 * Returns the value of the copymode parameter.<p> 306 * 307 * @return the value of the copymode parameter 308 */ 309 public String getParamCopymode() { 310 311 return m_paramCopymode; 312 } 313 314 /** 315 * Returns the value of the keeprights parameter.<p> 316 * 317 * @return the value of the keeprights parameter 318 */ 319 public String getParamKeeprights() { 320 321 return m_paramKeeprights; 322 } 323 324 /** 325 * Returns the value of the overwrite parameter.<p> 326 * 327 * @return the value of the overwrite parameter 328 */ 329 public String getParamOverwrite() { 330 331 return m_paramOverwrite; 332 } 333 334 /** 335 * Returns the value of the target parameter, 336 * or null if this parameter was not provided.<p> 337 * 338 * The target parameter selects the target name 339 * of the operation.<p> 340 * 341 * @return the value of the target parameter 342 */ 343 public String getParamTarget() { 344 345 return m_paramTarget; 346 } 347 348 /** 349 * Sets the value of the copymode parameter.<p> 350 * 351 * @param value the value of the copymode parameter 352 */ 353 public void setParamCopymode(String value) { 354 355 m_paramCopymode = value; 356 } 357 358 /** 359 * Sets the value of the "keeprights" parameter.<p> 360 * 361 * @param value the value of the "keeprights" parameter 362 */ 363 public void setParamKeeprights(String value) { 364 365 m_paramKeeprights = value; 366 } 367 368 /** 369 * Sets the value of the overwrite parameter.<p> 370 * 371 * @param paramOverwrite the value of the overwrite parameter 372 */ 373 public void setParamOverwrite(String paramOverwrite) { 374 375 m_paramOverwrite = paramOverwrite; 376 } 377 378 /** 379 * Sets the value of the target parameter.<p> 380 * 381 * @param value the value to set 382 */ 383 public void setParamTarget(String value) { 384 385 m_paramTarget = value; 386 } 387 388 /** 389 * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, jakarta.servlet.http.HttpServletRequest) 390 */ 391 @Override 392 protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) { 393 394 // fill the parameter values in the get/set methods 395 fillParamValues(request); 396 397 // check the required permissions to copy the resource 398 if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) { 399 // no write permissions for the resource, set cancel action to close dialog 400 setParamAction(DIALOG_CANCEL); 401 } 402 403 // set the dialog type 404 setParamDialogtype(DIALOG_TYPE); 405 // set the action for the JSP switch 406 if (DIALOG_TYPE.equals(getParamAction())) { 407 setAction(ACTION_COPY); 408 } else if (DIALOG_CONFIRMED.equals(getParamAction())) { 409 setAction(ACTION_CONFIRMED); 410 } else if (DIALOG_WAIT.equals(getParamAction())) { 411 setAction(ACTION_WAIT); 412 } else if (DIALOG_CANCEL.equals(getParamAction())) { 413 setAction(ACTION_CANCEL); 414 } else { 415 setAction(ACTION_DEFAULT); 416 // build title for copy dialog 417 setDialogTitle(Messages.GUI_COPY_RESOURCE_1, Messages.GUI_COPY_MULTI_2); 418 } 419 } 420 421 /** 422 * Performs the resource copying.<p> 423 * 424 * @return true, if the resource was copied, otherwise false 425 * @throws CmsException if copying is not successful 426 */ 427 @Override 428 protected boolean performDialogOperation() throws CmsException { 429 430 // check if the current resource is a folder for single operation 431 boolean isFolder = isOperationOnFolder(); 432 433 // on folder copy display "please wait" screen, not for simple file copy 434 if ((isMultiOperation() || isFolder) && !DIALOG_WAIT.equals(getParamAction())) { 435 // return false, this will trigger the "please wait" screen 436 return false; 437 } 438 439 // get the copy mode from request parameter value 440 CmsResourceCopyMode copyMode = CmsResource.COPY_PRESERVE_SIBLING; 441 try { 442 copyMode = CmsResourceCopyMode.valueOf(Integer.parseInt(getParamCopymode())); 443 } catch (Exception e) { 444 // can usually be ignored 445 if (LOG.isInfoEnabled()) { 446 LOG.info(e.getLocalizedMessage()); 447 } 448 } 449 450 // check the overwrite options 451 boolean overwrite = Boolean.valueOf(getParamOverwrite()).booleanValue(); 452 overwrite = ((isMultiOperation() && overwrite) || DIALOG_CONFIRMED.equals(getParamAction())); 453 454 // calculate the target name 455 String target = getParamTarget(); 456 if (target == null) { 457 target = ""; 458 } 459 460 String storedSiteRoot = null; 461 try { 462 // check if a site root was added to the target name 463 String sitePrefix = ""; 464 if (OpenCms.getSiteManager().getSiteRoot(target) != null) { 465 String siteRootFolder = getCms().getRequestContext().getSiteRoot(); 466 if (siteRootFolder.endsWith("/")) { 467 siteRootFolder = siteRootFolder.substring(0, siteRootFolder.length() - 1); 468 } 469 sitePrefix = siteRootFolder; 470 storedSiteRoot = getCms().getRequestContext().getSiteRoot(); 471 getCms().getRequestContext().setSiteRoot("/"); 472 } 473 474 Iterator<String> i = getResourceList().iterator(); 475 // iterate the resources to copy 476 while (i.hasNext()) { 477 String resName = i.next(); 478 try { 479 performSingleCopyOperation(resName, target, sitePrefix, copyMode, overwrite); 480 } catch (CmsException e) { 481 if (isMultiOperation()) { 482 // collect exceptions to create a detailed output 483 addMultiOperationException(e); 484 } else { 485 // for single operation, throw the exception immediately 486 throw e; 487 } 488 } 489 } 490 // check if exceptions occurred 491 checkMultiOperationException(Messages.get(), Messages.ERR_COPY_MULTI_0); 492 } finally { 493 // restore the site root 494 if (storedSiteRoot != null) { 495 getCms().getRequestContext().setSiteRoot(storedSiteRoot); 496 } 497 } 498 return true; 499 } 500 501 /** 502 * Performs the copy operation for a single VFS resource.<p> 503 * 504 * @param source the source VFS path 505 * @param target the target VFS path 506 * @param sitePrefix the site prefix 507 * @param copyMode the copy mode for siblings 508 * @param overwrite the overwrite flag 509 * 510 * @throws CmsException if copying the resource fails 511 */ 512 protected void performSingleCopyOperation( 513 String source, 514 String target, 515 String sitePrefix, 516 CmsResourceCopyMode copyMode, 517 boolean overwrite) 518 throws CmsException { 519 520 // calculate the target name 521 String finalTarget = CmsLinkManager.getAbsoluteUri(target, CmsResource.getParentFolder(source)); 522 523 if (finalTarget.equals(source) || (isMultiOperation() && finalTarget.startsWith(source))) { 524 throw new CmsVfsException(Messages.get().container(Messages.ERR_COPY_ONTO_ITSELF_1, finalTarget)); 525 } 526 527 try { 528 CmsResource res = getCms().readResource(finalTarget, CmsResourceFilter.ALL); 529 if (res.isFolder()) { 530 // target folder already exists, so we add the current folder name 531 if (!finalTarget.endsWith("/")) { 532 finalTarget += "/"; 533 } 534 finalTarget = finalTarget + CmsResource.getName(source); 535 } 536 } catch (CmsVfsResourceNotFoundException e) { 537 // target folder does not already exist, so target name is o.k. 538 if (LOG.isInfoEnabled()) { 539 LOG.info(e.getLocalizedMessage()); 540 } 541 } 542 543 // set the target parameter value 544 setParamTarget(finalTarget); 545 546 // delete existing target resource if selected or confirmed by the user 547 if (overwrite && getCms().existsResource(finalTarget)) { 548 checkLock(finalTarget); 549 getCms().deleteResource(finalTarget, CmsResource.DELETE_PRESERVE_SIBLINGS); 550 } 551 // copy the resource 552 getCms().copyResource(sitePrefix + source, finalTarget, copyMode); 553 } 554}