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, 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.galleries;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.galleries.shared.CmsImageInfoBean;
032import org.opencms.ade.galleries.shared.CmsPoint;
033import org.opencms.ade.galleries.shared.CmsResourceInfoBean;
034import org.opencms.ade.galleries.shared.rpc.I_CmsPreviewService;
035import org.opencms.file.CmsFile;
036import org.opencms.file.CmsObject;
037import org.opencms.file.CmsProperty;
038import org.opencms.file.CmsPropertyDefinition;
039import org.opencms.file.CmsResource;
040import org.opencms.file.CmsResourceFilter;
041import org.opencms.file.CmsVfsResourceNotFoundException;
042import org.opencms.file.history.I_CmsHistoryResource;
043import org.opencms.file.types.CmsResourceTypeImage;
044import org.opencms.file.types.CmsResourceTypeXmlContent;
045import org.opencms.file.types.I_CmsResourceType;
046import org.opencms.gwt.CmsGwtService;
047import org.opencms.gwt.CmsIconUtil;
048import org.opencms.gwt.CmsRpcException;
049import org.opencms.i18n.CmsLocaleManager;
050import org.opencms.jsp.util.CmsJspStandardContextBean;
051import org.opencms.loader.CmsImageScaler;
052import org.opencms.lock.CmsLock;
053import org.opencms.main.CmsException;
054import org.opencms.main.CmsLog;
055import org.opencms.main.CmsPermalinkResourceHandler;
056import org.opencms.main.OpenCms;
057import org.opencms.ui.components.CmsResourceIcon;
058import org.opencms.util.CmsStringUtil;
059import org.opencms.workplace.CmsWorkplaceMessages;
060import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
061import org.opencms.workplace.explorer.CmsResourceUtil;
062import org.opencms.xml.containerpage.CmsContainerBean;
063import org.opencms.xml.containerpage.CmsContainerElementBean;
064import org.opencms.xml.containerpage.CmsContainerPageBean;
065import org.opencms.xml.containerpage.CmsFormatterBean;
066import org.opencms.xml.containerpage.CmsFormatterConfiguration;
067import org.opencms.xml.containerpage.I_CmsFormatterBean;
068
069import java.util.Collections;
070import java.util.Date;
071import java.util.Iterator;
072import java.util.LinkedHashMap;
073import java.util.List;
074import java.util.Locale;
075import java.util.Map;
076import java.util.Map.Entry;
077import java.util.regex.Matcher;
078import java.util.regex.Pattern;
079
080import javax.servlet.http.HttpServletRequest;
081import javax.servlet.http.HttpServletResponse;
082
083import org.apache.commons.logging.Log;
084
085import com.google.common.collect.Lists;
086
087/**
088 * Handles all RPC services related to the gallery preview dialog.<p>
089 *
090 * @since 8.0.0
091 */
092public class CmsPreviewService extends CmsGwtService implements I_CmsPreviewService {
093
094    /** Regex used to parse the image.focalpoint property. */
095    public static final Pattern PATTERN_FOCAL_POINT = Pattern.compile(" *([0-9]+) *, *([0-9]+) *");
096
097    /** The logger instance for this class. */
098    private static final Log LOG = CmsLog.getLog(CmsPreviewService.class);
099
100    /** Serialization uid. */
101    private static final long serialVersionUID = -8175522641937277445L;
102
103    /**
104     * Renders the preview content for the given resource and locale.<p>
105     *
106     * @param request the current servlet request
107     * @param response the current servlet response
108     * @param cms the cms context
109     * @param resource the resource
110     * @param locale the content locale
111     *
112     * @return the rendered HTML preview content
113     */
114    public static String getPreviewContent(
115        HttpServletRequest request,
116        HttpServletResponse response,
117        CmsObject cms,
118        CmsResource resource,
119        Locale locale) {
120
121        try {
122            if (CmsResourceTypeXmlContent.isXmlContent(resource)) {
123                CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfiguration(
124                    cms,
125                    cms.getRequestContext().getRootUri());
126
127                CmsFormatterConfiguration formatters = adeConfig.getFormatters(cms, resource);
128                I_CmsFormatterBean formatter = formatters.getPreviewFormatter();
129                if (formatter != null) {
130                    CmsObject tempCms = OpenCms.initCmsObject(cms);
131                    tempCms.getRequestContext().setLocale(locale);
132                    CmsResource formatterResource = tempCms.readResource(formatter.getJspStructureId());
133                    request.setAttribute(CmsJspStandardContextBean.ATTRIBUTE_CMS_OBJECT, tempCms);
134                    CmsJspStandardContextBean standardContext = CmsJspStandardContextBean.getInstance(request);
135                    CmsContainerElementBean element = new CmsContainerElementBean(
136                        resource.getStructureId(),
137                        formatter.getJspStructureId(),
138                        null,
139                        false);
140                    if ((resource instanceof I_CmsHistoryResource) && (resource instanceof CmsFile)) {
141                        element.setHistoryFile((CmsFile)resource);
142                    }
143                    element.initResource(tempCms);
144                    CmsContainerBean containerBean = new CmsContainerBean(
145                        "PREVIEW",
146                        CmsFormatterBean.PREVIEW_TYPE,
147                        null,
148                        true,
149                        1,
150                        Collections.<CmsContainerElementBean> emptyList());
151                    containerBean.setWidth(String.valueOf(CmsFormatterBean.PREVIEW_WIDTH));
152
153                    standardContext.setContainer(containerBean);
154                    standardContext.setElement(element);
155                    standardContext.setEdited(true);
156                    standardContext.setPage(
157                        new CmsContainerPageBean(Collections.<CmsContainerBean> singletonList(containerBean)));
158                    String encoding = response.getCharacterEncoding();
159                    return (new String(
160                        OpenCms.getResourceManager().getLoader(
161                            formatterResource).dump(tempCms, formatterResource, null, locale, request, response),
162                        encoding)).trim();
163                }
164            }
165        } catch (Exception e) {
166            LOG.warn(e.getLocalizedMessage(), e);
167        }
168        return null;
169    }
170
171    /**
172     * Reads the focal point from a resource.<p>
173     *
174     * @param cms  the CMS context to use
175     * @param resource the resource
176     * @return the focal point (or null, if the focal point property is not set or contains an invalid value)
177     *
178     * @throws CmsException if something goes wrong
179     */
180    public static CmsPoint readFocalPoint(CmsObject cms, CmsResource resource) throws CmsException {
181
182        CmsProperty focalPointProp = cms.readPropertyObject(
183            resource,
184            CmsPropertyDefinition.PROPERTY_IMAGE_FOCAL_POINT,
185            false);
186        CmsPoint focalPoint = null;
187        if (!focalPointProp.isNullProperty()) {
188            String focalPointVal = focalPointProp.getValue();
189            Matcher matcher = PATTERN_FOCAL_POINT.matcher(focalPointVal);
190            if (matcher.matches()) {
191                int fx = Integer.parseInt(matcher.group(1));
192                int fy = Integer.parseInt(matcher.group(2));
193                focalPoint = new CmsPoint(fx, fy);
194
195            }
196        }
197        return focalPoint;
198    }
199
200    /**
201     * @see org.opencms.ade.galleries.shared.rpc.I_CmsPreviewService#getImageInfo(java.lang.String, java.lang.String)
202     */
203    public CmsImageInfoBean getImageInfo(String resourcePath, String locale) throws CmsRpcException {
204
205        CmsObject cms = getCmsObject();
206        CmsImageInfoBean resInfo = new CmsImageInfoBean();
207        try {
208            int pos = resourcePath.indexOf("?");
209            String resName = resourcePath;
210            if (pos > -1) {
211                resName = resourcePath.substring(0, pos);
212            }
213            CmsResource resource = readResourceFromCurrentOrRootSite(cms, resName);
214            readResourceInfo(cms, resource, resInfo, locale);
215            resInfo.setViewLink(
216                CmsStringUtil.joinPaths(
217                    OpenCms.getSystemInfo().getOpenCmsContext(),
218                    CmsPermalinkResourceHandler.PERMALINK_HANDLER,
219                    resource.getStructureId().toString()));
220            resInfo.setHash(resource.getStructureId().hashCode());
221            CmsImageScaler scaler = new CmsImageScaler(cms, resource);
222            int height = -1;
223            int width = -1;
224            if (scaler.isValid()) {
225                height = scaler.getHeight();
226                width = scaler.getWidth();
227            }
228            CmsPoint focalPoint = readFocalPoint(cms, resource);
229            resInfo.setFocalPoint(focalPoint);
230
231            resInfo.setHeight(height);
232            resInfo.setWidth(width);
233            CmsProperty property = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_COPYRIGHT, false);
234            if (!property.isNullProperty()) {
235                resInfo.setCopyright(property.getValue());
236            }
237        } catch (Exception e) {
238            error(e);
239        }
240        return resInfo;
241    }
242
243    /**
244     * @see org.opencms.ade.galleries.shared.rpc.I_CmsPreviewService#getResourceInfo(java.lang.String, java.lang.String)
245     */
246    public CmsResourceInfoBean getResourceInfo(String resourcePath, String locale) throws CmsRpcException {
247
248        CmsObject cms = getCmsObject();
249        CmsResourceInfoBean resInfo = new CmsResourceInfoBean();
250        try {
251            int pos = resourcePath.indexOf("?");
252            String resName = resourcePath;
253            if (pos > -1) {
254                resName = resourcePath.substring(0, pos);
255            }
256            CmsResource resource = readResourceFromCurrentOrRootSite(cms, resName);
257            readResourceInfo(cms, resource, resInfo, locale);
258        } catch (CmsException e) {
259            error(e);
260        }
261        return resInfo;
262    }
263
264    /**
265     * Retrieves the resource information and puts it into the provided resource info bean.<p>
266     *
267     * @param cms the initialized cms object
268     * @param resource the resource
269     * @param resInfo the resource info bean
270     * @param locale the content locale
271     *
272     * @throws CmsException if something goes wrong
273     */
274    public void readResourceInfo(CmsObject cms, CmsResource resource, CmsResourceInfoBean resInfo, String locale)
275    throws CmsException {
276
277        I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource.getTypeId());
278        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms);
279        resInfo.setTitle(resource.getName());
280        resInfo.setStructureId(resource.getStructureId());
281        resInfo.setDescription(CmsWorkplaceMessages.getResourceTypeName(wpLocale, type.getTypeName()));
282        resInfo.setResourcePath(cms.getSitePath(resource));
283        resInfo.setResourceType(type.getTypeName());
284        resInfo.setBigIconClasses(
285            CmsIconUtil.getIconClasses(CmsIconUtil.getDisplayType(cms, resource), resource.getName(), false));
286        // set the default file and detail type info
287        String detailType = CmsResourceIcon.getDefaultFileOrDetailType(cms, resource);
288        if (detailType != null) {
289            resInfo.setSmallIconClasses(CmsIconUtil.getIconClasses(detailType, null, true));
290        }
291        resInfo.setSize((resource.getLength() / 1024) + " kb");
292        resInfo.setLastModified(new Date(resource.getDateLastModified()));
293        resInfo.setNoEditReason(new CmsResourceUtil(cms, resource).getNoEditReason(wpLocale, true));
294        // reading default explorer-type properties
295        CmsExplorerTypeSettings setting = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName());
296        List<String> properties;
297        if (OpenCms.getResourceManager().matchResourceType(
298            CmsResourceTypeImage.getStaticTypeName(),
299            resource.getTypeId())) {
300            properties = Lists.newArrayList(
301                CmsPropertyDefinition.PROPERTY_TITLE,
302                CmsPropertyDefinition.PROPERTY_COPYRIGHT);
303        } else {
304            properties = setting.getProperties();
305            String reference = setting.getReference();
306            while ((properties.size() == 0) && !CmsStringUtil.isEmptyOrWhitespaceOnly(reference)) {
307                // looking up properties from referenced explorer types if properties list is empty
308                setting = OpenCms.getWorkplaceManager().getExplorerTypeSetting(reference);
309                properties = setting.getProperties();
310                reference = setting.getReference();
311            }
312        }
313        Map<String, String> props = new LinkedHashMap<String, String>();
314        Iterator<String> propIt = properties.iterator();
315        while (propIt.hasNext()) {
316            String propertyName = propIt.next();
317            CmsProperty property = cms.readPropertyObject(resource, propertyName, false);
318            if (!property.isNullProperty()) {
319                props.put(property.getName(), property.getValue());
320            } else {
321                props.put(propertyName, null);
322            }
323        }
324        resInfo.setProperties(props);
325        resInfo.setPreviewContent(getPreviewContent(cms, resource, CmsLocaleManager.getLocale(locale)));
326    }
327
328    /**
329     * @see org.opencms.ade.galleries.shared.rpc.I_CmsPreviewService#updateImageProperties(java.lang.String, java.lang.String, java.util.Map)
330     */
331    public CmsImageInfoBean updateImageProperties(String resourcePath, String locale, Map<String, String> properties)
332    throws CmsRpcException {
333
334        try {
335            saveProperties(resourcePath, properties);
336        } catch (CmsException e) {
337            error(e);
338        }
339        return getImageInfo(resourcePath, locale);
340    }
341
342    /**
343     * @see org.opencms.ade.galleries.shared.rpc.I_CmsPreviewService#updateResourceProperties(java.lang.String, java.lang.String, java.util.Map)
344     */
345    public CmsResourceInfoBean updateResourceProperties(
346        String resourcePath,
347        String locale,
348        Map<String, String> properties)
349    throws CmsRpcException {
350
351        try {
352            saveProperties(resourcePath, properties);
353        } catch (CmsException e) {
354            error(e);
355        }
356        return getResourceInfo(resourcePath, locale);
357    }
358
359    /**
360     * Renders the preview content for the given resource and locale.<p>
361     *
362     * @param cms the cms context
363     * @param resource the resource
364     * @param locale the content locale
365     *
366     * @return the rendered HTML preview content
367     */
368    private String getPreviewContent(CmsObject cms, CmsResource resource, Locale locale) {
369
370        return getPreviewContent(getRequest(), getResponse(), cms, resource, locale);
371    }
372
373    /**
374     * Tries to read a resource either from the current site or from the root site.<p>
375     *
376     * @param cms the CMS context to use
377     * @param name the resource path
378     *
379     * @return the resource which was read
380     * @throws CmsException if something goes wrong
381     */
382    private CmsResource readResourceFromCurrentOrRootSite(CmsObject cms, String name) throws CmsException {
383
384        CmsResource resource = null;
385        try {
386            resource = cms.readResource(name, CmsResourceFilter.IGNORE_EXPIRATION);
387        } catch (CmsVfsResourceNotFoundException e) {
388            String originalSiteRoot = cms.getRequestContext().getSiteRoot();
389            try {
390                cms.getRequestContext().setSiteRoot("");
391                resource = cms.readResource(name, CmsResourceFilter.IGNORE_EXPIRATION);
392            } finally {
393                cms.getRequestContext().setSiteRoot(originalSiteRoot);
394            }
395
396        }
397        return resource;
398    }
399
400    /**
401     * Saves the given properties to the resource.<p>
402     *
403     * @param resourcePath the resource path
404     * @param properties the properties
405     *
406     * @throws CmsException if something goes wrong
407     */
408    private void saveProperties(String resourcePath, Map<String, String> properties) throws CmsException {
409
410        CmsResource resource;
411        CmsObject cms = getCmsObject();
412        int pos = resourcePath.indexOf("?");
413        String resName = resourcePath;
414        if (pos > -1) {
415            resName = resourcePath.substring(0, pos);
416        }
417        resource = cms.readResource(resName);
418
419        if (properties != null) {
420            for (Entry<String, String> entry : properties.entrySet()) {
421                String propertyName = entry.getKey();
422                String propertyValue = entry.getValue();
423                if (CmsStringUtil.isEmptyOrWhitespaceOnly(propertyValue)) {
424                    propertyValue = "";
425                }
426                try {
427                    CmsProperty currentProperty = cms.readPropertyObject(resource, propertyName, false);
428                    // detect if property is a null property or not
429                    if (currentProperty.isNullProperty()) {
430                        // create new property object and set key and value
431                        currentProperty = new CmsProperty();
432                        currentProperty.setName(propertyName);
433                        if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
434                            // set structure value
435                            currentProperty.setStructureValue(propertyValue);
436                            currentProperty.setResourceValue(null);
437                        } else {
438                            // set resource value
439                            currentProperty.setStructureValue(null);
440                            currentProperty.setResourceValue(propertyValue);
441                        }
442                    } else if (currentProperty.getStructureValue() != null) {
443                        // structure value has to be updated
444                        currentProperty.setStructureValue(propertyValue);
445                        currentProperty.setResourceValue(null);
446                    } else {
447                        // resource value has to be updated
448                        currentProperty.setStructureValue(null);
449                        currentProperty.setResourceValue(propertyValue);
450                    }
451                    CmsLock lock = cms.getLock(resource);
452                    if (lock.isUnlocked()) {
453                        // lock resource before operation
454                        cms.lockResource(resName);
455                    }
456                    // write the property to the resource
457                    cms.writePropertyObject(resName, currentProperty);
458                    // unlock the resource
459                    cms.unlockResource(resName);
460                } catch (CmsException e) {
461                    // writing the property failed, log error
462                    log(e.getLocalizedMessage());
463                }
464            }
465        }
466    }
467
468}