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.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsPropertyDefinition;
033import org.opencms.file.CmsResource;
034import org.opencms.file.CmsResourceFilter;
035import org.opencms.i18n.CmsEncoder;
036import org.opencms.i18n.CmsMessages;
037import org.opencms.jsp.CmsJspActionElement;
038import org.opencms.jsp.CmsJspNavBuilder;
039import org.opencms.jsp.CmsJspNavBuilder.Visibility;
040import org.opencms.jsp.CmsJspNavElement;
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.CmsDialog;
047import org.opencms.workplace.CmsWorkplace;
048import org.opencms.workplace.CmsWorkplaceSettings;
049
050import java.util.ArrayList;
051import java.util.List;
052
053import javax.servlet.http.HttpServletRequest;
054import javax.servlet.http.HttpServletResponse;
055import javax.servlet.jsp.JspException;
056import javax.servlet.jsp.PageContext;
057
058import org.apache.commons.logging.Log;
059
060/**
061 * Provides methods for the change navigation dialog.<p>
062 *
063 * The following files use this class:
064 * <ul>
065 * <li>/commons/chnav.jsp
066 * </ul>
067 * <p>
068 *
069 * @since 6.0.0
070 */
071public class CmsChnav extends CmsDialog {
072
073    /** Value for the action: change the navigation. */
074    public static final int ACTION_CHNAV = 100;
075
076    /** The dialog type. */
077    public static final String DIALOG_TYPE = "chnav";
078
079    /** Request parameter name for the navigation position. */
080    public static final String PARAM_NAVPOS = "navpos";
081
082    /** Request parameter name for the navigation text. */
083    public static final String PARAM_NAVTEXT = "navtext";
084
085    /** The log object for this class. */
086    private static final Log LOG = CmsLog.getLog(CmsChnav.class);
087
088    /** The NavPos parameter. */
089    private String m_paramNavpos;
090
091    /** The NavText parameter. */
092    private String m_paramNavtext;
093
094    /**
095     * Public constructor.<p>
096     *
097     * @param jsp an initialized JSP action element
098     */
099    public CmsChnav(CmsJspActionElement jsp) {
100
101        super(jsp);
102    }
103
104    /**
105     * Public constructor with JSP variables.<p>
106     *
107     * @param context the JSP page context
108     * @param req the JSP request
109     * @param res the JSP response
110     */
111    public CmsChnav(PageContext context, HttpServletRequest req, HttpServletResponse res) {
112
113        this(new CmsJspActionElement(context, req, res));
114    }
115
116    /**
117     * Builds the HTML for the select box of the navigation position.<p>
118     *
119     * @param cms the CmsObject
120     * @param filename the current file
121     * @param attributes optional attributes for the &lt;select&gt; tag, do not add the "name" atribute!
122     * @param messages the localized workplace messages
123     *
124     * @return the HTML for a navigation position select box
125     */
126    public static String buildNavPosSelector(CmsObject cms, String filename, String attributes, CmsMessages messages) {
127
128        // get current file navigation element
129        CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(cms);
130        CmsJspNavElement curNav = navBuilder.getNavigationForResource(filename);
131
132        // get the parent folder of the current file
133        filename = CmsResource.getParentFolder(filename);
134
135        // get navigation of the current folder
136        List<CmsJspNavElement> navList = navBuilder.getNavigationForFolder(
137            filename,
138            Visibility.includeHidden,
139            CmsResourceFilter.DEFAULT);
140        float maxValue = 0;
141        float nextPos = 0;
142
143        // calculate value for the first navigation position
144        float firstValue = 1;
145        if (navList.size() > 0) {
146            try {
147                CmsJspNavElement ne = navList.get(0);
148                maxValue = ne.getNavPosition();
149            } catch (Exception e) {
150                // should usually never happen
151                LOG.error(e.getLocalizedMessage());
152            }
153        }
154
155        if (maxValue != 0) {
156            firstValue = maxValue / 2;
157        }
158
159        List<String> options = new ArrayList<String>(navList.size() + 1);
160        List<String> values = new ArrayList<String>(navList.size() + 1);
161
162        // add the first entry: before first element
163        options.add(messages.key(Messages.GUI_CHNAV_POS_FIRST_0));
164        values.add(firstValue + "");
165
166        // show all present navigation elements in box
167        for (int i = 0; i < navList.size(); i++) {
168            CmsJspNavElement ne = navList.get(i);
169            String navText = ne.getNavText();
170            float navPos = ne.getNavPosition();
171            // get position of next nav element
172            nextPos = navPos + 2;
173            if ((i + 1) < navList.size()) {
174                nextPos = navList.get(i + 1).getNavPosition();
175            }
176            // calculate new position of current nav element
177            float newPos;
178            if ((nextPos - navPos) > 1) {
179                newPos = navPos + 1;
180            } else {
181                newPos = (navPos + nextPos) / 2;
182            }
183
184            // check new maxValue of positions and increase it
185            if (navPos > maxValue) {
186                maxValue = navPos;
187            }
188
189            // if the element is the current file, mark it in selectbox
190            if ((curNav != null) && curNav.getNavText().equals(navText) && (curNav.getNavPosition() == navPos)) {
191                options.add(
192                    CmsEncoder.escapeHtml(
193                        messages.key(Messages.GUI_CHNAV_POS_CURRENT_1, new Object[] {ne.getFileName()})));
194                values.add("-1");
195            } else {
196                options.add(CmsEncoder.escapeHtml(navText + " [" + ne.getFileName() + "]"));
197                values.add(newPos + "");
198            }
199        }
200
201        // add the entry: at the last position
202        options.add(messages.key(Messages.GUI_CHNAV_POS_LAST_0));
203        values.add((maxValue + 1) + "");
204
205        // add the entry: no change
206        options.add(messages.key(Messages.GUI_CHNAV_NO_CHANGE_0));
207        if ((curNav != null) && (curNav.getNavPosition() == Float.MAX_VALUE)) {
208            // current resource has no valid position, use "last position"
209            values.add((maxValue + 1) + "");
210        } else {
211            // current resource has valid position, use "-1" for no change
212            values.add("-1");
213        }
214
215        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(attributes)) {
216            attributes = " " + attributes;
217        } else {
218            attributes = "";
219        }
220        return CmsWorkplace.buildSelect(
221            "name=\"" + PARAM_NAVPOS + "\"" + attributes,
222            options,
223            values,
224            values.size() - 1,
225            true);
226    }
227
228    /**
229     * Performs the navigation change.<p>
230     *
231     * @throws JspException if including a JSP subelement is not successful
232     */
233    public void actionChangeNav() throws JspException {
234
235        // save initialized instance of this class in request attribute for included sub-elements
236        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
237
238        // get request parameters
239        String filename = getParamResource();
240        // do not use #getParamNavText since it is decoded, see CmsWorkplace#fillParamValues(HttpServletRequest)
241        String newText = getJsp().getRequest().getParameter(PARAM_NAVTEXT);
242        String selectedPosString = getParamNavpos();
243
244        try {
245            // lock resource if autolock is enabled
246            checkLock(getParamResource());
247            // save the new NavText if not null
248            if (newText != null) {
249                CmsProperty newNavText = new CmsProperty();
250                newNavText.setName(CmsPropertyDefinition.PROPERTY_NAVTEXT);
251                CmsProperty oldNavText = getCms().readPropertyObject(
252                    filename,
253                    CmsPropertyDefinition.PROPERTY_NAVTEXT,
254                    false);
255                if (oldNavText.isNullProperty()) {
256                    // property value was not already set
257                    if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
258                        newNavText.setStructureValue(newText);
259                    } else {
260                        newNavText.setResourceValue(newText);
261                    }
262                } else {
263                    if (oldNavText.getStructureValue() != null) {
264                        newNavText.setStructureValue(newText);
265                        newNavText.setResourceValue(oldNavText.getResourceValue());
266                    } else {
267                        newNavText.setResourceValue(newText);
268                    }
269                }
270
271                String oldStructureValue = oldNavText.getStructureValue();
272                String newStructureValue = newNavText.getStructureValue();
273                if (CmsStringUtil.isEmpty(oldStructureValue)) {
274                    oldStructureValue = CmsProperty.DELETE_VALUE;
275                }
276                if (CmsStringUtil.isEmpty(newStructureValue)) {
277                    newStructureValue = CmsProperty.DELETE_VALUE;
278                }
279
280                String oldResourceValue = oldNavText.getResourceValue();
281                String newResourceValue = newNavText.getResourceValue();
282                if (CmsStringUtil.isEmpty(oldResourceValue)) {
283                    oldResourceValue = CmsProperty.DELETE_VALUE;
284                }
285                if (CmsStringUtil.isEmpty(newResourceValue)) {
286                    newResourceValue = CmsProperty.DELETE_VALUE;
287                }
288
289                // change nav text only if it has been changed
290                if (!oldResourceValue.equals(newResourceValue) || !oldStructureValue.equals(newStructureValue)) {
291                    getCms().writePropertyObject(getParamResource(), newNavText);
292                }
293            }
294
295            // determine the selected position
296            float selectedPos = -1;
297            try {
298                selectedPos = Float.parseFloat(selectedPosString);
299            } catch (Exception e) {
300                // can usually be ignored
301                if (LOG.isInfoEnabled()) {
302                    LOG.info(e.getLocalizedMessage());
303                }
304            }
305
306            // only update the position if a change is requested
307            if (selectedPos != -1) {
308                CmsProperty newNavPos = new CmsProperty();
309                newNavPos.setName(CmsPropertyDefinition.PROPERTY_NAVPOS);
310                CmsProperty oldNavPos = getCms().readPropertyObject(
311                    filename,
312                    CmsPropertyDefinition.PROPERTY_NAVPOS,
313                    false);
314                if (oldNavPos.isNullProperty()) {
315                    // property value was not already set
316                    if (OpenCms.getWorkplaceManager().isDefaultPropertiesOnStructure()) {
317                        newNavPos.setStructureValue(selectedPosString);
318                    } else {
319                        newNavPos.setResourceValue(selectedPosString);
320                    }
321                } else {
322                    if (oldNavPos.getStructureValue() != null) {
323                        newNavPos.setStructureValue(selectedPosString);
324                        newNavPos.setResourceValue(oldNavPos.getResourceValue());
325                    } else {
326                        newNavPos.setResourceValue(selectedPosString);
327                    }
328                }
329                getCms().writePropertyObject(filename, newNavPos);
330            }
331        } catch (Throwable e) {
332            // error during chnav, show error dialog
333            includeErrorpage(this, e);
334        }
335        // chnav operation was successful, return to workplace
336        actionCloseDialog();
337    }
338
339    /**
340     * Builds the HTML for the select box of the navigation position.<p>
341     *
342     * @return the HTML for a navigation position select box
343     */
344    public String buildNavPosSelector() {
345
346        return buildNavPosSelector(getCms(), getParamResource(), null, getMessages());
347    }
348
349    /**
350     * Returns the escaped NavText property value of the current resource.<p>
351     *
352     * @return the NavText property value of the current resource
353     */
354    public String getCurrentNavText() {
355
356        try {
357            String navText = getCms().readPropertyObject(
358                getParamResource(),
359                CmsPropertyDefinition.PROPERTY_NAVTEXT,
360                false).getValue();
361            if (navText == null) {
362                navText = "";
363            }
364            return CmsEncoder.escapeXml(navText);
365        } catch (CmsException e) {
366            // can usually be ignored
367            if (LOG.isInfoEnabled()) {
368                LOG.info(e.getLocalizedMessage());
369            }
370            return "";
371        }
372
373    }
374
375    /**
376     * Returns the value of the navigation position parameter,
377     * or null if this parameter was not provided.<p>
378     *
379     * The navigation position parameter defines the new value for
380     * the NavPos property.<p>
381     *
382     * @return the value of the target parameter
383     */
384    public String getParamNavpos() {
385
386        return m_paramNavpos;
387    }
388
389    /**
390     * Returns the value of the navigation text parameter,
391     * or null if this parameter was not provided.<p>
392     *
393     * The navigation text parameter defines the new value for
394     * the NavText property.<p>
395     *
396     * @return the value of the target parameter
397     */
398    public String getParamNavtext() {
399
400        return m_paramNavtext;
401    }
402
403    /**
404     * Sets the value of the navigation position parameter.<p>
405     *
406     * @param value the value to set
407     */
408    public void setParamNavpos(String value) {
409
410        m_paramNavpos = value;
411    }
412
413    /**
414     * Sets the value of the navigation text parameter.<p>
415     *
416     * @param value the value to set
417     */
418    public void setParamNavtext(String value) {
419
420        m_paramNavtext = value;
421    }
422
423    /**
424     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
425     */
426    @Override
427    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
428
429        // fill the parameter values in the get/set methods
430        fillParamValues(request);
431
432        // check the required permissions to change navigation of the resource
433        if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) {
434            // no write permissions for the resource, set cancel action to close dialog
435            setParamAction(DIALOG_CANCEL);
436        }
437
438        // set the dialog type
439        setParamDialogtype(DIALOG_TYPE);
440        // set the action for the JSP switch
441        if (DIALOG_TYPE.equals(getParamAction())) {
442            setAction(ACTION_CHNAV);
443        } else if (DIALOG_LOCKS_CONFIRMED.equals(getParamAction())) {
444            setAction(ACTION_LOCKS_CONFIRMED);
445        } else if (DIALOG_CANCEL.equals(getParamAction())) {
446            setAction(ACTION_CANCEL);
447        } else {
448            setAction(ACTION_DEFAULT);
449            // build title for chnav dialog
450            setParamTitle(key(Messages.GUI_CHNAV_1, new Object[] {CmsResource.getName(getParamResource())}));
451        }
452    }
453
454}