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.commons;
029
030import org.opencms.configuration.CmsParameterConfiguration;
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.file.types.CmsResourceTypeXmlPage;
037import org.opencms.i18n.CmsEncoder;
038import org.opencms.jsp.CmsJspActionElement;
039import org.opencms.lock.CmsLock;
040import org.opencms.lock.CmsLockFilter;
041import org.opencms.main.CmsException;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.security.CmsPermissionSet;
045import org.opencms.util.CmsStringUtil;
046import org.opencms.workplace.CmsDialogSelector;
047import org.opencms.workplace.CmsTabDialog;
048import org.opencms.workplace.CmsWorkplace;
049import org.opencms.workplace.CmsWorkplaceSettings;
050import org.opencms.workplace.I_CmsDialogHandler;
051import org.opencms.workplace.I_CmsPostUploadDialogHandler;
052import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
053
054import java.util.ArrayList;
055import java.util.Collections;
056import java.util.HashMap;
057import java.util.Iterator;
058import java.util.List;
059import java.util.Map;
060import java.util.RandomAccess;
061
062import javax.servlet.http.HttpServletRequest;
063import javax.servlet.http.HttpServletResponse;
064import javax.servlet.jsp.JspException;
065import javax.servlet.jsp.PageContext;
066
067import org.apache.commons.logging.Log;
068
069/**
070 * Provides methods for the properties dialog.<p>
071 *
072 * The following files use this class:
073 * <ul>
074 * <li>/commons/property_advanced.jsp
075 * </ul>
076 * <p>
077 *
078 * @since 6.0.0
079 */
080public class CmsPropertyAdvanced extends CmsTabDialog implements I_CmsDialogHandler, I_CmsPostUploadDialogHandler {
081
082    /** Value for the action: save defined property. */
083    public static final int ACTION_SAVE_DEFINE = 400;
084
085    /** Value for the action: save edited properties. */
086    public static final int ACTION_SAVE_EDIT = 300;
087
088    /** Value for the action: show define property form. */
089    public static final int ACTION_SHOW_DEFINE = 200;
090
091    /** Value for the action: show edit properties form. */
092    public static final int ACTION_SHOW_EDIT = 100;
093
094    /** Constant for the "Define" button in the build button method. */
095    public static final int BUTTON_DEFINE = 201;
096
097    /** Constant for the "Finish" button in the build button method. */
098    public static final int BUTTON_FINISH = 202;
099
100    /** Request parameter value for the action: save defined property. */
101    public static final String DIALOG_SAVE_DEFINE = "savedefine";
102
103    /** Request parameter value for the action: save edited properties. */
104    public static final String DIALOG_SAVE_EDIT = "saveedit";
105
106    /** Request parameter value for the action: show information form. */
107    public static final String DIALOG_SHOW_DEFAULT = "default";
108
109    /** Request parameter value for the action: show define property form. */
110    public static final String DIALOG_SHOW_DEFINE = "define";
111
112    /** Request parameter value for the action: show edit properties form. */
113    public static final String DIALOG_SHOW_EDIT = "edit";
114
115    /** The dialog type. */
116    public static final String DIALOG_TYPE = "property";
117
118    /** Value for the dialog mode: new resource wizard. */
119    public static final String MODE_WIZARD = "wizard";
120
121    /** Value for the dialog mode: new resource wizard with creation of index page for new folder. */
122    public static final String MODE_WIZARD_CREATEINDEX = "wizardcreateindex";
123
124    /** Value for the dialog mode: new resource wizard with index page created in new folder. */
125    public static final String MODE_WIZARD_INDEXCREATED = "wizardindexcreated";
126
127    /** Key name for the resource panel. */
128    public static final String PANEL_RESOURCE = "panel.properties.resource";
129
130    /** Key name for the structure panel. */
131    public static final String PANEL_STRUCTURE = "panel.properties.structure";
132
133    /** Request parameter name for the new property definition. */
134    public static final String PARAM_DIALOGMODE = "dialogmode";
135
136    /** Configuration key for the dialog handler flag. */
137    public static final String PARAM_HIDEADVANCED = "hideadvanced";
138
139    /** Request parameter name for the new property definition. */
140    public static final String PARAM_NEWPROPERTY = "newproperty";
141
142    /** Configuration key for the upload handler class. */
143    public static final String PARAM_POSTUPLOADHANDLER = "upload-handler";
144
145    /** Configuration key for the group name. */
146    public static final String PARAM_SHOWGROUP = "showgroup";
147
148    /** Prefix for the hidden fields. */
149    public static final String PREFIX_HIDDEN = "valhidden";
150
151    /** Prefix for the hidden resource value. */
152    public static final String PREFIX_RESOURCE = "valresource";
153
154    /** Prefix for the hidden structure value. */
155    public static final String PREFIX_STRUCTURE = "valstructure";
156
157    /** Prefix for the use property checkboxes. */
158    public static final String PREFIX_USEPROPERTY = "useprop";
159
160    /** Prefix for the input values. */
161    public static final String PREFIX_VALUE = "valprop";
162
163    /** Name for the shared (resource) property tab. */
164    public static final String TAB_RESOURCE = "tabres";
165
166    /** Name for the individual (structure) property tab. */
167    public static final String TAB_STRUCTURE = "tabstr";
168
169    /** The URI to the customized property dialog. */
170    public static final String URI_PROPERTY_CUSTOM_DIALOG = PATH_DIALOGS + "property_custom.jsp";
171
172    /** The URI to the standard property dialog. */
173    public static final String URI_PROPERTY_DIALOG = PATH_DIALOGS + "property_advanced.jsp";
174
175    /** The URI to the property dialog handler. */
176    public static final String URI_PROPERTY_DIALOG_HANDLER = PATH_DIALOGS + "property.jsp";
177
178    /** The log object for this class. */
179    private static final Log LOG = CmsLog.getLog(CmsPropertyAdvanced.class);
180
181    /** Holds all active properties for the current resource. */
182    private Map<String, CmsProperty> m_activeProperties;
183
184    /** Parameters of this class. */
185    private CmsParameterConfiguration m_handlerParams = new CmsParameterConfiguration();
186
187    /** Helper object storing the current editable state of the resource. */
188    private Boolean m_isEditable;
189
190    /** Helper to determine if the edited resource is a folder. */
191    private boolean m_isFolder;
192
193    /** Helper stores the mode this dialog is in, because it can be called from "new" wizard. */
194    private String m_paramDialogMode;
195
196    /** A parameter used by this dialog. */
197    private String m_paramIndexPageType;
198
199    /** Request parameter members. */
200    private String m_paramNewproperty;
201
202    /** A parameter used by this dialog. */
203    private String m_paramUseTempfileProject;
204
205    /** Stores the values of properties in a String array. */
206    private List<String[]> m_propertyValues;
207
208    /** Helper to determine if the user switched the tab views of the dialog. */
209    private boolean m_tabSwitched;
210
211    /** The configured upload handler. */
212    private I_CmsPostUploadDialogHandler m_uploadHandler = new CmsDefaultUploadHandler();
213
214    /**
215     * Default constructor needed for dialog handler implementation.<p>
216     *
217     * Do not use this constructor on JSP pages.<p>
218     */
219    public CmsPropertyAdvanced() {
220
221        super(null);
222    }
223
224    /**
225     * Public constructor with JSP action element.<p>
226     *
227     * @param jsp an initialized JSP action element
228     */
229    public CmsPropertyAdvanced(CmsJspActionElement jsp) {
230
231        super(jsp);
232    }
233
234    /**
235     * Public constructor with JSP variables.<p>
236     *
237     * @param context the JSP page context
238     * @param req the JSP request
239     * @param res the JSP response
240     */
241    public CmsPropertyAdvanced(PageContext context, HttpServletRequest req, HttpServletResponse res) {
242
243        this(new CmsJspActionElement(context, req, res));
244    }
245
246    /**
247     * Transforms a list of CmsProperty objects with structure and resource values into a map with
248     * CmsProperty object values keyed by property keys.<p>
249     *
250     * @param list a list of CmsProperty objects
251     * @return a map with CmsPropery object values keyed by property keys
252     */
253    public static Map<String, CmsProperty> getPropertyMap(List<CmsProperty> list) {
254
255        Map<String, CmsProperty> result = null;
256        String key = null;
257        CmsProperty property = null;
258
259        if ((list == null) || (list.size() == 0)) {
260            return Collections.emptyMap();
261        }
262
263        result = new HashMap<String, CmsProperty>();
264
265        // choose the fastest method to iterate the list
266        if (list instanceof RandomAccess) {
267            for (int i = 0, n = list.size(); i < n; i++) {
268                property = list.get(i);
269                key = property.getName();
270                result.put(key, property);
271            }
272        } else {
273            Iterator<CmsProperty> i = list.iterator();
274            while (i.hasNext()) {
275                property = i.next();
276                key = property.getName();
277                result.put(key, property);
278            }
279        }
280
281        return result;
282    }
283
284    /**
285     * Used to close the current JSP dialog.<p>
286     *
287     * This method overwrites the close dialog method in the super class,
288     * because in case a new folder is created, after this dialog a new xml page might be created.<p>
289     *
290     * It tries to include the URI stored in the workplace settings.
291     * This URI is determined by the frame name, which has to be set
292     * in the framename parameter.<p>
293     *
294     * @throws JspException if including an element fails
295     */
296    @Override
297    public void actionCloseDialog() throws JspException {
298
299        if ((getAction() == ACTION_SAVE_EDIT) && MODE_WIZARD.equals(getParamDialogmode())) {
300            // set request attribute to reload the folder tree after creating a folder in wizard mode
301            try {
302                CmsResource res = getCms().readResource(getParamResource(), CmsResourceFilter.ALL);
303                if (res.isFolder()) {
304                    List<String> folderList = new ArrayList<String>(1);
305                    folderList.add(CmsResource.getParentFolder(getParamResource()));
306                    getJsp().getRequest().setAttribute(REQUEST_ATTRIBUTE_RELOADTREE, folderList);
307                }
308            } catch (CmsException e) {
309                // should usually never happen
310                if (LOG.isInfoEnabled()) {
311                    LOG.info(e.getLocalizedMessage());
312                }
313            }
314        } else if (MODE_WIZARD_INDEXCREATED.equals(getParamDialogmode())) {
315            // set request attribute to reload the folder tree after creating an xml page in a new created folder in wizard mode
316            getSettings().setExplorerResource(
317                CmsResource.getParentFolder(CmsResource.getParentFolder(getParamResource())),
318                getCms());
319            List<String> folderList = new ArrayList<String>(1);
320            folderList.add(CmsResource.getParentFolder(CmsResource.getParentFolder(getParamResource())));
321            getJsp().getRequest().setAttribute(REQUEST_ATTRIBUTE_RELOADTREE, folderList);
322        }
323        super.actionCloseDialog();
324    }
325
326    /**
327     * Performs the define property action, will be called by the JSP page.<p>
328     *
329     * @throws JspException if problems including sub-elements occur
330     */
331    public void actionDefine() throws JspException {
332
333        // save initialized instance of this class in request attribute for included sub-elements
334        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
335        try {
336            performDefineOperation();
337            // set the request parameters before returning to the overview
338            setParamAction(DIALOG_SHOW_DEFAULT);
339            setParamNewproperty(null);
340            sendForward(CmsWorkplace.VFS_PATH_COMMONS + "property_advanced.jsp", paramsAsParameterMap());
341        } catch (Throwable e) {
342            // error defining property, show error dialog
343            includeErrorpage(this, e);
344        }
345    }
346
347    /**
348     * Deletes the current resource if the dialog is in wizard mode.<p>
349     *
350     * If the dialog is not in wizard mode, the resource is not deleted.<p>
351     *
352     * @throws JspException if including the error page fails
353     */
354    public void actionDeleteResource() throws JspException {
355
356        if ((getParamDialogmode() != null) && getParamDialogmode().startsWith(MODE_WIZARD)) {
357            // only delete resource if dialog mode is a wizard mode
358            try {
359                getCms().deleteResource(getParamResource(), CmsResource.DELETE_PRESERVE_SIBLINGS);
360            } catch (Throwable e) {
361                // error deleting the resource, show error dialog
362                includeErrorpage(this, e);
363            }
364        }
365    }
366
367    /**
368     * Performs the edit properties action, will be called by the JSP page.<p>
369     *
370     * @param request the HttpServletRequest
371     * @throws JspException if problems including sub-elements occur
372     */
373    public void actionEdit(HttpServletRequest request) throws JspException {
374
375        // save initialized instance of this class in request attribute for included sub-elements
376        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
377        try {
378            if (isEditable()) {
379                performDialogOperation(request);
380            }
381        } catch (Throwable e) {
382            // error editing property, show error dialog
383            includeErrorpage(this, e);
384        }
385    }
386
387    /**
388     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String)
389     */
390    public void addConfigurationParameter(String paramName, String paramValue) {
391
392        if (m_handlerParams == null) {
393            m_handlerParams = new CmsParameterConfiguration();
394        }
395        if (PARAM_HIDEADVANCED.equalsIgnoreCase(paramName)) {
396            m_handlerParams.add(PARAM_HIDEADVANCED, paramValue.trim());
397        }
398        if (PARAM_SHOWGROUP.equalsIgnoreCase(paramName)) {
399            m_handlerParams.add(PARAM_SHOWGROUP, paramValue.trim());
400        }
401        if (PARAM_POSTUPLOADHANDLER.equalsIgnoreCase(paramName)) {
402            m_handlerParams.add(PARAM_POSTUPLOADHANDLER, paramValue.trim());
403        }
404    }
405
406    /**
407     * Creates the HTML String for the active properties overview of the current resource.<p>
408     *
409     * @return the HTML output String for active properties of the resource
410     */
411    public String buildActivePropertiesList() {
412
413        StringBuffer retValue = new StringBuffer(256);
414        List<CmsPropertyDefinition> propertyDef = new ArrayList<CmsPropertyDefinition>();
415        try {
416            // get all property definitions
417            propertyDef = getCms().readAllPropertyDefinitions();
418        } catch (CmsException e) {
419            // should usually never happen
420            if (LOG.isInfoEnabled()) {
421                LOG.info(e.getLocalizedMessage());
422            }
423        }
424
425        Iterator<CmsPropertyDefinition> j = propertyDef.iterator();
426        int i = 0;
427        while (j.hasNext()) {
428            CmsPropertyDefinition curProperty = j.next();
429            retValue.append(CmsEncoder.escapeXml(curProperty.getName()));
430            if ((i + 1) < propertyDef.size()) {
431                retValue.append("<br>");
432            }
433            i++;
434        }
435
436        return retValue.toString();
437    }
438
439    /**
440     * Creates the HTML String for the edit properties form.<p>
441     *
442     * The values of the form are set delayed, have a look at the
443     * JavaDoc of buildSetFormValues() for a detailed description.<p>
444     *
445     * @return the HTML output String for the edit properties form
446     */
447    public String buildEditForm() {
448
449        StringBuffer result = new StringBuffer(4096);
450
451        // get currently active tab
452        String activeTab = getActiveTabName();
453
454        // initialize "disabled" attribute for the input fields
455        String disabled = "";
456        if (!isEditable()) {
457            disabled = " disabled=\"disabled\"";
458        }
459
460        // get all properties for the resource
461        List<String[]> properties = getPropertyValues();
462
463        // check for presence of property definitions, should always be true
464        if (properties.size() > 0) {
465            // there are properties defined for this resource, build the form list
466            result.append("<table border=\"0\" style=\"width:100%\">\n");
467            result.append("<tr>\n");
468            result.append("\t<td class=\"textbold\" nowrap>");
469            result.append(key(Messages.GUI_PROPERTY_0));
470            result.append("</td>\n");
471            result.append("\t<td class=\"textbold\">");
472            result.append(key(Messages.GUI_PROPERTY_VALUE_0));
473            result.append("</td>\n");
474            // build column for checkbox
475            result.append("\t<td class=\"textbold\" style=\"white-space: nowrap;\">");
476            result.append("&nbsp;");
477            result.append("</td>\n");
478            result.append("</tr>\n");
479            result.append("<tr><td colspan=\"3\"><span style=\"height: 6px;\"></span></td></tr>\n");
480
481            // show all possible properties for the resource
482            Iterator<String[]> i = properties.iterator();
483            while (i.hasNext()) {
484                String[] curProp = i.next();
485                // create a single property row
486                result.append(buildPropertyRow(curProp[0], curProp[1], curProp[2], curProp[3], disabled, activeTab));
487            }
488            result.append("</table>");
489        } else {
490            // there are no properties defined for this resource, show nothing (should never happen)
491            result.append(key(Messages.GUI_PROPERTY_ADVANCED_NO_PROPDEFS_0));
492        }
493        return result.toString();
494    }
495
496    /**
497     * Builds the JavaScript to set the property form values delayed.<p>
498     *
499     * The values of the properties are not inserted directly in the &lt;input&gt; tag,
500     * because there is a display issue when the property values are very long.
501     * This method creates JavaScript to set the property input field values delayed.
502     * On the JSP, the code which is created from this method has to be executed delayed after
503     * the creation of the html form, e.g. in the &lt;body&gt; tag with the attribute
504     * onload="window.setTimeout('doSet()',50);".<p>
505     *
506     * @return the JavaScript to set the property form values delayed
507     */
508    public String buildSetFormValues() {
509
510        StringBuffer result = new StringBuffer(1024);
511        // get currently active tab
512        String activeTab = getActiveTabName();
513        // get structure panel name
514        String structurePanelName = key(Messages.GUI_PROPERTIES_INDIVIDUAL_0);
515        Iterator<String[]> i = getPropertyValues().iterator();
516        while (i.hasNext()) {
517            String[] curProp = i.next();
518            // determine the shown value
519            String shownValue = curProp[1];
520            // in "shared properties" form, show resource value if no structure value is set
521            if (structurePanelName.equals(activeTab) && "".equals(curProp[2]) && !"".equals(curProp[3])) {
522                shownValue = curProp[3];
523            }
524            if (!"".equals(shownValue)) {
525                // create the JS output for a single property if not empty
526                result.append("\tdocument.getElementById(\"");
527                result.append(PREFIX_VALUE);
528                result.append(curProp[0]);
529                result.append("\").value = \"");
530                result.append(CmsStringUtil.escapeJavaScript(shownValue));
531                result.append("\";\n");
532            }
533
534        }
535        return result.toString();
536    }
537
538    /**
539     * Builds a button row with an "Ok", a "Cancel" and a "Define" button.<p>
540     *
541     * @return the button row
542     */
543    public String dialogButtonsOkCancelDefine() {
544
545        if (isEditable()) {
546            int okButton = BUTTON_OK;
547            if ((getParamDialogmode() != null) && getParamDialogmode().startsWith(MODE_WIZARD)) {
548                // in wizard mode, display finish button instead of ok button
549                okButton = BUTTON_FINISH;
550            }
551            return dialogButtons(
552                new int[] {okButton, BUTTON_CANCEL, BUTTON_DEFINE},
553                new String[] {null, null, "onclick=\"definePropertyForm();\""});
554        } else {
555            return dialogButtons(new int[] {BUTTON_CLOSE}, new String[] {null});
556
557        }
558    }
559
560    /**
561     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration()
562     */
563    public CmsParameterConfiguration getConfiguration() {
564
565        if (m_handlerParams == null) {
566            m_handlerParams = new CmsParameterConfiguration();
567        }
568        return m_handlerParams;
569    }
570
571    /**
572     * @see org.opencms.workplace.I_CmsDialogHandler#getDialogHandler()
573     */
574    public String getDialogHandler() {
575
576        return CmsDialogSelector.DIALOG_PROPERTY;
577    }
578
579    /**
580     * @see org.opencms.workplace.I_CmsDialogHandler#getDialogUri(java.lang.String, CmsJspActionElement)
581     */
582    public String getDialogUri(String resource, CmsJspActionElement jsp) {
583
584        try {
585            CmsResource res = jsp.getCmsObject().readResource(resource, CmsResourceFilter.ALL);
586            if (CmsResourceTypeXmlPage.isXmlPage(res)) {
587                // display special property dialog for xmlpage types
588                return PATH_WORKPLACE + "editors/dialogs/property.jsp";
589            }
590            String resTypeName = OpenCms.getResourceManager().getResourceType(res.getTypeId()).getTypeName();
591            // get settings for resource type
592            CmsExplorerTypeSettings settings = getSettingsForType(resTypeName);
593            if (settings.isPropertiesEnabled()) {
594                // special properties for this type enabled, display customized dialog
595                return URI_PROPERTY_CUSTOM_DIALOG;
596            }
597        } catch (CmsException e) {
598            // should usually never happen
599            if (LOG.isInfoEnabled()) {
600                LOG.info(e.getLocalizedMessage());
601            }
602        }
603        return URI_PROPERTY_DIALOG;
604    }
605
606    /**
607     * Returns the value of the dialogmode parameter,
608     * or null if this parameter was not provided.<p>
609     *
610     * The dialogmode parameter stores the different modes of the property dialog,
611     * e.g. for displaying other buttons in the new resource wizard.<p>
612     *
613     * @return the value of the usetempfileproject parameter
614     */
615    public String getParamDialogmode() {
616
617        return m_paramDialogMode;
618    }
619
620    /**
621     * Returns the paramIndexPageType.<p>
622     *
623     * @return the paramIndexPageType
624     */
625    public String getParamIndexPageType() {
626
627        return m_paramIndexPageType;
628    }
629
630    /**
631     * Returns the value of the new property parameter,
632     * or null if this parameter was not provided.<p>
633     *
634     * The new property parameter stores the name of the
635     * new defined property.<p>
636     *
637     * @return the value of the new property parameter
638     */
639    public String getParamNewproperty() {
640
641        return m_paramNewproperty;
642    }
643
644    /**
645     * Returns the value of the usetempfileproject parameter,
646     * or null if this parameter was not provided.<p>
647     *
648     * The usetempfileproject parameter stores if the file resides
649     * in the temp file project.<p>
650     *
651     * @return the value of the usetempfileproject parameter
652     */
653    public String getParamUsetempfileproject() {
654
655        return m_paramUseTempfileProject;
656    }
657
658    /**
659     * @see org.opencms.workplace.CmsTabDialog#getTabParameterOrder()
660     */
661    @Override
662    public List<String> getTabParameterOrder() {
663
664        ArrayList<String> orderList = new ArrayList<String>(2);
665        orderList.add(TAB_STRUCTURE);
666        orderList.add(TAB_RESOURCE);
667        return orderList;
668    }
669
670    /**
671     * @see org.opencms.workplace.CmsTabDialog#getTabs()
672     */
673    @Override
674    public List<String> getTabs() {
675
676        ArrayList<String> tabList = new ArrayList<String>(2);
677        if (OpenCms.getWorkplaceManager().isEnableAdvancedPropertyTabs()) {
678            // tabs are enabled, show both tabs except for folders
679            if (m_isFolder) {
680                // resource is a folder, show only the individual properties tab
681                tabList.add(key(Messages.GUI_PROPERTIES_INDIVIDUAL_0));
682            } else {
683                // resource is no folder, show both tabs
684                tabList.add(key(Messages.GUI_PROPERTIES_INDIVIDUAL_0));
685                tabList.add(key(Messages.GUI_PROPERTIES_SHARED_0));
686            }
687        } else {
688            // tabs are disabled, show only the configured tab except for folders
689            if (m_isFolder || OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
690                tabList.add(key(Messages.GUI_PROPERTIES_INDIVIDUAL_0));
691            } else {
692                tabList.add(key(Messages.GUI_PROPERTIES_SHARED_0));
693            }
694        }
695        return tabList;
696    }
697
698    /**
699     * @see org.opencms.workplace.I_CmsPostUploadDialogHandler#getUploadHook(org.opencms.file.CmsObject, java.lang.String)
700     */
701    public String getUploadHook(CmsObject cms, String uploadFolderSitePath) {
702
703        return m_uploadHandler.getUploadHook(cms, uploadFolderSitePath);
704    }
705
706    /**
707     * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration()
708     */
709    public void initConfiguration() {
710
711        String uploadHandlerName = m_handlerParams.get(PARAM_POSTUPLOADHANDLER);
712        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(uploadHandlerName)) {
713            try {
714                Class<?> uploadHandlerClass = Class.forName(uploadHandlerName);
715                if (I_CmsPostUploadDialogHandler.class.isAssignableFrom(uploadHandlerClass)) {
716                    m_uploadHandler = (I_CmsPostUploadDialogHandler)(uploadHandlerClass.newInstance());
717                } else {
718                    LOG.error("Invalid upload handler class: " + uploadHandlerName);
719                }
720            } catch (InstantiationException e) {
721                LOG.error("Could not instantiate upload handler: " + uploadHandlerName, e);
722            } catch (ClassNotFoundException e) {
723                LOG.error("Upload handler class not found: " + uploadHandlerName, e);
724            } catch (IllegalAccessException e) {
725                LOG.error(e.getLocalizedMessage(), e);
726            }
727        }
728    }
729
730    /**
731     * Sets the value of the dialogmode parameter.<p>
732     *
733     * @param value the value to set
734     */
735    public void setParamDialogmode(String value) {
736
737        m_paramDialogMode = value;
738    }
739
740    /**
741     * Sets the paramIndexPageType.<p>
742     *
743     * @param paramIndexPageType the paramIndexPageType to set
744     */
745    public void setParamIndexPageType(String paramIndexPageType) {
746
747        m_paramIndexPageType = paramIndexPageType;
748    }
749
750    /**
751     * Sets the value of the new property parameter.<p>
752     *
753     * @param value the value to set
754     */
755    public void setParamNewproperty(String value) {
756
757        m_paramNewproperty = value;
758    }
759
760    /**
761     * Sets the value of the usetempfileproject parameter.<p>
762     *
763     * @param value the value to set
764     */
765    public void setParamUsetempfileproject(String value) {
766
767        m_paramUseTempfileProject = value;
768    }
769
770    /**
771     * @see org.opencms.workplace.CmsDialog#dialogButtonsHtml(java.lang.StringBuffer, int, java.lang.String)
772     */
773    @Override
774    protected void dialogButtonsHtml(StringBuffer result, int button, String attribute) {
775
776        attribute = appendDelimiter(attribute);
777
778        switch (button) {
779            case BUTTON_DEFINE:
780                result.append("<input name=\"define\" type=\"button\" value=\"");
781                result.append(key(Messages.GUI_PROPERTY_DEFINE_0));
782                result.append("\" class=\"dialogbutton\"");
783                result.append(attribute);
784                result.append(">\n");
785                break;
786            case BUTTON_FINISH:
787                result.append("<input name=\"finish\" type=\"submit\" value=\"");
788                result.append(key(Messages.GUI_PROPERTY_FINISH_0));
789                result.append("\" class=\"dialogbutton\"");
790                result.append(attribute);
791                result.append(">\n");
792                break;
793            default:
794                super.dialogButtonsHtml(result, button, attribute);
795        }
796    }
797
798    /**
799     * Returns a map with CmsProperty object values keyed by property keys.<p>
800     *
801     * @return a map with CmsProperty object values
802     */
803    protected Map<String, CmsProperty> getActiveProperties() {
804
805        // get all used properties for the resource
806        if (m_activeProperties == null) {
807            try {
808                m_activeProperties = CmsPropertyAdvanced.getPropertyMap(
809                    getCms().readPropertyObjects(getParamResource(), false));
810            } catch (CmsException e) {
811                // create an empty list
812                if (LOG.isInfoEnabled()) {
813                    LOG.info(e.getLocalizedMessage());
814                }
815                m_activeProperties = new HashMap<String, CmsProperty>();
816            }
817        }
818        return m_activeProperties;
819    }
820
821    /**
822     * Returns the explorer type settings of the resource type, considering eventual references to another type.<p>
823     *
824     * @param resTypeName the resource type name
825     * @return the explorer type settings of the resource type
826     */
827    protected CmsExplorerTypeSettings getSettingsForType(String resTypeName) {
828
829        // get settings for resource type
830        CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(resTypeName);
831        if (!settings.hasEditOptions() && CmsStringUtil.isNotEmpty(settings.getReference())) {
832            // refers to another resource type, get settings of referred type
833            settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(settings.getReference());
834        }
835        return settings;
836    }
837
838    /**
839     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
840     */
841    @Override
842    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
843
844        // fill the parameter values in the get/set methods
845        fillParamValues(request);
846
847        // get the active tab from request parameter or display first tab
848        getActiveTab();
849
850        // check the resource type of the edited resource
851        m_isFolder = false;
852        try {
853            CmsResource resource = getCms().readResource(getParamResource(), CmsResourceFilter.ALL);
854            if (resource.isFolder()) {
855                m_isFolder = true;
856                if (!getParamResource().endsWith("/")) {
857                    // append folder separator to resource name
858                    setParamResource(getParamResource() + "/");
859                }
860            }
861        } catch (CmsException e) {
862            // error reading resource, log the error
863            LOG.error(e.getLocalizedMessage());
864        }
865
866        // set the dialog type
867        setParamDialogtype(DIALOG_TYPE);
868        boolean isPopup = Boolean.valueOf(getParamIsPopup()).booleanValue();
869        // set the action for the JSP switch
870        if (DIALOG_SHOW_DEFINE.equals(getParamAction())) {
871            setAction(ACTION_SHOW_DEFINE);
872            setParamTitle(key(Messages.GUI_PROPERTY_NEW_DEF_1, new Object[] {CmsResource.getName(getParamResource())}));
873        } else if (DIALOG_SAVE_EDIT.equals(getParamAction())) {
874            if (isPopup) {
875                setAction(ACTION_CLOSEPOPUP_SAVE);
876            } else {
877                setAction(ACTION_SAVE_EDIT);
878            }
879        } else if (DIALOG_SAVE_DEFINE.equals(getParamAction())) {
880            setAction(ACTION_SAVE_DEFINE);
881        } else if (DIALOG_CANCEL.equals(getParamAction())) {
882            if (isPopup) {
883                setAction(ACTION_CLOSEPOPUP);
884            } else {
885                setAction(ACTION_CANCEL);
886            }
887        } else {
888            // set the default action: show edit form
889            setAction(ACTION_DEFAULT);
890            if (!isEditable()) {
891                setParamTitle(
892                    key(Messages.GUI_PROPERTIES_OF_1, new Object[] {CmsResource.getName(getParamResource())}));
893            } else {
894                setParamTitle(
895                    key(Messages.GUI_PROPERTIES_EDIT_1, new Object[] {CmsResource.getName(getParamResource())}));
896            }
897            // check if the user switched a tab
898            m_tabSwitched = false;
899            if (DIALOG_SWITCHTAB.equals(getParamAction())) {
900                m_tabSwitched = true;
901            }
902        }
903    }
904
905    /**
906     * Returns whether the properties are editable or not depending on the lock state of the resource and the current project.<p>
907     *
908     * @return true if properties are editable, otherwise false
909     */
910    protected boolean isEditable() {
911
912        if (m_isEditable == null) {
913
914            if (getCms().getRequestContext().getCurrentProject().isOnlineProject()
915                || !getCms().isInsideCurrentProject(getParamResource())) {
916                // we are in the online project or resource does not belong to project, no editing allowed
917                m_isEditable = Boolean.FALSE;
918
919            } else {
920                // we are in an offline project
921
922                // check permissions
923                if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) {
924                    getSettings().setErrorMessage(null);
925                    m_isEditable = Boolean.FALSE;
926                    return m_isEditable.booleanValue();
927                }
928
929                // check lock state
930                String resourceName = getParamResource();
931                CmsResource file = null;
932                CmsLock lock = null;
933                try {
934                    file = getCms().readResource(resourceName, CmsResourceFilter.ALL);
935                    // check if resource is a folder
936                    if (file.isFolder() && !resourceName.endsWith("/")) {
937                        resourceName += "/";
938                    }
939                } catch (CmsException e) {
940                    // should usually never happen
941                    if (LOG.isInfoEnabled()) {
942                        LOG.info(e.getLocalizedMessage());
943                    }
944                }
945
946                try {
947                    // get the lock for the resource
948                    lock = getCms().getLock(file);
949                } catch (CmsException e) {
950                    lock = CmsLock.getNullLock();
951
952                    if (LOG.isErrorEnabled()) {
953                        LOG.error(e.getLocalizedMessage(), e);
954                    }
955                }
956
957                if (!lock.isNullLock()) {
958                    // determine if resource is editable...
959                    if (lock.isDirectlyOwnedBy(getCms().getRequestContext().getCurrentUser())) {
960                        // lock is exclusive and belongs to the current user
961                        if (lock.isInProject(getCms().getRequestContext().getCurrentProject())
962                            || Boolean.valueOf(getParamUsetempfileproject()).booleanValue()) {
963                            // resource is locked in the current project or the tempfileproject is used
964                            m_isEditable = Boolean.TRUE;
965                            return m_isEditable.booleanValue();
966                        }
967                    }
968                } else if (OpenCms.getWorkplaceManager().autoLockResources()) {
969                    if ((file == null) || file.isFolder()) {
970                        // check locked resources in folder
971                        try {
972                            List<String> lockedResources = getCms().getLockedResources(
973                                resourceName,
974                                CmsLockFilter.FILTER_ALL.filterNotOwnedByUserId(
975                                    getCms().getRequestContext().getCurrentUser().getId()));
976                            if (!lockedResources.isEmpty()) {
977                                m_isEditable = Boolean.FALSE;
978                                return m_isEditable.booleanValue();
979                            }
980                        } catch (CmsException e1) {
981                            // should usually never happen
982                            if (LOG.isErrorEnabled()) {
983                                LOG.error(e1.getLocalizedMessage(), e1);
984                            }
985                        }
986                    }
987                    m_isEditable = Boolean.TRUE;
988                    return m_isEditable.booleanValue();
989                }
990                // lock is null or belongs to other user and/or project, properties are not editable
991                m_isEditable = Boolean.FALSE;
992            }
993        }
994        return m_isEditable.booleanValue();
995    }
996
997    /**
998     * Builds the html for a single property entry row.<p>
999     *
1000     * The output depends on the currently active tab (shared or individual properties)
1001     * and on the present values of the current property.<p>
1002     *
1003     * The values of the property are not inserted directly in the &lt;input&gt; tag,
1004     * because there is a display issue when the property values are very long.
1005     * Have a look at buildSetFormValues() for a detailed description.<p>
1006     *
1007     * @param propName the name of the property
1008     * @param propValue the displayed value of the property
1009     * @param valueStructure the structure value of the property
1010     * @param valueResource the resource value of the property
1011     * @param disabled contains attribute String to disable the fields
1012     * @param activeTab the name of the currently active dialog tab
1013     * @return the html for a single property entry row
1014     */
1015    private StringBuffer buildPropertyRow(
1016        String propName,
1017        String propValue,
1018        String valueStructure,
1019        String valueResource,
1020        String disabled,
1021        String activeTab) {
1022
1023        StringBuffer result = new StringBuffer(256);
1024        //        boolean existsPropertyValues = getActiveProperties().size() > 0;
1025        String structurePanelName = key(Messages.GUI_PROPERTIES_INDIVIDUAL_0);
1026        String inputAttrs = "class=\"maxwidth\"";
1027        if (structurePanelName.equals(activeTab)) {
1028            // in "shared properties" form, show resource value if no structure value is set
1029            if ("".equals(valueStructure) && !"".equals(valueResource)) {
1030                inputAttrs = "class=\"dialogmarkedfield\"";
1031            }
1032        }
1033        result.append("<tr>\n");
1034        result.append("\t<td style=\"white-space: nowrap;\">" + propName);
1035        result.append("</td>\n");
1036        result.append("\t<td class=\"maxwidth\">");
1037
1038        // build text input field
1039        result.append("<input type=\"text\" ");
1040        result.append(inputAttrs);
1041        result.append(" name=\"");
1042        result.append(PREFIX_VALUE);
1043        result.append(propName);
1044        result.append("\" id=\"");
1045        result.append(PREFIX_VALUE);
1046        result.append(propName);
1047        result.append("\"");
1048        result.append(" onFocus=\"deleteResEntry('");
1049        result.append(propName);
1050        result.append("', '");
1051        result.append(activeTab);
1052        result.append("');\"");
1053        result.append(" onBlur=\"checkResEntry('");
1054        result.append(propName);
1055        result.append("', '");
1056        result.append(activeTab);
1057        result.append("');\" onKeyup=\"checkValue('");
1058        result.append(propName);
1059        result.append("', '");
1060        result.append(activeTab);
1061        result.append("');\"");
1062        result.append(disabled);
1063        result.append(">");
1064
1065        // build hidden input field for structure value
1066        result.append("<input type=\"hidden\" name=\"");
1067        result.append(PREFIX_STRUCTURE);
1068        result.append(propName);
1069        result.append("\" id=\"");
1070        result.append(PREFIX_STRUCTURE);
1071        result.append(propName);
1072        result.append("\" value=\"");
1073        result.append(CmsEncoder.escapeXml(valueStructure));
1074        result.append("\">");
1075
1076        // build hidden input field for resource value
1077        result.append("<input type=\"hidden\" name=\"");
1078        result.append(PREFIX_RESOURCE);
1079        result.append(propName);
1080        result.append("\" id=\"");
1081        result.append(PREFIX_RESOURCE);
1082        result.append(propName);
1083        result.append("\" value=\"");
1084        result.append(CmsEncoder.escapeXml(valueResource));
1085        result.append("\"></td>\n");
1086        result.append("\t<td class=\"propertydialog-checkboxcell\">");
1087        // show checkbox always
1088        String prefix = PREFIX_RESOURCE;
1089        if (structurePanelName.equals(activeTab)) {
1090            prefix = PREFIX_STRUCTURE;
1091        }
1092        result.append("<input type=\"checkbox\" name=\"");
1093        result.append(PREFIX_USEPROPERTY);
1094        result.append(propName);
1095        result.append("\" id=\"");
1096        result.append(PREFIX_USEPROPERTY);
1097        result.append(propName);
1098        result.append("\" value=\"true\"");
1099        result.append(disabled);
1100        if (CmsStringUtil.isNotEmpty(propValue)) {
1101            result.append(" checked=\"checked\" ");
1102        }
1103        result.append("onClick=\"toggleDelete('");
1104        result.append(propName);
1105        result.append("', '");
1106        result.append(prefix);
1107        result.append("', '");
1108        result.append(activeTab);
1109        result.append("');\">");
1110        result.append("</td>\n");
1111
1112        result.append("</tr>\n");
1113        return result;
1114    }
1115
1116    /**
1117     * Creates a list of String arrays containing the property names and values.<p>
1118     *
1119     * The list items consist of the following Strings:
1120     * <ol>
1121     * <li>The name of the property</li>
1122     * <li>The currently active property value</li>
1123     * <li>The value of the structure</li>
1124     * <li>The value of the resource</li>
1125     * </ol>
1126     *
1127     * @return the list of property values in display order
1128     */
1129    private List<String[]> getPropertyValues() {
1130
1131        // check if list has to be generated
1132        if (m_propertyValues == null) {
1133
1134            // get currently active tab
1135            String activeTab = getActiveTabName();
1136            String structurePanelName = key(Messages.GUI_PROPERTIES_INDIVIDUAL_0);
1137
1138            // get all properties for the resource
1139            List<CmsPropertyDefinition> propertyDef = new ArrayList<CmsPropertyDefinition>();
1140            try {
1141                propertyDef = getCms().readAllPropertyDefinitions();
1142            } catch (CmsException e) {
1143                // should usually never happen
1144                if (LOG.isInfoEnabled()) {
1145                    LOG.info(e.getLocalizedMessage());
1146                }
1147            }
1148            m_propertyValues = new ArrayList<String[]>(propertyDef.size());
1149
1150            // get all used properties for the resource
1151            Map<String, CmsProperty> activeProperties = getActiveProperties();
1152
1153            // iterate over all possible properties for the resource
1154            Iterator<CmsPropertyDefinition> i = propertyDef.iterator();
1155            while (i.hasNext()) {
1156                CmsPropertyDefinition currentPropertyDef = i.next();
1157                String propName = CmsEncoder.escapeXml(currentPropertyDef.getName());
1158                String propValue = "";
1159                String valueStructure = "";
1160                String valueResource = "";
1161                if (m_tabSwitched) {
1162                    // switched the tab, get values from hidden fields
1163                    if (structurePanelName.equals(activeTab)) {
1164                        // structure form
1165                        propValue = getJsp().getRequest().getParameter(PREFIX_STRUCTURE + propName);
1166                        valueStructure = getJsp().getRequest().getParameter(PREFIX_STRUCTURE + propName);
1167                        if (!isEditable()) {
1168                            // values from disabled fields are not posted
1169                            valueResource = getJsp().getRequest().getParameter(PREFIX_RESOURCE + propName);
1170                        } else {
1171                            valueResource = getJsp().getRequest().getParameter(PREFIX_VALUE + propName);
1172                        }
1173                    } else {
1174                        // resource form
1175                        propValue = getJsp().getRequest().getParameter(PREFIX_RESOURCE + propName);
1176                        if (!isEditable()) {
1177                            // values from disabled fields are not posted
1178                            valueStructure = getJsp().getRequest().getParameter(PREFIX_STRUCTURE + propName);
1179                        } else {
1180                            valueStructure = getJsp().getRequest().getParameter(PREFIX_VALUE + propName);
1181                        }
1182                        valueResource = getJsp().getRequest().getParameter(PREFIX_RESOURCE + propName);
1183                        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(valueStructure)
1184                            && valueStructure.equals(valueResource)) {
1185                            // the resource value was shown in the input field, set structure value to empty String
1186                            valueStructure = "";
1187                        }
1188                    }
1189                } else {
1190                    // initial call of edit form, get property values from database
1191                    CmsProperty currentProperty = activeProperties.get(propName);
1192                    if (currentProperty == null) {
1193                        currentProperty = new CmsProperty();
1194                    }
1195                    if (structurePanelName.equals(activeTab)) {
1196                        // show the structure properties
1197                        propValue = currentProperty.getStructureValue();
1198                    } else {
1199                        // show the resource properties
1200                        propValue = currentProperty.getResourceValue();
1201                    }
1202                    valueStructure = currentProperty.getStructureValue();
1203                    valueResource = currentProperty.getResourceValue();
1204                }
1205                // check values for null
1206                if (propValue == null) {
1207                    propValue = "";
1208                }
1209                if (valueStructure == null) {
1210                    valueStructure = "";
1211                }
1212                if (valueResource == null) {
1213                    valueResource = "";
1214                }
1215
1216                // remove unnecessary blanks from values
1217                propValue = propValue.trim();
1218                valueStructure = valueStructure.trim();
1219                valueResource = valueResource.trim();
1220                String[] property = new String[] {propName, propValue, valueStructure, valueResource};
1221                m_propertyValues.add(property);
1222            }
1223        }
1224        // return the filled list
1225        return m_propertyValues;
1226    }
1227
1228    /**
1229     * Performs the definition of a new property.<p>
1230     *
1231     * @return true, if the new property was created, otherwise false
1232     * @throws CmsException if creation is not successful
1233     */
1234    private boolean performDefineOperation() throws CmsException {
1235
1236        boolean useTempfileProject = Boolean.valueOf(getParamUsetempfileproject()).booleanValue();
1237        try {
1238            if (useTempfileProject) {
1239                switchToTempProject();
1240            }
1241            String newProperty = getParamNewproperty();
1242            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(newProperty)) {
1243                getCms().createPropertyDefinition(newProperty);
1244                return true;
1245            } else {
1246                throw new CmsException(Messages.get().container(Messages.ERR_INVALID_PROP_0));
1247            }
1248        } finally {
1249            if (useTempfileProject) {
1250                switchToCurrentProject();
1251            }
1252        }
1253    }
1254
1255    /**
1256     * Performs the editing of the resources properties.<p>
1257     *
1258     * @param request the HttpServletRequest
1259     * @return true, if the properties were successfully changed, otherwise false
1260     * @throws CmsException if editing is not successful
1261     */
1262    private boolean performDialogOperation(HttpServletRequest request) throws CmsException {
1263
1264        List<CmsPropertyDefinition> propertyDef = getCms().readAllPropertyDefinitions();
1265        boolean useTempfileProject = Boolean.valueOf(getParamUsetempfileproject()).booleanValue();
1266        try {
1267            if (useTempfileProject) {
1268                switchToTempProject();
1269            }
1270            Map<String, CmsProperty> activeProperties = getActiveProperties();
1271            String activeTab = getActiveTabName();
1272            List<CmsProperty> propertiesToWrite = new ArrayList<CmsProperty>();
1273
1274            // check all property definitions of the resource for new values
1275            Iterator<CmsPropertyDefinition> i = propertyDef.iterator();
1276            while (i.hasNext()) {
1277                CmsPropertyDefinition curPropDef = i.next();
1278                String propName = CmsEncoder.escapeXml(curPropDef.getName());
1279                String valueStructure = null;
1280                String valueResource = null;
1281
1282                if (key(Messages.GUI_PROPERTIES_INDIVIDUAL_0).equals(activeTab)) {
1283                    // get parameters from the structure tab
1284                    valueStructure = request.getParameter(PREFIX_VALUE + propName);
1285                    valueResource = request.getParameter(PREFIX_RESOURCE + propName);
1286                    if ((valueStructure != null)
1287                        && !"".equals(valueStructure.trim())
1288                        && valueStructure.equals(valueResource)) {
1289                        // the resource value was shown/entered in input field, set structure value to empty String
1290                        valueStructure = "";
1291                    }
1292                } else {
1293                    // get parameters from the resource tab
1294                    valueStructure = request.getParameter(PREFIX_STRUCTURE + propName);
1295                    valueResource = request.getParameter(PREFIX_VALUE + propName);
1296                }
1297
1298                // check values for blanks and null
1299                if (valueStructure != null) {
1300                    valueStructure = valueStructure.trim();
1301                }
1302
1303                if (valueResource != null) {
1304                    valueResource = valueResource.trim();
1305                }
1306
1307                // create new CmsProperty object to store
1308                CmsProperty newProperty = new CmsProperty();
1309                newProperty.setName(curPropDef.getName());
1310                newProperty.setStructureValue(valueStructure);
1311                newProperty.setResourceValue(valueResource);
1312
1313                // get the old property values
1314                CmsProperty oldProperty = activeProperties.get(curPropDef.getName());
1315                if (oldProperty == null) {
1316                    // property was not set, create new empty property object
1317                    oldProperty = new CmsProperty();
1318                    oldProperty.setName(curPropDef.getName());
1319                }
1320
1321                boolean writeStructureValue = false;
1322                boolean writeResourceValue = false;
1323                String oldValue = oldProperty.getStructureValue();
1324                String newValue = newProperty.getStructureValue();
1325
1326                // write the structure value if the existing structure value is not null and we want to delete the structure value
1327                writeStructureValue = ((oldValue != null) && newProperty.isDeleteStructureValue());
1328                // or if we want to write a value which is neither the delete value or an empty value
1329                writeStructureValue |= !newValue.equals(oldValue)
1330                    && !"".equalsIgnoreCase(newValue)
1331                    && !CmsProperty.DELETE_VALUE.equalsIgnoreCase(newValue);
1332                // set the structure value explicitly to null to leave it as is in the database
1333                if (!writeStructureValue) {
1334                    newProperty.setStructureValue(null);
1335                }
1336
1337                oldValue = oldProperty.getResourceValue();
1338                newValue = newProperty.getResourceValue();
1339
1340                // write the resource value if the existing resource value is not null and we want to delete the resource value
1341                writeResourceValue = ((oldValue != null) && newProperty.isDeleteResourceValue());
1342                // or if we want to write a value which is neither the delete value or an empty value
1343                writeResourceValue |= !newValue.equals(oldValue)
1344                    && !"".equalsIgnoreCase(newValue)
1345                    && !CmsProperty.DELETE_VALUE.equalsIgnoreCase(newValue);
1346                // set the resource value explicitly to null to leave it as is in the database
1347                if (!writeResourceValue) {
1348                    newProperty.setResourceValue(null);
1349                }
1350
1351                if (writeStructureValue || writeResourceValue) {
1352                    // add property to list only if property values have changed
1353                    propertiesToWrite.add(newProperty);
1354                }
1355            }
1356            if (propertiesToWrite.size() > 0) {
1357                // lock resource if autolock is enabled
1358                checkLock(getParamResource());
1359                //write the new property values
1360                getCms().writePropertyObjects(getParamResource(), propertiesToWrite);
1361            }
1362        } finally {
1363            if (useTempfileProject) {
1364                switchToCurrentProject();
1365            }
1366        }
1367        return true;
1368    }
1369}