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