content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software GmbH & Co. KG, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.workplace.editors;
  
  import org.opencms.file.CmsFile;
  import org.opencms.file.CmsObject;
  import org.opencms.file.CmsProperty;
  import org.opencms.file.CmsPropertyDefinition;
  import org.opencms.file.CmsResource;
  import org.opencms.file.CmsResourceFilter;
  import org.opencms.i18n.CmsEncoder;
  import org.opencms.i18n.CmsLocaleManager;
  import org.opencms.jsp.CmsJspActionElement;
  import org.opencms.lock.CmsLock;
  import org.opencms.lock.CmsLockType;
  import org.opencms.main.CmsException;
  import org.opencms.main.CmsLog;
  import org.opencms.main.OpenCms;
  import org.opencms.security.CmsPermissionSet;
  import org.opencms.security.I_CmsPrincipal;
  import org.opencms.ui.CmsVaadinUtils;
  import org.opencms.util.CmsStringUtil;
  import org.opencms.workplace.CmsWorkplace;
  import org.opencms.xml.content.CmsXmlContent;
  import org.opencms.xml.content.CmsXmlContentFactory;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Locale;
  
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpSession;
  import javax.servlet.jsp.JspException;
  
  import org.apache.commons.logging.Log;
  
  /**
   * Provides basic methods for building the file editors of OpenCms.<p>
   *
   * The editor classes have to extend this class and implement action methods for common editor actions.<p>
   *
   * @since 6.0.0
   */
  public abstract class CmsEditor extends CmsEditorBase {
  
      /** Value for the action: change the body. */
      public static final int ACTION_CHANGE_BODY = 124;
  
      /** Value for the action: delete the current locale. */
      public static final int ACTION_DELETELOCALE = 140;
  
      /** Value for the action: exit. */
      public static final int ACTION_EXIT = 122;
  
      /** Value for the action: show a preview. */
      public static final int ACTION_PREVIEW = 126;
  
      /** Value for the action: save. */
      public static final int ACTION_SAVE = 121;
  
      /** Constant value for the customizable action button. */
      public static final int ACTION_SAVEACTION = 130;
  
      /** Value for the action: save and exit. */
      public static final int ACTION_SAVEEXIT = 123;
  
      /** Value for the action: show the editor. */
      public static final int ACTION_SHOW = 125;
  
      /** Value for the action: an error occurred. */
      public static final int ACTION_SHOW_ERRORMESSAGE = 127;
  
      /** Value for the action parameter: change the element. */
      public static final String EDITOR_CHANGE_ELEMENT = "changeelement";
  
      /** Value for the action parameter: cleanup content. */
      public static final String EDITOR_CLEANUP = "cleanup";
  
      /** Value for the action parameter: close browser window (accidentally). */
      public static final String EDITOR_CLOSEBROWSER = "closebrowser";
  
      /** Value for the action parameter: delete the current locale. */
      public static final String EDITOR_DELETELOCALE = "deletelocale";
  
      /** Value for the action parameter: exit editor. */
      public static final String EDITOR_EXIT = "exit";
  
      /** Value for the action parameter: show a preview. */
      public static final String EDITOR_PREVIEW = "preview";
  
      /** Value for the action parameter: save content. */
      public static final String EDITOR_SAVE = "save";
  
      /** Value for the customizable action button. */
      public static final String EDITOR_SAVEACTION = "saveaction";
  
      /** Value for the action parameter: save and exit. */
      public static final String EDITOR_SAVEEXIT = "saveexit";
  
      /** Value for the action parameter: show the editor. */
      public static final String EDITOR_SHOW = "show";
  
      /** Value for the action parameter: an error occurred. */
      public static final String EDITOR_SHOW_ERRORMESSAGE = "error";
  
      /** Marker for empty locale in locale selection. */
      public static final String EMPTY_LOCALE = " [-]";
  
      /** Parameter name for the request parameter "backlink". */
      public static final String PARAM_BACKLINK = "backlink";
  
      /** Parameter name for the request parameter "content". */
      public static final String PARAM_CONTENT = "content";
  
      /** Parameter name for the request parameter "directedit". */
      public static final String PARAM_DIRECTEDIT = "directedit";
  
      /** Parameter name for the request parameter "editastext". */
      public static final String PARAM_EDITASTEXT = "editastext";
  
      /** Parameter name for the request parameter "editormode". */
      public static final String PARAM_EDITORMODE = "editormode";
  
      /** Parameter name for the request parameter "element language". */
      public static final String PARAM_ELEMENTLANGUAGE = "elementlanguage";
  
      /** Parameter name for the request parameter "loaddefault". */
      public static final String PARAM_LOADDEFAULT = "loaddefault";
  
      /** Parameter name for the request parameter "modified". */
      public static final String PARAM_MODIFIED = "modified";
  
      /** Parameter name for the request parameter "old element language". */
      public static final String PARAM_OLDELEMENTLANGUAGE = "oldelementlanguage";
  
      /** Parameter name for the request parameter "tempfile". */
      public static final String PARAM_TEMPFILE = "tempfile";
  
      /** Stores the VFS editor path. */
      public static final String PATH_EDITORS = PATH_WORKPLACE + "editors/";
  
      /** The log object for this class. */
      private static final Log LOG = CmsLog.getLog(CmsEditor.class);
  
      /** The editor session info bean. */
      private CmsEditorSessionInfo m_editorSessionInfo;
  
      /** The encoding to use (will be read from the file property). */
      private String m_fileEncoding;
  
      /** Back link parameter. */
      private String m_paramBackLink;
  
      /** Content parameter. */
      private String m_paramContent;
  
      /** Direct edit parameter. */
      private String m_paramDirectedit;
  
      /** Edit as text parameter. */
      private String m_paramEditAsText;
  
      /** Editor mode parameter. */
      private String m_paramEditormode;
  
      /** Element language parameter. */
      private String m_paramElementlanguage;
  
      /** Load default parameter. */
      private String m_paramLoadDefault;
  
      /** Modified parameter. */
      private String m_paramModified;
  
      /** Old element language parameter. */
      private String m_paramOldelementlanguage;
  
      /** Temporary file parameter. */
      private String m_paramTempFile;
  
      /** Helper variable to store the uri to the editors pictures. */
      private String m_picsUri;
  
      /**
       * Public constructor.<p>
       *
       * @param jsp an initialized JSP action element
       */
      public CmsEditor(CmsJspActionElement jsp) {
  
          super(jsp);
      }
  
      /**
       * Unlocks the edited resource when in direct edit mode or when the resource was not modified.<p>
       *
       * @param forceUnlock if true, the resource will be unlocked anyway
       */
      public abstract void actionClear(boolean forceUnlock);
  
      /**
       * Performs the exit editor action.<p>
       *
       * @throws CmsException if something goes wrong
       * @throws IOException if a forward fails
       * @throws ServletException if a forward fails
       * @throws JspException if including an element fails
       */
      public abstract void actionExit() throws CmsException, IOException, ServletException, JspException;
  
      /**
       * Performs the save content action.<p>
       *
       * @throws IOException if a redirection fails
       * @throws JspException if including an element fails
       */
      public abstract void actionSave() throws IOException, JspException;
  
      /**
       * Builds the html String for the element language selector.<p>
       *
       * @param attributes optional attributes for the &lt;select&gt; tag
       * @param resourceName the name of the resource to edit
       * @param selectedLocale the currently selected Locale
       * @return the html for the element language selectbox
       */
      public String buildSelectElementLanguage(String attributes, String resourceName, Locale selectedLocale) {
  
          // get locale names based on properties and global settings
          List<Locale> locales = OpenCms.getLocaleManager().getAvailableLocales(getCms(), resourceName);
          List<String> options = new ArrayList<String>(locales.size());
          List<String> selectList = new ArrayList<String>(locales.size());
          int currentIndex = -1;
  
          //get the locales already used in the resource
          List<Locale> contentLocales = new ArrayList<Locale>();
          try {
  
              CmsResource res = getCms().readResource(resourceName, CmsResourceFilter.IGNORE_EXPIRATION);
              String temporaryFilename = CmsWorkplace.getTemporaryFileName(resourceName);
              if (getCms().existsResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION)) {
                  res = getCms().readResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION);
              }
              CmsFile file = getCms().readFile(res);
              CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file);
              contentLocales = xmlContent.getLocales();
          } catch (CmsException e) {
              // to nothing here in case the resource could not be opened
              if (LOG.isErrorEnabled()) {
                  LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, resourceName), e);
              }
          }
  
          for (int counter = 0; counter < locales.size(); counter++) {
              // create the list of options and values
              Locale curLocale = locales.get(counter);
              selectList.add(curLocale.toString());
              StringBuffer buf = new StringBuffer();
              buf.append(curLocale.getDisplayName(getLocale()));
              if (!contentLocales.contains(curLocale)) {
                  buf.append(EMPTY_LOCALE);
              }
              options.add(buf.toString());
              if (curLocale.equals(selectedLocale)) {
                  // set the selected index of the selector
                  currentIndex = counter;
              }
          }
  
          if (currentIndex == -1) {
              // no matching element language found, use first element language in list
              if (selectList.size() > 0) {
                  currentIndex = 0;
                  setParamElementlanguage(selectList.get(0));
              }
          }
  
          return buildSelect(attributes, options, selectList, currentIndex, false);
      }
  
      /**
       * Generates a button for the OpenCms editor.<p>
       *
       * @param href the href link for the button, if none is given the button will be disabled
       * @param target the href link target for the button, if none is given the target will be same window
       * @param image the image name for the button, skin path will be automatically added as prefix
       * @param label the label for the text of the button
       * @param type 0: image only (default), 1: image and text, 2: text only
       * @param useCustomImage if true, the button has to be placed in the editors "custom pics" folder
       *
       * @return a button for the OpenCms editor
       */
      public String button(String href, String target, String image, String label, int type, boolean useCustomImage) {
  
          if (useCustomImage) {
              // search the picture in the "custom pics" folder
              return button(href, target, image, label, type, getPicsUri());
          } else {
              // search the picture in the common "buttons" folder
              return button(href, target, image, label, type);
          }
      }
  
      /**
       * Returns the editor action for a "cancel" button.<p>
       *
       * This overwrites the cancel method of the CmsDialog class.<p>
       *
       * Always use this value, do not write anything directly in the html page.<p>
       *
       * @return the default action for a "cancel" button
       */
      public String buttonActionCancel() {
  
          String target = null;
          if (Boolean.valueOf(getParamDirectedit()).booleanValue()) {
              // editor is in direct edit mode
              if (CmsStringUtil.isNotEmpty(getParamBacklink())) {
                  // set link to the specified back link target
                  target = getParamBacklink();
              } else {
                  // set link to the edited resource
                  target = getParamResource();
              }
          } else {
              // in workplace mode, show explorer view
              target = CmsVaadinUtils.getWorkplaceLink();
          }
          return "onclick=\"top.location.href='" + getJsp().link(target) + "';\"";
      }
  
      /**
       * Builds the html to display the special action button for the direct edit mode of the editor.<p>
       *
       * @param jsFunction the JavaScript function which will be executed on the mouseup event
       * @param type 0: image only (default), 1: image and text, 2: text only
       * @return the html to display the special action button
       */
      public String buttonActionDirectEdit(String jsFunction, int type) {
  
          // get the action class from the OpenCms runtime property
          I_CmsEditorActionHandler actionClass = OpenCms.getWorkplaceManager().getEditorActionHandler();
          String url;
          String name;
          boolean active = false;
          if (actionClass != null) {
              // get button parameters and state from action class
              url = actionClass.getButtonUrl(getJsp(), getParamResource());
              name = actionClass.getButtonName();
              active = actionClass.isButtonActive(getJsp(), getParamResource());
          } else {
              // action class not defined, display inactive button
              url = getSkinUri() + "buttons/publish_in.png";
              name = Messages.GUI_EXPLORER_CONTEXT_PUBLISH_0;
          }
          String image = url.substring(url.lastIndexOf("/") + 1);
          if (url.endsWith(".gif")) {
              image = image.substring(0, image.length() - 4);
          }
  
          if (active) {
              // create the link for the button
              return button(
                  "javascript:" + jsFunction,
                  null,
                  image,
                  name,
                  type,
                  url.substring(0, url.lastIndexOf("/") + 1));
          } else {
              // create the inactive button
              return button(null, null, image, name, type, url.substring(0, url.lastIndexOf("/") + 1));
          }
      }
  
      /**
       * @see org.opencms.workplace.CmsWorkplace#checkLock(String, CmsLockType)
       */
      @Override
      public void checkLock(String resource, CmsLockType type) throws CmsException {
  
          CmsResource res = getCms().readResource(resource, CmsResourceFilter.ALL);
          CmsLock lock = getCms().getLock(res);
          if (!lock.isNullLock()) {
              setParamModified(Boolean.TRUE.toString());
          }
  
          // for resources with siblings make sure all sibling have at least a
          // temporary lock
          if ((res.getSiblingCount() > 1) && (lock.isInherited())) {
              super.checkLock(resource, CmsLockType.TEMPORARY);
          } else {
              super.checkLock(resource, type);
          }
      }
  
      /**
       * Generates a button for delete locale.<p>
       *
       * @param href the href link for the button, if none is given the button will be disabled
       * @param target the href link target for the button, if none is given the target will be same window
       * @param image the image name for the button, skin path will be automatically added as prefix
       * @param label the label for the text of the button
       * @param type 0: image only (default), 1: image and text, 2: text only
       *
       * @return a button for the OpenCms workplace
       */
      public String deleteLocaleButton(String href, String target, String image, String label, int type) {
  
          String filename = getParamResource();
  
          try {
              CmsResource res = getCms().readResource(filename, CmsResourceFilter.IGNORE_EXPIRATION);
  
              String temporaryFilename = CmsWorkplace.getTemporaryFileName(filename);
              if (getCms().existsResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION)) {
                  res = getCms().readResource(temporaryFilename, CmsResourceFilter.IGNORE_EXPIRATION);
              }
              CmsFile file = getCms().readFile(res);
              CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file);
              int locales = xmlContent.getLocales().size();
              // there are less than 2 locales, so disable the delete locale button
              if (locales < 2) {
                  href = null;
                  target = null;
                  image += "_in";
              }
          } catch (CmsException e) {
              // to nothing here in case the resource could not be opened
              if (LOG.isErrorEnabled()) {
                  LOG.error(Messages.get().getBundle().key(Messages.LOG_GET_LOCALES_1, filename), e);
              }
          }
          return button(href, target, image, label, type, getSkinUri() + "buttons/");
  
      }
  
      /**
       * Returns the instantiated editor display option class from the workplace manager.<p>
       *
       * This is a convenience method to be used on editor JSPs.<p>
       *
       * @return the instantiated editor display option class
       */
      public CmsEditorDisplayOptions getEditorDisplayOptions() {
  
          return OpenCms.getWorkplaceManager().getEditorDisplayOptions();
      }
  
      /**
       * Returns the URI to the editor resource folder where button images and javascripts are located.<p>
       *
       * @return the URI to the editor resource folder
       */
      public abstract String getEditorResourceUri();
  
      /**
       * Returns the OpenCms request context path.<p>
       *
       * This is a convenience method to use in the editor.<p>
       *
       * @return the OpenCms request context path
       */
      public String getOpenCmsContext() {
  
          return OpenCms.getSystemInfo().getOpenCmsContext();
      }
  
      /**
       * Returns the back link when closing the editor.<p>
       *
       * @return the back link
       */
      public String getParamBacklink() {
  
          if ((m_editorSessionInfo != null)
              && CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_editorSessionInfo.getBackLink())) {
              m_paramBackLink = m_editorSessionInfo.getBackLink();
          }
          if (m_paramBackLink == null) {
              m_paramBackLink = "";
          }
          return m_paramBackLink;
      }
  
      /**
       * Returns the content of the editor.<p>
       * @return the content of the editor
       */
      public String getParamContent() {
  
          if (m_paramContent == null) {
              m_paramContent = "";
          }
          return m_paramContent;
      }
  
      /**
       * Returns the direct edit flag parameter.<p>
       *
       * @return the direct edit flag parameter
       */
      public String getParamDirectedit() {
  
          if (m_editorSessionInfo != null) {
              return String.valueOf(m_editorSessionInfo.isDirectEdit());
          }
          return m_paramDirectedit;
      }
  
      /**
       * Returns the edit as text parameter.<p>
       *
       * @return the edit as text parameter
       */
      public String getParamEditastext() {
  
          return m_paramEditAsText;
      }
  
      /**
       * Returns the editor mode parameter.<p>
       *
       * @return the editor mode parameter
       */
      public String getParamEditormode() {
  
          return m_paramEditormode;
      }
  
      /**
       * Returns the current element language.<p>
       *
       * @return the current element language
       */
      public String getParamElementlanguage() {
  
          if (m_paramElementlanguage == null) {
              if ((m_editorSessionInfo != null) && (m_editorSessionInfo.getElementLocale() != null)) {
                  m_paramElementlanguage = m_editorSessionInfo.getElementLocale().toString();
              }
          }
          return m_paramElementlanguage;
      }
  
      /**
       * Returns the "loaddefault" parameter to determine if the default editor should be loaded.<p>
       *
       * @return the "loaddefault" parameter
       */
      public String getParamLoaddefault() {
  
          return m_paramLoadDefault;
      }
  
      /**
       * Returns the modified parameter indicating if the resource has been saved.<p>
       *
       * @return the modified parameter indicating if the resource has been saved
       */
      public String getParamModified() {
  
          return m_paramModified;
      }
  
      /**
       * Returns the old element language.<p>
       *
       * @return the old element language
       */
      public String getParamOldelementlanguage() {
  
          return m_paramOldelementlanguage;
      }
  
      /**
       * Returns the name of the temporary file.<p>
       *
       * @return the name of the temporary file
       */
      public String getParamTempfile() {
  
          return m_paramTempFile;
      }
  
      /**
       * Returns the path to the images used by this editor.<p>
       *
       * @return the path to the images used by this editor
       */
      public String getPicsUri() {
  
          if (m_picsUri == null) {
              m_picsUri = getEditorResourceUri() + "pics/";
          }
          return m_picsUri;
      }
  
      /**
       * Sets the back link when closing the editor.<p>
       *
       * @param backLink the back link
       */
      public void setParamBacklink(String backLink) {
  
          m_paramBackLink = backLink;
      }
  
      /**
       * Sets the content of the editor.<p>
       *
       * @param content the content of the editor
       */
      public void setParamContent(String content) {
  
          if (content == null) {
              content = "";
          }
          m_paramContent = content;
      }
  
      /**
       * Sets the direct edit flag parameter.<p>
       *
       * @param direct the direct edit flag parameter
       */
      public void setParamDirectedit(String direct) {
  
          m_paramDirectedit = direct;
      }
  
      /**
       * Sets the  edit as text parameter.<p>
       *
       * @param editAsText <code>"true"</code> if the resource should be handled like a text file
       */
      public void setParamEditastext(String editAsText) {
  
          m_paramEditAsText = editAsText;
      }
  
      /**
       * Sets the editor mode parameter.<p>
       *
       * @param mode the editor mode parameter
       */
      public void setParamEditormode(String mode) {
  
          m_paramEditormode = mode;
      }
  
      /**
       * Sets the current element language.<p>
       *
       * @param elementLanguage the current element language
       */
      public void setParamElementlanguage(String elementLanguage) {
  
          m_paramElementlanguage = elementLanguage;
      }
  
      /**
       * Sets the "loaddefault" parameter to determine if the default editor should be loaded.<p>
       *
       * @param loadDefault the "loaddefault" parameter
       */
      public void setParamLoaddefault(String loadDefault) {
  
          m_paramLoadDefault = loadDefault;
      }
  
      /**
       * Sets the modified parameter indicating if the resource has been saved.<p>
       *
       * @param modified the modified parameter indicating if the resource has been saved
       */
      public void setParamModified(String modified) {
  
          m_paramModified = modified;
      }
  
      /**
       * Sets the old element language.<p>
       *
       * @param oldElementLanguage the old element language
       */
      public void setParamOldelementlanguage(String oldElementLanguage) {
  
          m_paramOldelementlanguage = oldElementLanguage;
      }
  
      /**
       * Sets the name of the temporary file.<p>
       *
       * @param fileName the name of the temporary file
       */
      public void setParamTempfile(String fileName) {
  
          m_paramTempFile = fileName;
      }
  
      /**
       * Closes the editor and forwards to the workplace or the resource depending on the editor mode.<p>
       *
       * @throws IOException if forwarding fails
       * @throws ServletException if forwarding fails
       * @throws JspException if including a JSP fails
       */
      protected void actionClose() throws IOException, JspException, ServletException {
  
          try {
              if (Boolean.valueOf(getParamDirectedit()).booleanValue()) {
                  // editor is in direct edit mode
                  if (CmsStringUtil.isNotEmpty(getParamBacklink())) {
                      // set link to the specified back link target
                      setParamCloseLink(getJsp().link(getParamBacklink()));
                  } else {
                      // set link to the edited resource
                      setParamCloseLink(getJsp().link(getParamResource()));
                  }
                  // save initialized instance of this class in request attribute for included sub-elements
                  getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
                  // load the common JSP close dialog
                  getJsp().include(FILE_DIALOG_CLOSE);
              } else {
                  if (CmsStringUtil.isNotEmpty(getParamBacklink())) {
                      // set link to the specified back link target
                      setParamCloseLink(getJsp().link(getParamBacklink()));
                      // save initialized instance of this class in request attribute for included sub-elements
                      getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
                      // load the common JSP close dialog
                      getJsp().include(FILE_DIALOG_CLOSE);
                  } else {
                      // forward to the workplace explorer view
                      sendForward(CmsVaadinUtils.getWorkplaceLink(), new HashMap<String, String[]>());
                  }
              }
          } finally {
              clearEditorSessionInfo();
          }
      }
  
      /**
       * Clears the editor session info bean.<p>
       */
      protected void clearEditorSessionInfo() {
  
          if (m_editorSessionInfo != null) {
              getSession().removeAttribute(m_editorSessionInfo.getEditorSessionInfoKey());
          }
          m_editorSessionInfo = null;
      }
  
      /**
       * Writes the content of a temporary file back to the original file.<p>
       *
       * @throws CmsException if something goes wrong
       */
      protected void commitTempFile() throws CmsException {
  
          CmsObject cms = getCms();
          CmsFile tempFile;
          List<CmsProperty> properties;
          try {
              switchToTempProject();
              tempFile = cms.readFile(getParamTempfile(), CmsResourceFilter.ALL);
              properties = cms.readPropertyObjects(getParamTempfile(), false);
          } finally {
              // make sure the project is reset in case of any exception
              switchToCurrentProject();
          }
          if (cms.existsResource(getParamResource(), CmsResourceFilter.ALL)) {
              // update properties of original file first (required if change in encoding occurred)
              cms.writePropertyObjects(getParamResource(), properties);
              // now replace the content of the original file
              CmsFile orgFile = cms.readFile(getParamResource(), CmsResourceFilter.ALL);
              orgFile.setContents(tempFile.getContents());
              getCloneCms().writeFile(orgFile);
          } else {
              // original file does not exist, remove visibility permission entries and copy temporary file
  
              // switch to the temporary file project
              try {
                  switchToTempProject();
                  // lock the temporary file
                  cms.changeLock(getParamTempfile());
                  // remove visibility permissions for everybody on temporary file if possible
                  if (cms.hasPermissions(tempFile, CmsPermissionSet.ACCESS_CONTROL)) {
                      cms.rmacc(
                          getParamTempfile(),
                          I_CmsPrincipal.PRINCIPAL_GROUP,
                          OpenCms.getDefaultUsers().getGroupUsers());
                  }
              } finally {
                  // make sure the project is reset in case of any exception
                  switchToCurrentProject();
              }
  
              cms.copyResource(getParamTempfile(), getParamResource(), CmsResource.COPY_AS_NEW);
              // ensure the content handler is called
              CmsFile orgFile = cms.readFile(getParamResource(), CmsResourceFilter.ALL);
              getCloneCms().writeFile(orgFile);
  
          }
          // remove the temporary file flag
          int flags = cms.readResource(getParamResource(), CmsResourceFilter.ALL).getFlags();
          if ((flags & CmsResource.FLAG_TEMPFILE) == CmsResource.FLAG_TEMPFILE) {
              flags ^= CmsResource.FLAG_TEMPFILE;
              cms.chflags(getParamResource(), flags);
          }
      }
  
      /**
       * Creates a temporary file which is needed while working in an editor with preview option.<p>
       *
       * @return the file name of the temporary file
       * @throws CmsException if something goes wrong
       */
      protected String createTempFile() throws CmsException {
  
          return OpenCms.getWorkplaceManager().createTempFile(getCms(), getParamResource(), getSettings().getProject());
      }
  
      /**
       * Decodes the given content the same way the client would do it.<p>
       *
       * Content is decoded as if it was encoded using the JavaScript
       * "encodeURIComponent()" function.<p>
       *
       * @param content the content to decode
       * @return the decoded content
       */
      protected String decodeContent(String content) {
  
          return CmsEncoder.unescape(content, CmsEncoder.ENCODING_UTF_8);
      }
  
      /**
       * Decodes an individual parameter value, ensuring the content is always decoded in UTF-8.<p>
       *
       * For editors the content is always encoded using the
       * JavaScript encodeURIComponent() method on the client,
       * which always encodes in UTF-8.<p>
       *
       * @param paramName the name of the parameter
       * @param paramValue the unencoded value of the parameter
       * @return the encoded value of the parameter
       */
      @Override
      protected String decodeParamValue(String paramName, String paramValue) {
  
          if ((paramName != null) && (paramValue != null)) {
              if (PARAM_CONTENT.equals(paramName)) {
                  // content will be always encoded in UTF-8 unicode by the editor client
                  return CmsEncoder.decode(paramValue, CmsEncoder.ENCODING_UTF_8);
              } else if (PARAM_RESOURCE.equals(paramName) || PARAM_TEMPFILE.equals(paramName)) {
                  String filename = CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding());
                  if (PARAM_TEMPFILE.equals(paramName) || CmsStringUtil.isEmpty(getParamTempfile())) {
                      // always use value from temp file if it is available
                      setFileEncoding(getFileEncoding(getCms(), filename));
                  }
                  return filename;
              } else {
                  return CmsEncoder.decode(paramValue, getCms().getRequestContext().getEncoding());
              }
          } else {
              return null;
          }
      }
  
      /**
       * Deletes a temporary file from the OpenCms VFS, needed when exiting an editor.<p>
       */
      protected void deleteTempFile() {
  
          try {
              // switch to the temporary file project
              switchToTempProject();
              // delete the temporary file
              getCms().deleteResource(getParamTempfile(), CmsResource.DELETE_PRESERVE_SIBLINGS);
          } catch (CmsException e) {
              // should usually never happen
              if (LOG.isInfoEnabled()) {
                  LOG.info(e.getLocalizedMessage(), e);
              }
          } finally {
              try {
                  // switch back to the current project
                  switchToCurrentProject();
              } catch (CmsException e) {
                  // should usually never happen
                  if (LOG.isInfoEnabled()) {
                      LOG.info(e.getLocalizedMessage(), e);
                  }
              }
          }
      }
  
      /**
       * Encodes the given content so that it can be transfered to the client.<p>
       *
       * Content is encoded so that it is compatible with the JavaScript
       * "decodeURIComponent()" function.<p>
       *
       * @param content the content to encode
       * @return the encoded content
       */
      protected String encodeContent(String content) {
  
          return CmsEncoder.escapeWBlanks(content, CmsEncoder.ENCODING_UTF_8);
      }
  
      /**
       * Returns a cloned cms instance that prevents the time range resource filter check.<p>
       *
       * Use it always for unmarshalling and file writing.<p>
       *
       * @return a cloned cms instance that prevents the time range resource filter check
       *
       * @throws CmsException if something goes wrong
       */
      protected CmsObject getCloneCms() throws CmsException {
  
          CmsObject cloneCms = OpenCms.initCmsObject(getCms());
          cloneCms.getRequestContext().setRequestTime(CmsResource.DATE_RELEASED_EXPIRED_IGNORE);
          return cloneCms;
      }
  
      /**
       * Returns the editor session info bean.<p>
       *
       * @return the editor session info bean
       */
      protected CmsEditorSessionInfo getEditorSessionInfo() {
  
          return m_editorSessionInfo;
      }
  
      /**
       * Returns the encoding parameter.<p>
       *
       * @return the encoding parameter
       */
      protected String getFileEncoding() {
  
          return m_fileEncoding;
      }
  
      /**
       * Helper method to determine the encoding of the given file in the VFS,
       * which must be set using the "content-encoding" property.<p>
       *
       * @param cms the CmsObject
       * @param filename the name of the file which is to be checked
       * @return the encoding for the file
       */
      protected String getFileEncoding(CmsObject cms, String filename) {
  
          try {
              return cms.readPropertyObject(filename, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true).getValue(
                  OpenCms.getSystemInfo().getDefaultEncoding());
          } catch (CmsException e) {
              return OpenCms.getSystemInfo().getDefaultEncoding();
          }
      }
  
      /**
       * Initializes the editor content when openening the editor for the first time.<p>
       */
      protected abstract void initContent();
  
      /**
       * @see org.opencms.workplace.CmsWorkplace#initMessages()
       */
      @Override
      protected void initMessages() {
  
          initSessionInfo();
          super.initMessages();
      }
  
      /**
       * Initializes the editor session info bean.<p>
       */
      protected void initSessionInfo() {
  
          CmsResource editedResource = null;
          try {
              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getParamResource())) {
                  editedResource = getCms().readResource(getParamResource());
              }
          } catch (CmsException e) {
              // ignore
          }
  
          CmsEditorSessionInfo info = null;
          if (editedResource != null) {
              HttpSession session = getSession();
              info = (CmsEditorSessionInfo)session.getAttribute(
                  CmsEditorSessionInfo.getEditorSessionInfoKey(editedResource));
              if (info == null) {
                  info = new CmsEditorSessionInfo(editedResource.getStructureId());
              }
              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramBackLink)) {
                  info.setBackLink(m_paramBackLink);
              }
              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramElementlanguage)) {
                  info.setElementLocale(CmsLocaleManager.getLocale(m_paramElementlanguage));
              }
              if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_paramDirectedit)) {
                  info.setDirectEdit(Boolean.parseBoolean(m_paramDirectedit));
              }
              session.setAttribute(info.getEditorSessionInfoKey(), info);
          }
          m_editorSessionInfo = info;
      }
  
      /**
       * Sets the encoding parameter.<p>
       *
       * @param value the encoding value to set
       */
      protected void setFileEncoding(String value) {
  
          m_fileEncoding = CmsEncoder.lookupEncoding(value, value);
      }
  
      /**
       * Shows the selected error page in case of an exception.<p>
       *
       * @param exception the current exception
       * @throws JspException if inclusion of the error page fails
       */
      protected void showErrorPage(Exception exception) throws JspException {
  
          // reset the action parameter
          setParamAction("");
          showErrorPage(this, exception);
          // save not successful, set cancel action
          setAction(ACTION_CANCEL);
          return;
      }
  
      /**
       * Shows the selected error page in case of an exception.<p>
       *
       * @param editor initialized instance of the editor class
       * @param exception the current exception
       * @throws JspException if inclusion of the error page fails
       */
      protected void showErrorPage(Object editor, Exception exception) throws JspException {
  
          // save initialized instance of the editor class in request attribute for included sub-elements
          getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, editor);
  
          // reading of file contents failed, show error dialog
          setAction(ACTION_SHOW_ERRORMESSAGE);
          setParamTitle(key(Messages.GUI_TITLE_EDIT_1, new Object[] {CmsResource.getName(getParamResource())}));
          if (exception != null) {
              getJsp().getRequest().setAttribute(ATTRIBUTE_THROWABLE, exception);
              if (CmsLog.getLog(editor).isWarnEnabled()) {
                  CmsLog.getLog(editor).warn(exception.getLocalizedMessage(), exception);
              }
          }
          // include the common error dialog
          getJsp().include(FILE_DIALOG_SCREEN_ERRORPAGE);
      }
  }
  