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