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.CmsProperty;
032import org.opencms.file.CmsPropertyDefinition;
033import org.opencms.file.CmsResource;
034import org.opencms.file.CmsResourceFilter;
035import org.opencms.file.CmsUser;
036import org.opencms.i18n.CmsEncoder;
037import org.opencms.jsp.CmsJspActionElement;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.main.OpenCms;
041import org.opencms.security.CmsRole;
042import org.opencms.util.CmsStringUtil;
043import org.opencms.workplace.CmsWorkplaceSettings;
044import org.opencms.workplace.I_CmsDialogHandler;
045import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
046
047import java.util.Iterator;
048import java.util.List;
049
050import javax.servlet.http.HttpServletRequest;
051import javax.servlet.http.HttpServletResponse;
052import javax.servlet.jsp.JspException;
053import javax.servlet.jsp.PageContext;
054
055import org.apache.commons.logging.Log;
056
057/**
058 * Provides methods for the customized property dialog.<p>
059 *
060 * This is a special dialog that is used for the different resource types in the workplace.<p>
061 * For the xmlpage resource type, this class is extended in the editor subpackage.<p>
062 *
063 * The following files use this class:
064 * <ul>
065 * <li>/commons/property_custom.jsp
066 * </ul>
067 * <p>
068 *
069 * @since 6.0.0
070 */
071public class CmsPropertyCustom extends CmsPropertyAdvanced {
072
073    /** Value for the action: edit the properties. */
074    public static final int ACTION_EDIT = 500;
075
076    /** The log object for this class. */
077    private static final Log LOG = CmsLog.getLog(CmsPropertyCustom.class);
078
079    /** Helper object holding the information about the customized properties. */
080    private CmsExplorerTypeSettings m_explorerTypeSettings;
081
082    /** Flag to determine if navigation properties are shown. */
083    private boolean m_showNavigation;
084
085    /**
086     * Public constructor with JSP action element.<p>
087     *
088     * @param jsp an initialized JSP action element
089     */
090    public CmsPropertyCustom(CmsJspActionElement jsp) {
091
092        super(jsp);
093    }
094
095    /**
096     * Public constructor with JSP variables.<p>
097     *
098     * @param context the JSP page context
099     * @param req the JSP request
100     * @param res the JSP response
101     */
102    public CmsPropertyCustom(PageContext context, HttpServletRequest req, HttpServletResponse res) {
103
104        this(new CmsJspActionElement(context, req, res));
105    }
106
107    /**
108     * Performs the edit properties action, will be called by the JSP page.<p>
109     *
110     * @param request the HttpServletRequest
111     * @throws JspException if problems including sub-elements occur
112     */
113    @Override
114    public void actionEdit(HttpServletRequest request) throws JspException {
115
116        // save initialized instance of this class in request attribute for included sub-elements
117        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
118        try {
119            // save the changes only if resource is properly locked
120            if (isEditable()) {
121                performEditOperation(request);
122            }
123        } catch (Throwable e) {
124            // Cms error defining property, show error dialog
125            includeErrorpage(this, e);
126        }
127    }
128
129    /**
130     * Creates the HTML String for the edit properties form.<p>
131     *
132     * @return the HTML output String for the edit properties form
133     */
134    @Override
135    public String buildEditForm() {
136
137        StringBuffer result = new StringBuffer(2048);
138
139        // check if the properties are editable
140        boolean editable = isEditable();
141
142        // create the column heads
143        result.append("<table border=\"0\" style=\"width:100%\">\n");
144        result.append("<tr>\n");
145        result.append("\t<td class=\"textbold\" nowrap>");
146        result.append(key(Messages.GUI_PROPERTY_0));
147        result.append("</td>\n");
148        result.append("\t<td class=\"textbold\">");
149        result.append(key(Messages.GUI_PROPERTY_VALUE_0));
150        result.append("</td>\n");
151        // empty column for the checkboxes
152        result.append("\t<td class=\"textbold\" style=\"white-space: nowrap;\">");
153        result.append("&nbsp;");
154        result.append("</td>\n");
155        result.append("</tr>\n");
156        result.append("<tr><td><span style=\"height: 6px;\"></span></td></tr>\n");
157
158        // create the text property input rows from explorer type settings
159        result.append(buildTextInput(editable));
160
161        // show navigation properties if enabled in explorer type settings
162        if (showNavigation()) {
163            result.append(buildNavigationProperties(editable));
164        }
165        result.append("</table>");
166
167        return result.toString();
168    }
169
170    /**
171     * Builds the JavaScript to set the property form values delayed.<p>
172     *
173     * The values of the properties are not inserted directly in the &lt;input&gt; tag,
174     * because there is a display issue when the property values are very long.
175     * This method creates JavaScript to set the property input field values delayed.
176     * On the JSP, the code which is created from this method has to be executed delayed after
177     * the creation of the html form, e.g. in the &lt;body&gt; tag with the attribute
178     * onload="window.setTimeout('doSet()',50);".<p>
179     *
180     * @return the JavaScript to set the property form values delayed
181     */
182    @Override
183    public String buildSetFormValues() {
184
185        StringBuffer result = new StringBuffer(1024);
186        Iterator<String> i = getExplorerTypeSettings().getProperties().iterator();
187        // iterate over the customized properties
188        while (i.hasNext()) {
189            String curProperty = i.next();
190            if (getActiveProperties().containsKey(curProperty)) {
191                CmsProperty property = getActiveProperties().get(curProperty);
192                String propValue = property.getValue();
193                if (propValue != null) {
194                    propValue = propValue.trim();
195                    propValue = CmsStringUtil.escapeJavaScript(propValue);
196                    // create the JS output for a single property
197                    result.append("\tdocument.getElementById(\"");
198                    result.append(PREFIX_VALUE);
199                    result.append(curProperty);
200                    result.append("\").value = \"");
201                    result.append(propValue);
202                    result.append("\";\n");
203                }
204            }
205        }
206        // check if the navigation text property value has to be added
207        if (showNavigation() && getActiveProperties().containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT)) {
208            CmsProperty property = getActiveProperties().get(CmsPropertyDefinition.PROPERTY_NAVTEXT);
209            String propValue = property.getValue();
210            if (propValue != null) {
211                propValue = propValue.trim();
212                propValue = CmsStringUtil.escapeJavaScript(propValue);
213                // create the JS output for a single property
214                result.append("\tdocument.getElementById(\"");
215                result.append(PREFIX_VALUE);
216                result.append(CmsPropertyDefinition.PROPERTY_NAVTEXT);
217                result.append("\").value = \"");
218                result.append(propValue);
219                result.append("\";\n");
220            }
221        }
222        return result.toString();
223    }
224
225    /**
226     * Builds a button row with an "ok", a "cancel" and an "advanced" button.<p>
227     *
228     * @param okAttributes additional attributes for the "ok" button
229     * @param cancelAttributes additional attributes for the "cancel" button
230     * @param advancedAttributes additional attributes for the "advanced" button
231     * @return the button row
232     */
233    @Override
234    public String dialogButtonsOkCancelAdvanced(
235        String okAttributes,
236        String cancelAttributes,
237        String advancedAttributes) {
238
239        if (isEditable()) {
240            int okButton = BUTTON_OK;
241            if ((getParamDialogmode() != null) && getParamDialogmode().startsWith(MODE_WIZARD)) {
242                // in wizard mode, display finish button instead of ok button
243                okButton = BUTTON_FINISH;
244            }
245            // hide "advanced" button
246            if (isHideButtonAdvanced()) {
247                return dialogButtons(
248                    new int[] {okButton, BUTTON_CANCEL},
249                    new String[] {okAttributes, cancelAttributes});
250            }
251            // show "advanced" button
252            return dialogButtons(
253                new int[] {okButton, BUTTON_CANCEL, BUTTON_ADVANCED},
254                new String[] {okAttributes, cancelAttributes, advancedAttributes});
255        } else {
256            // hide "advanced" button
257            if (isHideButtonAdvanced()) {
258                return dialogButtons(new int[] {BUTTON_CLOSE}, new String[] {cancelAttributes});
259            }
260            // show "advanced" button
261            return dialogButtons(
262                new int[] {BUTTON_CLOSE, BUTTON_ADVANCED},
263                new String[] {cancelAttributes, advancedAttributes});
264        }
265    }
266
267    /**
268     * Returns the explorer type settings for the current resource type.<p>
269     *
270     * @return the explorer type settings for the current resource type
271     */
272    public CmsExplorerTypeSettings getExplorerTypeSettings() {
273
274        return m_explorerTypeSettings;
275    }
276
277    /**
278     * Sets the explorer type settings for the current resource type.<p>
279     *
280     * @param typeSettings the explorer type settings for the current resource type
281     */
282    public void setExplorerTypeSettings(CmsExplorerTypeSettings typeSettings) {
283
284        m_explorerTypeSettings = typeSettings;
285    }
286
287    /**
288     * Sets if navigation properties are shown.<p>
289     *
290     * @param showNav true, if navigation properties are shown, otherwise false
291     */
292    public void setShowNavigation(boolean showNav) {
293
294        m_showNavigation = showNav;
295    }
296
297    /**
298     * Returns if navigation properties are shown.<p>
299     *
300     * @return true, if navigation properties are shown, otherwise false
301     */
302    public boolean showNavigation() {
303
304        return m_showNavigation;
305    }
306
307    /**
308     * Builds the HTML code for the special properties of an xmlpage resource.<p>
309     *
310     * @param editable indicates if the properties are editable
311     * @return the HTML code for the special properties of a file resource
312     */
313    protected StringBuffer buildNavigationProperties(boolean editable) {
314
315        StringBuffer result = new StringBuffer(1024);
316
317        // create "disabled" attribute if properties are not editable
318        String disabled = "";
319        if (!editable) {
320            disabled = " disabled=\"disabled\"";
321        }
322
323        // create "add to navigation" checkbox
324        result.append(buildTableRowStart(key(Messages.GUI_PROPERTY_ADD_TO_NAV_0)));
325        result.append(
326            "<input type=\"checkbox\" name=\"enablenav\" id=\"enablenav\" value=\"true\" onClick=\"toggleNav();\"");
327        if (getActiveProperties().containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT)
328            || getActiveProperties().containsKey(CmsPropertyDefinition.PROPERTY_NAVPOS)) {
329            result.append(" checked=\"checked\"");
330        }
331        result.append(disabled);
332        result.append(">");
333        result.append("</td>\n");
334        result.append("\t<td class=\"textcenter\">");
335        result.append("&nbsp;");
336        result.append(buildTableRowEnd());
337
338        // create NavText input row
339        result.append(
340            buildPropertyEntry(CmsPropertyDefinition.PROPERTY_NAVTEXT, key(Messages.GUI_LABEL_NAVTEXT_0), editable));
341
342        // create NavPos select box row
343        result.append(buildTableRowStart(key(Messages.GUI_CHNAV_INSERT_AFTER_0)));
344        result.append(CmsChnav.buildNavPosSelector(
345            getCms(),
346            getParamResource(),
347            disabled + " id=\"navpos\" class=\"maxwidth noborder\"",
348            getMessages()));
349        // get the old NavPos value and store it in hidden field
350        String navPos = null;
351        try {
352            navPos = getCms().readPropertyObject(
353                getParamResource(),
354                CmsPropertyDefinition.PROPERTY_NAVPOS,
355                false).getValue();
356        } catch (CmsException e) {
357            // should usually never happen
358            if (LOG.isInfoEnabled()) {
359                LOG.info(e.getLocalizedMessage());
360            }
361        }
362        if (navPos == null) {
363            navPos = "";
364        }
365        result.append("<input type=\"hidden\" name=\"");
366        result.append(PREFIX_HIDDEN);
367        result.append(CmsPropertyDefinition.PROPERTY_NAVPOS);
368        result.append("\" value=\"");
369        result.append(navPos);
370        result.append("\">");
371        result.append("</td>\n");
372        result.append("\t<td class=\"textcenter\">");
373        result.append("&nbsp;");
374        result.append(buildTableRowEnd());
375
376        return result;
377    }
378
379    /**
380     * Builds the html for a single text input property row.<p>
381     *
382     * The html does not include the value of the created property,
383     * the values are set delayed (see buildSetFormValues() for details).<p>
384     *
385     * @param propertyName the name of the property
386     * @param propertyTitle the nice name of the property
387     * @param editable indicates if the properties are editable
388     * @return the html for a single text input property row
389     */
390    protected StringBuffer buildPropertyEntry(String propertyName, String propertyTitle, boolean editable) {
391
392        StringBuffer result = new StringBuffer(256);
393        // create "disabled" attribute if properties are not editable
394        String disabled = "";
395        if (!editable) {
396            disabled = " disabled=\"disabled\"";
397        }
398        result.append(buildTableRowStart(propertyTitle));
399        // the property is used, so create text field with checkbox and hidden field
400        CmsProperty currentProperty = getActiveProperties().get(propertyName);
401        String propValue = "";
402        if (currentProperty != null) {
403            propValue = currentProperty.getValue();
404        }
405        if (propValue != null) {
406            propValue = propValue.trim();
407        }
408        propValue = CmsEncoder.escapeXml(propValue);
409
410        // create text input field
411        result.append("<input type=\"text\" class=\"maxwidth\"");
412        result.append(" name=\"");
413        result.append(PREFIX_VALUE);
414        result.append(propertyName);
415        result.append("\" id=\"");
416        result.append(PREFIX_VALUE);
417        result.append(propertyName);
418        result.append("\"");
419        if (editable) {
420            result.append(" onKeyup=\"checkValue('");
421            result.append(propertyName);
422            result.append("');\"");
423        }
424        result.append(disabled);
425        result.append(">");
426
427        // create hidden field for value
428        result.append("<input type=\"hidden\" name=\"");
429        result.append(PREFIX_HIDDEN);
430        result.append(propertyName);
431        result.append("\" id=\"");
432        result.append(PREFIX_HIDDEN);
433        result.append(propertyName);
434        result.append("\" value=\"");
435        result.append(propValue);
436        result.append("\">");
437        result.append("</td>\n");
438        result.append("\t<td class=\"propertydialog-checkboxcell\">");
439
440        // create activate/deactivate checkbox
441        result.append("<input type=\"checkbox\" name=\"");
442        result.append(PREFIX_USEPROPERTY);
443        result.append(propertyName);
444        result.append("\" id=\"");
445        result.append(PREFIX_USEPROPERTY);
446        result.append(propertyName);
447        result.append("\" value=\"true\"");
448        if (CmsStringUtil.isNotEmpty(propValue)) {
449            result.append(" checked=\"checked\"");
450        }
451        if (editable) {
452            result.append(" onClick=\"toggleDelete('");
453            result.append(propertyName);
454            result.append("');\"");
455        }
456        result.append(disabled + ">");
457
458        result.append(buildTableRowEnd());
459        return result;
460    }
461
462    /**
463     * Builds the HTML for the end of a table row for a single property.<p>
464     *
465     * @return the HTML code for a table row end
466     */
467    protected String buildTableRowEnd() {
468
469        return "</td>\n</tr>\n";
470    }
471
472    /**
473     * Builds the HTML for the start of a table row for a single property.<p>
474     *
475     * @param propertyName the name of the current property
476     * @return the HTML code for the start of a table row
477     */
478    protected StringBuffer buildTableRowStart(String propertyName) {
479
480        StringBuffer result = new StringBuffer(96);
481        result.append("<tr>\n");
482        result.append("\t<td style=\"white-space: nowrap;\" unselectable=\"on\">");
483        result.append(propertyName);
484        result.append("</td>\n");
485        result.append("\t<td class=\"maxwidth\">");
486        return result;
487    }
488
489    /**
490     * Builds the HTML for the common text input property values stored in the String array "PROPERTIES".<p>
491     *
492     * @param editable indicates if the properties are editable
493     * @return the HTML code for the common text input fields
494     */
495    protected StringBuffer buildTextInput(boolean editable) {
496
497        StringBuffer result = new StringBuffer(256);
498        Iterator<String> i = getExplorerTypeSettings().getProperties().iterator();
499        // iterate over the properties
500        while (i.hasNext()) {
501            String curProperty = i.next();
502            result.append(buildPropertyEntry(curProperty, curProperty, editable));
503        }
504        return result;
505    }
506
507    /**
508     * Initializes the explorer type settings for the current resource type.<p>
509     */
510    protected void initExplorerTypeSettings() {
511
512        try {
513            CmsResource res = getCms().readResource(getParamResource(), CmsResourceFilter.ALL);
514            if (res.isFolder()) {
515                if (!getParamResource().endsWith("/")) {
516                    // append folder separator to resource name
517                    setParamResource(getParamResource() + "/");
518                }
519            }
520            String resTypeName = OpenCms.getResourceManager().getResourceType(res.getTypeId()).getTypeName();
521            // get settings for resource type
522            setExplorerTypeSettings(getSettingsForType(resTypeName));
523            setShowNavigation(getExplorerTypeSettings().isShowNavigation());
524        } catch (Throwable e) {
525            // error reading file, show error dialog
526            try {
527                includeErrorpage(this, e);
528            } catch (JspException exc) {
529                LOG.error(
530                    Messages.get().getBundle().key(Messages.LOG_ERROR_INCLUDE_FAILED_1, FILE_DIALOG_SCREEN_ERRORPAGE));
531            }
532        }
533    }
534
535    /**
536     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
537     */
538    @Override
539    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
540
541        // fill the parameter values in the get/set methods
542        fillParamValues(request);
543        // get the explorer type settings for the current resource
544        initExplorerTypeSettings();
545        // set the dialog type
546        setParamDialogtype(DIALOG_TYPE);
547        boolean isPopup = Boolean.valueOf(getParamIsPopup()).booleanValue();
548        // set the action for the JSP switch
549        if (DIALOG_SHOW_DEFAULT.equals(getParamAction())) {
550            // save changed properties and redirect to the default OpenCms dialog
551            setAction(ACTION_DEFAULT);
552            try {
553                actionEdit(request);
554                sendForward(CmsPropertyAdvanced.URI_PROPERTY_DIALOG, paramsAsParameterMap());
555                return;
556            } catch (Exception e) {
557                // should usually never happen
558                if (LOG.isInfoEnabled()) {
559                    LOG.info(e.getLocalizedMessage());
560                }
561            }
562        } else if (DIALOG_SAVE_EDIT.equals(getParamAction())) {
563            // save the edited properties
564            if (isPopup) {
565                setAction(ACTION_CLOSEPOPUP_SAVE);
566            } else {
567                setAction(ACTION_SAVE_EDIT);
568            }
569        } else if (DIALOG_CANCEL.equals(getParamAction())) {
570            // save the edited properties
571            if (isPopup) {
572                setAction(ACTION_CLOSEPOPUP);
573            } else {
574                setAction(ACTION_CANCEL);
575            }
576        } else {
577            setAction(ACTION_EDIT);
578            String resName = CmsResource.getName(getParamResource());
579            if (CmsResource.isTemporaryFileName(resName)) {
580                resName = resName.substring(1);
581            }
582            setParamTitle(key(Messages.GUI_PROPERTIES_1, new Object[] {resName}));
583        }
584    }
585
586    /**
587     * Checks the optional parameters of the handler configuration. <p>
588     *
589     * Decides if the "advanced" button should be shown or not.
590     * The "advanced" button is shown if no parameters are given (default)
591     * or the "hideadvanced" attribute is set to false. The "advanced" button
592     * is hidden only, if "hideadvanced" is true and the user is not a member
593     * of the specified user groups.
594     *
595     * @return  false if the "advanced" button is shown (default) <br>
596     *          true if the "advanced" button is hidden
597     */
598    protected boolean isHideButtonAdvanced() {
599
600        I_CmsDialogHandler handler = OpenCms.getWorkplaceManager().getDialogHandler(getDialogHandler());
601        CmsParameterConfiguration handlerParams = handler.getConfiguration();
602        if ((handlerParams != null) && handlerParams.containsKey(PARAM_HIDEADVANCED)) {
603            // checks if "hideadvanced" is set to true
604            boolean isHideAdvancedSet = false;
605            List<String> hAdvanced = handlerParams.getList(PARAM_HIDEADVANCED);
606            if (!hAdvanced.isEmpty()) {
607                isHideAdvancedSet = Boolean.valueOf(hAdvanced.get(0)).booleanValue();
608            }
609            if (isHideAdvancedSet) {
610                // if user has the role root admin
611                if (OpenCms.getRoleManager().hasRole(getCms(), CmsRole.ROOT_ADMIN)) {
612                    return false;
613                }
614                if (handlerParams.containsKey(PARAM_SHOWGROUP)) {
615                    // check if user is one of the configured groups
616                    CmsUser currentUser = getCms().getRequestContext().getCurrentUser();
617                    List<String> confGroups = handlerParams.getList(PARAM_SHOWGROUP);
618                    for (String groupName : confGroups) {
619                        try {
620                            if (getCms().userInGroup(currentUser.getName(), groupName)) {
621                                return false;
622                            }
623                        } catch (CmsException e) {
624                            LOG.error(e.getLocalizedMessage(), e);
625                        }
626                    }
627                }
628                return true;
629            }
630        }
631        return false;
632    }
633
634    /**
635     * Performs the editing of the resources properties.<p>
636     *
637     * @param request the HttpServletRequest
638     * @return true, if the properties were successfully changed, otherwise false
639     * @throws CmsException if editing is not successful
640     */
641    protected boolean performEditOperation(HttpServletRequest request) throws CmsException {
642
643        boolean useTempfileProject = Boolean.valueOf(getParamUsetempfileproject()).booleanValue();
644        try {
645            if (useTempfileProject) {
646                switchToTempProject();
647            }
648            // write the common properties defined in the explorer type settings
649            Iterator<String> i = getExplorerTypeSettings().getProperties().iterator();
650            // iterate over the properties
651            while (i.hasNext()) {
652                String curProperty = i.next();
653                String paramValue = request.getParameter(PREFIX_VALUE + curProperty);
654                String oldValue = request.getParameter(PREFIX_HIDDEN + curProperty);
655                writeProperty(curProperty, paramValue, oldValue);
656            }
657
658            // write the navigation properties if enabled
659            if (showNavigation()) {
660                // get the navigation enabled parameter
661                String paramValue = request.getParameter("enablenav");
662                String oldValue = null;
663                if (Boolean.valueOf(paramValue).booleanValue()) {
664                    // navigation enabled, update params
665                    paramValue = request.getParameter("navpos");
666                    if (!"-1".equals(paramValue) && !String.valueOf(Float.MAX_VALUE).equals(paramValue)) {
667                        // update the property only when it is different from "-1" (meaning no change)
668                        oldValue = request.getParameter(PREFIX_HIDDEN + CmsPropertyDefinition.PROPERTY_NAVPOS);
669                        writeProperty(CmsPropertyDefinition.PROPERTY_NAVPOS, paramValue, oldValue);
670                    }
671                    paramValue = request.getParameter(PREFIX_VALUE + CmsPropertyDefinition.PROPERTY_NAVTEXT);
672                    oldValue = request.getParameter(PREFIX_HIDDEN + CmsPropertyDefinition.PROPERTY_NAVTEXT);
673                    writeProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT, paramValue, oldValue);
674                } else {
675                    // navigation disabled, delete property values
676                    writeProperty(CmsPropertyDefinition.PROPERTY_NAVPOS, null, null);
677                    writeProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT, null, null);
678                }
679            }
680        } finally {
681            if (useTempfileProject) {
682                switchToCurrentProject();
683            }
684        }
685        return true;
686    }
687
688    /**
689     * Writes a property value for a resource, if the value was changed.<p>
690     *
691     * If a property definition for the resource does not exist,
692     * it is automatically created by this method.<p>
693     *
694     * @param propName the name of the property
695     * @param propValue the new value of the property
696     * @param oldValue the old value of the property
697     * @throws CmsException if something goes wrong
698     */
699    protected void writeProperty(String propName, String propValue, String oldValue) throws CmsException {
700
701        // get the current property object
702        CmsProperty currentProperty = getActiveProperties().get(propName);
703        if (currentProperty == null) {
704            // new property, create new property object
705            currentProperty = new CmsProperty();
706            currentProperty.setName(propName);
707        } else {
708            // clone the property, because the original property is frozen
709            currentProperty = currentProperty.cloneAsProperty();
710        }
711
712        // check if there is a parameter value for the current property
713        boolean emptyParam = true;
714        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(propValue)) {
715            emptyParam = false;
716        }
717
718        if (emptyParam) {
719            // parameter is empty, check if the property has to be deleted
720            if (getActiveProperties().containsKey(propName)) {
721                // lock resource if autolock is enabled
722                checkLock(getParamResource());
723                // determine the value to delete
724                if (currentProperty.getStructureValue() != null) {
725                    currentProperty.setStructureValue(CmsProperty.DELETE_VALUE);
726                    currentProperty.setResourceValue(null);
727                } else {
728                    currentProperty.setResourceValue(CmsProperty.DELETE_VALUE);
729                    currentProperty.setStructureValue(null);
730                }
731                // write the updated property object
732                getCms().writePropertyObject(getParamResource(), currentProperty);
733            }
734        } else {
735            // parameter is not empty, check if the value has changed
736            if (!propValue.equals(oldValue)) {
737                // lock resource if autolock is enabled
738                checkLock(getParamResource());
739                if ((currentProperty.getStructureValue() == null) && (currentProperty.getResourceValue() == null)) {
740                    // new property, determine setting from OpenCms workplace configuration
741                    if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
742                        currentProperty.setStructureValue(propValue);
743                        currentProperty.setResourceValue(null);
744                    } else {
745                        currentProperty.setResourceValue(propValue);
746                        currentProperty.setStructureValue(null);
747                    }
748                } else if (currentProperty.getStructureValue() != null) {
749                    // structure value has to be updated
750                    currentProperty.setStructureValue(propValue);
751                    currentProperty.setResourceValue(null);
752                } else {
753                    // resource value has to be updated
754                    currentProperty.setResourceValue(propValue);
755                    currentProperty.setStructureValue(null);
756                }
757                // set auto-creation of the property to true
758                currentProperty.setAutoCreatePropertyDefinition(true);
759                // write the updated property object
760                getCms().writePropertyObject(getParamResource(), currentProperty);
761            }
762        }
763    }
764}