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 GmbH & Co. KG, 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;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProperty;
033import org.opencms.file.CmsPropertyDefinition;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.i18n.CmsEncoder;
037import org.opencms.i18n.CmsLocaleManager;
038import org.opencms.jsp.CmsJspActionElement;
039import org.opencms.lock.CmsLock;
040import org.opencms.lock.CmsLockType;
041import org.opencms.main.CmsException;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.security.CmsPermissionSet;
045import org.opencms.security.I_CmsPrincipal;
046import org.opencms.ui.CmsVaadinUtils;
047import org.opencms.util.CmsStringUtil;
048import org.opencms.workplace.CmsWorkplace;
049import org.opencms.xml.content.CmsXmlContent;
050import org.opencms.xml.content.CmsXmlContentFactory;
051
052import java.io.IOException;
053import java.util.ArrayList;
054import java.util.HashMap;
055import java.util.List;
056import java.util.Locale;
057
058import javax.servlet.ServletException;
059import javax.servlet.http.HttpSession;
060import javax.servlet.jsp.JspException;
061
062import org.apache.commons.logging.Log;
063
064/**
065 * Provides basic methods for building the file editors of OpenCms.<p>
066 *
067 * The editor classes have to extend this class and implement action methods for common editor actions.<p>
068 *
069 * @since 6.0.0
070 */
071public abstract class CmsEditor extends CmsEditorBase {
072
073    /** Value for the action: change the body. */
074    public static final int ACTION_CHANGE_BODY = 124;
075
076    /** Value for the action: delete the current locale. */
077    public static final int ACTION_DELETELOCALE = 140;
078
079    /** Value for the action: exit. */
080    public static final int ACTION_EXIT = 122;
081
082    /** Value for the action: show a preview. */
083    public static final int ACTION_PREVIEW = 126;
084
085    /** Value for the action: save. */
086    public static final int ACTION_SAVE = 121;
087
088    /** Constant value for the customizable action button. */
089    public static final int ACTION_SAVEACTION = 130;
090
091    /** Value for the action: save and exit. */
092    public static final int ACTION_SAVEEXIT = 123;
093
094    /** Value for the action: show the editor. */
095    public static final int ACTION_SHOW = 125;
096
097    /** Value for the action: an error occurred. */
098    public static final int ACTION_SHOW_ERRORMESSAGE = 127;
099
100    /** Value for the action parameter: change the element. */
101    public static final String EDITOR_CHANGE_ELEMENT = "changeelement";
102
103    /** Value for the action parameter: cleanup content. */
104    public static final String EDITOR_CLEANUP = "cleanup";
105
106    /** Value for the action parameter: close browser window (accidentally). */
107    public static final String EDITOR_CLOSEBROWSER = "closebrowser";
108
109    /** Value for the action parameter: delete the current locale. */
110    public static final String EDITOR_DELETELOCALE = "deletelocale";
111
112    /** Value for the action parameter: exit editor. */
113    public static final String EDITOR_EXIT = "exit";
114
115    /** Value for the action parameter: show a preview. */
116    public static final String EDITOR_PREVIEW = "preview";
117
118    /** Value for the action parameter: save content. */
119    public static final String EDITOR_SAVE = "save";
120
121    /** Value for the customizable action button. */
122    public static final String EDITOR_SAVEACTION = "saveaction";
123
124    /** Value for the action parameter: save and exit. */
125    public static final String EDITOR_SAVEEXIT = "saveexit";
126
127    /** Value for the action parameter: show the editor. */
128    public static final String EDITOR_SHOW = "show";
129
130    /** Value for the action parameter: an error occurred. */
131    public static final String EDITOR_SHOW_ERRORMESSAGE = "error";
132
133    /** Marker for empty locale in locale selection. */
134    public static final String EMPTY_LOCALE = " [-]";
135
136    /** Parameter name for the request parameter "backlink". */
137    public static final String PARAM_BACKLINK = "backlink";
138
139    /** Parameter name for the request parameter "content". */
140    public static final String PARAM_CONTENT = "content";
141
142    /** Parameter name for the request parameter "directedit". */
143    public static final String PARAM_DIRECTEDIT = "directedit";
144
145    /** Parameter name for the request parameter "editastext". */
146    public static final String PARAM_EDITASTEXT = "editastext";
147
148    /** Parameter name for the request parameter "editormode". */
149    public static final String PARAM_EDITORMODE = "editormode";
150
151    /** Parameter name for the request parameter "element language". */
152    public static final String PARAM_ELEMENTLANGUAGE = "elementlanguage";
153
154    /** Parameter name for the request parameter "loaddefault". */
155    public static final String PARAM_LOADDEFAULT = "loaddefault";
156
157    /** Parameter name for the request parameter "modified". */
158    public static final String PARAM_MODIFIED = "modified";
159
160    /** Parameter name for the request parameter "old element language". */
161    public static final String PARAM_OLDELEMENTLANGUAGE = "oldelementlanguage";
162
163    /** Parameter name for the request parameter "tempfile". */
164    public static final String PARAM_TEMPFILE = "tempfile";
165
166    /** Stores the VFS editor path. */
167    public static final String PATH_EDITORS = PATH_WORKPLACE + "editors/";
168
169    /** The log object for this class. */
170    private static final Log LOG = CmsLog.getLog(CmsEditor.class);
171
172    /** The editor session info bean. */
173    private CmsEditorSessionInfo m_editorSessionInfo;
174
175    /** The encoding to use (will be read from the file property). */
176    private String m_fileEncoding;
177
178    /** Back link parameter. */
179    private String m_paramBackLink;
180
181    /** Content parameter. */
182    private String m_paramContent;
183
184    /** Direct edit parameter. */
185    private String m_paramDirectedit;
186
187    /** Edit as text parameter. */
188    private String m_paramEditAsText;
189
190    /** Editor mode parameter. */
191    private String m_paramEditormode;
192
193    /** Element language parameter. */
194    private String m_paramElementlanguage;
195
196    /** Load default parameter. */
197    private String m_paramLoadDefault;
198
199    /** Modified parameter. */
200    private String m_paramModified;
201
202    /** Old element language parameter. */
203    private String m_paramOldelementlanguage;
204
205    /** Temporary file parameter. */
206    private String m_paramTempFile;
207
208    /** Helper variable to store the uri to the editors pictures. */
209    private String m_picsUri;
210
211    /**
212     * Public constructor.<p>
213     *
214     * @param jsp an initialized JSP action element
215     */
216    public CmsEditor(CmsJspActionElement jsp) {
217
218        super(jsp);
219    }
220
221    /**
222     * Unlocks the edited resource when in direct edit mode or when the resource was not modified.<p>
223     *
224     * @param forceUnlock if true, the resource will be unlocked anyway
225     */
226    public abstract void actionClear(boolean forceUnlock);
227
228    /**
229     * Performs the exit editor action.<p>
230     *
231     * @throws CmsException if something goes wrong
232     * @throws IOException if a forward fails
233     * @throws ServletException if a forward fails
234     * @throws JspException if including an element fails
235     */
236    public abstract void actionExit() throws CmsException, IOException, ServletException, JspException;
237
238    /**
239     * Performs the save content action.<p>
240     *
241     * @throws IOException if a redirection fails
242     * @throws JspException if including an element fails
243     */
244    public abstract void actionSave() throws IOException, JspException;
245
246    /**
247     * Builds the html String for the element language selector.<p>
248     *
249     * @param attributes optional attributes for the &lt;select&gt; tag
250     * @param resourceName the name of the resource to edit
251     * @param selectedLocale the currently selected Locale
252     * @return the html for the element language selectbox
253     */
254    public String buildSelectElementLanguage(String attributes, String resourceName, Locale selectedLocale) {
255
256        // get locale names based on properties and global settings
257        List<Locale> locales = OpenCms.getLocaleManager().getAvailableLocales(getCms(), resourceName);
258        List<String> options = new ArrayList<String>(locales.size());
259        List<String> selectList = new ArrayList<String>(locales.size());
260        int currentIndex = -1;
261
262        //get the locales already used in the resource
263        List<Locale> contentLocales = new ArrayList<Locale>();
264        try {
265
266            CmsResource res = getCms().readResource(resourceName, CmsResourceFilter.IGNORE_EXPIRATION);
267            String temporaryFilename = CmsWorkplace.getTemporaryFileName(resourceName);
268            if (getCms().existsResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION)) {
269                res = getCms().readResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION);
270            }
271            CmsFile file = getCms().readFile(res);
272            CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file);
273            contentLocales = xmlContent.getLocales();
274        } catch (CmsException e) {
275            // to nothing here in case the resource could not be opened
276            if (LOG.isErrorEnabled()) {
277                LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, resourceName), e);
278            }
279        }
280
281        for (int counter = 0; counter < locales.size(); counter++) {
282            // create the list of options and values
283            Locale curLocale = locales.get(counter);
284            selectList.add(curLocale.toString());
285            StringBuffer buf = new StringBuffer();
286            buf.append(curLocale.getDisplayName(getLocale()));
287            if (!contentLocales.contains(curLocale)) {
288                buf.append(EMPTY_LOCALE);
289            }
290            options.add(buf.toString());
291            if (curLocale.equals(selectedLocale)) {
292                // set the selected index of the selector
293                currentIndex = counter;
294            }
295        }
296
297        if (currentIndex == -1) {
298            // no matching element language found, use first element language in list
299            if (selectList.size() > 0) {
300                currentIndex = 0;
301                setParamElementlanguage(selectList.get(0));
302            }
303        }
304
305        return buildSelect(attributes, options, selectList, currentIndex, false);
306    }
307
308    /**
309     * Generates a button for the OpenCms editor.<p>
310     *
311     * @param href the href link for the button, if none is given the button will be disabled
312     * @param target the href link target for the button, if none is given the target will be same window
313     * @param image the image name for the button, skin path will be automatically added as prefix
314     * @param label the label for the text of the button
315     * @param type 0: image only (default), 1: image and text, 2: text only
316     * @param useCustomImage if true, the button has to be placed in the editors "custom pics" folder
317     *
318     * @return a button for the OpenCms editor
319     */
320    public String button(String href, String target, String image, String label, int type, boolean useCustomImage) {
321
322        if (useCustomImage) {
323            // search the picture in the "custom pics" folder
324            return button(href, target, image, label, type, getPicsUri());
325        } else {
326            // search the picture in the common "buttons" folder
327            return button(href, target, image, label, type);
328        }
329    }
330
331    /**
332     * Returns the editor action for a "cancel" button.<p>
333     *
334     * This overwrites the cancel method of the CmsDialog class.<p>
335     *
336     * Always use this value, do not write anything directly in the html page.<p>
337     *
338     * @return the default action for a "cancel" button
339     */
340    public String buttonActionCancel() {
341
342        String target = null;
343        if (Boolean.valueOf(getParamDirectedit()).booleanValue()) {
344            // editor is in direct edit mode
345            if (CmsStringUtil.isNotEmpty(getParamBacklink())) {
346                // set link to the specified back link target
347                target = getParamBacklink();
348            } else {
349                // set link to the edited resource
350                target = getParamResource();
351            }
352        } else {
353            // in workplace mode, show explorer view
354            target = CmsVaadinUtils.getWorkplaceLink();
355        }
356        return "onclick=\"top.location.href='" + getJsp().link(target) + "';\"";
357    }
358
359    /**
360     * Builds the html to display the special action button for the direct edit mode of the editor.<p>
361     *
362     * @param jsFunction the JavaScript function which will be executed on the mouseup event
363     * @param type 0: image only (default), 1: image and text, 2: text only
364     * @return the html to display the special action button
365     */
366    public String buttonActionDirectEdit(String jsFunction, int type) {
367
368        // get the action class from the OpenCms runtime property
369        I_CmsEditorActionHandler actionClass = OpenCms.getWorkplaceManager().getEditorActionHandler();
370        String url;
371        String name;
372        boolean active = false;
373        if (actionClass != null) {
374            // get button parameters and state from action class
375            url = actionClass.getButtonUrl(getJsp(), getParamResource());
376            name = actionClass.getButtonName();
377            active = actionClass.isButtonActive(getJsp(), getParamResource());
378        } else {
379            // action class not defined, display inactive button
380            url = getSkinUri() + "buttons/publish_in.png";
381            name = Messages.GUI_EXPLORER_CONTEXT_PUBLISH_0;
382        }
383        String image = url.substring(url.lastIndexOf("/") + 1);
384        if (url.endsWith(".gif")) {
385            image = image.substring(0, image.length() - 4);
386        }
387
388        if (active) {
389            // create the link for the button
390            return button(
391                "javascript:" + jsFunction,
392                null,
393                image,
394                name,
395                type,
396                url.substring(0, url.lastIndexOf("/") + 1));
397        } else {
398            // create the inactive button
399            return button(null, null, image, name, type, url.substring(0, url.lastIndexOf("/") + 1));
400        }
401    }
402
403    /**
404     * @see org.opencms.workplace.CmsWorkplace#checkLock(String, CmsLockType)
405     */
406    @Override
407    public void checkLock(String resource, CmsLockType type) throws CmsException {
408
409        CmsResource res = getCms().readResource(resource, CmsResourceFilter.ALL);
410        CmsLock lock = getCms().getLock(res);
411        if (!lock.isNullLock()) {
412            setParamModified(Boolean.TRUE.toString());
413        }
414
415        // for resources with siblings make sure all sibling have at least a
416        // temporary lock
417        if ((res.getSiblingCount() > 1) && (lock.isInherited())) {
418            super.checkLock(resource, CmsLockType.TEMPORARY);
419        } else {
420            super.checkLock(resource, type);
421        }
422    }
423
424    /**
425     * Generates a button for delete locale.<p>
426     *
427     * @param href the href link for the button, if none is given the button will be disabled
428     * @param target the href link target for the button, if none is given the target will be same window
429     * @param image the image name for the button, skin path will be automatically added as prefix
430     * @param label the label for the text of the button
431     * @param type 0: image only (default), 1: image and text, 2: text only
432     *
433     * @return a button for the OpenCms workplace
434     */
435    public String deleteLocaleButton(String href, String target, String image, String label, int type) {
436
437        String filename = getParamResource();
438
439        try {
440            CmsResource res = getCms().readResource(filename, CmsResourceFilter.IGNORE_EXPIRATION);
441
442            String temporaryFilename = CmsWorkplace.getTemporaryFileName(filename);
443            if (getCms().existsResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION)) {
444                res = getCms().readResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION);
445            }
446            CmsFile file = getCms().readFile(res);
447            CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file);
448            int locales = xmlContent.getLocales().size();
449            // there are less than 2 locales, so disable the delete locale button
450            if (locales < 2) {
451                href = null;
452                target = null;
453                image += "_in";
454            }
455        } catch (CmsException e) {
456            // to nothing here in case the resource could not be opened
457            if (LOG.isErrorEnabled()) {
458                LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, filename), e);
459            }
460        }
461        return button(href, target, image, label, type, getSkinUri() + "buttons/");
462
463    }
464
465    /**
466     * Returns the instantiated editor display option class from the workplace manager.<p>
467     *
468     * This is a convenience method to be used on editor JSPs.<p>
469     *
470     * @return the instantiated editor display option class
471     */
472    public CmsEditorDisplayOptions getEditorDisplayOptions() {
473
474        return OpenCms.getWorkplaceManager().getEditorDisplayOptions();
475    }
476
477    /**
478     * Returns the URI to the editor resource folder where button images and javascripts are located.<p>
479     *
480     * @return the URI to the editor resource folder
481     */
482    public abstract String getEditorResourceUri();
483
484    /**
485     * Returns the OpenCms request context path.<p>
486     *
487     * This is a convenience method to use in the editor.<p>
488     *
489     * @return the OpenCms request context path
490     */
491    public String getOpenCmsContext() {
492
493        return OpenCms.getSystemInfo().getOpenCmsContext();
494    }
495
496    /**
497     * Returns the back link when closing the editor.<p>
498     *
499     * @return the back link
500     */
501    public String getParamBacklink() {
502
503        if ((m_editorSessionInfo != null)
504            && CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_editorSessionInfo.getBackLink())) {
505            m_paramBackLink = m_editorSessionInfo.getBackLink();
506        }
507        if (m_paramBackLink == null) {
508            m_paramBackLink = "";
509        }
510        return m_paramBackLink;
511    }
512
513    /**
514     * Returns the content of the editor.<p>
515     * @return the content of the editor
516     */
517    public String getParamContent() {
518
519        if (m_paramContent == null) {
520            m_paramContent = "";
521        }
522        return m_paramContent;
523    }
524
525    /**
526     * Returns the direct edit flag parameter.<p>
527     *
528     * @return the direct edit flag parameter
529     */
530    public String getParamDirectedit() {
531
532        if (m_editorSessionInfo != null) {
533            return String.valueOf(m_editorSessionInfo.isDirectEdit());
534        }
535        return m_paramDirectedit;
536    }
537
538    /**
539     * Returns the edit as text parameter.<p>
540     *
541     * @return the edit as text parameter
542     */
543    public String getParamEditastext() {
544
545        return m_paramEditAsText;
546    }
547
548    /**
549     * Returns the editor mode parameter.<p>
550     *
551     * @return the editor mode parameter
552     */
553    public String getParamEditormode() {
554
555        return m_paramEditormode;
556    }
557
558    /**
559     * Returns the current element language.<p>
560     *
561     * @return the current element language
562     */
563    public String getParamElementlanguage() {
564
565        if (m_paramElementlanguage == null) {
566            if ((m_editorSessionInfo != null) && (m_editorSessionInfo.getElementLocale() != null)) {
567                m_paramElementlanguage = m_editorSessionInfo.getElementLocale().toString();
568            }
569        }
570        return m_paramElementlanguage;
571    }
572
573    /**
574     * Returns the "loaddefault" parameter to determine if the default editor should be loaded.<p>
575     *
576     * @return the "loaddefault" parameter
577     */
578    public String getParamLoaddefault() {
579
580        return m_paramLoadDefault;
581    }
582
583    /**
584     * Returns the modified parameter indicating if the resource has been saved.<p>
585     *
586     * @return the modified parameter indicating if the resource has been saved
587     */
588    public String getParamModified() {
589
590        return m_paramModified;
591    }
592
593    /**
594     * Returns the old element language.<p>
595     *
596     * @return the old element language
597     */
598    public String getParamOldelementlanguage() {
599
600        return m_paramOldelementlanguage;
601    }
602
603    /**
604     * Returns the name of the temporary file.<p>
605     *
606     * @return the name of the temporary file
607     */
608    public String getParamTempfile() {
609
610        return m_paramTempFile;
611    }
612
613    /**
614     * Returns the path to the images used by this editor.<p>
615     *
616     * @return the path to the images used by this editor
617     */
618    public String getPicsUri() {
619
620        if (m_picsUri == null) {
621            m_picsUri = getEditorResourceUri() + "pics/";
622        }
623        return m_picsUri;
624    }
625
626    /**
627     * Sets the back link when closing the editor.<p>
628     *
629     * @param backLink the back link
630     */
631    public void setParamBacklink(String backLink) {
632
633        m_paramBackLink = backLink;
634    }
635
636    /**
637     * Sets the content of the editor.<p>
638     *
639     * @param content the content of the editor
640     */
641    public void setParamContent(String content) {
642
643        if (content == null) {
644            content = "";
645        }
646        m_paramContent = content;
647    }
648
649    /**
650     * Sets the direct edit flag parameter.<p>
651     *
652     * @param direct the direct edit flag parameter
653     */
654    public void setParamDirectedit(String direct) {
655
656        m_paramDirectedit = direct;
657    }
658
659    /**
660     * Sets the  edit as text parameter.<p>
661     *
662     * @param editAsText <code>"true"</code> if the resource should be handled like a text file
663     */
664    public void setParamEditastext(String editAsText) {
665
666        m_paramEditAsText = editAsText;
667    }
668
669    /**
670     * Sets the editor mode parameter.<p>
671     *
672     * @param mode the editor mode parameter
673     */
674    public void setParamEditormode(String mode) {
675
676        m_paramEditormode = mode;
677    }
678
679    /**
680     * Sets the current element language.<p>
681     *
682     * @param elementLanguage the current element language
683     */
684    public void setParamElementlanguage(String elementLanguage) {
685
686        m_paramElementlanguage = elementLanguage;
687    }
688
689    /**
690     * Sets the "loaddefault" parameter to determine if the default editor should be loaded.<p>
691     *
692     * @param loadDefault the "loaddefault" parameter
693     */
694    public void setParamLoaddefault(String loadDefault) {
695
696        m_paramLoadDefault = loadDefault;
697    }
698
699    /**
700     * Sets the modified parameter indicating if the resource has been saved.<p>
701     *
702     * @param modified the modified parameter indicating if the resource has been saved
703     */
704    public void setParamModified(String modified) {
705
706        m_paramModified = modified;
707    }
708
709    /**
710     * Sets the old element language.<p>
711     *
712     * @param oldElementLanguage the old element language
713     */
714    public void setParamOldelementlanguage(String oldElementLanguage) {
715
716        m_paramOldelementlanguage = oldElementLanguage;
717    }
718
719    /**
720     * Sets the name of the temporary file.<p>
721     *
722     * @param fileName the name of the temporary file
723     */
724    public void setParamTempfile(String fileName) {
725
726        m_paramTempFile = fileName;
727    }
728
729    /**
730     * Closes the editor and forwards to the workplace or the resource depending on the editor mode.<p>
731     *
732     * @throws IOException if forwarding fails
733     * @throws ServletException if forwarding fails
734     * @throws JspException if including a JSP fails
735     */
736    protected void actionClose() throws IOException, JspException, ServletException {
737
738        try {
739            if (Boolean.valueOf(getParamDirectedit()).booleanValue()) {
740                // editor is in direct edit mode
741                if (CmsStringUtil.isNotEmpty(getParamBacklink())) {
742                    // set link to the specified back link target
743                    setParamCloseLink(getJsp().link(getParamBacklink()));
744                } else {
745                    // set link to the edited resource
746                    setParamCloseLink(getJsp().link(getParamResource()));
747                }
748                // save initialized instance of this class in request attribute for included sub-elements
749                getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
750                // load the common JSP close dialog
751                getJsp().include(FILE_DIALOG_CLOSE);
752            } else {
753                if (CmsStringUtil.isNotEmpty(getParamBacklink())) {
754                    // set link to the specified back link target
755                    setParamCloseLink(getJsp().link(getParamBacklink()));
756                    // save initialized instance of this class in request attribute for included sub-elements
757                    getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
758                    // load the common JSP close dialog
759                    getJsp().include(FILE_DIALOG_CLOSE);
760                } else {
761                    // forward to the workplace explorer view
762                    sendForward(CmsVaadinUtils.getWorkplaceLink(), new HashMap<String, String[]>());
763                }
764            }
765        } finally {
766            clearEditorSessionInfo();
767        }
768    }
769
770    /**
771     * Clears the editor session info bean.<p>
772     */
773    protected void clearEditorSessionInfo() {
774
775        if (m_editorSessionInfo != null) {
776            getSession().removeAttribute(m_editorSessionInfo.getEditorSessionInfoKey());
777        }
778        m_editorSessionInfo = null;
779    }
780
781    /**
782     * Writes the content of a temporary file back to the original file.<p>
783     *
784     * @throws CmsException if something goes wrong
785     */
786    protected void commitTempFile() throws CmsException {
787
788        CmsObject cms = getCms();
789        CmsFile tempFile;
790        List<CmsProperty> properties;
791        try {
792            switchToTempProject();
793            tempFile = cms.readFile(getParamTempfile(), CmsResourceFilter.ALL);
794            properties = cms.readPropertyObjects(getParamTempfile(), false);
795        } finally {
796            // make sure the project is reset in case of any exception
797            switchToCurrentProject();
798        }
799        if (cms.existsResource(getParamResource(), CmsResourceFilter.ALL)) {
800            // update properties of original file first (required if change in encoding occurred)
801            cms.writePropertyObjects(getParamResource(), properties);
802            // now replace the content of the original file
803            CmsFile orgFile = cms.readFile(getParamResource(), CmsResourceFilter.ALL);
804            orgFile.setContents(tempFile.getContents());
805            getCloneCms().writeFile(orgFile);
806        } else {
807            // original file does not exist, remove visibility permission entries and copy temporary file
808
809            // switch to the temporary file project
810            try {
811                switchToTempProject();
812                // lock the temporary file
813                cms.changeLock(getParamTempfile());
814                // remove visibility permissions for everybody on temporary file if possible
815                if (cms.hasPermissions(tempFile, CmsPermissionSet.ACCESS_CONTROL)) {
816                    cms.rmacc(
817                        getParamTempfile(),
818                        I_CmsPrincipal.PRINCIPAL_GROUP,
819                        OpenCms.getDefaultUsers().getGroupUsers());
820                }
821            } finally {
822                // make sure the project is reset in case of any exception
823                switchToCurrentProject();
824            }
825
826            cms.copyResource(getParamTempfile(), getParamResource(), CmsResource.COPY_AS_NEW);
827            // ensure the content handler is called
828            CmsFile orgFile = cms.readFile(getParamResource(), CmsResourceFilter.ALL);
829            getCloneCms().writeFile(orgFile);
830
831        }
832        // remove the temporary file flag
833        int flags = cms.readResource(getParamResource(), CmsResourceFilter.ALL).getFlags();
834        if ((flags & CmsResource.FLAG_TEMPFILE) == CmsResource.FLAG_TEMPFILE) {
835            flags ^= CmsResource.FLAG_TEMPFILE;
836            cms.chflags(getParamResource(), flags);
837        }
838    }
839
840    /**
841     * Creates a temporary file which is needed while working in an editor with preview option.<p>
842     *
843     * @return the file name of the temporary file
844     * @throws CmsException if something goes wrong
845     */
846    protected String createTempFile() throws CmsException {
847
848        return OpenCms.getWorkplaceManager().createTempFile(getCms(), getParamResource(), getSettings().getProject());
849    }
850
851    /**
852     * Decodes the given content the same way the client would do it.<p>
853     *
854     * Content is decoded as if it was encoded using the JavaScript
855     * "encodeURIComponent()" function.<p>
856     *
857     * @param content the content to decode
858     * @return the decoded content
859     */
860    protected String decodeContent(String content) {
861
862        return CmsEncoder.unescape(content, CmsEncoder.ENCODING_UTF_8);
863    }
864
865    /**
866     * Decodes an individual parameter value, ensuring the content is always decoded in UTF-8.<p>
867     *
868     * For editors the content is always encoded using the
869     * JavaScript encodeURIComponent() method on the client,
870     * which always encodes in UTF-8.<p>
871     *
872     * @param paramName the name of the parameter
873     * @param paramValue the unencoded value of the parameter
874     * @return the encoded value of the parameter
875     */
876    @Override
877    protected String decodeParamValue(String paramName, String paramValue) {
878
879        if ((paramName != null) && (paramValue != null)) {
880            if (PARAM_CONTENT.equals(paramName)) {
881                // content will be always encoded in UTF-8 unicode by the editor client
882                return CmsEncoder.decode(paramValue, CmsEncoder.ENCODING_UTF_8);
883            } else if (PARAM_RESOURCE.equals(paramName) || PARAM_TEMPFILE.equals(paramName)) {
884                String filename = CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding());
885                if (PARAM_TEMPFILE.equals(paramName) || CmsStringUtil.isEmpty(getParamTempfile())) {
886                    // always use value from temp file if it is available
887                    setFileEncoding(getFileEncoding(getCms(), filename));
888                }
889                return filename;
890            } else {
891                return CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding());
892            }
893        } else {
894            return null;
895        }
896    }
897
898    /**
899     * Deletes a temporary file from the OpenCms VFS, needed when exiting an editor.<p>
900     */
901    protected void deleteTempFile() {
902
903        try {
904            // switch to the temporary file project
905            switchToTempProject();
906            // delete the temporary file
907            getCms().deleteResource(getParamTempfile(), CmsResource.DELETE_PRESERVE_SIBLINGS);
908        } catch (CmsException e) {
909            // should usually never happen
910            if (LOG.isInfoEnabled()) {
911                LOG.info(e.getLocalizedMessage(), e);
912            }
913        } finally {
914            try {
915                // switch back to the current project
916                switchToCurrentProject();
917            } catch (CmsException e) {
918                // should usually never happen
919                if (LOG.isInfoEnabled()) {
920                    LOG.info(e.getLocalizedMessage(), e);
921                }
922            }
923        }
924    }
925
926    /**
927     * Encodes the given content so that it can be transfered to the client.<p>
928     *
929     * Content is encoded so that it is compatible with the JavaScript
930     * "decodeURIComponent()" function.<p>
931     *
932     * @param content the content to encode
933     * @return the encoded content
934     */
935    protected String encodeContent(String content) {
936
937        return CmsEncoder.escapeWBlanks(content, CmsEncoder.ENCODING_UTF_8);
938    }
939
940    /**
941     * Returns a cloned cms instance that prevents the time range resource filter check.<p>
942     *
943     * Use it always for unmarshalling and file writing.<p>
944     *
945     * @return a cloned cms instance that prevents the time range resource filter check
946     *
947     * @throws CmsException if something goes wrong
948     */
949    protected CmsObject getCloneCms() throws CmsException {
950
951        CmsObject cloneCms = OpenCms.initCmsObject(getCms());
952        cloneCms.getRequestContext().setRequestTime(CmsResource.DATE_RELEASED_EXPIRED_IGNORE);
953        return cloneCms;
954    }
955
956    /**
957     * Returns the editor session info bean.<p>
958     *
959     * @return the editor session info bean
960     */
961    protected CmsEditorSessionInfo getEditorSessionInfo() {
962
963        return m_editorSessionInfo;
964    }
965
966    /**
967     * Returns the encoding parameter.<p>
968     *
969     * @return the encoding parameter
970     */
971    protected String getFileEncoding() {
972
973        return m_fileEncoding;
974    }
975
976    /**
977     * Helper method to determine the encoding of the given file in the VFS,
978     * which must be set using the "content-encoding" property.<p>
979     *
980     * @param cms the CmsObject
981     * @param filename the name of the file which is to be checked
982     * @return the encoding for the file
983     */
984    protected String getFileEncoding(CmsObject cms, String filename) {
985
986        try {
987            return cms.readPropertyObject(filename, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true).getValue(
988                OpenCms.getSystemInfo().getDefaultEncoding());
989        } catch (CmsException e) {
990            return OpenCms.getSystemInfo().getDefaultEncoding();
991        }
992    }
993
994    /**
995     * Initializes the editor content when openening the editor for the first time.<p>
996     */
997    protected abstract void initContent();
998
999    /**
1000     * @see org.opencms.workplace.CmsWorkplace#initMessages()
1001     */
1002    @Override
1003    protected void initMessages() {
1004
1005        initSessionInfo();
1006        super.initMessages();
1007    }
1008
1009    /**
1010     * Initializes the editor session info bean.<p>
1011     */
1012    protected void initSessionInfo() {
1013
1014        CmsResource editedResource = null;
1015        try {
1016            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getParamResource())) {
1017                editedResource = getCms().readResource(getParamResource());
1018            }
1019        } catch (CmsException e) {
1020            // ignore
1021        }
1022
1023        CmsEditorSessionInfo info = null;
1024        if (editedResource != null) {
1025            HttpSession session = getSession();
1026            info = (CmsEditorSessionInfo)session.getAttribute(
1027                CmsEditorSessionInfo.getEditorSessionInfoKey(editedResource));
1028            if (info == null) {
1029                info = new CmsEditorSessionInfo(editedResource.getStructureId());
1030            }
1031            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramBackLink)) {
1032                info.setBackLink(m_paramBackLink);
1033            }
1034            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramElementlanguage)) {
1035                info.setElementLocale(CmsLocaleManager.getLocale(m_paramElementlanguage));
1036            }
1037            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramDirectedit)) {
1038                info.setDirectEdit(Boolean.parseBoolean(m_paramDirectedit));
1039            }
1040            session.setAttribute(info.getEditorSessionInfoKey(), info);
1041        }
1042        m_editorSessionInfo = info;
1043    }
1044
1045    /**
1046     * Sets the encoding parameter.<p>
1047     *
1048     * @param value the encoding value to set
1049     */
1050    protected void setFileEncoding(String value) {
1051
1052        m_fileEncoding = CmsEncoder.lookupEncoding(value, value);
1053    }
1054
1055    /**
1056     * Shows the selected error page in case of an exception.<p>
1057     *
1058     * @param exception the current exception
1059     * @throws JspException if inclusion of the error page fails
1060     */
1061    protected void showErrorPage(Exception exception) throws JspException {
1062
1063        // reset the action parameter
1064        setParamAction("");
1065        showErrorPage(this, exception);
1066        // save not successful, set cancel action
1067        setAction(ACTION_CANCEL);
1068        return;
1069    }
1070
1071    /**
1072     * Shows the selected error page in case of an exception.<p>
1073     *
1074     * @param editor initialized instance of the editor class
1075     * @param exception the current exception
1076     * @throws JspException if inclusion of the error page fails
1077     */
1078    protected void showErrorPage(Object editor, Exception exception) throws JspException {
1079
1080        // save initialized instance of the editor class in request attribute for included sub-elements
1081        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, editor);
1082
1083        // reading of file contents failed, show error dialog
1084        setAction(ACTION_SHOW_ERRORMESSAGE);
1085        setParamTitle(key(Messages.GUI_TITLE_EDIT_1, new Object[] {CmsResource.getName(getParamResource())}));
1086        if (exception != null) {
1087            getJsp().getRequest().setAttribute(ATTRIBUTE_THROWABLE, exception);
1088            if (CmsLog.getLog(editor).isWarnEnabled()) {
1089                CmsLog.getLog(editor).warn(exception.getLocalizedMessage(), exception);
1090            }
1091        }
1092        // include the common error dialog
1093        getJsp().include(FILE_DIALOG_SCREEN_ERRORPAGE);
1094    }
1095}