View Javadoc
1   /**
2    * This file Copyright (c) 2015-2017 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.resources.app.action;
35  
36  import info.magnolia.commands.CommandsManager;
37  import info.magnolia.context.Context;
38  import info.magnolia.event.EventBus;
39  import info.magnolia.i18nsystem.SimpleTranslator;
40  import info.magnolia.jcr.RuntimeRepositoryException;
41  import info.magnolia.jcr.util.NodeTypes;
42  import info.magnolia.jcr.util.NodeUtil;
43  import info.magnolia.resources.app.utils.ResourceUtils;
44  import info.magnolia.ui.api.context.UiContext;
45  import info.magnolia.ui.api.event.AdmincentralEventBus;
46  import info.magnolia.ui.api.event.ContentChangedEvent;
47  import info.magnolia.ui.framework.action.MarkNodeAsDeletedAction;
48  import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
49  import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
50  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
51  
52  import java.util.ArrayList;
53  import java.util.Comparator;
54  import java.util.List;
55  
56  import javax.inject.Named;
57  import javax.jcr.Node;
58  import javax.jcr.RepositoryException;
59  import javax.jcr.Session;
60  
61  import org.apache.commons.lang3.StringUtils;
62  import org.slf4j.Logger;
63  import org.slf4j.LoggerFactory;
64  
65  import com.google.common.base.Predicate;
66  import com.google.common.collect.FluentIterable;
67  import com.google.common.collect.Lists;
68  import com.google.common.collect.Ordering;
69  import com.vaadin.v7.data.Item;
70  
71  /**
72   * This action is used for marking the resource as deleted.
73   * <p>
74   * This will create a new Node version marked as deleted.
75   *
76   * @see MarkResourceAsDeletedActionDefinition
77   */
78  public class MarkResourceAsDeletedAction extends MarkNodeAsDeletedAction {
79  
80      private static final Logger log = LoggerFactory.getLogger(MarkResourceAsDeletedAction.class);
81  
82      private final List<JcrItemAdapter> jcrItemAdapters;
83      private final SimpleTranslator i18n;
84      private final boolean isDirectory;
85      private final List<String> changedItemPaths = new ArrayList<>();
86  
87      public MarkResourceAsDeletedAction(MarkResourceAsDeletedActionDefinition definition, List<Item> items, CommandsManager commandsManager, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n, Context context) {
88          this(definition, ResourceUtils.getJcrItemAdaptersFor(context, items), commandsManager, eventBus, uiContext, i18n, items.get(0));
89      }
90  
91      public MarkResourceAsDeletedAction(MarkResourceAsDeletedActionDefinition definition, Item item, CommandsManager commandsManager, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n, Context context) {
92          this(definition, ResourceUtils.getJcrItemAdaptersFor(context, Lists.newArrayList(item)), commandsManager, eventBus, uiContext, i18n, item);
93      }
94  
95      private MarkResourceAsDeletedAction(MarkResourceAsDeletedActionDefinition definition, List<JcrItemAdapter> jcrItemAdapters, CommandsManager commandsManager, EventBus eventBus, UiContext uiContext, SimpleTranslator i18n, Item item) {
96          super(definition, jcrItemAdapters, commandsManager, eventBus, uiContext, i18n);
97          this.isDirectory = ResourceUtils.isDirectory(item);
98          this.jcrItemAdapters = jcrItemAdapters;
99          this.i18n = i18n;
100         for (JcrItemAdapter itemAdapter : jcrItemAdapters) {
101             try {
102                 changedItemPaths.add(itemAdapter.getJcrItem().getPath());
103             } catch (RepositoryException e) {
104                 log.warn("Unable to obtain path for item {}", itemAdapter.getItemId());
105             }
106         }
107     }
108 
109     @Override
110     protected void onPostExecute() throws Exception {
111         eventBus.fireEvent(new ContentChangedEvent(changedItemPaths));
112     }
113 
114     @Override
115     protected List<JcrItemAdapter> getItems() {
116         List<JcrItemAdapter> items = Lists.newArrayList();
117         for (JcrItemAdapter jcrItemAdapter : jcrItemAdapters) {
118             items.addAll(getItems(jcrItemAdapter));
119         }
120         return items;
121     }
122 
123     @Override
124     protected List<JcrItemAdapter> getSortedItems(Comparator<JcrItemAdapter> comparator) {
125         return Ordering.from(comparator).sortedCopy(getItems());
126     }
127 
128     @Override
129     protected Comparator<JcrItemAdapter> getItemComparator() {
130         return new Comparator<JcrItemAdapter>() {
131             @Override
132             public int compare(JcrItemAdapter o1, JcrItemAdapter o2) {
133                 try {
134                     return o2.getJcrItem().getDepth() - o1.getJcrItem().getDepth();
135                 } catch (RepositoryException e) {
136                     return 0;
137                 }
138             }
139         };
140     }
141 
142     private List<JcrItemAdapter> getItems(JcrItemAdapter jcrItemAdapter) {
143         List<JcrItemAdapter> items = Lists.newArrayList();
144 
145         try {
146             Session jcrSession = jcrItemAdapter.getJcrItem().getSession();
147             Node node = jcrSession.getNode(jcrItemAdapter.getJcrItem().getPath());
148 
149             boolean hasSiblings = !getSiblings(node).isEmpty();
150             if (hasSiblings) {
151                 items.add(jcrItemAdapter);
152             } else {
153                 findNodeAndEmptyAncestorsFromJcr(items, node, null);
154             }
155         } catch (RepositoryException e) {
156             uiContext.openNotification(MessageStyleTypeEnum.ERROR, false, i18n.translate("resources.actions.deleteResource.notification.error"));
157             throw new RuntimeException(e);
158         }
159         return items;
160     }
161 
162     private void findNodeAndEmptyAncestorsFromJcr(List<JcrItemAdapter> items, Node node, final Node removedChildNode) throws RepositoryException {
163         if (node.getDepth() == 0) {
164             return;
165         }
166 
167         List<Node> siblings = getSiblings(node);
168         List<Node> filteredSiblings = filterSiblings(siblings, removedChildNode);
169 
170         boolean hasNoMoreResources = filteredSiblings.size() < 1;
171         if (hasNoMoreResources) {
172             Node parent = node.getParent();
173             items.add(new JcrNodeAdapter(node));
174             findNodeAndEmptyAncestorsFromJcr(items, parent, node);
175         }
176     }
177 
178     private List<Node> filterSiblings(List<Node> siblings, final Node removedChildNode) {
179         return FluentIterable
180                 .from(siblings)
181                 .filter(filterRemovedNode(removedChildNode))
182                 .filter(markedAsDeleted())
183                 .toList();
184     }
185 
186     private List<Node> getSiblings(Node node) throws RepositoryException {
187         List<Node> resources = Lists.newArrayList(NodeUtil.getNodes(node, NodeTypes.Content.NAME));
188         List<Node> folders = Lists.newArrayList(NodeUtil.getNodes(node, NodeTypes.Folder.NAME));
189         resources.addAll(folders);
190 
191         return resources;
192     }
193 
194     @Override
195     protected String getSuccessMessage() {
196         if (isDirectory) {
197             return i18n.translate("resources.actions.markFolderAsDeleted.notification.success", super.getItems().size());
198         } else {
199             return i18n.translate("resources.actions.markFileAsDeleted.notification.success", super.getItems().size());
200         }
201     }
202 
203     @Override
204     protected String getFailureMessage() {
205         if (isDirectory) {
206             return i18n.translate("resources.actions.markFolderAsDeleted.notification.error", super.getItems().size());
207         } else {
208             return i18n.translate("resources.actions.markFileAsDeleted.notification.error", super.getItems().size());
209         }
210     }
211 
212     private Predicate<Node> markedAsDeleted() {
213         return new Predicate<Node>() {
214             @Override
215             public boolean apply(Node node) {
216                 try {
217                     return !node.hasProperty(NodeTypes.Deleted.NAME);
218                 } catch (RepositoryException e) {
219                     throw new RuntimeRepositoryException(e);
220                 }
221             }
222         };
223     }
224 
225     private Predicate<Node> filterRemovedNode(final Node removedChildNode) {
226         return new Predicate<Node>() {
227             @Override
228             public boolean apply(Node node) {
229                 try {
230                     if (StringUtils.equals(node.getPath(), removedChildNode.getPath())) {
231                         return false;
232                     }
233                 } catch (RepositoryException e) {
234                     throw new RuntimeRepositoryException(e);
235                 }
236                 return true;
237             }
238         };
239     }
240 }