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.CmsResource;
031import org.opencms.file.CmsResource.CmsResourceCopyMode;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.file.CmsVfsException;
034import org.opencms.file.CmsVfsResourceAlreadyExistsException;
035import org.opencms.file.CmsVfsResourceNotFoundException;
036import org.opencms.jsp.CmsJspActionElement;
037import org.opencms.lock.CmsLockException;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.main.OpenCms;
041import org.opencms.security.CmsPermissionSet;
042import org.opencms.staticexport.CmsLinkManager;
043import org.opencms.util.CmsStringUtil;
044import org.opencms.workplace.CmsMultiDialog;
045import org.opencms.workplace.CmsWorkplaceSettings;
046
047import java.util.ArrayList;
048import java.util.Iterator;
049import java.util.List;
050
051import javax.servlet.http.HttpServletRequest;
052import javax.servlet.http.HttpServletResponse;
053import javax.servlet.jsp.JspException;
054import javax.servlet.jsp.PageContext;
055
056import org.apache.commons.logging.Log;
057
058/**
059 * Provides methods for the copy resources dialog.<p>
060 *
061 * The following files use this class:
062 * <ul>
063 * <li>/commons/copy.jsp
064 * </ul>
065 * <p>
066 *
067 * @since 6.0.0
068 */
069public class CmsCopy extends CmsMultiDialog {
070
071    /** Value for the action: copy the resource. */
072    public static final int ACTION_COPY = 100;
073
074    /** The dialog type. */
075    public static final String DIALOG_TYPE = "copy";
076
077    /** Request parameter name for the keep rights flag. */
078    public static final String PARAM_KEEPRIGHTS = "keeprights";
079
080    /** Request parameter name for the overwrite flag. */
081    public static final String PARAM_OVERWRITE = "overwrite";
082
083    /** The log object for this class. */
084    private static final Log LOG = CmsLog.getLog(CmsCopy.class);
085
086    /** A parameter of this dialog. */
087    private String m_paramCopymode;
088
089    /** A parameter of this dialog. */
090    private String m_paramKeeprights;
091
092    /** A parameter of this dialog. */
093    private String m_paramOverwrite;
094
095    /** A parameter of this dialog. */
096    private String m_paramTarget;
097
098    /**
099     * Public constructor with JSP action element.<p>
100     *
101     * @param jsp an initialized JSP action element
102     */
103    public CmsCopy(CmsJspActionElement jsp) {
104
105        super(jsp);
106    }
107
108    /**
109     * Public constructor with JSP variables.<p>
110     *
111     * @param context the JSP page context
112     * @param req the JSP request
113     * @param res the JSP response
114     */
115    public CmsCopy(PageContext context, HttpServletRequest req, HttpServletResponse res) {
116
117        this(new CmsJspActionElement(context, req, res));
118    }
119
120    /**
121     * Performs the copy action, will be called by the JSP page.<p>
122     *
123     * @throws JspException if problems including sub-elements occur
124     */
125    public void actionCopy() throws JspException {
126
127        // save initialized instance of this class in request attribute for included sub-elements
128        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
129        CmsResource resource = null;
130        try {
131            boolean isFolder = false;
132            String source = getResourceList().get(0);
133            String target = CmsLinkManager.getAbsoluteUri(getParamTarget(), CmsResource.getParentFolder(source));
134            if (!isMultiOperation()) {
135                resource = getCms().readResource(source, CmsResourceFilter.ALL);
136                isFolder = resource.isFolder();
137            } else {
138                String siteRootFolder = null;
139                try {
140                    // check if a site root was added to the target name
141                    if (OpenCms.getSiteManager().getSiteRoot(target) != null) {
142                        siteRootFolder = getCms().getRequestContext().getSiteRoot();
143                        if (siteRootFolder.endsWith("/")) {
144                            siteRootFolder = siteRootFolder.substring(0, siteRootFolder.length() - 1);
145                        }
146                        getCms().getRequestContext().setSiteRoot("/");
147                    }
148                    resource = getCms().readResource(target, CmsResourceFilter.ALL);
149                } finally {
150                    if (siteRootFolder != null) {
151                        getCms().getRequestContext().setSiteRoot(siteRootFolder);
152                    }
153                }
154                if (!resource.isFolder()) {
155                    // no folder selected for multi operation, throw exception
156                    throw new CmsVfsException(
157                        Messages.get().container(Messages.ERR_COPY_MULTI_TARGET_NOFOLDER_1, target));
158                }
159            }
160            if (performDialogOperation()) {
161                // if no exception is caused and "true" is returned copy operation was successful
162                if (isMultiOperation() || isFolder) {
163                    // set request attribute to reload the explorer tree view
164                    List<String> folderList = new ArrayList<String>(1);
165                    String targetParent = CmsResource.getParentFolder(target);
166                    folderList.add(targetParent);
167                    getJsp().getRequest().setAttribute(REQUEST_ATTRIBUTE_RELOADTREE, folderList);
168                }
169                actionCloseDialog();
170            } else {
171                // "false" returned, display "please wait" screen
172                getJsp().include(FILE_DIALOG_SCREEN_WAIT);
173            }
174        } catch (Throwable e) {
175            // check if this exception requires a confirmation or error screen for single resource operations
176            if (!isMultiOperation()
177                && ((e instanceof CmsVfsResourceAlreadyExistsException) || (e instanceof CmsLockException))
178                && (resource != null)
179                && !(resource.isFolder())) {
180                // file copy but file already exists, now check target file type
181                int targetType = -1;
182                String storedSiteRoot = null;
183                try {
184                    if (OpenCms.getSiteManager().getSiteRoot(getParamTarget()) != null) {
185                        storedSiteRoot = getCms().getRequestContext().getSiteRoot();
186                        getCms().getRequestContext().setSiteRoot("/");
187                    }
188                    CmsResource targetRes = getCms().readResource(getParamTarget());
189                    targetType = targetRes.getTypeId();
190                } catch (CmsException e2) {
191                    // can usually be ignored
192                    if (LOG.isInfoEnabled()) {
193                        LOG.info(e2.getLocalizedMessage());
194                    }
195                } finally {
196                    if (storedSiteRoot != null) {
197                        getCms().getRequestContext().setSiteRoot(storedSiteRoot);
198                    }
199                }
200                if (resource.getTypeId() == targetType) {
201                    // file type of target is the same as source, show confirmation dialog
202                    setParamMessage(
203                        CmsStringUtil.escapeHtml(key(
204                            Messages.GUI_COPY_CONFIRM_OVERWRITE_2,
205                            new Object[] {getParamResource(), getParamTarget()})));
206                    getJsp().include(FILE_DIALOG_SCREEN_CONFIRM);
207                } else {
208                    // file type is different, create error message
209                    includeErrorpage(this, e);
210                }
211            } else {
212                // error during copy, show error dialog
213                includeErrorpage(this, e);
214            }
215        }
216    }
217
218    /**
219     * Builds the input radio buttons to select between preserving links or creating new resources when copying.<p>
220     *
221     * @return the HTML code for the radio buttons
222     */
223    public String buildRadioCopyMode() {
224
225        StringBuffer retValue = new StringBuffer(256);
226
227        // check if the current resource is a folder for single operation
228        boolean isFolder = isOperationOnFolder();
229        String checkedAttr = " checked=\"checked\"";
230
231        if (isMultiOperation() || isFolder) {
232            // for multi resource operations or folders, show an additional option "preserve links"
233            CmsResourceCopyMode defaultMode = getSettings().getUserSettings().getDialogCopyFolderMode();
234            retValue.append("<input type=\"radio\" name=\"copymode\" value=\"");
235            retValue.append(CmsResource.COPY_AS_SIBLING.getMode());
236            retValue.append("\"");
237            if (defaultMode == CmsResource.COPY_AS_SIBLING) {
238                retValue.append(checkedAttr);
239            }
240            retValue.append("> ");
241            String msgKey;
242            if (isMultiOperation()) {
243                msgKey = Messages.GUI_COPY_MULTI_CREATE_SIBLINGS_0;
244            } else {
245                msgKey = Messages.GUI_COPY_CREATE_SIBLINGS_0;
246            }
247            retValue.append(key(msgKey));
248            retValue.append("<br>\n");
249            retValue.append("<input type=\"radio\" name=\"copymode\" value=\"");
250            retValue.append(CmsResource.COPY_PRESERVE_SIBLING.getMode());
251            retValue.append("\"");
252            if (defaultMode == CmsResource.COPY_PRESERVE_SIBLING) {
253                retValue.append(checkedAttr);
254            }
255            retValue.append("> ");
256            retValue.append(key(Messages.GUI_COPY_ALL_NO_SIBLINGS_0));
257            retValue.append("<br>\n");
258            retValue.append("<input type=\"radio\" name=\"copymode\" value=\"");
259            retValue.append(CmsResource.COPY_AS_NEW.getMode());
260            retValue.append("\"");
261            if (defaultMode == CmsResource.COPY_AS_NEW) {
262                retValue.append(checkedAttr);
263            }
264            retValue.append("> ");
265            retValue.append(key(Messages.GUI_COPY_ALL_0));
266            retValue.append("<br>\n");
267
268            if (isMultiOperation()) {
269                // show overwrite option for multi resource copy
270                retValue.append(dialogSpacer());
271                retValue.append("<input type=\"checkbox\" name=\"");
272                retValue.append(PARAM_OVERWRITE);
273                retValue.append("\" value=\"true\"> ");
274                retValue.append(key(Messages.GUI_COPY_MULTI_OVERWRITE_0));
275                retValue.append("<br>\n");
276            }
277        } else {
278            // for files, show copy option "copy as sibling" and "copy as new resource"
279            CmsResourceCopyMode defaultMode = getSettings().getUserSettings().getDialogCopyFileMode();
280            retValue.append("<input type=\"radio\" name=\"copymode\" value=\"");
281            retValue.append(CmsResource.COPY_AS_SIBLING.getMode());
282            retValue.append("\"");
283            if (defaultMode == CmsResource.COPY_AS_SIBLING) {
284                retValue.append(checkedAttr);
285            }
286            retValue.append("> ");
287            retValue.append(key(Messages.GUI_CREATE_SIBLING_0));
288            retValue.append("<br>\n");
289            retValue.append("<input type=\"radio\" name=\"copymode\" value=\"");
290            retValue.append(CmsResource.COPY_AS_NEW.getMode());
291            retValue.append("\"");
292            if (defaultMode == CmsResource.COPY_AS_NEW) {
293                retValue.append(checkedAttr);
294            }
295            retValue.append("> ");
296            retValue.append(key(Messages.GUI_COPY_AS_NEW_0));
297            retValue.append("<br>\n");
298        }
299
300        return retValue.toString();
301    }
302
303    /**
304     * Returns the value of the copymode parameter.<p>
305     *
306     * @return the value of the copymode parameter
307     */
308    public String getParamCopymode() {
309
310        return m_paramCopymode;
311    }
312
313    /**
314     * Returns the value of the keeprights parameter.<p>
315     *
316     * @return the value of the keeprights parameter
317     */
318    public String getParamKeeprights() {
319
320        return m_paramKeeprights;
321    }
322
323    /**
324     * Returns the value of the overwrite parameter.<p>
325     *
326     * @return the value of the overwrite parameter
327     */
328    public String getParamOverwrite() {
329
330        return m_paramOverwrite;
331    }
332
333    /**
334     * Returns the value of the target parameter,
335     * or null if this parameter was not provided.<p>
336     *
337     * The target parameter selects the target name
338     * of the operation.<p>
339     *
340     * @return the value of the target parameter
341     */
342    public String getParamTarget() {
343
344        return m_paramTarget;
345    }
346
347    /**
348     * Sets the value of the copymode parameter.<p>
349     *
350     * @param value the value of the copymode parameter
351     */
352    public void setParamCopymode(String value) {
353
354        m_paramCopymode = value;
355    }
356
357    /**
358     * Sets the value of the "keeprights" parameter.<p>
359     *
360     * @param value the value of the "keeprights" parameter
361     */
362    public void setParamKeeprights(String value) {
363
364        m_paramKeeprights = value;
365    }
366
367    /**
368     * Sets the value of the overwrite parameter.<p>
369     *
370     * @param paramOverwrite the value of the overwrite parameter
371     */
372    public void setParamOverwrite(String paramOverwrite) {
373
374        m_paramOverwrite = paramOverwrite;
375    }
376
377    /**
378     * Sets the value of the target parameter.<p>
379     *
380     * @param value the value to set
381     */
382    public void setParamTarget(String value) {
383
384        m_paramTarget = value;
385    }
386
387    /**
388     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
389     */
390    @Override
391    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
392
393        // fill the parameter values in the get/set methods
394        fillParamValues(request);
395
396        // check the required permissions to copy the resource
397        if (!checkResourcePermissions(CmsPermissionSet.ACCESS_WRITE, false)) {
398            // no write permissions for the resource, set cancel action to close dialog
399            setParamAction(DIALOG_CANCEL);
400        }
401
402        // set the dialog type
403        setParamDialogtype(DIALOG_TYPE);
404        // set the action for the JSP switch
405        if (DIALOG_TYPE.equals(getParamAction())) {
406            setAction(ACTION_COPY);
407        } else if (DIALOG_CONFIRMED.equals(getParamAction())) {
408            setAction(ACTION_CONFIRMED);
409        } else if (DIALOG_WAIT.equals(getParamAction())) {
410            setAction(ACTION_WAIT);
411        } else if (DIALOG_CANCEL.equals(getParamAction())) {
412            setAction(ACTION_CANCEL);
413        } else {
414            setAction(ACTION_DEFAULT);
415            // build title for copy dialog
416            setDialogTitle(Messages.GUI_COPY_RESOURCE_1, Messages.GUI_COPY_MULTI_2);
417        }
418    }
419
420    /**
421     * Performs the resource copying.<p>
422     *
423     * @return true, if the resource was copied, otherwise false
424     * @throws CmsException if copying is not successful
425     */
426    @Override
427    protected boolean performDialogOperation() throws CmsException {
428
429        // check if the current resource is a folder for single operation
430        boolean isFolder = isOperationOnFolder();
431
432        // on folder copy display "please wait" screen, not for simple file copy
433        if ((isMultiOperation() || isFolder) && !DIALOG_WAIT.equals(getParamAction())) {
434            // return false, this will trigger the "please wait" screen
435            return false;
436        }
437
438        // get the copy mode from request parameter value
439        CmsResourceCopyMode copyMode = CmsResource.COPY_PRESERVE_SIBLING;
440        try {
441            copyMode = CmsResourceCopyMode.valueOf(Integer.parseInt(getParamCopymode()));
442        } catch (Exception e) {
443            // can usually be ignored
444            if (LOG.isInfoEnabled()) {
445                LOG.info(e.getLocalizedMessage());
446            }
447        }
448
449        // check the overwrite options
450        boolean overwrite = Boolean.valueOf(getParamOverwrite()).booleanValue();
451        overwrite = ((isMultiOperation() && overwrite) || DIALOG_CONFIRMED.equals(getParamAction()));
452
453        // calculate the target name
454        String target = getParamTarget();
455        if (target == null) {
456            target = "";
457        }
458
459        String storedSiteRoot = null;
460        try {
461            // check if a site root was added to the target name
462            String sitePrefix = "";
463            if (OpenCms.getSiteManager().getSiteRoot(target) != null) {
464                String siteRootFolder = getCms().getRequestContext().getSiteRoot();
465                if (siteRootFolder.endsWith("/")) {
466                    siteRootFolder = siteRootFolder.substring(0, siteRootFolder.length() - 1);
467                }
468                sitePrefix = siteRootFolder;
469                storedSiteRoot = getCms().getRequestContext().getSiteRoot();
470                getCms().getRequestContext().setSiteRoot("/");
471            }
472
473            Iterator<String> i = getResourceList().iterator();
474            // iterate the resources to copy
475            while (i.hasNext()) {
476                String resName = i.next();
477                try {
478                    performSingleCopyOperation(resName, target, sitePrefix, copyMode, overwrite);
479                } catch (CmsException e) {
480                    if (isMultiOperation()) {
481                        // collect exceptions to create a detailed output
482                        addMultiOperationException(e);
483                    } else {
484                        // for single operation, throw the exception immediately
485                        throw e;
486                    }
487                }
488            }
489            // check if exceptions occurred
490            checkMultiOperationException(Messages.get(), Messages.ERR_COPY_MULTI_0);
491        } finally {
492            // restore the site root
493            if (storedSiteRoot != null) {
494                getCms().getRequestContext().setSiteRoot(storedSiteRoot);
495            }
496        }
497        return true;
498    }
499
500    /**
501     * Performs the copy operation for a single VFS resource.<p>
502     *
503     * @param source the source VFS path
504     * @param target the target VFS path
505     * @param sitePrefix the site prefix
506     * @param copyMode the copy mode for siblings
507     * @param overwrite the overwrite flag
508     *
509     * @throws CmsException if copying the resource fails
510     */
511    protected void performSingleCopyOperation(
512        String source,
513        String target,
514        String sitePrefix,
515        CmsResourceCopyMode copyMode,
516        boolean overwrite) throws CmsException {
517
518        // calculate the target name
519        String finalTarget = CmsLinkManager.getAbsoluteUri(target, CmsResource.getParentFolder(source));
520
521        if (finalTarget.equals(source) || (isMultiOperation() && finalTarget.startsWith(source))) {
522            throw new CmsVfsException(Messages.get().container(Messages.ERR_COPY_ONTO_ITSELF_1, finalTarget));
523        }
524
525        try {
526            CmsResource res = getCms().readResource(finalTarget, CmsResourceFilter.ALL);
527            if (res.isFolder()) {
528                // target folder already exists, so we add the current folder name
529                if (!finalTarget.endsWith("/")) {
530                    finalTarget += "/";
531                }
532                finalTarget = finalTarget + CmsResource.getName(source);
533            }
534        } catch (CmsVfsResourceNotFoundException e) {
535            // target folder does not already exist, so target name is o.k.
536            if (LOG.isInfoEnabled()) {
537                LOG.info(e.getLocalizedMessage());
538            }
539        }
540
541        // set the target parameter value
542        setParamTarget(finalTarget);
543
544        // delete existing target resource if selected or confirmed by the user
545        if (overwrite && getCms().existsResource(finalTarget)) {
546            checkLock(finalTarget);
547            getCms().deleteResource(finalTarget, CmsResource.DELETE_PRESERVE_SIBLINGS);
548        }
549        // copy the resource
550        getCms().copyResource(sitePrefix + source, finalTarget, copyMode);
551    }
552}