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.tools.content.languagecopy;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.i18n.CmsLocaleManager;
033import org.opencms.i18n.CmsMessageContainer;
034import org.opencms.lock.CmsLock;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsLog;
037import org.opencms.main.OpenCms;
038import org.opencms.report.A_CmsReportThread;
039import org.opencms.report.CmsHtmlReport;
040import org.opencms.report.CmsLogReport;
041import org.opencms.report.CmsMultiplexReport;
042import org.opencms.report.I_CmsReport;
043import org.opencms.xml.content.CmsXmlContent;
044import org.opencms.xml.content.CmsXmlContentFactory;
045
046import java.util.Iterator;
047import java.util.List;
048import java.util.Locale;
049
050import org.apache.commons.logging.Log;
051
052/**
053 * Copies language nodes in XML contents.<p>
054 *
055 * @since 7.5.1
056 */
057public class CmsLanguageCopyThread extends A_CmsReportThread {
058
059    /** The log object for this class. */
060    private static final Log LOG = CmsLog.getLog(CmsLanguageCopyThread.class);
061
062    /** The resources to copy. */
063    private String[] m_copyresources;
064
065    /** The delete flag. */
066    private boolean m_delete;
067
068    /** The special multiplex report. */
069    private I_CmsReport m_report;
070
071    /** The source language. */
072    private String m_sourceLanguage;
073
074    /** The source language. */
075    private String m_targetLanguage;
076
077    /**
078     * Copies language nodes in XML contents.<p>
079     *
080     * @param cms the current cms context
081     * @param copyResources the resources to copy
082     * @param delete the delete flag
083     * @param sourceLanguage the source language
084     * @param targetLanguage the target language
085     */
086    public CmsLanguageCopyThread(
087        final CmsObject cms,
088        String[] copyResources,
089        boolean delete,
090        String sourceLanguage,
091        String targetLanguage) {
092
093        super(cms, Messages.get().getBundle().key(Messages.GUI_REPORT_LANGUAGECOPY_NAME_0));
094        m_copyresources = copyResources;
095        m_sourceLanguage = sourceLanguage;
096        m_targetLanguage = targetLanguage;
097        m_delete = delete;
098        initHtmlReport(cms.getRequestContext().getLocale());
099        CmsMultiplexReport report = new CmsMultiplexReport();
100        Locale locale = cms.getRequestContext().getLocale();
101        report.addReport(new CmsHtmlReport(locale, cms.getRequestContext().getSiteRoot()));
102        report.addReport(new CmsLogReport(locale, CmsLanguageCopyThread.class));
103        m_report = report;
104    }
105
106    /**
107     * @see org.opencms.report.A_CmsReportThread#getReportUpdate()
108     */
109    @Override
110    public String getReportUpdate() {
111
112        return getReport().getReportUpdate();
113    }
114
115    /**
116     * @see java.lang.Runnable#run()
117     */
118    @Override
119    public void run() {
120
121        CmsMultiplexReport report = (CmsMultiplexReport)getReport();
122        int totalFiles = m_copyresources.length;
123        Locale sourceLocale = CmsLocaleManager.getLocale(m_sourceLanguage);
124        Locale targetLocale = CmsLocaleManager.getLocale(m_targetLanguage);
125        report.println(
126            Messages.get().container(
127                Messages.GUI_REPORT_LANGUAGEC0PY_START_3,
128                new Object[] {Integer.valueOf(totalFiles), sourceLocale, targetLocale}),
129            I_CmsReport.FORMAT_HEADLINE);
130
131        try {
132            copyLanguageNodes();
133        } catch (Throwable e) {
134            report.println(e);
135            if (LOG.isErrorEnabled()) {
136                LOG.error(Messages.get().getBundle().key(Messages.ERR_REPORT_LANGUAGEC0PY_0), e);
137            }
138        }
139        // List the errors:
140        List<Object> errors = report.getErrors();
141        List<Object> warnings = report.getWarnings();
142
143        report.println(
144            Messages.get().container(
145                Messages.GUI_REPORT_LANGUAGEC0PY_END_2,
146                new Object[] {Integer.valueOf(warnings.size()), Integer.valueOf(errors.size())}),
147            I_CmsReport.FORMAT_HEADLINE);
148        for (Object f : warnings) {
149            if (f instanceof CmsMessageContainer) {
150                report.println((CmsMessageContainer)f, I_CmsReport.FORMAT_WARNING);
151            }
152        }
153        for (Object f : errors) {
154            if (f instanceof CmsMessageContainer) {
155                report.println((CmsMessageContainer)f, I_CmsReport.FORMAT_ERROR);
156            }
157        }
158    }
159
160    /**
161     * @see org.opencms.report.A_CmsReportThread#getReport()
162     */
163    @Override
164    protected I_CmsReport getReport() {
165
166        return m_report;
167    }
168
169    /**
170     * Does the job.<p>
171     */
172    private void copyLanguageNodes() {
173
174        CmsObject cms = getCms();
175        CmsMultiplexReport report = (CmsMultiplexReport)getReport();
176        CmsFile file;
177        CmsXmlContent content;
178        int totalFiles = m_copyresources.length;
179        int processedFiles = 0;
180        Locale sourceLocale = CmsLocaleManager.getLocale(m_sourceLanguage);
181        Locale targetLocale = CmsLocaleManager.getLocale(m_targetLanguage);
182
183        for (int i = 0; i < m_copyresources.length; i++) {
184            processedFiles++;
185            report.print(
186                org.opencms.report.Messages.get().container(
187                    org.opencms.report.Messages.RPT_SUCCESSION_2,
188                    new Object[] {String.valueOf(processedFiles), String.valueOf(totalFiles)}),
189                I_CmsReport.FORMAT_NOTE);
190
191            report.print(
192                Messages.get().container(Messages.RPT_LOCALIZATION_BYPASS_1, new Object[] {m_copyresources[i]}));
193            report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
194
195            try {
196                file = cms.readFile(m_copyresources[i]);
197                content = CmsXmlContentFactory.unmarshal(cms, file);
198
199                if (!content.hasLocale(sourceLocale)) {
200                    report.println(
201                        Messages.get().container(
202                            Messages.GUI_REPORT_LANGUAGEC0PY_WARN_SOURCELOCALE_MISSING_1,
203                            new Object[] {sourceLocale}),
204                        I_CmsReport.FORMAT_WARNING);
205                    CmsMessageContainer container = Messages.get().container(
206                        Messages.GUI_REPORT_LANGUAGEC0PY_WARN_SOURCELOCALE_MISSING_2,
207                        new Object[] {m_copyresources[i], sourceLocale});
208                    report.addWarning(container);
209                } else if (content.hasLocale(targetLocale)) {
210                    report.println(
211                        Messages.get().container(
212                            Messages.GUI_REPORT_LANGUAGEC0PY_WARN_TARGETLOCALE_EXISTS_1,
213                            new Object[] {targetLocale}),
214                        I_CmsReport.FORMAT_WARNING);
215                    CmsMessageContainer container = Messages.get().container(
216                        Messages.GUI_REPORT_LANGUAGEC0PY_WARN_TARGETLOCALE_EXISTS_2,
217                        new Object[] {m_copyresources[i], targetLocale});
218                    report.addWarning(container);
219                } else {
220                    content.copyLocale(sourceLocale, targetLocale);
221                    if (m_delete) {
222                        content.removeLocale(sourceLocale);
223                    }
224                    file.setContents(content.marshal());
225                    CmsLock lock = cms.getLock(file);
226                    if (lock.isInherited()) {
227                        unlockInherited(file.getRootPath());
228                        cms.lockResource(m_copyresources[i]);
229                    } else {
230                        if (lock.isNullLock()) {
231                            cms.lockResource(m_copyresources[i]);
232                        } else {
233                            if (!lock.isLockableBy(cms.getRequestContext().getCurrentUser())) {
234                                cms.changeLock(m_copyresources[i]);
235                            }
236                        }
237                    }
238                    cms.writeFile(file);
239                    cms.unlockResource(m_copyresources[i]);
240                    report.println(
241                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
242                        I_CmsReport.FORMAT_OK);
243                }
244
245            } catch (Throwable f) {
246                CmsMessageContainer error = Messages.get().container(
247                    Messages.GUI_REPORT_LANGUAGEC0PY_ERROR_2,
248                    new String[] {m_copyresources[i], CmsException.getStackTraceAsString(f)});
249
250                report.println(
251                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_FAILED_0),
252                    I_CmsReport.FORMAT_ERROR);
253                // report.println(f);
254                report.addError(error);
255
256            }
257        }
258    }
259
260    /**
261     * Returns the lock of a possible locked parent folder of a resource, system locks are ignored.<p>
262     *
263     * @param absoluteResourceName the name of the resource
264     *
265     * @return the lock of a parent folder, or {@link CmsLock#getNullLock()}
266     *            if no parent folders are locked by a non system lock
267     */
268    private CmsLock getParentFolderLock(final String absoluteResourceName) {
269
270        Iterator<CmsLock> itLocks = OpenCms.getMemoryMonitor().getAllCachedLocks().iterator();
271        while (itLocks.hasNext()) {
272            CmsLock lock = itLocks.next();
273            if (lock.getResourceName().endsWith("/")
274                && absoluteResourceName.startsWith(lock.getResourceName())
275                && !absoluteResourceName.equals(lock.getResourceName())) {
276                // system locks does not get inherited
277                lock = lock.getEditionLock();
278                // check the lock
279                if (!lock.isUnlocked()) {
280                    return lock;
281                }
282            }
283        }
284        return CmsLock.getNullLock();
285    }
286
287    /**
288     * Returns the inherited lock of a resource.<p>
289     *
290     * @param absoluteResourcename the absolute path of the resource
291     *
292     * @return the inherited lock or the null lock
293     */
294    private CmsLock getParentLock(final String absoluteResourcename) {
295
296        CmsLock parentFolderLock = getParentFolderLock(absoluteResourcename);
297        if (!parentFolderLock.isNullLock()) {
298            return parentFolderLock;
299        }
300        return CmsLock.getNullLock();
301    }
302
303    /**
304     * Recursively steps up to the resource that is the originator of the given
305     * resource which has an inherited lock.<p>
306     *
307     * @param absoluteResourcename the absolute resource with the inherited lock
308     *
309     * @throws CmsException if something goes wrong
310     */
311    private void unlockInherited(final String absoluteResourcename) throws CmsException {
312
313        CmsObject cms = getCms();
314        CmsLock parentLock = getParentLock(absoluteResourcename);
315        if (!parentLock.isNullLock()) {
316            if (parentLock.isInherited()) {
317                unlockInherited(parentLock.getResourceName());
318            } else {
319                if (!parentLock.isLockableBy(cms.getRequestContext().getCurrentUser())) {
320                    cms.changeLock(cms.getRequestContext().removeSiteRoot(parentLock.getResourceName()));
321                }
322                cms.unlockResource(cms.getRequestContext().removeSiteRoot(parentLock.getResourceName()));
323            }
324        }
325
326    }
327}