001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (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, 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.gwt;
029
030import org.opencms.ade.containerpage.CmsRelationTargetListBean;
031import org.opencms.file.CmsFile;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProperty;
034import org.opencms.file.CmsPropertyDefinition;
035import org.opencms.file.CmsResource;
036import org.opencms.file.CmsResourceFilter;
037import org.opencms.file.CmsUser;
038import org.opencms.file.CmsVfsResourceNotFoundException;
039import org.opencms.file.types.CmsResourceTypeJsp;
040import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
041import org.opencms.file.types.CmsResourceTypeXmlContent;
042import org.opencms.file.types.I_CmsResourceType;
043import org.opencms.gwt.shared.CmsListInfoBean;
044import org.opencms.gwt.shared.CmsResourceStatusBean;
045import org.opencms.gwt.shared.CmsResourceStatusRelationBean;
046import org.opencms.gwt.shared.CmsResourceStatusTabId;
047import org.opencms.i18n.CmsLocaleManager;
048import org.opencms.i18n.CmsMessageContainer;
049import org.opencms.lock.CmsLock;
050import org.opencms.main.CmsException;
051import org.opencms.main.CmsLog;
052import org.opencms.main.OpenCms;
053import org.opencms.relations.CmsRelation;
054import org.opencms.relations.CmsRelationFilter;
055import org.opencms.relations.CmsRelationType;
056import org.opencms.relations.I_CmsLinkParseable;
057import org.opencms.search.galleries.CmsGallerySearch;
058import org.opencms.search.galleries.CmsGallerySearchResult;
059import org.opencms.util.CmsStringUtil;
060import org.opencms.util.CmsUUID;
061import org.opencms.workplace.explorer.CmsResourceUtil;
062import org.opencms.xml.content.CmsXmlContent;
063import org.opencms.xml.content.CmsXmlContentFactory;
064
065import java.util.ArrayList;
066import java.util.HashMap;
067import java.util.LinkedHashMap;
068import java.util.List;
069import java.util.Locale;
070import java.util.Map;
071
072import org.apache.commons.logging.Log;
073
074/**
075 * Helper class to generate all the data which is necessary for the resource status dialog(s).<p>
076 */
077public class CmsDefaultResourceStatusProvider {
078
079    /** The log instance for this class. */
080    private static final Log LOG = CmsLog.getLog(CmsDefaultResourceStatusProvider.class);
081
082    /**
083     * Gets the relation targets for a resource.<p>
084     * 
085     * @param cms the current CMS context 
086     * @param source the structure id of the resource for which we want the relation targets 
087     * @param additionalIds the structure ids of additional resources to include with the relation targets 
088     * @param cancelIfChanged if this is true, this method will stop immediately if it finds a changed resource among the relation targets
089     * 
090     * @return a bean containing a list of relation targets 
091     * 
092     * @throws CmsException if something goes wrong 
093     */
094    public static CmsRelationTargetListBean getContainerpageRelationTargets(
095        CmsObject cms,
096        CmsUUID source,
097        List<CmsUUID> additionalIds,
098        boolean cancelIfChanged) throws CmsException {
099
100        CmsRelationTargetListBean result = new CmsRelationTargetListBean();
101        CmsResource content = cms.readResource(source, CmsResourceFilter.IGNORE_EXPIRATION);
102        boolean isContainerPage = CmsResourceTypeXmlContainerPage.isContainerPage(content);
103        for (CmsUUID structureId : additionalIds) {
104            try {
105                CmsResource res = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
106                result.add(res);
107                if (res.getState().isChanged() && cancelIfChanged) {
108                    return result;
109                }
110            } catch (CmsException e) {
111                LOG.error(e.getLocalizedMessage(), e);
112            }
113        }
114        List<CmsRelation> relations = cms.readRelations(CmsRelationFilter.relationsFromStructureId(source));
115        for (CmsRelation relation : relations) {
116            if (relation.getType() == CmsRelationType.XSD) {
117                continue;
118            }
119            try {
120                CmsResource target = relation.getTarget(cms, CmsResourceFilter.IGNORE_EXPIRATION);
121                I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(target);
122                if (isContainerPage && (type instanceof CmsResourceTypeJsp)) {
123                    // ignore formatters for container pages, as the normal user probably doesn't want to deal with them  
124                    continue;
125                }
126                result.add(target);
127                if (target.getState().isChanged() && cancelIfChanged) {
128                    return result;
129                }
130            } catch (CmsException e) {
131                LOG.error(e.getLocalizedMessage(), e);
132            }
133        }
134        return result;
135    }
136
137    /**
138     * Collects all the data to display in the resource status dialog.<p>
139     * 
140     * @param cms the current CMS context 
141     * @param structureId the structure id of the resource for which we want the information
142     * @param contentLocale the content locale 
143     * @param includeTargets true if relation targets should be included 
144     * @param additionalStructureIds structure ids of additional resources to include with the relation targets
145     *  
146     * @return the resource status information 
147     * @throws CmsException if something goes wrong 
148     */
149    public CmsResourceStatusBean getResourceStatus(
150        CmsObject cms,
151        CmsUUID structureId,
152        String contentLocale,
153        boolean includeTargets,
154        List<CmsUUID> additionalStructureIds) throws CmsException {
155
156        Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
157        cms.getRequestContext().setLocale(locale);
158        CmsResource resource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
159        String localizedTitle = null;
160        if (!CmsStringUtil.isEmptyOrWhitespaceOnly(contentLocale)) {
161            Locale realLocale = CmsLocaleManager.getLocale(contentLocale);
162            CmsGallerySearchResult result = CmsGallerySearch.searchById(cms, structureId, realLocale);
163            if (!CmsStringUtil.isEmptyOrWhitespaceOnly(result.getTitle())) {
164                localizedTitle = result.getTitle();
165            }
166        }
167        CmsResourceUtil resourceUtil = new CmsResourceUtil(cms, resource);
168        List<CmsProperty> properties = cms.readPropertyObjects(resource, false);
169        CmsResourceStatusBean result = new CmsResourceStatusBean();
170        result.setDateCreated(CmsVfsService.formatDateTime(cms, resource.getDateCreated()));
171        long dateExpired = resource.getDateExpired();
172        if (dateExpired != CmsResource.DATE_EXPIRED_DEFAULT) {
173            result.setDateExpired(CmsVfsService.formatDateTime(cms, dateExpired));
174        }
175        result.setDateLastModified(CmsVfsService.formatDateTime(cms, resource.getDateLastModified()));
176        long dateReleased = resource.getDateReleased();
177        if (dateReleased != CmsResource.DATE_RELEASED_DEFAULT) {
178            result.setDateReleased(CmsVfsService.formatDateTime(cms, dateReleased));
179        }
180        String lastProject = resourceUtil.getLockedInProjectName();
181        if ("".equals(lastProject)) {
182            lastProject = null;
183        }
184        result.setLastProject(lastProject);
185
186        result.setListInfo(CmsVfsService.getPageInfo(cms, resource));
187        CmsLock lock = cms.getLock(resource);
188        CmsUser lockOwner = null;
189        if (!lock.isUnlocked()) {
190            lockOwner = cms.readUser(lock.getUserId());
191            result.setLockState(org.opencms.workplace.list.Messages.get().getBundle(locale).key(
192                org.opencms.workplace.list.Messages.GUI_EXPLORER_LIST_ACTION_LOCK_NAME_2,
193                lockOwner.getName(),
194                lastProject));
195        } else {
196            result.setLockState(org.opencms.workplace.list.Messages.get().getBundle(locale).key(
197                org.opencms.workplace.list.Messages.GUI_EXPLORER_LIST_ACTION_UNLOCK_NAME_0));
198        }
199
200        CmsProperty navText = CmsProperty.get(CmsPropertyDefinition.PROPERTY_NAVTEXT, properties);
201        if (navText != null) {
202            result.setNavText(navText.getValue());
203        }
204        result.setPermissions(resourceUtil.getPermissionString());
205        result.setSize(resource.getLength());
206        result.setStateBean(resource.getState());
207        CmsProperty title = CmsProperty.get(CmsPropertyDefinition.PROPERTY_TITLE, properties);
208        if (localizedTitle != null) {
209            result.setTitle(localizedTitle);
210            result.getListInfo().setTitle(localizedTitle);
211        } else if (title != null) {
212            result.setTitle(title.getValue());
213        }
214        result.setUserCreated(resourceUtil.getUserCreated());
215        result.setUserLastModified(resourceUtil.getUserLastModified());
216
217        I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(resource.getTypeId());
218        result.setResourceType(resType.getTypeName());
219        result.setStructureId(resource.getStructureId());
220        if (resType instanceof CmsResourceTypeXmlContent) {
221            CmsFile file = cms.readFile(resource);
222            CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, file);
223            List<Locale> locales = content.getLocales();
224            List<String> localeStrings = new ArrayList<String>();
225            for (Locale l : locales) {
226                localeStrings.add(l.toString());
227            }
228            result.setLocales(localeStrings);
229        }
230
231        List<CmsRelation> relations = cms.readRelations(CmsRelationFilter.relationsToStructureId(resource.getStructureId()));
232        Map<CmsUUID, CmsResource> relationSources = new HashMap<CmsUUID, CmsResource>();
233
234        if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) {
235            // People may link to the folder of a container page instead of the page itself
236            try {
237                CmsResource parent = cms.readParentFolder(resource.getStructureId());
238                List<CmsRelation> parentRelations = cms.readRelations(CmsRelationFilter.relationsToStructureId(parent.getStructureId()));
239                relations.addAll(parentRelations);
240            } catch (CmsException e) {
241                LOG.error(e.getLocalizedMessage(), e);
242            }
243        }
244
245        // find all distinct relation sources 
246        for (CmsRelation relation : relations) {
247            CmsResource currentSource = relation.getSource(cms, CmsResourceFilter.IGNORE_EXPIRATION);
248            relationSources.put(currentSource.getStructureId(), currentSource);
249        }
250
251        for (CmsResource relationResource : relationSources.values()) {
252            try {
253                CmsResourceStatusRelationBean relationBean = createRelationBean(cms, relationResource);
254                result.getRelationSources().add(relationBean);
255            } catch (CmsVfsResourceNotFoundException notfound) {
256                LOG.error(notfound.getLocalizedMessage(), notfound);
257                continue;
258            }
259        }
260        if (includeTargets) {
261            result.getRelationTargets().addAll(getTargets(cms, structureId, additionalStructureIds));
262        }
263        result.setTabs(getTabClientData(cms, resource));
264        return result;
265    }
266
267    /**
268     * Gets the list of relation targets for a resource.<p>
269     * 
270     * @param cms the current CMS context 
271     * @param structureId the structure id of the resource for which we want the relation targets 
272     * @param additionalStructureIds structure ids of additional resources to include with the relation target
273     *  
274     * @return the list of relation beans for the relation targets
275     *  
276     * @throws CmsException if something goes wrong 
277     */
278    protected List<CmsResourceStatusRelationBean> getTargets(
279        CmsObject cms,
280        CmsUUID structureId,
281        List<CmsUUID> additionalStructureIds) throws CmsException {
282
283        CmsRelationTargetListBean listBean = getContainerpageRelationTargets(
284            cms,
285            structureId,
286            additionalStructureIds,
287            false);
288        List<CmsResourceStatusRelationBean> result = new ArrayList<CmsResourceStatusRelationBean>();
289        for (CmsResource target : listBean.getResources()) {
290            try {
291                CmsResourceStatusRelationBean relationBean = createRelationBean(cms, target);
292                result.add(relationBean);
293            } catch (CmsException e) {
294                LOG.error(e.getLocalizedMessage(), e);
295            }
296        }
297        return result;
298
299    }
300
301    /** 
302     * Creates a bean for a single resource which is part of a relation list.<p> 
303     * 
304     * @param cms the current CMS context 
305     * @param relationResource the resource 
306     * 
307     * @return the status bean for the resource
308     * 
309     * @throws CmsException if something goes wrong 
310     */
311    CmsResourceStatusRelationBean createRelationBean(CmsObject cms, CmsResource relationResource) throws CmsException {
312
313        CmsListInfoBean sourceBean = CmsVfsService.getPageInfo(cms, relationResource);
314        String link = null;
315        try {
316            link = OpenCms.getLinkManager().substituteLink(cms, relationResource);
317        } catch (Exception e) {
318            LOG.warn(e.getLocalizedMessage(), e);
319        }
320        CmsResourceStatusRelationBean relationBean = new CmsResourceStatusRelationBean(
321            sourceBean,
322            link,
323            relationResource.getStructureId());
324        if (CmsResourceTypeXmlContent.isXmlContent(relationResource)) {
325            relationBean.setIsXmlContent(true);
326        }
327        String sitePath = cms.getSitePath(relationResource);
328        relationBean.setSitePath(sitePath);
329        return relationBean;
330    }
331
332    /**
333     * Determines the arrangement of tabs to display, together with their labels.<p>
334     * 
335     * @param cms the current CMS context
336     * @param res the resource for which the dialog should be displayed 
337     * @return the tab configuration for the dialog 
338     */
339    private LinkedHashMap<CmsResourceStatusTabId, String> getTabClientData(CmsObject cms, CmsResource res) {
340
341        Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
342        LinkedHashMap<CmsResourceStatusTabId, String> result = new LinkedHashMap<CmsResourceStatusTabId, String>();
343        Map<CmsResourceStatusTabId, CmsMessageContainer> tabs = getTabData(res);
344        for (Map.Entry<CmsResourceStatusTabId, CmsMessageContainer> entry : tabs.entrySet()) {
345            result.put(entry.getKey(), entry.getValue().key(locale));
346        }
347        return result;
348    }
349
350    /**
351     * Determines the arrangement of tabs to display, together with their labels.<p>
352     * 
353     * @param res the resource for which the dialog should be displayed 
354     * @return the tab configuration for the dialog 
355     */
356    private Map<CmsResourceStatusTabId, CmsMessageContainer> getTabData(CmsResource res) {
357
358        Map<CmsResourceStatusTabId, CmsMessageContainer> tabs;
359        if (CmsResourceTypeXmlContainerPage.isContainerPage(res)) {
360            tabs = CmsResourceStatusConstants.STATUS_TABS_CONTAINER_PAGE;
361        } else if (OpenCms.getResourceManager().getResourceType(res) instanceof I_CmsLinkParseable) {
362            tabs = CmsResourceStatusConstants.STATUS_TABS_CONTENT;
363        } else {
364            tabs = CmsResourceStatusConstants.STATUS_TABS_OTHER;
365        }
366        return tabs;
367    }
368
369}