001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.jsp.CmsJspActionElement;
035import org.opencms.main.CmsException;
036import org.opencms.security.CmsPermissionSet;
037import org.opencms.util.CmsStringUtil;
038import org.opencms.widgets.CmsCalendarWidget;
039import org.opencms.workplace.CmsMultiDialog;
040import org.opencms.workplace.CmsWorkplaceSettings;
041
042import java.text.ParseException;
043import java.util.Iterator;
044
045import jakarta.servlet.http.HttpServletRequest;
046import jakarta.servlet.http.HttpServletResponse;
047import jakarta.servlet.jsp.JspException;
048import jakarta.servlet.jsp.PageContext;
049
050/**
051 * Provides methods for the touch resource(s) dialog.<p>
052 *
053 * The following files use this class:
054 * <ul>
055 * <li>/commons/touch.jsp
056 * </ul>
057 * <p>
058 *
059 * @since 6.0.0
060 */
061public class CmsTouch extends CmsMultiDialog {
062
063    /** Value for the action: touch. */
064    public static final int ACTION_TOUCH = 100;
065
066    /** Default value for date last modified, the release and expire date. */
067    public static final String DEFAULT_DATE_STRING = "-";
068
069    /** The dialog type. */
070    public static final String DIALOG_TYPE = "touch";
071
072    /** Request parameter name for the content flag. */
073    public static final String PARAM_CONTENT = "content";
074
075    /** Request parameter name for timestamp. */
076    public static final String PARAM_NEWTIMESTAMP = "newtimestamp";
077
078    /** Request parameter name for the recursive flag. */
079    public static final String PARAM_RECURSIVE = "recursive";
080
081    /** Content parameter. */
082    private String m_paramContent;
083
084    /** Timestamp parameter. */
085    private String m_paramNewtimestamp;
086
087    /** Recursive parameter. */
088    private String m_paramRecursive;
089
090    /**
091     * Public constructor.<p>
092     *
093     * @param jsp an initialized JSP action element
094     */
095    public CmsTouch(CmsJspActionElement jsp) {
096
097        super(jsp);
098    }
099
100    /**
101     * Public constructor with JSP variables.<p>
102     *
103     * @param context the JSP page context
104     * @param req the JSP request
105     * @param res the JSP response
106     */
107    public CmsTouch(PageContext context, HttpServletRequest req, HttpServletResponse res) {
108
109        this(new CmsJspActionElement(context, req, res));
110    }
111
112    /**
113     * Performs a touch operation for a single resource.<p>
114     *
115     * @param resourceName the resource name of the resource to touch
116     * @param timeStamp the new time stamp
117     * @param recursive the flag if the touch operation is recursive
118     * @param correctDate the flag if the new time stamp is a correct date
119     * @param touchContent if the content has to be rewritten
120     *
121     * @throws CmsException if touching the resource fails
122     */
123    public static void touchSingleResource(
124        CmsObject cms,
125        String resourceName,
126        long timeStamp,
127        boolean recursive,
128        boolean correctDate,
129        boolean touchContent)
130    throws CmsException {
131
132        CmsResource sourceRes = cms.readResource(resourceName, CmsResourceFilter.ALL);
133        if (!correctDate) {
134            // no date value entered, use current resource modification date
135            timeStamp = sourceRes.getDateLastModified();
136        }
137        cms.setDateLastModified(resourceName, timeStamp, recursive);
138
139        if (touchContent) {
140            if (sourceRes.isFile()) {
141                hardTouch(cms, sourceRes);
142            } else if (recursive) {
143                Iterator<CmsResource> it = cms.readResources(resourceName, CmsResourceFilter.ALL, true).iterator();
144                while (it.hasNext()) {
145                    CmsResource subRes = it.next();
146                    if (subRes.isFile()) {
147                        hardTouch(cms, subRes);
148                    }
149                }
150            }
151        }
152    }
153
154    /**
155     * Rewrites the content of the given file.<p>
156     *
157     * @param resource the resource to rewrite the content for
158     *
159     * @throws CmsException if something goes wrong
160     */
161    private static void hardTouch(CmsObject cms, CmsResource resource) throws CmsException {
162
163        CmsFile file = cms.readFile(resource);
164        file.setContents(file.getContents());
165        cms.writeFile(file);
166    }
167
168    /**
169     * Performs the resource touching, will be called by the JSP page.<p>
170     *
171     * @throws JspException if problems including sub-elements occur
172     */
173    public void actionTouch() throws JspException {
174
175        // save initialized instance of this class in request attribute for included sub-elements
176        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
177        try {
178            if (performDialogOperation()) {
179                // if no exception is caused and "true" is returned the touch operation was successful
180                actionCloseDialog();
181            } else {
182                // "false" returned, display "please wait" screen
183                getJsp().include(FILE_DIALOG_SCREEN_WAIT);
184            }
185        } catch (Throwable e) {
186            includeErrorpage(this, e);
187        }
188    }
189
190    /**
191     * Creates the "rewrite content" checkbox.<p>
192     *
193     * @return the String with the checkbox input field
194     */
195    public String buildCheckContent() {
196
197        StringBuffer retValue = new StringBuffer(256);
198
199        retValue.append("<tr>\n\t<td colspan=\"3\" style=\"white-space: nowrap;\" unselectable=\"on\">");
200        retValue.append("<input type=\"checkbox\" name=\"");
201        retValue.append(PARAM_CONTENT);
202        retValue.append("\" value=\"true\">&nbsp;");
203        retValue.append(key(Messages.GUI_TOUCH_MODIFY_CONTENT_0));
204        retValue.append("</td>\n</tr>\n");
205        return retValue.toString();
206    }
207
208    /**
209     * Creates the "recursive" checkbox for touching subresources of folders.<p>
210     *
211     * @return the String with the checkbox input field or an empty String for folders.
212     */
213    public String buildCheckRecursive() {
214
215        StringBuffer retValue = new StringBuffer(256);
216
217        // show the checkbox only for folders
218        if (isOperationOnFolder()) {
219            retValue.append("<tr>\n\t<td colspan=\"3\" style=\"white-space: nowrap;\" unselectable=\"on\">");
220            retValue.append("<input type=\"checkbox\" name=\"");
221            retValue.append(PARAM_RECURSIVE);
222            retValue.append("\" value=\"true\">&nbsp;");
223            retValue.append(key(Messages.GUI_TOUCH_MODIFY_SUBRESOURCES_0));
224            retValue.append("</td>\n</tr>\n");
225        }
226        return retValue.toString();
227    }
228
229    /**
230     * Creates the HTML JavaScript and stylesheet includes required by the calendar for the head of the page.<p>
231     *
232     * @return the necessary HTML code for the js and stylesheet includes
233     *
234     * @deprecated use {@link CmsCalendarWidget#calendarIncludes(java.util.Locale)}, this is just here so that old JSP still work
235     */
236    @Deprecated
237    public String calendarIncludes() {
238
239        return CmsCalendarWidget.calendarIncludes(getLocale());
240    }
241
242    /**
243     * Generates the HTML to initialize the JavaScript calendar element on the end of a page.<p>
244     *
245     * @param inputFieldId the ID of the input field where the date is pasted to
246     * @param triggerButtonId the ID of the button which triggers the calendar
247     * @param align initial position of the calendar popup element
248     * @param singleClick if true, a single click selects a date and closes the calendar, otherwise calendar is closed by doubleclick
249     * @param weekNumbers show the week numbers in the calendar or not
250     * @param mondayFirst show monday as first day of week
251     * @param dateStatusFunc name of the function which determines if/how a date should be disabled
252     * @param showTime true if the time selector should be shown, otherwise false
253     * @return the HTML code to initialize a calendar poup element
254     *
255     * @deprecated use {@link CmsCalendarWidget#calendarInit(org.opencms.i18n.CmsMessages, String, String, String, boolean, boolean, boolean, String, boolean)}, this is just here so that old JSP still work
256     */
257    @Deprecated
258    public String calendarInit(
259        String inputFieldId,
260        String triggerButtonId,
261        String align,
262        boolean singleClick,
263        boolean weekNumbers,
264        boolean mondayFirst,
265        String dateStatusFunc,
266        boolean showTime) {
267
268        return CmsCalendarWidget.calendarInit(
269            getMessages(),
270            inputFieldId,
271            triggerButtonId,
272            align,
273            singleClick,
274            weekNumbers,
275            mondayFirst,
276            dateStatusFunc,
277            showTime);
278    }
279
280    /**
281     * Returns the current date and time as String formatted in localized pattern.<p>
282     *
283     * @return the current date and time as String formatted in localized pattern
284     */
285    public String getCurrentDateTime() {
286
287        // get the current date & time
288        return CmsCalendarWidget.getCalendarLocalizedTime(getLocale(), getMessages(), System.currentTimeMillis());
289    }
290
291    /**
292     * Returns the value of the content parameter,
293     * or null if this parameter was not provided.<p>
294     *
295     * The content parameter on files decides if also the content is rewritten.<p>
296     *
297     * @return the value of the content parameter
298     */
299    public String getParamContent() {
300
301        return m_paramContent;
302    }
303
304    /**
305     * Returns the value of the new timestamp parameter,
306     * or null if this parameter was not provided.<p>
307     *
308     * The timestamp parameter stores the new timestamp as String.<p>
309     *
310     * @return the value of the new timestamp parameter
311     */
312    public String getParamNewtimestamp() {
313
314        return m_paramNewtimestamp;
315    }
316
317    /**
318     * Returns the value of the recursive parameter,
319     * or null if this parameter was not provided.<p>
320     *
321     * The recursive parameter on folders decides if all subresources
322     * of the folder should be touched, too.<p>
323     *
324     * @return the value of the recursive parameter
325     */
326    public String getParamRecursive() {
327
328        return m_paramRecursive;
329    }
330
331    /**
332     * Sets the value of the content parameter.<p>
333     *
334     * @param value the value to set
335     */
336    public void setParamContent(String value) {
337
338        m_paramContent = value;
339    }
340
341    /**
342     * Sets the value of the new timestamp parameter.<p>
343     *
344     * @param value the value to set
345     */
346    public void setParamNewtimestamp(String value) {
347
348        m_paramNewtimestamp = value;
349    }
350
351    /**
352     * Sets the value of the recursive parameter.<p>
353     *
354     * @param value the value to set
355     */
356    public void setParamRecursive(String value) {
357
358        m_paramRecursive = value;
359    }
360
361    /**
362     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, jakarta.servlet.http.HttpServletRequest)
363     */
364    @Override
365    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
366
367        // fill the parameter values in the get/set methods
368        fillParamValues(request);
369
370        // check the required permissions to touch the resource
371        if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) {
372            // no write permissions for the resource, set cancel action to close dialog
373            setParamAction(DIALOG_CANCEL);
374        }
375
376        // set the dialog type
377        setParamDialogtype(DIALOG_TYPE);
378
379        // set the action for the JSP switch
380        if (DIALOG_TYPE.equals(getParamAction())) {
381            setAction(ACTION_TOUCH);
382        } else if (DIALOG_WAIT.equals(getParamAction())) {
383            setAction(ACTION_WAIT);
384        } else if (DIALOG_CANCEL.equals(getParamAction())) {
385            setAction(ACTION_CANCEL);
386        } else if (DIALOG_LOCKS_CONFIRMED.equals(getParamAction())) {
387            setAction(ACTION_LOCKS_CONFIRMED);
388        } else {
389            setAction(ACTION_DEFAULT);
390            // build title for touch dialog
391            setDialogTitle(Messages.GUI_TOUCH_RESOURCE_1, Messages.GUI_TOUCH_MULTI_2);
392        }
393    }
394
395    /**
396     * Performs the resource touching.<p>
397     *
398     * @return true, if the resource was touched, otherwise false
399     * @throws CmsException if touching is not successful
400     */
401    @Override
402    protected boolean performDialogOperation() throws CmsException {
403
404        // on folder touch or multi resource operation display "please wait" screen, not for simple file copy
405        if (!DIALOG_WAIT.equals(getParamAction())) {
406            // check if the "please wait" screen has to be shown
407            if (isMultiOperation()) {
408                // show please wait for every multi resource operation
409                return false;
410            } else {
411                // check if the single resource is a folder
412                CmsResource sourceRes = getCms().readResource(getParamResource(), CmsResourceFilter.ALL);
413                if (sourceRes.isFolder()) {
414                    return false;
415                }
416            }
417        }
418
419        // get the new timestamp for the resource(s) from request parameter
420        long timeStamp = 0;
421        boolean correctDate = false;
422        try {
423            if (CmsStringUtil.isNotEmpty(getParamNewtimestamp())) {
424                timeStamp = CmsCalendarWidget.getCalendarDate(getMessages(), getParamNewtimestamp(), true);
425                correctDate = true;
426            }
427        } catch (ParseException e) {
428            throw new CmsException(Messages.get().container(Messages.ERR_PARSE_TIMESTAMP_1, getParamNewtimestamp()), e);
429        }
430
431        // get the flag if the touch is recursive from request parameter
432        boolean touchRecursive = Boolean.valueOf(getParamRecursive()).booleanValue();
433        // get the flag to the touch the content from request parameter
434        boolean touchContent = Boolean.valueOf(getParamContent()).booleanValue();
435
436        // now touch the resource(s)
437        Iterator<String> i = getResourceList().iterator();
438        while (i.hasNext()) {
439            String resName = i.next();
440            try {
441                // lock resource if autolock is enabled
442                checkLock(resName);
443                touchSingleResource(getCms(), resName, timeStamp, touchRecursive, correctDate, touchContent);
444            } catch (CmsException e) {
445                // collect exceptions to create a detailed output
446                addMultiOperationException(e);
447            }
448        }
449        checkMultiOperationException(Messages.get(), Messages.ERR_TOUCH_MULTI_0);
450
451        return true;
452    }
453}