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.file.types; 029 030import org.opencms.db.CmsSecurityManager; 031import org.opencms.file.CmsDataNotImplementedException; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProperty; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.file.CmsVfsException; 037import org.opencms.file.CmsVfsResourceNotFoundException; 038import org.opencms.lock.CmsLockType; 039import org.opencms.main.CmsException; 040import org.opencms.main.CmsIllegalArgumentException; 041import org.opencms.main.OpenCms; 042import org.opencms.util.CmsFileUtil; 043import org.opencms.util.CmsStringUtil; 044 045import java.util.List; 046 047/** 048 * Resource type descriptor for the type "folder".<p> 049 * 050 * @since 6.0.0 051 */ 052public abstract class A_CmsResourceTypeFolderBase extends A_CmsResourceType { 053 054 /** Attribute to control shallow copying. */ 055 public static final String ATTR_SHALLOW_FOLDER_COPY = "shallow_folder_copy"; 056 057 /** The serial version id. */ 058 private static final long serialVersionUID = -698470184142645873L; 059 060 /** 061 * Default constructor, used to initialize member variables.<p> 062 */ 063 public A_CmsResourceTypeFolderBase() { 064 065 super(); 066 } 067 068 /** 069 * @see org.opencms.file.types.I_CmsResourceType#chtype(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, int) 070 */ 071 @Override 072 public void chtype(CmsObject cms, CmsSecurityManager securityManager, CmsResource filename, int newType) 073 throws CmsException, CmsDataNotImplementedException { 074 075 if (!OpenCms.getResourceManager().getResourceType(newType).isFolder()) { 076 // it is not possible to change the type of a folder to a file type 077 throw new CmsDataNotImplementedException( 078 Messages.get().container(Messages.ERR_CHTYPE_FOLDER_1, cms.getSitePath(filename))); 079 } 080 super.chtype(cms, securityManager, filename, newType); 081 } 082 083 /** 084 * @see org.opencms.file.types.I_CmsResourceType#copyResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, java.lang.String, CmsResource.CmsResourceCopyMode) 085 */ 086 @Override 087 public void copyResource( 088 CmsObject cms, 089 CmsSecurityManager securityManager, 090 CmsResource source, 091 String destination, 092 CmsResource.CmsResourceCopyMode siblingMode) 093 throws CmsIllegalArgumentException, CmsException { 094 095 // first validate the destination name 096 destination = validateFoldername(destination); 097 098 // collect all resources in the folder (but exclude deleted ones) 099 List<CmsResource> resources = securityManager.readChildResources( 100 cms.getRequestContext(), 101 source, 102 CmsResourceFilter.IGNORE_EXPIRATION, 103 true, 104 true); 105 106 // handle the folder itself 107 super.copyResource(cms, securityManager, source, destination, siblingMode); 108 Boolean shallow = (Boolean)(cms.getRequestContext().getAttribute(ATTR_SHALLOW_FOLDER_COPY)); 109 if ((shallow == null) || !shallow.booleanValue()) { 110 // now walk through all sub-resources in the folder 111 for (int i = 0; i < resources.size(); i++) { 112 CmsResource childResource = resources.get(i); 113 String childDestination = destination.concat(childResource.getName()); 114 // handle child resources 115 getResourceType( 116 childResource).copyResource(cms, securityManager, childResource, childDestination, siblingMode); 117 } 118 } 119 } 120 121 /** 122 * @see org.opencms.file.types.I_CmsResourceType#createResource(org.opencms.file.CmsObject, CmsSecurityManager, java.lang.String, byte[], List) 123 */ 124 @Override 125 public CmsResource createResource( 126 CmsObject cms, 127 CmsSecurityManager securityManager, 128 String resourcename, 129 byte[] content, 130 List<CmsProperty> properties) 131 throws CmsException { 132 133 resourcename = validateFoldername(resourcename); 134 return super.createResource(cms, securityManager, resourcename, content, properties); 135 } 136 137 /** 138 * @see org.opencms.file.types.I_CmsResourceType#getLoaderId() 139 */ 140 @Override 141 public int getLoaderId() { 142 143 // folders have no loader 144 return -1; 145 } 146 147 /** 148 * @see org.opencms.file.types.A_CmsResourceType#isFolder() 149 */ 150 @Override 151 public boolean isFolder() { 152 153 return true; 154 } 155 156 /** 157 * @see org.opencms.file.types.I_CmsResourceType#moveResource(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, java.lang.String) 158 */ 159 @Override 160 public void moveResource( 161 CmsObject cms, 162 CmsSecurityManager securityManager, 163 CmsResource resource, 164 String destination) 165 throws CmsException, CmsIllegalArgumentException { 166 167 String dest = cms.getRequestContext().addSiteRoot(destination); 168 if (!CmsResource.isFolder(dest)) { 169 // ensure folder name end's with a / (required for the following comparison) 170 dest = dest.concat("/"); 171 } 172 if (resource.getRootPath().equals(dest)) { 173 // move to target with same name is not allowed 174 throw new CmsVfsException( 175 org.opencms.file.Messages.get().container(org.opencms.file.Messages.ERR_MOVE_SAME_NAME_1, destination)); 176 } 177 if (dest.startsWith(resource.getRootPath())) { 178 // move of folder inside itself is not allowed 179 throw new CmsVfsException( 180 org.opencms.file.Messages.get().container( 181 org.opencms.file.Messages.ERR_MOVE_SAME_FOLDER_2, 182 cms.getSitePath(resource), 183 destination)); 184 } 185 186 // check the destination 187 try { 188 String destinationWithoutTrailingSlash = dest; 189 if (destinationWithoutTrailingSlash.length() > 1) { 190 // we also want to catch the case where a file (not folder) with the destination path already exists 191 destinationWithoutTrailingSlash = CmsFileUtil.removeTrailingSeparator(dest); 192 } 193 securityManager.readResource(cms.getRequestContext(), destinationWithoutTrailingSlash, CmsResourceFilter.ALL); 194 throw new CmsVfsException( 195 org.opencms.file.Messages.get().container( 196 org.opencms.file.Messages.ERR_OVERWRITE_RESOURCE_2, 197 cms.getRequestContext().removeSiteRoot(resource.getRootPath()), 198 destinationWithoutTrailingSlash)); 199 } catch (CmsVfsResourceNotFoundException e) { 200 // ok 201 } 202 203 // first validate the destination name 204 dest = validateFoldername(dest); 205 String targetName = CmsResource.getName(destination).replace("/", ""); 206 CmsResource.checkResourceName(targetName); 207 208 securityManager.moveResource(cms.getRequestContext(), resource, dest); 209 } 210 211 /** 212 * @see org.opencms.file.types.I_CmsResourceType#replaceResource(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, int, byte[], List) 213 */ 214 @Override 215 public void replaceResource( 216 CmsObject cms, 217 CmsSecurityManager securityManager, 218 CmsResource resource, 219 int type, 220 byte[] content, 221 List<CmsProperty> properties) 222 throws CmsException, CmsDataNotImplementedException { 223 224 if (type != getTypeId()) { 225 // it is not possible to replace a folder with a different type 226 throw new CmsDataNotImplementedException( 227 Messages.get().container(Messages.ERR_REPLACE_RESOURCE_FOLDER_1, cms.getSitePath(resource))); 228 } 229 // properties of a folder can be replaced, content is ignored 230 super.replaceResource(cms, securityManager, resource, getTypeId(), null, properties); 231 } 232 233 /** 234 * @see org.opencms.file.types.I_CmsResourceType#setDateExpired(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, long, boolean) 235 */ 236 @Override 237 public void setDateExpired( 238 CmsObject cms, 239 CmsSecurityManager securityManager, 240 CmsResource resource, 241 long dateLastModified, 242 boolean recursive) 243 throws CmsException { 244 245 // handle the folder itself 246 super.setDateExpired(cms, securityManager, resource, dateLastModified, recursive); 247 248 if (recursive) { 249 // collect all resources in the folder (but exclude deleted ones) 250 List<CmsResource> resources = securityManager.readChildResources( 251 cms.getRequestContext(), 252 resource, 253 CmsResourceFilter.IGNORE_EXPIRATION, 254 true, 255 true); 256 257 // now walk through all sub-resources in the folder 258 for (int i = 0; i < resources.size(); i++) { 259 CmsResource childResource = resources.get(i); 260 // handle child resources 261 getResourceType( 262 childResource).setDateExpired(cms, securityManager, childResource, dateLastModified, recursive); 263 } 264 } 265 } 266 267 /** 268 * @see org.opencms.file.types.I_CmsResourceType#setDateLastModified(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, long, boolean) 269 */ 270 @Override 271 public void setDateLastModified( 272 CmsObject cms, 273 CmsSecurityManager securityManager, 274 CmsResource resource, 275 long dateLastModified, 276 boolean recursive) 277 throws CmsException { 278 279 // handle the folder itself 280 super.setDateLastModified(cms, securityManager, resource, dateLastModified, recursive); 281 282 if (recursive) { 283 // collect all resources in the folder (but exclude deleted ones) 284 List<CmsResource> resources = securityManager.readChildResources( 285 cms.getRequestContext(), 286 resource, 287 CmsResourceFilter.IGNORE_EXPIRATION, 288 true, 289 true); 290 291 // now walk through all sub-resources in the folder 292 for (int i = 0; i < resources.size(); i++) { 293 CmsResource childResource = resources.get(i); 294 // handle child resources 295 getResourceType(childResource).setDateLastModified( 296 cms, 297 securityManager, 298 childResource, 299 dateLastModified, 300 recursive); 301 } 302 } 303 } 304 305 /** 306 * @see org.opencms.file.types.I_CmsResourceType#setDateReleased(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, long, boolean) 307 */ 308 @Override 309 public void setDateReleased( 310 CmsObject cms, 311 CmsSecurityManager securityManager, 312 CmsResource resource, 313 long dateLastModified, 314 boolean recursive) 315 throws CmsException { 316 317 // handle the folder itself 318 super.setDateReleased(cms, securityManager, resource, dateLastModified, recursive); 319 320 if (recursive) { 321 // collect all resources in the folder (but exclude deleted ones) 322 List<CmsResource> resources = securityManager.readChildResources( 323 cms.getRequestContext(), 324 resource, 325 CmsResourceFilter.IGNORE_EXPIRATION, 326 true, 327 true); 328 329 // now walk through all sub-resources in the folder 330 for (int i = 0; i < resources.size(); i++) { 331 CmsResource childResource = resources.get(i); 332 // handle child resources 333 getResourceType( 334 childResource).setDateReleased(cms, securityManager, childResource, dateLastModified, recursive); 335 } 336 } 337 } 338 339 /** 340 * @see org.opencms.file.types.A_CmsResourceType#undelete(org.opencms.file.CmsObject, org.opencms.db.CmsSecurityManager, org.opencms.file.CmsResource, boolean) 341 */ 342 @Override 343 public void undelete(CmsObject cms, CmsSecurityManager securityManager, CmsResource resource, boolean recursive) 344 throws CmsException { 345 346 // handle the folder itself 347 super.undelete(cms, securityManager, resource, recursive); 348 349 if (recursive) { 350 // collect all resources in the folder (but exclude deleted ones) 351 List<CmsResource> resources = securityManager.readChildResources( 352 cms.getRequestContext(), 353 resource, 354 CmsResourceFilter.ALL, 355 true, 356 true); 357 358 // now walk through all sub-resources in the folder 359 for (int i = 0; i < resources.size(); i++) { 360 CmsResource childResource = resources.get(i); 361 // handle child resources 362 getResourceType(childResource).undelete(cms, securityManager, childResource, recursive); 363 } 364 } 365 } 366 367 /** 368 * @see org.opencms.file.types.I_CmsResourceType#undoChanges(org.opencms.file.CmsObject, CmsSecurityManager, CmsResource, CmsResource.CmsResourceUndoMode) 369 */ 370 @Override 371 public void undoChanges( 372 CmsObject cms, 373 CmsSecurityManager securityManager, 374 CmsResource resource, 375 CmsResource.CmsResourceUndoMode mode) 376 throws CmsException { 377 378 boolean recursive = mode.isRecursive(); 379 if (mode == CmsResource.UNDO_MOVE_CONTENT) { 380 // undo move only? 381 String originalPath = securityManager.resourceOriginalPath(cms.getRequestContext(), resource); 382 if (originalPath.equals(resource.getRootPath())) { 383 // resource not moved 384 recursive = false; 385 } 386 } 387 388 List<CmsResource> resources = null; 389 if (recursive) { // recursive? 390 // collect all resources in the folder (including deleted ones) 391 resources = securityManager.readChildResources( 392 cms.getRequestContext(), 393 resource, 394 CmsResourceFilter.ALL, 395 true, 396 true); 397 } 398 399 // handle the folder itself, undo move op 400 super.undoChanges(cms, securityManager, resource, mode); 401 402 // the folder may have been moved back to its original position 403 CmsResource undoneResource2 = securityManager.readResource( 404 cms.getRequestContext(), 405 resource.getStructureId(), 406 CmsResourceFilter.ALL); 407 boolean isMoved = !undoneResource2.getRootPath().equals(resource.getRootPath()); 408 409 if (recursive && (resources != null)) { // recursive? 410 // now walk through all sub-resources in the folder, and undo first 411 for (int i = 0; i < resources.size(); i++) { 412 CmsResource childResource = resources.get(i); 413 414 I_CmsResourceType type = getResourceType(childResource); 415 416 if (isMoved) { 417 securityManager.lockResource(cms.getRequestContext(), childResource, CmsLockType.EXCLUSIVE); 418 } 419 if (!childResource.getState().isNew()) { 420 // can not use undo for new resources 421 if (childResource.isFolder()) { 422 // recurse into this method for subfolders 423 type.undoChanges(cms, securityManager, childResource, mode); 424 } else if (!childResource.getState().isNew()) { 425 // undo changes for changed files 426 securityManager.undoChanges(cms.getRequestContext(), childResource, mode); 427 } else { 428 // undo move for new files? move with the folder 429 if (mode.isUndoMove()) { 430 String newPath = cms.getRequestContext().removeSiteRoot( 431 securityManager.readResource( 432 cms.getRequestContext(), 433 resource.getStructureId(), 434 CmsResourceFilter.ALL).getRootPath() + childResource.getName()); 435 type.moveResource(cms, securityManager, childResource, newPath); 436 } 437 } 438 } else if (isMoved) { 439 // we still need to update the resource path for new resources 440 String newPath = cms.getRequestContext().removeSiteRoot( 441 securityManager.readResource( 442 cms.getRequestContext(), 443 resource.getStructureId(), 444 CmsResourceFilter.ALL).getRootPath() + childResource.getName()); 445 type.moveResource(cms, securityManager, childResource, newPath); 446 } 447 } 448 449 // now iterate again all sub-resources in the folder, and actualize the relations 450 for (int i = 0; i < resources.size(); i++) { 451 CmsResource childResource = resources.get(i); 452 updateRelationForUndo(cms, securityManager, childResource); 453 } 454 } 455 } 456 457 /** 458 * Checks if there are at least one character in the folder name, 459 * also ensures that it starts and ends with a '/'.<p> 460 * 461 * @param resourcename folder name to check (complete path) 462 * 463 * @return the validated folder name 464 * 465 * @throws CmsIllegalArgumentException if the folder name is empty or <code>null</code> 466 */ 467 private String validateFoldername(String resourcename) throws CmsIllegalArgumentException { 468 469 if (CmsStringUtil.isEmpty(resourcename)) { 470 throw new CmsIllegalArgumentException( 471 org.opencms.db.Messages.get().container(org.opencms.db.Messages.ERR_BAD_RESOURCENAME_1, resourcename)); 472 } 473 if (!CmsResource.isFolder(resourcename)) { 474 resourcename = resourcename.concat("/"); 475 } 476 if (resourcename.charAt(0) != '/') { 477 resourcename = "/".concat(resourcename); 478 } 479 return resourcename; 480 } 481}