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.workplace.editors.directedit;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsResourceTypeConfig;
032import org.opencms.ade.contenteditor.shared.CmsEditorConstants;
033import org.opencms.file.CmsResource;
034import org.opencms.file.CmsResourceFilter;
035import org.opencms.file.types.CmsResourceTypeXmlContent;
036import org.opencms.file.types.I_CmsResourceType;
037import org.opencms.gwt.shared.CmsGwtConstants;
038import org.opencms.gwt.shared.I_CmsCollectorInfoFactory;
039import org.opencms.gwt.shared.I_CmsContentLoadCollectorInfo;
040import org.opencms.i18n.CmsEncoder;
041import org.opencms.json.JSONException;
042import org.opencms.json.JSONObject;
043import org.opencms.jsp.util.CmsJspStandardContextBean;
044import org.opencms.lock.CmsLock;
045import org.opencms.main.CmsException;
046import org.opencms.main.CmsLog;
047import org.opencms.main.OpenCms;
048import org.opencms.security.CmsPermissionSet;
049import org.opencms.util.CmsStringUtil;
050import org.opencms.util.CmsUUID;
051import org.opencms.workplace.editors.Messages;
052import org.opencms.workplace.explorer.CmsResourceUtil;
053import org.opencms.xml.containerpage.CmsContainerElementBean;
054
055import java.util.Random;
056
057import javax.servlet.jsp.JspException;
058import javax.servlet.jsp.PageContext;
059
060import org.apache.commons.logging.Log;
061
062import com.google.web.bindery.autobean.shared.AutoBean;
063import com.google.web.bindery.autobean.shared.AutoBeanCodex;
064import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
065
066/**
067 * Provider for the OpenCms AdvancedDirectEdit.<p>
068 *
069 * Since OpenCms version 8.0.0.<p>
070 *
071 * This provider DOES NOT support {@link CmsDirectEditMode#MANUAL} mode.<p>
072 *
073 * @since 8.0.0
074 */
075public class CmsAdvancedDirectEditProvider extends A_CmsDirectEditProvider {
076
077    /** The log object for this class. */
078    private static final Log LOG = CmsLog.getLog(CmsAdvancedDirectEditProvider.class);
079
080    /** Indicates the permissions for the last element the was opened. */
081    protected int m_lastPermissionMode;
082
083    /** True if the elements should be assigned randomly generated ids. */
084    protected boolean m_useIds;
085
086    /** The random number generator used for element ids. */
087    private Random m_random = new Random();
088
089    /**
090     * Returns the end HTML for a disabled direct edit button.<p>
091     *
092     * @return the end HTML for a disabled direct edit button
093     */
094    public String endDirectEditDisabled() {
095
096        return "";
097    }
098
099    /**
100     * Returns the end HTML for an enabled direct edit button.<p>
101     *
102     * @return the end HTML for an enabled direct edit button
103     */
104    public String endDirectEditEnabled() {
105
106        return "<div class=\"" + CmsGwtConstants.CLASS_EDITABLE_END + "\"></div>\n";
107    }
108
109    /**
110     * Generates a random element id.<p>
111     *
112     * @return a random  element id
113     */
114    public synchronized String getRandomId() {
115
116        return "editable_" + Math.abs(m_random.nextLong());
117    }
118
119    /**
120     * @see org.opencms.workplace.editors.directedit.A_CmsDirectEditProvider#getResourceInfo(java.lang.String)
121     *
122     * Similar to the method in the superclass, but removes the write permission check, as this is handled differently.
123     */
124    @Override
125    public CmsDirectEditResourceInfo getResourceInfo(String resourceName) {
126
127        try {
128            // first check some simple preconditions for direct edit
129            if (m_cms.getRequestContext().getCurrentProject().isOnlineProject()) {
130                // don't show direct edit button in online project
131                return CmsDirectEditResourceInfo.INACTIVE;
132            }
133            if (CmsResource.isTemporaryFileName(resourceName)) {
134                // don't show direct edit button on a temporary file
135                return CmsDirectEditResourceInfo.INACTIVE;
136            }
137            if (!m_cms.isInsideCurrentProject(resourceName)) {
138                // don't show direct edit button on files not belonging to the current project
139                return CmsDirectEditResourceInfo.INACTIVE;
140            }
141            // read the target resource
142            CmsResource resource = m_cms.readResource(resourceName, CmsResourceFilter.ALL);
143            if (!OpenCms.getResourceManager().getResourceType(resource.getTypeId()).isDirectEditable()
144                && !resource.isFolder()) {
145                // don't show direct edit button for non-editable resources
146                return CmsDirectEditResourceInfo.INACTIVE;
147            }
148            // check the resource lock
149            CmsLock lock = m_cms.getLock(resource);
150            boolean locked = !(lock.isUnlocked()
151                || lock.isOwnedInProjectBy(
152                    m_cms.getRequestContext().getCurrentUser(),
153                    m_cms.getRequestContext().getCurrentProject()));
154            // check the users permissions on the resource
155            // only if write permissions are granted the resource may be direct editable
156            if (locked) {
157                // a locked resource must be shown as "disabled"
158                return new CmsDirectEditResourceInfo(CmsDirectEditPermissions.DISABLED, resource, lock);
159            }
160            // if we have write permission and the resource is not locked then direct edit is enabled
161            return new CmsDirectEditResourceInfo(CmsDirectEditPermissions.ENABLED, resource, lock);
162
163        } catch (Exception e) {
164            // all exceptions: don't mix up the result HTML, always return INACTIVE mode
165            if (LOG.isWarnEnabled()) {
166                LOG.warn(
167                    org.opencms.workplace.editors.Messages.get().getBundle().key(
168                        org.opencms.workplace.editors.Messages.LOG_CALC_EDIT_MODE_FAILED_1,
169                        resourceName),
170                    e);
171            }
172        }
173        // otherwise the resource is not direct editable
174        return CmsDirectEditResourceInfo.INACTIVE;
175    }
176
177    /**
178     * @see org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider#insertDirectEditEnd(javax.servlet.jsp.PageContext)
179     */
180    public void insertDirectEditEnd(PageContext context) throws JspException {
181
182        String content;
183        switch (m_lastPermissionMode) {
184
185            case 1: // disabled
186                //                content = endDirectEditDisabled();
187                //                break;
188            case 2: // enabled
189                content = endDirectEditEnabled();
190                break;
191            default: // inactive or undefined
192                content = null;
193        }
194        m_lastPermissionMode = 0;
195        print(context, content);
196    }
197
198    /**
199     * @see org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider#insertDirectEditIncludes(javax.servlet.jsp.PageContext, org.opencms.workplace.editors.directedit.CmsDirectEditParams)
200     */
201    @SuppressWarnings("unused")
202    public void insertDirectEditIncludes(PageContext context, CmsDirectEditParams params) throws JspException {
203
204        // For Advanced Direct Edit all necessary js and css-code is included by the enableADE tag. Further includes in the head are not needed.
205
206    }
207
208    /**
209     * @see org.opencms.workplace.editors.directedit.A_CmsDirectEditProvider#insertDirectEditListMetadata(javax.servlet.jsp.PageContext, org.opencms.gwt.shared.I_CmsContentLoadCollectorInfo)
210     */
211    @Override
212    public void insertDirectEditListMetadata(PageContext context, I_CmsContentLoadCollectorInfo info)
213    throws JspException {
214
215        if (m_cms.getRequestContext().getCurrentProject().isOnlineProject()) {
216            // the metadata is only needed for editing
217            return;
218        }
219        I_CmsCollectorInfoFactory collectorInfoFactory = AutoBeanFactorySource.create(I_CmsCollectorInfoFactory.class);
220        AutoBean<I_CmsContentLoadCollectorInfo> collectorInfoAutoBean = collectorInfoFactory.wrapCollectorInfo(info);
221        String serializedCollectorInfo = AutoBeanCodex.encode(collectorInfoAutoBean).getPayload();
222
223        String marker = "<div class='"
224            + CmsGwtConstants.CLASS_COLLECTOR_INFO
225            + "' style='display: none !important;' "
226            + CmsGwtConstants.ATTR_DATA_COLLECTOR
227            + "='"
228            + CmsEncoder.escapeXml(serializedCollectorInfo)
229            + "'></div>";
230        print(context, marker);
231    }
232
233    /**
234     * @see org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider#insertDirectEditStart(javax.servlet.jsp.PageContext, org.opencms.workplace.editors.directedit.CmsDirectEditParams)
235     */
236    public boolean insertDirectEditStart(PageContext context, CmsDirectEditParams params) throws JspException {
237
238        String content;
239        // check the direct edit permissions of the current user
240        CmsDirectEditResourceInfo resourceInfo = getResourceInfo(params.getResourceName());
241
242        // check the permission mode
243        m_lastPermissionMode = resourceInfo.getPermissions().getPermission();
244        switch (m_lastPermissionMode) {
245            case 1: // disabled
246                //                content = startDirectEditDisabled(params, resourceInfo);
247                //                break;
248            case 2: // enabled
249                try {
250                    CmsJspStandardContextBean contextBean = CmsJspStandardContextBean.getInstance(context.getRequest());
251                    CmsContainerElementBean element = contextBean.getElement();
252                    if ((element != null) && element.getId().equals(resourceInfo.getResource().getStructureId())) {
253                        params.m_element = element.editorHash();
254                        params.setContainerElement(element);
255                    }
256                    content = startDirectEditEnabled(params, resourceInfo);
257                } catch (JSONException e) {
258                    throw new JspException(e);
259                }
260                break;
261            default: // inactive or undefined
262                content = null;
263        }
264        print(context, content);
265        return content != null;
266    }
267
268    /**
269     * Returns <code>false</code> because the default provider does not support manual button placement.<p>
270     *
271     * @see org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider#isManual(org.opencms.workplace.editors.directedit.CmsDirectEditMode)
272     */
273    @Override
274    public boolean isManual(CmsDirectEditMode mode) {
275
276        return false;
277    }
278
279    /**
280     * @see org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider#newInstance()
281     */
282    public I_CmsDirectEditProvider newInstance() {
283
284        CmsAdvancedDirectEditProvider result = new CmsAdvancedDirectEditProvider();
285        result.m_configurationParameters = m_configurationParameters;
286        return result;
287    }
288
289    /**
290     * Returns the start HTML for a disabled direct edit button.<p>
291     *
292     * @param params the direct edit parameters
293     * @param resourceInfo contains information about the resource to edit
294     *
295     * @return the start HTML for a disabled direct edit button
296     */
297    public String startDirectEditDisabled(CmsDirectEditParams params, CmsDirectEditResourceInfo resourceInfo) {
298
299        StringBuffer result = new StringBuffer(256);
300
301        result.append("<!-- EDIT BLOCK START (DISABLED): ");
302        result.append(params.m_resourceName);
303        result.append(" [");
304        result.append(resourceInfo.getResource().getState());
305        result.append("] ");
306        if (!resourceInfo.getLock().isUnlocked()) {
307            result.append(" locked ");
308            result.append(resourceInfo.getLock().getProject().getName());
309        }
310        result.append(" -->\n");
311        return result.toString();
312    }
313
314    /**
315     * Returns the start HTML for an enabled direct edit button.<p>
316     *
317     * @param params the direct edit parameters
318     * @param resourceInfo contains information about the resource to edit
319     *
320     * @return the start HTML for an enabled direct edit button
321     * @throws JSONException if a JSON handling error occurs
322     */
323    public String startDirectEditEnabled(CmsDirectEditParams params, CmsDirectEditResourceInfo resourceInfo)
324    throws JSONException {
325
326        String editLocale = m_cms.getRequestContext().getLocale().toString();
327        String editId = getNextDirectEditId();
328        String editNewLink = CmsEncoder.encode(params.getLinkForNew());
329        // putting together all needed data
330        JSONObject editableData = new JSONObject();
331        CmsResource resource = resourceInfo.getResource();
332        boolean writable = false;
333        if (resource != null) {
334            try {
335                writable = m_cms.hasPermissions(
336                    resource,
337                    CmsPermissionSet.ACCESS_WRITE,
338                    false,
339                    CmsResourceFilter.IGNORE_EXPIRATION);
340            } catch (CmsException e) {
341                LOG.error(e.getLocalizedMessage(), e);
342            }
343        }
344        editableData.put("editId", editId);
345        CmsContainerElementBean containerElement = params.getContainerElement();
346        if (containerElement != null) {
347            editableData.put(CmsGwtConstants.ATTR_ELEMENT_ID, containerElement.editorHash());
348        }
349
350        editableData.put("structureId", resourceInfo.getResource().getStructureId());
351        editableData.put("sitePath", params.getResourceName());
352        editableData.put("elementlanguage", editLocale);
353        editableData.put("elementname", params.getElement());
354        editableData.put("newlink", editNewLink);
355        editableData.put("hasResource", resource != null);
356        editableData.put("hasEdit", params.getButtonSelection().isShowEdit() && writable);
357        editableData.put("hasDelete", params.getButtonSelection().isShowDelete() && writable);
358        editableData.put("hasNew", params.getButtonSelection().isShowNew());
359        editableData.put("newtitle", m_messages.key(Messages.GUI_EDITOR_TITLE_NEW_0));
360        editableData.put(
361            "unreleaseOrExpired",
362            !resourceInfo.getResource().isReleasedAndNotExpired(System.currentTimeMillis()));
363        if (params.getId() != null) {
364            editableData.put(CmsEditorConstants.ATTR_CONTEXT_ID, params.getId().toString());
365        }
366        editableData.put(CmsEditorConstants.ATTR_POST_CREATE_HANDLER, params.getPostCreateHandler());
367        CmsUUID viewId = CmsUUID.getNullUUID();
368        boolean hasEditHandler = false;
369        if ((resourceInfo.getResource() != null) && resourceInfo.getResource().isFile()) {
370            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resourceInfo.getResource());
371            if (type instanceof CmsResourceTypeXmlContent) {
372                hasEditHandler = ((CmsResourceTypeXmlContent)type).getEditHandler(m_cms) != null;
373            }
374            CmsADEConfigData configData = OpenCms.getADEManager().lookupConfiguration(
375                m_cms,
376                resourceInfo.getResource().getRootPath());
377            CmsResourceTypeConfig typeConfig = configData.getResourceType(
378                OpenCms.getResourceManager().getResourceType(resourceInfo.getResource()).getTypeName());
379            if (typeConfig != null) {
380                viewId = typeConfig.getElementView();
381            }
382        }
383        editableData.put(CmsEditorConstants.ATTR_ELEMENT_VIEW, viewId);
384        editableData.put("hasEditHandler", hasEditHandler);
385        if (m_lastPermissionMode == 1) {
386
387            try {
388                String noEditReason = new CmsResourceUtil(m_cms, resourceInfo.getResource()).getNoEditReason(
389                    OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms),
390                    true);
391                if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(noEditReason)) {
392                    editableData.put("noEditReason", noEditReason);
393                }
394            } catch (CmsException e) {
395                LOG.error(e.getLocalizedMessage(), e);
396            }
397        }
398        StringBuffer result = new StringBuffer(512);
399        if (m_useIds) {
400            result.append(
401                "<div id=\""
402                    + getRandomId()
403                    + "\" class='"
404                    + CmsGwtConstants.CLASS_EDITABLE
405                    + "' "
406                    + CmsGwtConstants.ATTR_DATA_EDITABLE
407                    + "='").append(editableData.toString()).append("'></div>\n");
408        } else {
409            result.append(
410                "<div class='"
411                    + CmsGwtConstants.CLASS_EDITABLE
412                    + "' "
413                    + CmsGwtConstants.ATTR_DATA_EDITABLE
414                    + "='").append(editableData.toString()).append("'></div>\n");
415        }
416        return result.toString();
417    }
418}