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, 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.ui.dialogs;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsResource.CmsResourceUndoMode;
034import org.opencms.gwt.CmsVfsService;
035import org.opencms.lock.CmsLockActionRecord;
036import org.opencms.lock.CmsLockActionRecord.LockChange;
037import org.opencms.lock.CmsLockException;
038import org.opencms.lock.CmsLockUtil;
039import org.opencms.main.CmsException;
040import org.opencms.main.CmsLog;
041import org.opencms.main.OpenCms;
042import org.opencms.ui.A_CmsUI;
043import org.opencms.ui.CmsVaadinUtils;
044import org.opencms.ui.FontOpenCms;
045import org.opencms.ui.I_CmsDialogContext;
046import org.opencms.ui.components.CmsBasicDialog;
047import org.opencms.ui.components.CmsOkCancelActionHandler;
048import org.opencms.util.CmsUUID;
049import org.opencms.workplace.explorer.CmsResourceUtil;
050
051import java.util.ArrayList;
052import java.util.HashSet;
053import java.util.Set;
054
055import org.apache.commons.logging.Log;
056
057import com.vaadin.ui.Button;
058import com.vaadin.ui.Button.ClickEvent;
059import com.vaadin.ui.Button.ClickListener;
060import com.vaadin.v7.shared.ui.label.ContentMode;
061import com.vaadin.v7.ui.CheckBox;
062import com.vaadin.v7.ui.Label;
063import com.vaadin.v7.ui.OptionGroup;
064import com.vaadin.v7.ui.VerticalLayout;
065
066/**
067 * Dialog used to change resource modification times.<p>
068 */
069public class CmsUndoDialog extends CmsBasicDialog {
070
071    /** Logger instance for this class. */
072    private static final Log LOG = CmsLog.getLog(CmsUndoDialog.class);
073
074    /** Serial version id. */
075    private static final long serialVersionUID = 1L;
076
077    /** The dialog context. */
078    protected I_CmsDialogContext m_context;
079
080    /** The Cancel button. */
081    private Button m_cancelButton;
082
083    /** Check box to enable/disable modification of children. */
084    private OptionGroup m_modifySubresourcesField;
085
086    /** The OK  button. */
087    private Button m_okButton;
088
089    /** Label with info text. */
090    private Label m_infoText;
091
092    /** Label for displaying last modification date / user. */
093    private Label m_modifiedText;
094
095    /** The date selection field. */
096    private CheckBox m_undoMoveField;
097
098    /** The lock warning to display if sub-resources are locked. */
099    private VerticalLayout m_lockWarning;
100
101    /** The warning icon */
102    private Label m_icon;
103
104    /**
105     * Creates a new instance.<p>
106     *
107     * @param context the dialog context
108     * @param hasBlockingLocksOnSubResources flag, indicating if there are blocking locks on sub-resources
109     */
110    public CmsUndoDialog(I_CmsDialogContext context, boolean hasBlockingLocksOnSubResources) {
111
112        m_context = context;
113        CmsVaadinUtils.readAndLocalizeDesign(
114            this,
115            OpenCms.getWorkplaceManager().getMessages(A_CmsUI.get().getLocale()),
116            null);
117        m_infoText.setValue(
118            CmsVaadinUtils.getMessageText(org.opencms.workplace.commons.Messages.GUI_UNDO_CONFIRMATION_0));
119        boolean hasFolders = false;
120        boolean hasMoved = false;
121        if (context.getResources().size() == 1) {
122            CmsResource singleRes = context.getResources().get(0);
123            CmsResourceUtil resUtil = new CmsResourceUtil(context.getCms(), singleRes);
124            String fileName = CmsResource.getName(singleRes.getRootPath());
125            String date = CmsVfsService.formatDateTime(context.getCms(), singleRes.getDateLastModified());
126            String user = resUtil.getUserLastModified();
127            String key = org.opencms.workplace.commons.Messages.GUI_UNDO_LASTMODIFIED_INFO_3;
128            String message = CmsVaadinUtils.getMessageText(key, fileName, date, user);
129            m_modifiedText.setVisible(true);
130            m_modifiedText.setValue(message);
131        }
132        for (CmsResource resource : context.getResources()) {
133            if (resource.isFolder()) {
134                hasFolders = true;
135                break;
136            } else {
137                try {
138                    CmsObject cms = OpenCms.initCmsObject(context.getCms());
139                    cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
140                    CmsResource onlineres = cms.readResource(resource.getStructureId());
141                    hasMoved |= !onlineres.getRootPath().equals(resource.getRootPath());
142                } catch (CmsException e) {
143                    LOG.warn(e.getLocalizedMessage(), e);
144                }
145            }
146        }
147
148        boolean multi = context.getResources().size() > 1;
149        String undoMessage = getUndoMessage(multi, hasFolders);
150        m_infoText.setValue(undoMessage);
151        m_modifySubresourcesField.setVisible(hasFolders);
152        m_modifySubresourcesField.addItem("false");
153        m_modifySubresourcesField.setItemCaption("false", getNonRecursiveMessage(multi, hasFolders));
154        m_modifySubresourcesField.addItem("true");
155        m_modifySubresourcesField.setItemCaption("true", getRecursiveMessage(multi, hasFolders));
156        m_modifySubresourcesField.setValue("false");
157
158        m_undoMoveField.setVisible(hasFolders || hasMoved);
159        if (hasBlockingLocksOnSubResources) {
160            m_modifySubresourcesField.setItemEnabled("true", false);
161            m_undoMoveField.setEnabled(false);
162            m_icon.setContentMode(ContentMode.HTML);
163            m_icon.setValue(FontOpenCms.WARNING.getHtml());
164            m_lockWarning.setVisible(true);
165        }
166
167        m_cancelButton.addClickListener(new ClickListener() {
168
169            private static final long serialVersionUID = 1L;
170
171            public void buttonClick(ClickEvent event) {
172
173                cancel();
174            }
175
176        });
177
178        m_okButton.addClickListener(new ClickListener() {
179
180            private static final long serialVersionUID = 1L;
181
182            public void buttonClick(ClickEvent event) {
183
184                undo();
185
186            }
187        });
188        displayResourceInfo(m_context.getResources());
189
190        setActionHandler(new CmsOkCancelActionHandler() {
191
192            private static final long serialVersionUID = 1L;
193
194            @Override
195            protected void cancel() {
196
197                CmsUndoDialog.this.cancel();
198            }
199
200            @Override
201            protected void ok() {
202
203                undo();
204            }
205        });
206    }
207
208    /**
209     * Undoes the changes.<p>
210     */
211    protected void undo() {
212
213        try {
214            boolean recursive = Boolean.parseBoolean(m_modifySubresourcesField.getValue().toString());
215            boolean undoMove = m_undoMoveField.getValue().booleanValue();
216            CmsObject cms = m_context.getCms();
217            Set<CmsUUID> updateResources = new HashSet<CmsUUID>();
218            for (CmsResource resource : m_context.getResources()) {
219                updateResources.add(resource.getStructureId());
220                if (undoMove) {
221                    // in case a move is undone, add the former parent folder
222                    updateResources.add(cms.readParentFolder(resource.getStructureId()).getStructureId());
223                }
224                CmsLockActionRecord actionRecord = null;
225                try {
226                    actionRecord = CmsLockUtil.ensureLock(
227                        m_context.getCms(),
228                        resource,
229                        !(resource.isFile() || recursive || undoMove));
230                    CmsResourceUndoMode mode = CmsResourceUndoMode.getUndoMode(undoMove, recursive);
231                    cms.undoChanges(cms.getSitePath(resource), mode);
232                    if (undoMove) {
233                        // in case a move is undone, also add the new parent folder
234                        updateResources.add(cms.readParentFolder(resource.getStructureId()).getStructureId());
235                    }
236                } finally {
237                    if ((actionRecord != null) && (actionRecord.getChange() == LockChange.locked)) {
238                        try {
239                            m_context.getCms().unlockResource(cms.readResource(resource.getStructureId()));
240                        } catch (CmsLockException e) {
241                            LOG.warn(e.getLocalizedMessage(), e);
242                        }
243                    }
244
245                }
246
247            }
248            m_context.finish(updateResources);
249        } catch (Exception e) {
250            m_context.error(e);
251        }
252
253    }
254
255    /**
256     * Cancels the dialog.<p>
257     */
258    void cancel() {
259
260        m_context.finish(new ArrayList<CmsUUID>());
261    }
262
263    /**
264     * Gets the message for the non-recursive modification option.<p>
265     *
266     * @param multi true if selection contains multiple resources
267     * @param hasFolder true if selection contains a folder
268     *
269     * @return the message text
270     */
271    String getNonRecursiveMessage(boolean multi, boolean hasFolder) {
272
273        return CmsVaadinUtils.getMessageText(key("GUI_UNDO_NONRECURSIVE_", multi, hasFolder));
274
275    }
276
277    /**
278     * Gets the message for the recursive modification option.<p>
279     *
280     * @param multi true if selection contains multiple resources
281     * @param hasFolder true if selection contains a folder
282     *
283     * @return the message text
284     */
285    String getRecursiveMessage(boolean multi, boolean hasFolder) {
286
287        return CmsVaadinUtils.getMessageText(key("GUI_UNDO_RECURSIVE_", multi, hasFolder));
288
289    }
290
291    /**
292     * Gets the undo message.<p>
293     *
294     * @param multi true if selection contains multiple resources
295     * @param hasFolder true if selection contains a folder
296     *
297     * @return the message text
298     */
299    String getUndoMessage(boolean multi, boolean hasFolder) {
300
301        return CmsVaadinUtils.getMessageText(key("GUI_UNDO_", multi, hasFolder));
302    }
303
304    /**
305     * Generates message key for a given combination of prefix, multi/single file , and folder/ no folder.<p>
306     *
307     * @param prefix the key prefix
308     * @param multi true if we have a multi-resource selection case
309     * @param hasFolder true if the selection contains a foldr
310     *
311     * @return the message key for the given input parameters
312     */
313    private String key(String prefix, boolean multi, boolean hasFolder) {
314
315        return prefix + (multi ? "MULTI" : "SINGLE") + "_" + (hasFolder ? "FOLDER" : "FILE") + "_0";
316    }
317
318}