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.tools.content;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResourceFilter;
034import org.opencms.file.types.CmsResourceTypeXmlPage;
035import org.opencms.i18n.CmsLocaleManager;
036import org.opencms.i18n.CmsMessages;
037import org.opencms.jsp.CmsJspActionElement;
038import org.opencms.main.CmsException;
039import org.opencms.main.OpenCms;
040import org.opencms.report.I_CmsReport;
041import org.opencms.util.CmsStringUtil;
042import org.opencms.workplace.CmsReport;
043import org.opencms.workplace.CmsWorkplaceSettings;
044import org.opencms.xml.page.CmsXmlPage;
045import org.opencms.xml.page.CmsXmlPageFactory;
046
047import java.util.ArrayList;
048import java.util.Iterator;
049import java.util.List;
050import java.util.Locale;
051
052import jakarta.servlet.http.HttpServletRequest;
053import jakarta.servlet.http.HttpServletResponse;
054import jakarta.servlet.jsp.JspException;
055import jakarta.servlet.jsp.PageContext;
056
057/**
058 * Provides methods for the merge pages dialog.<p>
059 *
060 * @since 6.0.0
061 */
062public class CmsMergePages extends CmsReport {
063
064    /** A constant representing the select option all templates. */
065    public static final String ALL = "ALL";
066
067    /** Key for pages found in folder 1 exclusivly. */
068    public static final int FOLDER1_EXCLUSIVE = 0;
069
070    /** Key for pages found in folder 2 exclusivly. */
071    public static final int FOLDER2_EXCLUSIVE = 1;
072
073    /** Key for pages found in both folders but as different types. */
074    public static final int FOLDERS_DIFFERENTTYPES = 4;
075
076    /** Key for pages found in both folders as individual resources. */
077    public static final int FOLDERS_EQUALNAMES = 3;
078
079    /** Key for pages found in both folders as siblings. */
080    public static final int FOLDERS_SIBLING = 2;
081
082    /** The dialog type. */
083    public static final String DIALOG_TYPE = "mergepages";
084
085    /** Request parameter name for the first folder to merge. */
086    public static final String PARAM_FOLDER1 = "folder1";
087
088    /** Request parameter name for the second folder to merge. */
089    public static final String PARAM_FOLDER2 = "folder2";
090
091    /** the cms object. */
092    private CmsObject m_cms;
093
094    /** the error message. */
095    private String m_errorMessage;
096
097    /** List of pages found in folder 1 exclusivly. */
098    private List m_folder1Exclusive;
099
100    /** List of pages found in folder 2 exclusivly. */
101    private List m_folder2Exclusive;
102
103    /** List of pages found in  both folders but as different types. */
104    private List m_foldersDifferenttypes;
105
106    /** List of pages found in both folders as individual resources. */
107    private List m_foldersEqualnames;
108
109    /** List of pages found in both folders as siblings. */
110    private List m_foldersSibling;
111
112    /** The first folder to merge. */
113    private String m_paramFolder1;
114
115    /** The second folder to merge. */
116    private String m_paramFolder2;
117
118    /** the report for the output. */
119    private I_CmsReport m_report;
120
121    /**
122     * Public constructor with JSP action element.<p>
123     *
124     * @param jsp an initialized JSP action element
125     */
126    public CmsMergePages(CmsJspActionElement jsp) {
127
128        super(jsp);
129        m_folder1Exclusive = new ArrayList();
130        m_folder2Exclusive = new ArrayList();
131        m_foldersSibling = new ArrayList();
132        m_foldersEqualnames = new ArrayList();
133        m_foldersDifferenttypes = new ArrayList();
134    }
135
136    /**
137     * Public constructor with JSP variables.<p>
138     *
139     * @param cms the current CmsObject
140     * @param context the JSP page context
141     * @param req the JSP request
142     * @param res the JSP response
143     */
144    public CmsMergePages(CmsObject cms, PageContext context, HttpServletRequest req, HttpServletResponse res) {
145
146        this(new CmsJspActionElement(context, req, res));
147        m_cms = cms;
148    }
149
150    /**
151     * Merges the specified resources.<p>
152     *
153     * @param report the cms report
154     */
155    public void actionMerge(I_CmsReport report) {
156
157        m_report = report;
158        m_report.println(Messages.get().container(Messages.RPT_MERGE_PAGES_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
159
160        try {
161            // collect all pages and sort them depending on their state
162            collectResources();
163            // merge all pages that can be merged
164            mergePages();
165            // cleanup
166            cleanup();
167        } catch (CmsException e) {
168            m_report.println(e);
169        }
170
171    }
172
173    /**
174     * Performs the move report, will be called by the JSP page.<p>
175     *
176     * @throws JspException if problems including sub-elements occur
177     */
178    public void actionReport() throws JspException {
179
180        // save initialized instance of this class in request attribute for included sub-elements
181        getJsp().getRequest().setAttribute(SESSION_WORKPLACE_CLASS, this);
182        switch (getAction()) {
183            case ACTION_REPORT_END:
184                actionCloseDialog();
185                break;
186            case ACTION_REPORT_UPDATE:
187                setParamAction(REPORT_UPDATE);
188                getJsp().include(FILE_REPORT_OUTPUT);
189                break;
190            case ACTION_REPORT_BEGIN:
191            case ACTION_CONFIRMED:
192            default:
193                CmsMergePagesThread thread = new CmsMergePagesThread(getCms(), this);
194                thread.start();
195                setParamAction(REPORT_BEGIN);
196                setParamThread(thread.getUUID().toString());
197                getJsp().include(FILE_REPORT_OUTPUT);
198                break;
199        }
200    }
201
202    /**
203     * @see org.opencms.workplace.CmsWorkplace#getCms()
204     */
205    @Override
206    public CmsObject getCms() {
207
208        if (m_cms == null) {
209            return super.getCms();
210        }
211
212        return m_cms;
213    }
214
215    /**
216     * Returns the errorMessage.<p>
217     *
218     * @return the errorMessage
219     */
220    public String getErrorMessage() {
221
222        if (CmsStringUtil.isEmpty(m_errorMessage)) {
223            return "";
224        }
225
226        return m_errorMessage;
227    }
228
229    /**
230     * Returns the first folder.<p>
231     *
232     * @return the folder
233     */
234    public String getParamFolder1() {
235
236        return m_paramFolder1;
237    }
238
239    /**
240     * Returns the second folder.<p>
241     *
242     * @return the folder
243     */
244    public String getParamFolder2() {
245
246        return m_paramFolder2;
247    }
248
249    /**
250     * Sets the errorMessage.<p>
251     *
252     * @param errorMessage the errorMessage to set
253     */
254    public void setErrorMessage(String errorMessage) {
255
256        m_errorMessage = errorMessage;
257    }
258
259    /**
260     * Sets the first folder to merge.<p>
261     *
262     * @param folder1 the first folder name to set
263     */
264    public void setParamFolder1(String folder1) {
265
266        m_paramFolder1 = folder1;
267    }
268
269    /**
270     * Sets the second folder to merge.<p>
271     *
272     * @param folder2 the second folder name to set
273     */
274    public void setParamFolder2(String folder2) {
275
276        m_paramFolder2 = folder2;
277    }
278
279    /**
280     * Does validate the request parameters and returns a buffer with error messages.<p>
281     * @param cms the current cms object
282     * If there were no error messages, the buffer is empty.<p>
283     */
284    public void validateParameters(CmsObject cms) {
285
286        CmsMessages messages = Messages.get().getBundle(getLocale());
287        StringBuffer validationErrors = new StringBuffer();
288        if (CmsStringUtil.isEmpty(getParamFolder1())) {
289            validationErrors.append(messages.key(Messages.GUI_MERGE_PAGES_VALIDATE_FIRST_FOLDER_0)).append("<br>");
290        } else {
291            try {
292                cms.readResource(getParamFolder1());
293            } catch (CmsException e) {
294                validationErrors.append(
295                    messages.key(Messages.GUI_MERGE_PAGES_VALIDATE_FIRST_FOLDER_1, getParamFolder1())).append("<br>");
296            }
297        }
298        if (CmsStringUtil.isEmpty(getParamFolder2())) {
299            validationErrors.append(messages.key(Messages.GUI_MERGE_PAGES_VALIDATE_SECOND_FOLDER_0)).append("<br>");
300        } else {
301            try {
302                cms.readResource(getParamFolder2());
303            } catch (CmsException e) {
304                validationErrors.append(
305                    messages.key(Messages.GUI_MERGE_PAGES_VALIDATE_SECOND_FOLDER_1, getParamFolder2())).append("<br>");
306            }
307        }
308        if (getParamFolder1().equals(getParamFolder2())) {
309            validationErrors.append(messages.key(Messages.GUI_MERGE_PAGES_VALIDATE_SAME_FOLDER_0)).append("<br>");
310        }
311
312        setErrorMessage(validationErrors.toString());
313    }
314
315    /**
316     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, jakarta.servlet.http.HttpServletRequest)
317     */
318    @Override
319    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
320
321        // fill the parameter values in the get/set methods
322        fillParamValues(request);
323        // set the dialog type
324        setParamDialogtype(DIALOG_TYPE);
325        // set the action for the JSP switch
326        // set the action for the JSP switch
327        if (DIALOG_CONFIRMED.equals(getParamAction())) {
328            setAction(ACTION_CONFIRMED);
329        } else if (DIALOG_OK.equals(getParamAction())) {
330            setAction(ACTION_OK);
331        } else if (DIALOG_CANCEL.equals(getParamAction())) {
332            setAction(ACTION_CANCEL);
333        } else if (REPORT_UPDATE.equals(getParamAction())) {
334            setAction(ACTION_REPORT_UPDATE);
335        } else if (REPORT_BEGIN.equals(getParamAction())) {
336            setAction(ACTION_REPORT_BEGIN);
337        } else if (REPORT_END.equals(getParamAction())) {
338            setAction(ACTION_REPORT_END);
339        } else {
340            setAction(ACTION_DEFAULT);
341            // add the title for the dialog
342            setParamTitle(Messages.get().getBundle(getLocale()).key(Messages.GUI_TITLE_MERGEPAGES_0));
343        }
344    }
345
346    /**
347     * Analyses a page in the source morge folder and tests if a resouce with the same name exists in the target merge folder.<p>
348     *
349     * The method then calcualtes a action for further processing of this page, possible values are:
350     * <ul>
351     * <li>C_FOLDER1_EXCLUSIVE: exclusivly found in folder 1</li>
352     * <li>C_FOLDER2_EXCLUSIVE: exclusivly found in folder 2</li>
353     * <li>C_FOLDERS_SIBLING: found in both folders as siblings of each other </li>
354     * <li>C_FOLDERS_EQUALNAMES: found in both folders as individual resources</li>
355     * <li>C_FOLDERS_DIFFERENTTYPES: found in both folders as different types</li>
356     * </ul>
357     * @param res the resource to test
358     * @param sourceMergeFolder the path to the source merge folder
359     * @param targetMergefolder the path to the target merge folder
360     * @param currentFolder integer value (1 or 2) showing if the source folder is folder 1 or folder 2
361     * @return value of the action to do with this page
362     */
363    private int analyse(CmsResource res, String sourceMergeFolder, String targetMergefolder, int currentFolder) {
364
365        int retValue = -1;
366        String resourcenameOther = getResourceNameInOtherFolder(
367            m_cms.getSitePath(res),
368            sourceMergeFolder,
369            targetMergefolder);
370        try {
371            CmsResource otherRes = m_cms.readResource(resourcenameOther, CmsResourceFilter.IGNORE_EXPIRATION);
372            // there was a resource with the same name in the other merge folder
373            // now check if it is already a sibling of the current resource
374            if (res.getResourceId().equals(otherRes.getResourceId())) {
375                // it is a sibling, so set the action to "sibling already";
376                retValue = FOLDERS_SIBLING;
377            } else {
378                // it is no sibling, now test if it has the same resource type than the oringinal resource
379                if (res.getTypeId() == otherRes.getTypeId()) {
380                    // both resources have the same type, so set the action to  "same name". Only those resources can be merged
381                    retValue = FOLDERS_EQUALNAMES;
382                } else {
383                    // both resources have different types, so set the action to "different types"
384                    retValue = FOLDERS_DIFFERENTTYPES;
385                }
386            }
387        } catch (CmsException e) {
388            // the resource was not found, so set the action mode to "found only in the source folder"
389            if (currentFolder == 1) {
390                retValue = FOLDER1_EXCLUSIVE;
391            } else {
392                retValue = FOLDER2_EXCLUSIVE;
393            }
394        }
395
396        return retValue;
397    }
398
399    /**
400     * Cleanup all internal storages.<p>    *
401     */
402    private void cleanup() {
403
404        m_folder1Exclusive = null;
405        m_folder2Exclusive = null;
406        m_foldersSibling = null;
407        m_foldersEqualnames = null;
408        m_foldersDifferenttypes = null;
409    }
410
411    /**
412     * Collect all pages in a folders and sort them depending on the required action to do.<p>
413     *
414     * @param sourceMergeFolder the source merge folder to collect all pages from
415     * @param targetMergefolder the target merge folder to compare to
416     * @param currentFolder integer value (1 or 2) showing if the source folder is folder 1 or folder 2
417     * @throws CmsException if something goes wrong
418     */
419    private void collectFolder(String sourceMergeFolder, String targetMergefolder, int currentFolder)
420    throws CmsException {
421
422        //get the list of all resources in the source merge folder
423        int xmlPageId = OpenCms.getResourceManager().getResourceType(
424            CmsResourceTypeXmlPage.getStaticTypeName()).getTypeId();
425        CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION.addRequireType(xmlPageId);
426        List folderResources = m_cms.readResources(sourceMergeFolder, filter, true);
427        Iterator i = folderResources.iterator();
428        int size = folderResources.size();
429        // now loop through all resources and check them against those in the target merge folder
430        m_report.println(
431            Messages.get().container(
432                Messages.RPT_SCAN_PAGES_IN_FOLDER_BEGIN_2,
433                sourceMergeFolder,
434                Integer.valueOf(size)),
435            I_CmsReport.FORMAT_HEADLINE);
436        int count = 1;
437        while (i.hasNext()) {
438            CmsResource res = (CmsResource)i.next();
439            String resName = m_cms.getSitePath(res);
440
441            m_report.print(
442                org.opencms.report.Messages.get().container(
443                    org.opencms.report.Messages.RPT_SUCCESSION_2,
444                    String.valueOf(count++),
445                    String.valueOf(size)),
446                I_CmsReport.FORMAT_NOTE);
447            m_report.println(Messages.get().container(Messages.RPT_PROCESS_1, resName), I_CmsReport.FORMAT_NOTE);
448
449            // now analyse the page and calculate the action to do
450            int action = analyse(res, sourceMergeFolder, targetMergefolder, currentFolder);
451            // add the name of the resource to the correct list
452            switch (action) {
453                case FOLDER1_EXCLUSIVE:
454                    m_folder1Exclusive.add(resName);
455                    m_report.println(Messages.get().container(Messages.RPT_FOLDER1_EXCLUSIVE_0), I_CmsReport.FORMAT_OK);
456                    break;
457                case FOLDER2_EXCLUSIVE:
458                    m_folder2Exclusive.add(resName);
459                    m_report.println(Messages.get().container(Messages.RPT_FOLDER2_EXCLUSIVE_0), I_CmsReport.FORMAT_OK);
460                    break;
461                case FOLDERS_SIBLING:
462                    if (!m_foldersSibling.contains(
463                        getResourceNameInOtherFolder(resName, sourceMergeFolder, targetMergefolder))) {
464                        m_foldersSibling.add(resName);
465                    }
466                    m_report.println(Messages.get().container(Messages.RPT_FOLDERS_SIBLING_0), I_CmsReport.FORMAT_OK);
467                    break;
468                case FOLDERS_EQUALNAMES:
469                    if (!m_foldersEqualnames.contains(
470                        getResourceNameInOtherFolder(resName, sourceMergeFolder, targetMergefolder))) {
471                        m_foldersEqualnames.add(resName);
472                    }
473                    m_report.println(
474                        Messages.get().container(Messages.RPT_FOLDERS_EQUALNAMES_0),
475                        I_CmsReport.FORMAT_OK);
476                    break;
477                case FOLDERS_DIFFERENTTYPES:
478                    if (!m_foldersDifferenttypes.contains(
479                        getResourceNameInOtherFolder(resName, sourceMergeFolder, targetMergefolder))) {
480                        m_foldersDifferenttypes.add(resName);
481                    }
482                    m_report.println(
483                        Messages.get().container(Messages.RPT_FOLDERS_DIFFERENTTYPES_0),
484                        I_CmsReport.FORMAT_OK);
485                    break;
486                default:
487                    break;
488            }
489            res = null;
490        }
491        folderResources = null;
492        m_report.println(
493            Messages.get().container(Messages.RPT_SCAN_PAGES_IN_FOLDER_END_0),
494            I_CmsReport.FORMAT_HEADLINE);
495
496    }
497
498    /**
499     * Collect all pages in the folders to merge and sort them depending on the required action to do.<p>
500     *
501     * The method will create several lists. Each list contains the resource names of pages
502     * and will be used in further steps of the merging process.
503     * <ul>
504     * <li>List m_folder1Exclusive: contains all pages which are exclusivly found in folder 1</li>
505     * <li>List m_folder2Exclusive: contains all pages which are exclusivly found in folder 2</li>
506     * <li>List m_foldersSibling: contains all pages which can be found in both folders and are siblings of each other </li>
507     * <li>List m_foldersEqualnames: contains all pages which can be found in both folders and are no siblings of each other</li>
508     * <li>List m_foldersDifferenttypes: contains all pages which can be found in both folders but are of different types</li>
509     * </ul>
510     *
511     * @throws CmsException if something goes wrong
512     */
513    private void collectResources() throws CmsException {
514
515        String defaultLocale = CmsLocaleManager.getDefaultLocale().toString();
516        String locale1 = m_cms.readPropertyObject(getParamFolder1(), "locale", true).getValue(defaultLocale);
517        String locale2 = m_cms.readPropertyObject(getParamFolder2(), "locale", true).getValue(defaultLocale);
518        m_report.println(
519            Messages.get().container(Messages.RPT_CREATE_EXTERNAL_LINK_0, getParamFolder1(), locale1),
520            I_CmsReport.FORMAT_NOTE);
521        m_report.println(
522            Messages.get().container(Messages.RPT_CREATE_EXTERNAL_LINK_0, getParamFolder2(), locale2),
523            I_CmsReport.FORMAT_NOTE);
524
525        // collect all resources in folder 1
526        collectFolder(getParamFolder1(), getParamFolder2(), 1);
527        // collect all resources in folder 2
528        collectFolder(getParamFolder2(), getParamFolder1(), 2);
529
530        // report the results of the collection
531        m_report.println(Messages.get().container(Messages.RPT_SCANNING_RESULTS_0), I_CmsReport.FORMAT_HEADLINE);
532
533        m_report.println(Messages.get().container(Messages.RPT_FOLDER1_EXCLUSIVE_0), I_CmsReport.FORMAT_HEADLINE);
534        reportList(m_folder1Exclusive, false);
535        m_report.println(Messages.get().container(Messages.RPT_FOLDER2_EXCLUSIVE_0), I_CmsReport.FORMAT_HEADLINE);
536        reportList(m_folder2Exclusive, false);
537        m_report.println(Messages.get().container(Messages.RPT_FOLDERS_SIBLING_0), I_CmsReport.FORMAT_HEADLINE);
538        reportList(m_foldersSibling, false);
539        m_report.println(Messages.get().container(Messages.RPT_FOLDERS_EQUALNAMES_0), I_CmsReport.FORMAT_HEADLINE);
540        reportList(m_foldersEqualnames, true);
541        m_report.println(Messages.get().container(Messages.RPT_FOLDERS_DIFFERENTTYPES_0), I_CmsReport.FORMAT_HEADLINE);
542        reportList(m_foldersDifferenttypes, false);
543
544    }
545
546    /**
547     * Gets the name of a resource in the other merge folder.<p>
548     *
549     * @param resName the complete path of a resource
550     * @param sourceMergeFolder the path to the source merge folder
551     * @param targetMergefolder the path to the target merge folder
552     * @return the name of a resource in the other merge folder
553     */
554    private String getResourceNameInOtherFolder(String resName, String sourceMergeFolder, String targetMergefolder) {
555
556        // get the resourcename of the resouce to test without the source merge folder
557        String resourcename = resName.substring(sourceMergeFolder.length());
558        // get the complete path of the resource in the other merge folder
559        return targetMergefolder + resourcename;
560    }
561
562    /**
563     * Merges those pages in the two merge folders that have the same name and are no siblings of each other yet.<p>
564     * @throws CmsException if something goes wrong
565     */
566    private void mergePages() throws CmsException {
567
568        int size = m_foldersEqualnames.size();
569        if (size > 0) {
570
571            m_report.println(
572                Messages.get().container(Messages.RPT_MERGE_PAGES_BEGIN_1, String.valueOf(size)),
573                I_CmsReport.FORMAT_HEADLINE);
574            String defaultLocale = CmsLocaleManager.getDefaultLocale().toString();
575            String locale2 = m_cms.readPropertyObject(getParamFolder2(), "locale", true).getValue(defaultLocale);
576
577            // lock the source and the target folder
578            m_report.print(Messages.get().container(Messages.RPT_LOCK_FOLDER_0), I_CmsReport.FORMAT_NOTE);
579            m_report.print(
580                org.opencms.report.Messages.get().container(
581                    org.opencms.report.Messages.RPT_ARGUMENT_1,
582                    getParamFolder1()));
583            m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
584            m_cms.lockResource(getParamFolder1());
585            m_report.println(
586                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
587                I_CmsReport.FORMAT_OK);
588
589            m_report.print(Messages.get().container(Messages.RPT_LOCK_FOLDER_0), I_CmsReport.FORMAT_NOTE);
590            m_report.print(
591                org.opencms.report.Messages.get().container(
592                    org.opencms.report.Messages.RPT_ARGUMENT_1,
593                    getParamFolder2()));
594            m_report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
595            m_cms.lockResource(getParamFolder2());
596            m_report.println(
597                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
598                I_CmsReport.FORMAT_OK);
599
600            // now loop through all collected resources
601            int count = 1;
602            Iterator i = m_foldersEqualnames.iterator();
603            while (i.hasNext()) {
604                String resFolder1Name = (String)i.next();
605                try {
606                    String resFolder2Name = getResourceNameInOtherFolder(
607                        resFolder1Name,
608                        getParamFolder1(),
609                        getParamFolder2());
610                    m_report.print(
611                        org.opencms.report.Messages.get().container(
612                            org.opencms.report.Messages.RPT_SUCCESSION_2,
613                            String.valueOf(count++),
614                            String.valueOf(size)),
615                        I_CmsReport.FORMAT_NOTE);
616                    m_report.print(Messages.get().container(Messages.RPT_PROCESS_0), I_CmsReport.FORMAT_NOTE);
617                    m_report.print(
618                        org.opencms.report.Messages.get().container(
619                            org.opencms.report.Messages.RPT_ARGUMENT_1,
620                            resFolder1Name));
621                    m_report.print(Messages.get().container(Messages.RPT_DOUBLE_ARROW_0), I_CmsReport.FORMAT_NOTE);
622                    m_report.print(
623                        org.opencms.report.Messages.get().container(
624                            org.opencms.report.Messages.RPT_ARGUMENT_1,
625                            resFolder2Name));
626                    m_report.println(
627                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
628
629                    // get the content of the resource in folder1
630                    String locale = m_cms.readPropertyObject(resFolder1Name, "locale", true).getValue(defaultLocale);
631                    m_report.print(
632                        Messages.get().container(Messages.RPT_READ_CONTENT_2, resFolder1Name, locale),
633                        I_CmsReport.FORMAT_NOTE);
634                    CmsResource resFolder1 = m_cms.readResource(resFolder1Name, CmsResourceFilter.IGNORE_EXPIRATION);
635                    CmsFile fileFolder1 = m_cms.readFile(resFolder1);
636                    CmsXmlPage pageFolder1 = CmsXmlPageFactory.unmarshal(m_cms, fileFolder1);
637                    m_report.println(
638                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
639                        I_CmsReport.FORMAT_OK);
640
641                    // get the content of the resource in folder2
642                    locale = m_cms.readPropertyObject(resFolder2Name, "locale", true).getValue(defaultLocale);
643                    m_report.print(
644                        Messages.get().container(Messages.RPT_READ_CONTENT_2, resFolder2Name, locale),
645                        I_CmsReport.FORMAT_NOTE);
646                    CmsResource resFolder2 = m_cms.readResource(resFolder2Name, CmsResourceFilter.IGNORE_EXPIRATION);
647                    CmsFile fileFolder2 = m_cms.readFile(resFolder2);
648                    CmsXmlPage pageFolder2 = CmsXmlPageFactory.unmarshal(m_cms, fileFolder2);
649                    m_report.println(
650                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
651                        I_CmsReport.FORMAT_OK);
652
653                    // now get all the text elements from the resource in folder 2 which match the the locale of folder 2
654                    Locale loc = CmsLocaleManager.getLocale(locale2);
655                    List textElements2 = pageFolder2.getNames(loc);
656                    Iterator j = textElements2.iterator();
657                    while (j.hasNext()) {
658                        String textElementName = (String)j.next();
659                        m_report.print(
660                            Messages.get().container(Messages.RPT_PROCESS_TEXT_ELEM_1, textElementName),
661                            I_CmsReport.FORMAT_NOTE);
662                        // get the text element from the resource in folder 2...
663                        String textElement = pageFolder2.getValue(textElementName, loc).getStringValue(m_cms);
664                        // and set it in the resource in folder 1...
665                        // WARNING: An existing content will be overwritten!
666                        if (!pageFolder1.hasValue(textElementName, loc)) {
667                            pageFolder1.addValue(textElementName, loc);
668                        }
669                        pageFolder1.setStringValue(m_cms, textElementName, loc, textElement);
670                        m_report.println(
671                            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
672                            I_CmsReport.FORMAT_OK);
673                    }
674                    // the resource in folder 1 now has all text elements in both locales, so update it in the vfs
675
676                    m_report.print(
677                        Messages.get().container(Messages.RPT_WRITE_CONTENT_1, resFolder1Name),
678                        I_CmsReport.FORMAT_NOTE);
679                    fileFolder1.setContents(pageFolder1.marshal());
680                    m_cms.writeFile(fileFolder1);
681                    m_report.println(
682                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
683                        I_CmsReport.FORMAT_OK);
684
685                    // save all properties from the resource in folder2
686                    m_report.print(
687                        Messages.get().container(Messages.RPT_READ_PROPERTIES_1, resFolder2Name),
688                        I_CmsReport.FORMAT_NOTE);
689                    List properties = m_cms.readPropertyObjects(resFolder2Name, false);
690                    m_report.println(
691                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
692                        I_CmsReport.FORMAT_OK);
693
694                    // the next thing to do is to delete the old resource in folder 2
695                    m_report.print(
696                        Messages.get().container(Messages.RPT_DELETE_PAGE_1, resFolder2Name),
697                        I_CmsReport.FORMAT_NOTE);
698                    m_cms.deleteResource(resFolder2Name, CmsResource.DELETE_PRESERVE_SIBLINGS);
699                    m_report.println(
700                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
701                        I_CmsReport.FORMAT_OK);
702
703                    // copy a sibling of the resource from folder 1 to folder 2
704                    m_report.print(
705                        Messages.get().container(Messages.RPT_COPY_2, resFolder1Name, resFolder2Name),
706                        I_CmsReport.FORMAT_NOTE);
707                    m_cms.copyResource(resFolder1Name, resFolder2Name, CmsResource.COPY_AS_SIBLING);
708                    m_report.println(
709                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
710                        I_CmsReport.FORMAT_OK);
711
712                    // restore the properties at the sibling in folder 2
713                    m_report.print(
714                        Messages.get().container(Messages.RPT_RESORE_PROPERTIES_1, resFolder2Name),
715                        I_CmsReport.FORMAT_NOTE);
716                    m_cms.writePropertyObjects(resFolder2Name, properties);
717                    m_report.println(
718                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
719                        I_CmsReport.FORMAT_OK);
720
721                    resFolder1 = null;
722                    resFolder2 = null;
723                    fileFolder1 = null;
724                    fileFolder2 = null;
725                    pageFolder1 = null;
726                    pageFolder2 = null;
727
728                } catch (CmsException e) {
729                    m_report.println(e);
730                }
731
732            }
733            // lock the source and the target folder
734            m_report.print(Messages.get().container(Messages.RPT_UNLOCK_1, getParamFolder1()), I_CmsReport.FORMAT_NOTE);
735            m_cms.unlockResource(getParamFolder1());
736            m_report.println(
737                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
738                I_CmsReport.FORMAT_OK);
739
740            m_report.print(Messages.get().container(Messages.RPT_UNLOCK_1, getParamFolder2()), I_CmsReport.FORMAT_NOTE);
741            m_cms.unlockResource(getParamFolder2());
742            m_report.println(
743                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
744                I_CmsReport.FORMAT_OK);
745
746            m_report.println(Messages.get().container(Messages.RPT_MERGE_PAGES_END_0), I_CmsReport.FORMAT_HEADLINE);
747        }
748    }
749
750    /**
751     * Creates a report list of all resources in one of the collected lists.<p>
752     *
753     * @param collected the list to create the output from
754     * @param doReport flag to enable detailed report
755     */
756    private void reportList(List collected, boolean doReport) {
757
758        int size = collected.size();
759        // now loop through all collected resources
760        m_report.println(
761            Messages.get().container(Messages.RPT_NUM_PAGES_1, Integer.valueOf(size)),
762            I_CmsReport.FORMAT_HEADLINE);
763        if (doReport) {
764            int count = 1;
765
766            Iterator i = collected.iterator();
767            while (i.hasNext()) {
768                String resName = (String)i.next();
769                m_report.print(
770                    org.opencms.report.Messages.get().container(
771                        org.opencms.report.Messages.RPT_SUCCESSION_2,
772                        String.valueOf(count++),
773                        String.valueOf(size)),
774                    I_CmsReport.FORMAT_NOTE);
775                m_report.println(Messages.get().container(Messages.RPT_PROCESS_1, resName), I_CmsReport.FORMAT_NOTE);
776            }
777        }
778        m_report.println(Messages.get().container(Messages.RPT_MERGE_PAGES_END_0), I_CmsReport.FORMAT_HEADLINE);
779    }
780}