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.ade.publish;
029
030import org.opencms.ade.publish.shared.CmsPublishOptions;
031import org.opencms.ade.publish.shared.CmsPublishResource;
032import org.opencms.ade.publish.shared.CmsPublishResourceInfo;
033import org.opencms.db.CmsPublishList;
034import org.opencms.db.CmsResourceState;
035import org.opencms.file.CmsObject;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.file.types.CmsResourceTypePlain;
039import org.opencms.file.types.CmsResourceTypeXmlContent;
040import org.opencms.gwt.CmsIconUtil;
041import org.opencms.gwt.CmsVfsService;
042import org.opencms.gwt.shared.CmsPermissionInfo;
043import org.opencms.main.CmsException;
044import org.opencms.main.CmsLog;
045import org.opencms.main.OpenCms;
046import org.opencms.publish.CmsPublishManager;
047import org.opencms.relations.CmsRelation;
048import org.opencms.relations.CmsRelationPublishValidator;
049import org.opencms.relations.CmsRelationValidatorInfoEntry;
050import org.opencms.report.CmsWorkplaceReport;
051import org.opencms.report.I_CmsReport;
052import org.opencms.security.CmsOrganizationalUnit;
053import org.opencms.ui.components.CmsResourceIcon;
054import org.opencms.util.CmsStringUtil;
055import org.opencms.util.CmsUUID;
056import org.opencms.workplace.explorer.CmsResourceUtil;
057
058import java.util.ArrayList;
059import java.util.Collection;
060import java.util.HashMap;
061import java.util.HashSet;
062import java.util.List;
063import java.util.Locale;
064import java.util.Map;
065import java.util.Set;
066
067import org.apache.commons.logging.Log;
068
069/**
070 * ADE publishing features.<p>
071 *
072 * @since 8.0.0
073 */
074public class CmsPublish {
075
076    /**
077     * Just for passing around resources and their related together but not mixed up.<p>
078     */
079    public class ResourcesAndRelated {
080
081        /** The related resources. */
082        private Set<CmsResource> m_relatedResources = new HashSet<CmsResource>();
083
084        /** The resources. */
085        private Set<CmsResource> m_resources = new HashSet<CmsResource>();
086
087        /**
088         * Constructor.<p>
089         */
090        public ResourcesAndRelated() {
091
092            // empty
093        }
094
095        /**
096         * Checks if the given resource is present in at least one of the sets.<p>
097         *
098         * @param resource the resource to test
099         *
100         * @return <code>true</code> if the given resource is present in at least one of the sets
101         */
102        public boolean contains(CmsResource resource) {
103
104            return m_resources.contains(resource) || m_relatedResources.contains(resource);
105        }
106
107        /**
108         * Returns the related resources.<p>
109         *
110         * @return the related resources
111         */
112        public Set<CmsResource> getRelatedResources() {
113
114            return m_relatedResources;
115        }
116
117        /**
118         * Returns the resources.<p>
119         *
120         * @return the resources
121         */
122        public Set<CmsResource> getResources() {
123
124            return m_resources;
125        }
126    }
127
128    /** The number of day groups. */
129    protected static final int GROUP_DAYS_NUMBER = 3;
130
131    /** The gap between session groups. */
132    protected static final int GROUP_SESSIONS_GAP = 8 * 60 * 60 * 1000;
133
134    /** The number of session groups. */
135    protected static final int GROUP_SESSIONS_NUMBER = 2;
136
137    /** The log object for this class. */
138    private static final Log LOG = CmsLog.getLog(CmsPublish.class);
139
140    /** The current cms context. */
141    protected final CmsObject m_cms;
142
143    /** The options. */
144    protected final CmsPublishOptions m_options;
145
146    /** The current user workplace locale. */
147    protected final Locale m_workplaceLocale;
148
149    /** The relation validator instance. */
150    private CmsRelationPublishValidator m_relationValidator;
151
152    /**
153     * Creates a new instance.<p>
154     *
155     * @param cms the CMS context to use
156     */
157    public CmsPublish(CmsObject cms) {
158
159        this(cms, new HashMap<String, String>());
160    }
161
162    /**
163     * Constructor with options.<p>
164     *
165     * @param cms the current cms context
166     * @param options the options to use
167     */
168    public CmsPublish(CmsObject cms, CmsPublishOptions options) {
169
170        m_cms = cms;
171        m_workplaceLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
172        m_options = options;
173    }
174
175    /**
176     * Constructor with default options.<p>
177     *
178     * @param cms the current cms context
179     * @param params the additional publish parameters
180     */
181    public CmsPublish(CmsObject cms, Map<String, String> params) {
182
183        this(cms, new CmsPublishOptions(params));
184    }
185
186    /**
187     * Returns the simple name if the ou is the same as the current user's ou.<p>
188     *
189     * @param cms the CMS context
190     * @param name the fully qualified name to check
191     *
192     * @return the simple name if the ou is the same as the current user's ou
193     */
194    protected static String getOuAwareName(CmsObject cms, String name) {
195
196        String ou = CmsOrganizationalUnit.getParentFqn(name);
197        if (ou.equals(cms.getRequestContext().getCurrentUser().getOuFqn())) {
198            return CmsOrganizationalUnit.getSimpleName(name);
199        }
200        return CmsOrganizationalUnit.SEPARATOR + name;
201    }
202
203    /**
204     * Checks for possible broken links when the given list of resources would be published.<p>
205     *
206     * @param pubResources list of resources to be published
207     *
208     * @return a list of resources that would produce broken links when published
209     */
210    public List<CmsPublishResource> getBrokenResources(List<CmsResource> pubResources) {
211
212        List<CmsPublishResource> resources = new ArrayList<CmsPublishResource>();
213        CmsPublishManager publishManager = OpenCms.getPublishManager();
214
215        CmsPublishList publishList;
216        try {
217            publishList = OpenCms.getPublishManager().getPublishListAll(
218                m_cms,
219                pubResources,
220                m_options.isIncludeSiblings(),
221                true);
222            if (m_options.isIncludeRelated()) {
223                CmsPublishList related = publishManager.getRelatedResourcesToPublish(m_cms, publishList);
224                publishList = publishManager.mergePublishLists(m_cms, publishList, related);
225            }
226
227        } catch (CmsException e) {
228            // should never happen
229            LOG.error(e.getLocalizedMessage(), e);
230            return resources;
231        }
232
233        CmsRelationPublishValidator validator = new CmsRelationPublishValidator(m_cms, publishList);
234        m_relationValidator = validator;
235        for (String resourceName : validator.keySet()) {
236            CmsRelationValidatorInfoEntry infoEntry = validator.getInfoEntry(resourceName);
237            try {
238                CmsResource resource = m_cms.readResource(
239                    m_cms.getRequestContext().removeSiteRoot(resourceName),
240                    CmsResourceFilter.ALL);
241                if (resource.getState().isDeleted()) {
242                    for (CmsRelation relation : infoEntry.getRelations()) {
243                        try {
244                            CmsResource theResource = relation.getSource(m_cms, CmsResourceFilter.ALL);
245                            CmsPublishResourceInfo info = new CmsPublishResourceInfo(
246                                Messages.get().getBundle(m_workplaceLocale).key(Messages.GUI_BROKEN_LINK_ONLINE_0),
247                                CmsPublishResourceInfo.Type.BROKENLINK);
248                            // HACK: GWT serialization does not like unmodifiable collections :(
249                            // Collections.singletonList(resourceToBean(resource, info, false, null)));
250                            ArrayList<CmsPublishResource> relatedList = new ArrayList<CmsPublishResource>();
251                            relatedList.add(resourceToBean(resource, info, false, null));
252                            CmsPublishResource pubRes = resourceToBean(theResource, null, false, relatedList);
253                            resources.add(pubRes);
254                        } catch (CmsException e) {
255                            // should never happen
256                            LOG.error(e.getLocalizedMessage(), e);
257                        }
258                    }
259                } else {
260                    try {
261                        List<CmsPublishResource> related = new ArrayList<CmsPublishResource>();
262                        for (CmsRelation relation : infoEntry.getRelations()) {
263                            try {
264                                CmsResource theResource = relation.getTarget(m_cms, CmsResourceFilter.ALL);
265                                CmsPublishResource pubRes = resourceToBean(theResource, null, false, null);
266                                related.add(pubRes);
267                            } catch (CmsException e) {
268                                CmsPublishResource pubRes = relationToBean(relation);
269                                related.add(pubRes);
270                                LOG.warn(e.getLocalizedMessage(), e);
271                            }
272                        }
273                        CmsPublishResourceInfo info = new CmsPublishResourceInfo(
274                            Messages.get().getBundle(m_workplaceLocale).key(Messages.GUI_RESOURCE_MISSING_ONLINE_0),
275                            CmsPublishResourceInfo.Type.MISSING);
276                        CmsPublishResource pubRes = resourceToBean(resource, info, false, related);
277                        resources.add(pubRes);
278                    } catch (Exception e) {
279                        // should never happen
280                        LOG.error(e.getLocalizedMessage(), e);
281                    }
282                }
283            } catch (CmsException e) {
284                // should never happen
285                LOG.error(e.getLocalizedMessage(), e);
286            }
287        }
288
289        return resources;
290    }
291
292    /**
293     * Gets the relation validator instance.<p>
294     *
295     * @return the relation validator
296     */
297    public CmsRelationPublishValidator getRelationValidator() {
298
299        return m_relationValidator;
300    }
301
302    /**
303     * Publishes the given list of resources.<p>
304     *
305     * @param resources list of resources to publish
306     *
307     * @throws CmsException if something goes wrong
308     */
309    public void publishResources(List<CmsResource> resources) throws CmsException {
310
311        CmsObject cms = m_cms;
312        I_CmsReport report = new CmsWorkplaceReport(
313            cms.getRequestContext().getLocale(),
314            cms.getRequestContext().getSiteRoot(),
315            null);
316        CmsPublishManager publishManager = OpenCms.getPublishManager();
317        CmsPublishList publishList = publishManager.getPublishListAll(m_cms, resources, false, true);
318        OpenCms.getPublishManager().publishProject(m_cms, report, publishList);
319    }
320
321    /**
322     * Creates a publish resource bean from the target information of a relation object.<p>
323     *
324     * @param relation the relation to use
325     *
326     * @return the publish resource bean for the relation target
327     */
328    public CmsPublishResource relationToBean(CmsRelation relation) {
329
330        CmsPermissionInfo permissionInfo = new CmsPermissionInfo(true, false, "");
331        CmsPublishResource bean = new CmsPublishResource(
332            relation.getTargetId(),
333            relation.getTargetPath(),
334            relation.getTargetPath(),
335            CmsResourceTypePlain.getStaticTypeName(),
336            CmsResourceState.STATE_UNCHANGED,
337            permissionInfo,
338            false /*type doesn't matter, we can't edit it anyway. */ ,
339            0,
340            null,
341            null,
342            false,
343            null,
344            null);
345        bean.setBigIconClasses(CmsIconUtil.getIconClasses(CmsResourceTypePlain.getStaticTypeName(), null, false));
346        return bean;
347    }
348
349    /**
350     * Removes the given resources from the user's publish list.<p>
351     *
352     * @param idsToRemove list of structure ids identifying the resources to be removed
353     *
354     * @throws CmsException if something goes wrong
355     */
356    public void removeResourcesFromPublishList(Collection<CmsUUID> idsToRemove) throws CmsException {
357
358        OpenCms.getPublishManager().removeResourceFromUsersPubList(m_cms, idsToRemove);
359    }
360
361    /**
362     * Creates a publish resource bean instance from the given parameters.<p>
363     *
364     * @param resource the resource
365     * @param info the publish information, if any
366     * @param removable if removable
367     * @param related the list of related resources
368     *
369     * @return the publish resource bean
370     *
371     * @throws CmsException if something goes wrong
372     */
373    protected CmsPublishResource resourceToBean(
374        CmsResource resource,
375        CmsPublishResourceInfo info,
376        boolean removable,
377        List<CmsPublishResource> related)
378    throws CmsException {
379
380        CmsResourceUtil resUtil = new CmsResourceUtil(m_cms, resource);
381        CmsPermissionInfo permissionInfo = OpenCms.getADEManager().getPermissionInfo(m_cms, resource, null);
382
383        String typeName = CmsIconUtil.getDisplayType(m_cms, resource);
384        String detailTypeName = null;
385        detailTypeName = CmsResourceIcon.getDefaultFileOrDetailType(m_cms, resource);
386
387        CmsPublishResource pubResource = new CmsPublishResource(
388            resource.getStructureId(),
389            resUtil.getFullPath(),
390            resUtil.getTitle(),
391            typeName,
392            resource.getState(),
393            permissionInfo,
394            CmsResourceTypeXmlContent.isXmlContent(resource),
395            resource.getDateLastModified(),
396            resUtil.getUserLastModified(),
397            CmsVfsService.formatDateTime(m_cms, resource.getDateLastModified()),
398            removable,
399            info,
400            related);
401        pubResource.setBigIconClasses(CmsIconUtil.getIconClasses(typeName, null, false));
402        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(detailTypeName)) {
403            pubResource.setSmallIconClasses(CmsIconUtil.getIconClasses(detailTypeName, null, true));
404        }
405        return pubResource;
406    }
407
408}