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.ade.postupload;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.postupload.shared.CmsPostUploadDialogBean;
032import org.opencms.ade.postupload.shared.CmsPostUploadDialogPanelBean;
033import org.opencms.ade.postupload.shared.I_CmsDialogConstants;
034import org.opencms.ade.postupload.shared.rpc.I_CmsPostUploadDialogService;
035import org.opencms.file.CmsObject;
036import org.opencms.file.CmsProperty;
037import org.opencms.file.CmsPropertyDefinition;
038import org.opencms.file.CmsResource;
039import org.opencms.file.types.I_CmsResourceType;
040import org.opencms.flex.CmsFlexController;
041import org.opencms.gwt.CmsGwtService;
042import org.opencms.gwt.CmsPropertyEditorHelper;
043import org.opencms.gwt.CmsRpcException;
044import org.opencms.gwt.CmsVfsService;
045import org.opencms.gwt.shared.CmsListInfoBean;
046import org.opencms.gwt.shared.property.CmsClientProperty;
047import org.opencms.gwt.shared.property.CmsPropertyModification;
048import org.opencms.main.CmsException;
049import org.opencms.main.CmsLog;
050import org.opencms.main.OpenCms;
051import org.opencms.util.CmsMacroResolver;
052import org.opencms.util.CmsStringUtil;
053import org.opencms.util.CmsUUID;
054import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
055import org.opencms.xml.content.CmsXmlContentProperty;
056import org.opencms.xml.content.CmsXmlContentPropertyHelper;
057
058import java.util.ArrayList;
059import java.util.HashSet;
060import java.util.LinkedHashMap;
061import java.util.LinkedHashSet;
062import java.util.List;
063import java.util.Locale;
064import java.util.Map;
065import java.util.Set;
066import java.util.stream.Collectors;
067
068import javax.servlet.http.HttpServletRequest;
069
070import org.apache.commons.logging.Log;
071
072import com.google.common.collect.Iterables;
073
074/**
075 * The service implementation for the org.opencms.ade.postupload module.<p>
076 */
077public class CmsPostUploadDialogService extends CmsGwtService implements I_CmsPostUploadDialogService {
078
079    /** Serial version id. */
080    private static final long serialVersionUID = 1L;
081
082    /** Logger instance for this class. */
083    private static final Log LOG = CmsLog.getLog(CmsPostUploadDialogService.class);
084
085    /**
086     * Creates a new instance.<p>
087     */
088    public CmsPostUploadDialogService() {
089
090        super();
091    }
092
093    /**
094     * Fetches the dialog data.<p>
095     *
096     * @param request the servlet request
097     *
098     * @return the dialog data
099     * @throws CmsRpcException if something goes wrong
100     */
101    public static CmsPostUploadDialogBean prefetch(HttpServletRequest request) throws CmsRpcException {
102
103        CmsPostUploadDialogService srv = new CmsPostUploadDialogService();
104        srv.setCms(CmsFlexController.getCmsObject(request));
105        srv.setRequest(request);
106        CmsPostUploadDialogBean result = null;
107        try {
108            result = srv.prefetch();
109        } finally {
110            srv.clearThreadStorage();
111        }
112        return result;
113    }
114
115    /**
116     * @see org.opencms.ade.postupload.shared.rpc.I_CmsPostUploadDialogService#load(org.opencms.util.CmsUUID, boolean,boolean)
117     */
118    public CmsPostUploadDialogPanelBean load(CmsUUID id, boolean useConfiguration, boolean addBasicProperties)
119    throws CmsRpcException {
120
121        try {
122            CmsResource res = getCmsObject().readResource(id);
123            List<CmsProperty> properties = getCmsObject().readPropertyObjects(res, false);
124            String title = CmsProperty.get(CmsPropertyDefinition.PROPERTY_TITLE, properties).getValue();
125            if (title == null) {
126                title = res.getName();
127            }
128            String description = CmsProperty.get(CmsPropertyDefinition.PROPERTY_DESCRIPTION, properties).getValue();
129            if (description == null) {
130                description = getCmsObject().getSitePath(res);
131            }
132            CmsListInfoBean listInfo = CmsVfsService.getPageInfo(getCmsObject(), res);
133
134            CmsPostUploadDialogPanelBean result = new CmsPostUploadDialogPanelBean(id, listInfo);
135            String warning = OpenCms.getADEManager().getUploadWarningTable().getMessage(res.getStructureId());
136            if (warning != null) {
137                Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject());
138                CmsMacroResolver resolver = new CmsMacroResolver();
139                resolver.setMessages(OpenCms.getWorkplaceManager().getMessages(wpLocale));
140                warning = resolver.resolveMacros(warning);
141                result.setWarning(warning);
142            }
143
144            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(res.getTypeId());
145            String typeName = type.getTypeName();
146            listInfo.setResourceType(typeName);
147
148            CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(typeName);
149
150            List<String> defaultProperties = settings.getProperties();
151            while (properties.isEmpty() && !CmsStringUtil.isEmptyOrWhitespaceOnly(settings.getReference())) {
152                settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(settings.getReference());
153                defaultProperties = settings.getProperties();
154            }
155
156            Map<String, CmsXmlContentProperty> propertyDefinitions = new LinkedHashMap<String, CmsXmlContentProperty>();
157            Map<String, CmsClientProperty> clientProperties = new LinkedHashMap<String, CmsClientProperty>();
158
159            // add the file name to the list of properties to allow renaming the uploaded file
160            CmsXmlContentProperty fileNamePropDef = new CmsXmlContentProperty(
161                CmsPropertyModification.FILE_NAME_PROPERTY,
162                "string",
163                "string",
164                "",
165                "",
166                "",
167                "",
168                Messages.get().getBundle(OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject())).key(
169                    Messages.GUI_UPLOAD_FILE_NAME_0),
170                "",
171                "",
172                "false");
173            propertyDefinitions.put(CmsPropertyModification.FILE_NAME_PROPERTY, fileNamePropDef);
174            clientProperties.put(
175                CmsPropertyModification.FILE_NAME_PROPERTY,
176                new CmsClientProperty(CmsPropertyModification.FILE_NAME_PROPERTY, res.getName(), res.getName()));
177
178            CmsADEConfigData configData = OpenCms.getADEManager().lookupConfiguration(
179                getCmsObject(),
180                res.getRootPath());
181            Map<String, CmsXmlContentProperty> propertyConfiguration = configData.getPropertyConfigurationAsMap();
182
183            Set<String> propertiesToShow = new LinkedHashSet<String>();
184            propertiesToShow.addAll(defaultProperties);
185            if (addBasicProperties) {
186                propertiesToShow.addAll(propertyConfiguration.keySet());
187            }
188            Set<String> requiredProperties = getRequiredProperties(getCmsObject(), res);
189            for (String propertyName : propertiesToShow) {
190                CmsXmlContentProperty propDef = null;
191                if (useConfiguration) {
192                    propDef = propertyConfiguration.get(propertyName);
193                }
194                if (propDef == null) {
195                    propDef = new CmsXmlContentProperty(
196                        propertyName,
197                        "string",
198                        "string",
199                        "",
200                        "",
201                        "",
202                        "",
203                        null,
204                        "",
205                        "",
206                        "false");
207                }
208                if (requiredProperties.contains(propertyName)) {
209                    String validationErrorMessage = Messages.get().getBundle(
210                        OpenCms.getWorkplaceManager().getWorkplaceLocale(getCmsObject())).key(
211                            Messages.GUI_POSTUPLOAD_REQUIRED_PROPERTY_1,
212                            propertyName);
213                    propDef = propDef.withValidation(".*?[^ ].*", "error", validationErrorMessage);
214                }
215
216                propertyDefinitions.put(propertyName, propDef);
217                CmsProperty property = CmsProperty.get(propertyName, properties);
218                if (property != null) {
219                    CmsClientProperty clientProperty = new CmsClientProperty(
220                        propertyName,
221                        property.getStructureValue(),
222                        property.getResourceValue());
223                    clientProperties.put(clientProperty.getName(), clientProperty);
224                }
225            }
226
227            propertyDefinitions = CmsXmlContentPropertyHelper.resolveMacrosInProperties(
228                propertyDefinitions,
229                CmsMacroResolver.newWorkplaceLocaleResolver(getCmsObject()));
230
231            CmsPropertyEditorHelper.updateWysiwygConfig(propertyDefinitions, getCmsObject(), res);
232
233            result.setPropertyDefinitions(propertyDefinitions);
234            result.setProperties(clientProperties);
235            return result;
236        } catch (CmsException e) {
237            error(e);
238            return null; // will never be reached
239        }
240    }
241
242    /**
243     * @see org.opencms.ade.postupload.shared.rpc.I_CmsPostUploadDialogService#prefetch()
244     */
245    public CmsPostUploadDialogBean prefetch() throws CmsRpcException {
246
247        try {
248
249            List<CmsResource> resources = new ArrayList<>();
250
251            if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(
252                getRequest().getParameter(I_CmsDialogConstants.PARAM_RESOURCES)))) {
253                // if the request parameter resources exists and contains a list of UUIDs
254                // this dialog is used as upload hook
255                String resourcesParam = getRequest().getParameter(I_CmsDialogConstants.PARAM_RESOURCES);
256                List<String> resourceUUIDs = CmsStringUtil.splitAsList(resourcesParam, ",");
257                for (String uuidAsString : resourceUUIDs) {
258                    CmsUUID uuid = new CmsUUID(uuidAsString);
259                    CmsResource res = getCmsObject().readResource(uuid);
260                    resources.add(res);
261                }
262            } else if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getRequest().getParameter("resource"))) {
263                // if there was no parameter "resources" set as request parameter
264                // this dialog is not used as upload hook try to read the resource parameter
265                String resourceParam = getRequest().getParameter("resource");
266                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(resourceParam)) {
267                    CmsResource res = getCmsObject().readResource(resourceParam);
268                    resources.add(res);
269                }
270            }
271            return createUploadDialogBean(resources);
272        } catch (CmsException e) {
273            error(e);
274            return null; // will never be reached
275        }
276    }
277
278    /**
279     * Creates the data bean for the dialog from the list of created resources.
280     *
281     * @param resources the resources
282     * @return the data bean for the dialog
283     */
284    private CmsPostUploadDialogBean createUploadDialogBean(List<CmsResource> resources) {
285
286        Map<CmsUUID, String> result = new LinkedHashMap<>();
287        CmsObject cms = getCmsObject();
288        // split resource list into two parts, ones that have required properties and ones that don't,
289        // then iterate over the ones with required properties first.
290        //
291        // this is because the buttons in the upload property dialog only trigger validation for the current tab,
292        // so we want the user to go through all resources which require validation first before they can exit the dialog.
293        Map<Boolean, List<CmsResource>> parts = resources.stream().collect(
294            Collectors.partitioningBy(res -> getRequiredProperties(cms, res).size() > 0));
295
296        for (CmsResource res : Iterables.concat(parts.get(Boolean.TRUE), parts.get(Boolean.FALSE))) {
297            result.put(res.getStructureId(), cms.getRequestContext().removeSiteRoot(res.getRootPath()));
298        }
299        Set<CmsUUID> reqValIds = parts.get(Boolean.TRUE).stream().map(res -> res.getStructureId()).collect(
300            Collectors.toSet());
301        return new CmsPostUploadDialogBean(result, reqValIds);
302    }
303
304    /**
305     * Gets the properties required for the given resource (as defined by the requiredOnUpload setting on the corresponding explorertype).
306     *
307     * @param cms the CMS context
308     * @param res a resource
309     * @return the set of required properties
310     */
311    private Set<String> getRequiredProperties(CmsObject cms, CmsResource res) {
312
313        Set<String> requiredProps = new HashSet<>();
314        try {
315            String typeName = OpenCms.getResourceManager().getResourceType(res).getTypeName();
316            CmsExplorerTypeSettings explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(typeName);
317            if (explorerType != null) {
318                for (String prop : explorerType.getProperties()) {
319                    if (explorerType.isPropertyRequiredOnUpload(prop)) {
320                        requiredProps.add(prop);
321                    }
322                }
323            }
324        } catch (Exception e) {
325            LOG.error(e.getLocalizedMessage(), e);
326            return requiredProps;
327        }
328        return requiredProps;
329    }
330
331}