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.staticexport;
029
030import org.opencms.db.CmsPublishedResource;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.file.CmsVfsResourceNotFoundException;
035import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
036import org.opencms.file.types.I_CmsResourceType;
037import org.opencms.loader.CmsXmlContainerPageLoader;
038import org.opencms.loader.CmsXmlContentLoader;
039import org.opencms.loader.I_CmsResourceLoader;
040import org.opencms.main.CmsException;
041import org.opencms.main.CmsLog;
042import org.opencms.main.OpenCms;
043import org.opencms.relations.CmsRelation;
044import org.opencms.relations.CmsRelationFilter;
045import org.opencms.report.I_CmsReport;
046import org.opencms.security.CmsPermissionViolationException;
047import org.opencms.security.CmsSecurityException;
048import org.opencms.util.CmsFileUtil;
049import org.opencms.util.CmsStringUtil;
050import org.opencms.util.CmsUUID;
051
052import java.io.File;
053import java.io.FileFilter;
054import java.util.ArrayList;
055import java.util.Collection;
056import java.util.Collections;
057import java.util.HashSet;
058import java.util.Iterator;
059import java.util.List;
060import java.util.Set;
061
062import org.apache.commons.logging.Log;
063
064/**
065 * Abstract base implementation for the <code>{@link I_CmsStaticExportHandler}</code> interface.<p>
066 *
067 * This class provides several util methods to be used by static export handlers.
068 *
069 * @since 6.1.7
070 *
071 * @see I_CmsStaticExportHandler
072 *
073 */
074public abstract class A_CmsStaticExportHandler implements I_CmsStaticExportHandler {
075
076    /**
077     * Implements the file filter used to remove variants with parameters of a base file.<p>
078     */
079    private static class PrefixFileFilter implements FileFilter {
080
081        /** The extension. */
082        private String m_baseExtension;
083
084        /** The base file. */
085        private String m_baseName;
086
087        /**
088         * Creates a new instance of PrefixFileFilter.<p>
089         *
090         * @param baseFile the base file to compare with.
091         */
092        public PrefixFileFilter(File baseFile) {
093
094            String fileName = baseFile.getName();
095            m_baseExtension = CmsFileUtil.getExtension(fileName);
096            m_baseName = fileName + "_";
097        }
098
099        /**
100         * Accepts the given file if its name starts with the name of of the base file (without extension)
101         * and ends with the extension.<p>
102         *
103         * @see java.io.FileFilter#accept(java.io.File)
104         */
105        public boolean accept(File f) {
106
107            return f.getName().startsWith(m_baseName) && f.getName().endsWith(m_baseExtension);
108        }
109    }
110
111    /** The log object for this class. */
112    private static final Log LOG = CmsLog.getLog(A_CmsStaticExportHandler.class);
113
114    /** Indicates if this content handler is busy. */
115    protected boolean m_busy;
116
117    /**
118     * @see org.opencms.staticexport.I_CmsStaticExportHandler#isBusy()
119     */
120    public boolean isBusy() {
121
122        return m_busy;
123    }
124
125    /**
126     * @see org.opencms.staticexport.I_CmsStaticExportHandler#performEventPublishProject(org.opencms.util.CmsUUID, org.opencms.report.I_CmsReport)
127     */
128    public abstract void performEventPublishProject(CmsUUID publishHistoryId, I_CmsReport report);
129
130    /**
131     * Scrubs all files from the export folder that might have been changed,
132     * so that the export is newly created after the next request to the resource.<p>
133     *
134     * @param publishHistoryId id of the last published project
135     *
136     * @return the list of {@link CmsPublishedResource} objects to export
137     */
138    public List<CmsPublishedResource> scrubExportFolders(CmsUUID publishHistoryId) {
139
140        if (LOG.isDebugEnabled()) {
141            LOG.debug(Messages.get().getBundle().key(Messages.LOG_SCRUBBING_EXPORT_FOLDERS_1, publishHistoryId));
142        }
143
144        Set<String> scrubbedFolders = new HashSet<String>();
145        Set<String> scrubbedFiles = new HashSet<String>();
146
147        // get a export user cms context
148        CmsObject cms;
149        try {
150            // this will always use the root site
151            cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
152        } catch (CmsException e) {
153            // this should never happen
154            LOG.error(Messages.get().getBundle().key(Messages.LOG_INIT_FAILED_0), e);
155            return Collections.emptyList();
156        }
157
158        List<CmsPublishedResource> publishedResources;
159        try {
160            publishedResources = cms.readPublishedResources(publishHistoryId);
161        } catch (CmsException e) {
162            LOG.error(
163                Messages.get().getBundle().key(Messages.LOG_READING_CHANGED_RESOURCES_FAILED_1, publishHistoryId),
164                e);
165            return Collections.emptyList();
166        }
167
168        publishedResources = addMovedLinkSources(cms, publishedResources);
169
170        // now iterate the actual resources to be exported
171        Iterator<CmsPublishedResource> itPubRes = publishedResources.iterator();
172        while (itPubRes.hasNext()) {
173            CmsPublishedResource res = itPubRes.next();
174            if (res.getState().isUnchanged()) {
175                // unchanged resources don't need to be deleted
176                continue;
177            }
178
179            scrubResource(cms, res, scrubbedFolders, scrubbedFiles);
180        }
181        return publishedResources;
182    }
183
184    /**
185     * Add the link sources of moved resources to the list of published resources.<p>
186     *
187     * @param cms the cms context
188     * @param publishedResources the published resources
189     *
190     * @return the list of published resources included the link sources of moved resources
191     */
192    protected List<CmsPublishedResource> addMovedLinkSources(
193        CmsObject cms,
194        List<CmsPublishedResource> publishedResources) {
195
196        long timer = System.currentTimeMillis();
197        if (LOG.isDebugEnabled()) {
198            LOG.debug(Messages.get().getBundle().key(Messages.LOG_SCRUB_EXPORT_START_MOVED_SOURCES_0));
199        }
200        publishedResources = new ArrayList<CmsPublishedResource>(publishedResources);
201        Set<String> pubResources = new HashSet<String>(publishedResources.size());
202        // this is needed since the CmsPublishedResource#equals(Object) method just compares ids and not paths
203        // and with moved files you have 2 entries with the same id and different paths...
204        for (CmsPublishedResource pubRes : publishedResources) {
205            pubResources.add(pubRes.getRootPath());
206        }
207        boolean modified = true;
208        // until no more resources are added
209        while (modified) {
210            modified = false;
211            Iterator<CmsPublishedResource> itPrePubRes = new ArrayList<CmsPublishedResource>(
212                publishedResources).iterator();
213            while (itPrePubRes.hasNext()) {
214                CmsPublishedResource res = itPrePubRes.next();
215                if (res.getMovedState() != CmsPublishedResource.STATE_MOVED_DESTINATION) {
216                    // handle only resources that are destination of move operations
217                    continue;
218                }
219                List<CmsRelation> relations = null;
220                try {
221                    // get all link sources to this resource
222                    relations = cms.getRelationsForResource(
223                        cms.getRequestContext().removeSiteRoot(res.getRootPath()),
224                        CmsRelationFilter.SOURCES);
225                } catch (CmsException e) {
226                    // should never happen
227                    if (LOG.isErrorEnabled()) {
228                        LOG.error(e.getLocalizedMessage(), e);
229                    }
230                }
231                if ((relations == null) || relations.isEmpty()) {
232                    // continue with next resource if no link sources found
233                    continue;
234                }
235                Iterator<CmsRelation> itRelations = relations.iterator();
236                while (itRelations.hasNext()) {
237                    CmsRelation relation = itRelations.next();
238                    CmsPublishedResource source = null;
239                    try {
240                        // get the link source
241                        source = new CmsPublishedResource(relation.getSource(cms, CmsResourceFilter.ALL));
242                    } catch (CmsException e) {
243                        // should never happen
244                        if (LOG.isWarnEnabled()) {
245                            LOG.warn(e.getLocalizedMessage());
246                        }
247                    }
248                    if ((source == null) || pubResources.contains(source.getRootPath())) {
249                        // continue if the link source could not been retrieved or if the list already contains it
250                        continue;
251                    }
252                    // add it, and set the modified flag to give it another round
253                    modified = true;
254                    pubResources.add(source.getRootPath());
255                    publishedResources.add(source);
256                }
257            }
258        }
259        if (LOG.isDebugEnabled()) {
260            LOG.debug(
261                Messages.get().getBundle().key(
262                    Messages.LOG_SCRUB_EXPORT_FINISH_MOVED_SOURCES_1,
263                    (System.currentTimeMillis() - timer) + ""));
264        }
265        return publishedResources;
266    }
267
268    /**
269     * Returns a list of related files to purge.<p>
270     *
271     * @param exportFileName the previous exported rfs filename (already purged)
272     * @param vfsName the vfs name of the resource (to be used to compute more sofisticated sets of related files to purge
273     *
274     * @return a list of related files to purge
275     */
276    protected abstract List<File> getRelatedFilesToPurge(String exportFileName, String vfsName);
277
278    /**
279     * Returns a list containing the root paths of all siblings of a resource.<p>
280     *
281     * @param cms the export user context
282     * @param resPath the path of the resource to get the siblings for
283     *
284     * @return a list containing the root paths of all siblings of a resource
285     */
286    protected List<String> getSiblingsList(CmsObject cms, String resPath) {
287
288        List<String> siblings = new ArrayList<String>();
289        try {
290            List<CmsResource> li = cms.readSiblings(resPath, CmsResourceFilter.ALL);
291            for (int i = 0, l = li.size(); i < l; i++) {
292                String vfsName = (li.get(i)).getRootPath();
293                siblings.add(vfsName);
294            }
295        } catch (CmsVfsResourceNotFoundException e) {
296            // resource not found, probably because the export user has no read permission on the resource, ignore
297        } catch (CmsSecurityException e) {
298            // security exception, probably because the export user has no read permission on the resource, ignore
299        } catch (CmsException e) {
300            // ignore, nothing to do about this
301            if (LOG.isWarnEnabled()) {
302                LOG.warn(Messages.get().getBundle().key(Messages.LOG_FETCHING_SIBLINGS_FAILED_1, resPath), e);
303            }
304        }
305        if (!siblings.contains(resPath)) {
306            // always add the resource itself, this has to be done because if the resource was
307            // deleted during publishing, the sibling lookup above will produce no results
308            siblings.add(resPath);
309        }
310        return siblings;
311    }
312
313    /**
314     * Deletes the given file from the RFS if it exists,
315     * also deletes all parameter variations of the file.<p>
316     *
317     * @param rfsFilePath the path of the RFS file to delete
318     * @param vfsName the VFS name of the file to delete (required for logging)
319     * @param cms the CMS context
320     */
321    protected void purgeFile(String rfsFilePath, String vfsName, CmsObject cms) {
322
323        File rfsFile = new File(rfsFilePath);
324
325        // first delete the base file
326        deleteFile(rfsFile, vfsName);
327        deleteStaticExportPublishedResource(rfsFile, vfsName, cms);
328        // now delete the file parameter variations
329        // get the parent folder
330        File parent = rfsFile.getParentFile();
331        if (parent != null) {
332            // list all files in the parent folder that are variations of the base file
333            File[] paramVariants = parent.listFiles(new PrefixFileFilter(rfsFile));
334            if (paramVariants != null) {
335                for (int v = 0; v < paramVariants.length; v++) {
336                    deleteFile(paramVariants[v], vfsName);
337                    deleteStaticExportPublishedResource(paramVariants[v], vfsName, cms);
338                }
339            }
340        }
341    }
342
343    /**
344     * Scrub a single file or folder.<p>
345     *
346     * @param cms an export cms object
347     * @param res the resource to check
348     * @param scrubbedFolders the list of already scrubbed folders
349     * @param scrubbedFiles the list of already scrubbed files
350     */
351    protected void scrubResource(
352        CmsObject cms,
353        CmsPublishedResource res,
354        Set<String> scrubbedFolders,
355        Set<String> scrubbedFiles) {
356
357        long timer = System.currentTimeMillis();
358        if (LOG.isDebugEnabled()) {
359            LOG.debug(Messages.get().getBundle().key(Messages.LOG_SCRUB_EXPORT_START_RESOURCE_1, res.getRootPath()));
360        }
361        try {
362            // ensure all siblings are scrubbed if the resource has one
363            String resPath = cms.getRequestContext().removeSiteRoot(res.getRootPath());
364            List<String> siblings = getSiblingsList(cms, resPath);
365
366            for (String vfsName : siblings) {
367
368                // get the link name for the published file
369                String rfsName = OpenCms.getStaticExportManager().getRfsName(cms, vfsName);
370                if (LOG.isDebugEnabled()) {
371                    LOG.debug(Messages.get().getBundle().key(Messages.LOG_CHECKING_STATIC_EXPORT_2, vfsName, rfsName));
372                }
373                if (rfsName.startsWith(OpenCms.getStaticExportManager().getRfsPrefix(vfsName))
374                    && (!scrubbedFiles.contains(rfsName))
375                    && (!scrubbedFolders.contains(CmsResource.getFolderPath(rfsName)))) {
376
377                    if (res.isFolder()) {
378                        if (res.getState().isDeleted()) {
379                            String exportFolderName = CmsFileUtil.normalizePath(
380                                OpenCms.getStaticExportManager().getExportPath(vfsName)
381                                    + rfsName.substring(
382                                        OpenCms.getStaticExportManager().getRfsPrefix(vfsName).length()));
383                            try {
384                                File exportFolder = new File(exportFolderName);
385                                // check if export folder exists, if so delete it
386                                if (exportFolder.exists() && exportFolder.canWrite()) {
387                                    CmsFileUtil.purgeDirectory(exportFolder);
388                                    // write log message
389                                    if (LOG.isInfoEnabled()) {
390                                        LOG.info(
391                                            Messages.get().getBundle().key(
392                                                Messages.LOG_FOLDER_DELETED_1,
393                                                exportFolderName));
394                                    }
395                                    scrubbedFolders.add(rfsName);
396                                    continue;
397                                }
398                            } catch (Throwable t) {
399                                // ignore, nothing to do about this
400                                if (LOG.isWarnEnabled()) {
401                                    LOG.warn(
402                                        Messages.get().getBundle().key(
403                                            Messages.LOG_FOLDER_DELETION_FAILED_2,
404                                            vfsName,
405                                            exportFolderName));
406                                }
407                            }
408                        }
409                    } else {
410                        // check if the file is the default file of the folder
411                        try {
412                            CmsResource defaultFile = cms.readDefaultFile(CmsResource.getFolderPath(vfsName));
413                            if (defaultFile != null) {
414                                String defaultfilePath = cms.getRequestContext().removeSiteRoot(
415                                    defaultFile.getRootPath());
416                                if (vfsName.equals(defaultfilePath)) {
417                                    // this is the default file, remove it additionally if present
418                                    String rfsNameDefault = CmsResource.getFolderPath(rfsName)
419                                        + CmsStaticExportManager.EXPORT_DEFAULT_FILE;
420                                    String rfsExportFileName = CmsFileUtil.normalizePath(
421                                        OpenCms.getStaticExportManager().getExportPath(vfsName)
422                                            + rfsNameDefault.substring(
423                                                OpenCms.getStaticExportManager().getRfsPrefix(vfsName).length()));
424
425                                    purgeFile(rfsExportFileName, vfsName, cms);
426                                }
427                            }
428                        } catch (CmsException e) {
429                            // failed to determine default file
430                        }
431                    }
432
433                    // add index_export.html or the index.html to the folder name
434                    rfsName = OpenCms.getStaticExportManager().addDefaultFileNameToFolder(rfsName, res.isFolder());
435                    if (LOG.isDebugEnabled()) {
436                        LOG.debug(Messages.get().getBundle().key(Messages.LOG_RFSNAME_1, rfsName));
437                    }
438                    String rfsExportFileName = CmsFileUtil.normalizePath(
439                        OpenCms.getStaticExportManager().getExportPath(vfsName)
440                            + rfsName.substring(OpenCms.getStaticExportManager().getRfsPrefix(vfsName).length()));
441                    if (LOG.isDebugEnabled()) {
442                        LOG.debug(Messages.get().getBundle().key(Messages.LOG_EXPORT_RFSNAME_1, rfsName));
443                    }
444                    // purge related files
445                    List<File> relFilesToPurge = getRelatedFilesToPurge(rfsExportFileName, vfsName);
446                    purgeFiles(relFilesToPurge, vfsName, scrubbedFiles, cms);
447
448                    if (!res.isFolder()) {
449                        I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(res.getType());
450                        I_CmsResourceLoader resLoader = OpenCms.getResourceManager().getLoader(resType.getLoaderId());
451                        if ((resLoader instanceof CmsXmlContentLoader)
452                            && !(resLoader instanceof CmsXmlContainerPageLoader)) {
453
454                            // only execute for XML content that are no container pages
455                            List<File> detailPageFiles = getDetailPageFiles(cms, res, vfsName);
456                            purgeFiles(detailPageFiles, vfsName, scrubbedFiles, cms);
457                            if (LOG.isDebugEnabled()) {
458                                LOG.debug(Messages.get().getBundle().key(Messages.LOG_PURGED_DETAILPAGES_0));
459                            }
460                            List<File> referencingContainerPages = getContainerPagesToPurge(cms, res.getStructureId());
461                            purgeFiles(referencingContainerPages, vfsName, scrubbedFiles, cms);
462                            if (LOG.isDebugEnabled()) {
463                                LOG.debug(Messages.get().getBundle().key(Messages.LOG_PURGED_CONTAINERPAGES_0));
464                            }
465                        }
466                    }
467                    // purge the file itself
468                    purgeFile(rfsExportFileName, vfsName, cms);
469                    scrubbedFiles.add(rfsName);
470                }
471            }
472        } catch (Throwable e) {
473            LOG.error(e.getLocalizedMessage(), e);
474        }
475        if (LOG.isDebugEnabled()) {
476            LOG.debug(
477                Messages.get().getBundle().key(
478                    Messages.LOG_SCRUB_EXPORT_FINISH_RESOURCE_2,
479                    res.getRootPath(),
480                    (System.currentTimeMillis() - timer) + ""));
481        }
482    }
483
484    /**
485     * Deletes the given file from the RFS, with error handling and logging.<p>
486     *
487     * If the parent folder of the file is empty after deletion, the parent folder
488     * is deleted also.<p>
489     *
490     * @param file the file to delete
491     * @param vfsName the VFS name of the file (required for logging)
492     */
493    private void deleteFile(File file, String vfsName) {
494
495        try {
496            if (file.exists() && file.canWrite()) {
497                file.delete();
498                // write log message
499                if (LOG.isInfoEnabled()) {
500                    LOG.info(Messages.get().getBundle().key(Messages.LOG_FILE_DELETED_1, getRfsName(file, vfsName)));
501                }
502                // delete the parent folder if it is empty (don't do this recursive)
503                File parent = new File(file.getParent());
504                if (parent.listFiles().length == 0) {
505                    if (parent.canWrite()) {
506                        parent.delete();
507                        if (LOG.isInfoEnabled()) {
508                            LOG.info(
509                                Messages.get().getBundle().key(Messages.LOG_FILE_DELETED_1, getRfsName(file, vfsName)));
510                        }
511                    }
512                }
513            }
514        } catch (Throwable t) {
515            // ignore, nothing to do about this
516            if (LOG.isWarnEnabled()) {
517                LOG.warn(
518                    Messages.get().getBundle().key(Messages.LOG_FILE_DELETION_FAILED_1, getRfsName(file, vfsName)),
519                    t);
520            }
521        }
522    }
523
524    /**
525     * Deletes the RFS file entry in the CMS_STATICEXPORT_LINKS table.<p>
526     *
527     * @param file the file to delete
528     * @param vfsName the VFS name of the file (required for logging)
529     * @param cms the current CMS context
530     */
531    private void deleteStaticExportPublishedResource(File file, String vfsName, CmsObject cms) {
532
533        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
534        String rfsName = getRfsName(file, vfsName);
535        String rfsExportFileName = rfsName.substring(manager.getRfsPrefix(vfsName).length());
536        try {
537            cms.deleteStaticExportPublishedResource(
538                rfsExportFileName,
539                CmsStaticExportManager.EXPORT_LINK_WITH_PARAMETER,
540                null);
541        } catch (CmsException e) {
542            if (LOG.isInfoEnabled()) {
543                LOG.info(Messages.get().getBundle().key(Messages.LOG_PURGED_RFSFILE_1, rfsExportFileName));
544            }
545        }
546    }
547
548    /**
549     * Gets the exported container pages that should be purged when the content with the given id is published.<p>
550     *
551     * @param cms the current CMS context
552     * @param targetId the structure id of the published content
553     *
554     * @return the list of files to purge
555     */
556    private List<File> getContainerPagesToPurge(CmsObject cms, CmsUUID targetId) {
557
558        try {
559            List<File> purgePages = new ArrayList<File>();
560            List<CmsRelation> relations = cms.readRelations(CmsRelationFilter.relationsToStructureId(targetId));
561            for (CmsRelation relation : relations) {
562                CmsResource source = null;
563                try {
564                    source = relation.getSource(cms, CmsResourceFilter.ALL);
565                } catch (CmsPermissionViolationException e) {
566                    // export user can't read the file
567                    continue;
568                }
569                if (CmsResourceTypeXmlContainerPage.isContainerPage(source)) {
570
571                    // purge pages directly containing the content
572
573                    String vfsName = source.getRootPath();
574                    String rfsName = OpenCms.getStaticExportManager().getRfsName(cms, vfsName);
575                    String exportPath = CmsFileUtil.normalizePath(
576                        OpenCms.getStaticExportManager().getExportPath(vfsName));
577                    String rfsExportFileName = exportPath
578                        + rfsName.substring(OpenCms.getStaticExportManager().getRfsPrefix(vfsName).length());
579                    File file = new File(rfsExportFileName);
580                    purgePages.add(file);
581                } else if (targetId.equals(source.getStructureId())
582                    && OpenCms.getResourceManager().getResourceType(source.getTypeId()).getTypeName().equals(
583                        CmsResourceTypeXmlContainerPage.GROUP_CONTAINER_TYPE_NAME)) {
584                            LOG.warn(
585                                Messages.get().getBundle().key(
586                                    Messages.LOG_WARN_ELEMENT_GROUP_REFERENCES_SELF_1,
587                                    source.getRootPath()));
588                        } else
589                    if (OpenCms.getResourceManager().getResourceType(source.getTypeId()).getTypeName().equals(
590                        CmsResourceTypeXmlContainerPage.GROUP_CONTAINER_TYPE_NAME)) {
591                            // purge pages containing group containers containing the content
592
593                            purgePages.addAll(getContainerPagesToPurge(cms, source.getStructureId()));
594                        }
595            }
596            return purgePages;
597        } catch (CmsException e) {
598            LOG.error(e.getLocalizedMessage(), e);
599            return Collections.emptyList();
600        }
601    }
602
603    /**
604     * Gets the exported detail page files which need to be purged.<p>
605     *
606     * @param cms the current cms context
607     * @param res the published resource
608     * @param vfsName the vfs name
609     *
610     * @return the list of files to be purged
611     */
612    private List<File> getDetailPageFiles(CmsObject cms, CmsPublishedResource res, String vfsName) {
613
614        List<File> files = new ArrayList<File>();
615        try {
616            if ((OpenCms.getRunLevel() < OpenCms.RUNLEVEL_4_SERVLET_ACCESS)) {
617                // Accessing the ADE manager during setup may not work.
618                // also folders can not be displayed in detail pages
619                return files;
620            }
621            List<String> urlNames = cms.getAllUrlNames(res.getStructureId());
622            Collection<String> detailpages = OpenCms.getADEManager().getDetailPageHandler().getAllDetailPages(
623                cms,
624                res.getType());
625            for (String urlName : urlNames) {
626                for (String detailPage : detailpages) {
627                    String rfsName = CmsStringUtil.joinPaths(
628                        OpenCms.getStaticExportManager().getRfsName(cms, detailPage),
629                        urlName,
630                        CmsStaticExportManager.DEFAULT_FILE);
631                    String rfsExportFileName = CmsFileUtil.normalizePath(
632                        OpenCms.getStaticExportManager().getExportPath(vfsName)
633                            + rfsName.substring(OpenCms.getStaticExportManager().getRfsPrefix(vfsName).length()));
634                    File file = new File(rfsExportFileName);
635                    if (file.exists() && !files.contains(file)) {
636                        files.add(file);
637                    }
638                }
639            }
640        } catch (CmsException e) {
641            LOG.error(e.getLocalizedMessage(), e);
642        }
643        return files;
644
645    }
646
647    /**
648     * Returns a list of files which are referenced by a container page.<p>
649     *
650     * @param cms the current cms object
651     * @param res the originally resource to purge (the container page)
652     * @param vfsName the vfs name of the originally resource to purge
653     */
654    //    private List<File> getRelatedSitemapFiles(CmsObject cms, CmsPublishedResource res, String vfsName) {
655    //
656    //        List<File> files = new ArrayList<File>();
657    //        try {
658    //            if (res.getType() == CmsResourceTypeXmlContainerPage.getContainerPageTypeId()) {
659    //                List<CmsInternalSitemapEntry> entries = OpenCms.getSitemapManager().getEntriesForStructureId(
660    //                    cms,
661    //                    res.getStructureId());
662    //                for (CmsInternalSitemapEntry entry : entries) {
663    //                    String rfsName = OpenCms.getStaticExportManager().getRfsName(cms, entry.getRootPath());
664    //                    // add index_export.html or the index.html to the folder name
665    //                    rfsName = OpenCms.getStaticExportManager().addDefaultFileNameToFolder(rfsName, res.isFolder());
666    //                    // get
667    //                    String rfsExportFileName = CmsFileUtil.normalizePath(OpenCms.getStaticExportManager().getExportPath(
668    //                        vfsName)
669    //                        + rfsName.substring(OpenCms.getStaticExportManager().getRfsPrefix(vfsName).length()));
670    //                    File file = new File(rfsExportFileName);
671    //                    if (file.exists() && !files.contains(file)) {
672    //                        files.add(file);
673    //                    }
674    //                }
675    //            }
676    //        } catch (CmsException e) {
677    //            LOG.error(e.getLocalizedMessage(), e);
678    //        }
679    //        return files;
680    //    }
681
682    /**
683     * Returns the export file name starting from the OpenCms webapp folder.<p>
684     *
685     * @param file the file to delete
686     * @param vfsName the VFS name of the file, the root path!
687     *
688     * @return the export file name starting from the OpenCms webapp folder
689     */
690    private String getRfsName(File file, String vfsName) {
691
692        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
693        String filePath = file.getAbsolutePath();
694        String result = CmsFileUtil.normalizePath(
695            manager.getRfsPrefix(vfsName)
696                + filePath.substring(OpenCms.getStaticExportManager().getExportPath(vfsName).length()));
697        return CmsStringUtil.substitute(result, new String(new char[] {File.separatorChar}), "/");
698    }
699
700    /**
701     * Purges a list of files from the rfs.<p>
702     *
703     * @param files the list of files to purge
704     * @param vfsName the vfs name of the originally file to purge
705     * @param scrubbedFiles the list which stores all the scrubbed files
706     * @param cms the CMS context
707     */
708    private void purgeFiles(List<File> files, String vfsName, Set<String> scrubbedFiles, CmsObject cms) {
709
710        for (File file : files) {
711            purgeFile(file.getAbsolutePath(), vfsName, cms);
712            String rfsName = CmsFileUtil.normalizePath(
713                OpenCms.getStaticExportManager().getRfsPrefix(vfsName)
714                    + "/"
715                    + file.getAbsolutePath().substring(
716                        OpenCms.getStaticExportManager().getExportPath(vfsName).length()));
717            rfsName = CmsStringUtil.substitute(rfsName, new String(new char[] {File.separatorChar}), "/");
718            scrubbedFiles.add(rfsName);
719        }
720    }
721}