View Javadoc
1   /**
2    * This file Copyright (c) 2010-2015 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.ui.contentapp.movedialog;
35  
36  import info.magnolia.context.MgnlContext;
37  import info.magnolia.event.EventBus;
38  import info.magnolia.event.ResettableEventBus;
39  import info.magnolia.event.SimpleEventBus;
40  import info.magnolia.i18nsystem.I18nizer;
41  import info.magnolia.i18nsystem.SimpleTranslator;
42  import info.magnolia.objectfactory.ComponentProvider;
43  import info.magnolia.ui.api.action.ActionDefinition;
44  import info.magnolia.ui.api.action.ConfiguredActionDefinition;
45  import info.magnolia.ui.api.app.AppContext;
46  import info.magnolia.ui.api.overlay.OverlayLayer.ModalityLevel;
47  import info.magnolia.ui.contentapp.DefinitionCloner;
48  import info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor;
49  import info.magnolia.ui.contentapp.field.WorkbenchField;
50  import info.magnolia.ui.contentapp.movedialog.action.MoveCancelledAction;
51  import info.magnolia.ui.contentapp.movedialog.action.MoveNodeActionDefinition;
52  import info.magnolia.ui.contentapp.movedialog.predicate.MoveAfterPossibilityPredicate;
53  import info.magnolia.ui.contentapp.movedialog.predicate.MoveBeforePossibilityPredicate;
54  import info.magnolia.ui.contentapp.movedialog.predicate.MoveInsidePossibilityPredicate;
55  import info.magnolia.ui.contentapp.movedialog.predicate.MovePossibilityPredicate;
56  import info.magnolia.ui.dialog.BaseDialogPresenter;
57  import info.magnolia.ui.dialog.DialogCloseHandler;
58  import info.magnolia.ui.dialog.DialogView;
59  import info.magnolia.ui.dialog.actionarea.DialogActionExecutor;
60  import info.magnolia.ui.dialog.actionarea.definition.ConfiguredEditorActionAreaDefinition;
61  import info.magnolia.ui.dialog.definition.ConfiguredDialogDefinition;
62  import info.magnolia.ui.dialog.definition.DialogDefinition;
63  import info.magnolia.ui.dialog.definition.SecondaryActionDefinition;
64  import info.magnolia.ui.framework.overlay.ViewAdapter;
65  import info.magnolia.ui.imageprovider.definition.ConfiguredImageProviderDefinition;
66  import info.magnolia.ui.vaadin.dialog.BaseDialog;
67  import info.magnolia.ui.vaadin.integration.NullItem;
68  import info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector;
69  import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnector;
70  import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
71  import info.magnolia.ui.workbench.WorkbenchPresenter;
72  import info.magnolia.ui.workbench.column.definition.ColumnDefinition;
73  import info.magnolia.ui.workbench.definition.ConfiguredWorkbenchDefinition;
74  import info.magnolia.ui.workbench.definition.ContentPresenterDefinition;
75  import info.magnolia.ui.workbench.tree.MoveHandler;
76  import info.magnolia.ui.workbench.tree.MoveLocation;
77  import info.magnolia.ui.workbench.tree.TreePresenter;
78  import info.magnolia.ui.workbench.tree.drop.DropConstraint;
79  
80  import java.util.HashMap;
81  import java.util.HashSet;
82  import java.util.Iterator;
83  import java.util.LinkedList;
84  import java.util.List;
85  import java.util.Map;
86  import java.util.Map.Entry;
87  import java.util.Set;
88  
89  import javax.inject.Inject;
90  import javax.jcr.RepositoryException;
91  
92  import com.vaadin.data.Item;
93  import com.vaadin.data.Property.ValueChangeEvent;
94  import com.vaadin.data.Property.ValueChangeListener;
95  
96  /**
97   * Implementation of {@link MoveDialogPresenter}.
98   */
99  public class MoveDialogPresenterImpl extends BaseDialogPresenter implements MoveDialogPresenter {
100 
101     private final DialogView dialogView;
102 
103     private final EventBus eventBus = new ResettableEventBus(new SimpleEventBus());
104 
105     private final WorkbenchPresenter workbenchPresenter;
106 
107     private final AppContext appContext;
108 
109     private final I18nizer i18nizer;
110 
111     private final ContentConnector contentConnector;
112 
113     private final MoveHandler dropHandler;
114 
115     private final DefinitionCloner cloner;
116 
117     private final Map<MoveLocation, ActionDefinition> actionMap = new HashMap<>();
118 
119     private final Map<MoveLocation, MovePossibilityPredicate> possibilityPredicates = new HashMap<>();
120 
121     private List<Item> nodesToMove;
122 
123     private DropConstraint constraint;
124 
125     private MoveActionCallback callback;
126 
127     private Item currentHostCandidate;
128 
129     private ConfiguredWorkbenchDefinition workbenchDefinition;
130 
131     @Inject
132     public MoveDialogPresenterImpl(ComponentProvider componentProvider, DialogView dialogView, WorkbenchPresenter workbenchPresenter, DialogActionExecutor executor, AppContext appContext, I18nizer i18nizer, SimpleTranslator simpleTranslator, ContentConnector contentConnector) {
133         super(componentProvider, executor, dialogView, i18nizer, simpleTranslator);
134         this.dialogView = dialogView;
135         this.workbenchPresenter = workbenchPresenter;
136         this.appContext = appContext;
137         this.i18nizer = i18nizer;
138         this.contentConnector = contentConnector;
139         this.dropHandler = componentProvider.newInstance(MoveHandler.class);
140         this.cloner = new DefinitionCloner();
141     }
142 
143     @Override
144     public Object[] getActionParameters(String actionName) {
145         return new Object[] { nodesToMove, callback, appContext, getHostCandidate(), dropHandler };
146     }
147 
148     @Override
149     public DialogView start(BrowserSubAppDescriptor subAppDescriptor, List<Item> nodesToMove, MoveActionCallback callback) {
150 
151         final ConfiguredImageProviderDefinition imageProviderDefinition = prepareImageProviderDefinition(subAppDescriptor);
152         this.workbenchDefinition = prepareWorkbenchDefinition(subAppDescriptor);
153 
154         this.nodesToMove = nodesToMove;
155         this.constraint = componentProvider.newInstance(workbenchDefinition.getDropConstraintClass());
156         this.callback = callback;
157 
158         initActions();
159         initMovePossibilityPredicates();
160 
161         final WorkbenchField field = new WorkbenchField(
162                 workbenchDefinition,
163                 imageProviderDefinition,
164                 workbenchPresenter,
165                 eventBus);
166         ViewAdapter viewAdapter = new ViewAdapter(field);
167         viewAdapter.asVaadinComponent().addStyleName("choose-dialog");
168         dialogView.setContent(viewAdapter);
169         field.addValueChangeListener(new ValueChangeListener() {
170             @Override
171             public void valueChange(ValueChangeEvent event) {
172                 currentHostCandidate = contentConnector.getItem(event.getProperty().getValue());
173                 updatePossibleMoveLocations(currentHostCandidate);
174 
175             }
176         });
177 
178         DialogDefinition dialogDefinition = prepareDialogDefinition();
179         getExecutor().setDialogDefinition(dialogDefinition);
180         dialogView.setCaption(dialogDefinition.getLabel());
181         dialogView.addDialogCloseHandler(new DialogCloseHandler() {
182             @Override
183             public void onDialogClose(DialogView dialogView) {
184                 ((ResettableEventBus) eventBus).reset();
185             }
186         });
187         super.start(dialogDefinition, appContext);
188         updatePossibleMoveLocations(getHostCandidate());
189 
190         getView().getActionAreaView().getViewForAction(MoveLocation.INSIDE.name()).asVaadinComponent().addStyleName("commit");
191         getView().setClosable(true);
192         return dialogView;
193     }
194 
195     private ConfiguredImageProviderDefinition prepareImageProviderDefinition(BrowserSubAppDescriptor subAppDescriptor) {
196         return (ConfiguredImageProviderDefinition) cloner.deepClone(subAppDescriptor.getImageProvider());
197     }
198 
199     private ConfiguredWorkbenchDefinition prepareWorkbenchDefinition(BrowserSubAppDescriptor subAppDescriptor) {
200         final ConfiguredWorkbenchDefinition workbenchDefinition =
201                 (ConfiguredWorkbenchDefinition) cloner.deepClone(subAppDescriptor.getWorkbench());
202 
203         workbenchDefinition.setDialogWorkbench(true);
204         workbenchDefinition.setEditable(false);
205 
206         List<ContentPresenterDefinition> contentPresenters = workbenchDefinition.getContentViews();
207         Iterator<ContentPresenterDefinition> it = contentPresenters.iterator();
208         ContentPresenterDefinition treePresenterDef = null;
209         while (it.hasNext() && treePresenterDef == null) {
210             ContentPresenterDefinition contentPresenterDef = it.next();
211             if (TreePresenter.class.isAssignableFrom(contentPresenterDef.getImplementationClass())) {
212                 treePresenterDef = prepareTreePresenter(contentPresenterDef);
213             }
214         }
215 
216         if (treePresenterDef != null) {
217             contentPresenters.clear();
218             contentPresenters.add(treePresenterDef);
219         }
220 
221         return workbenchDefinition;
222     }
223 
224     private ContentPresenterDefinition prepareTreePresenter(ContentPresenterDefinition treePresenter) {
225         ContentPresenterDefinition def = cloner.deepClone(treePresenter);
226         if (!def.getColumns().isEmpty()) {
227             ColumnDefinition column = def.getColumns().get(0);
228             def.getColumns().clear();
229             def.getColumns().add(column);
230         }
231         return def;
232     }
233 
234     protected void initMovePossibilityPredicates() {
235         possibilityPredicates.put(MoveLocation.AFTER, new MoveAfterPossibilityPredicate(constraint, nodesToMove));
236         possibilityPredicates.put(MoveLocation.BEFORE, new MoveBeforePossibilityPredicate(constraint, nodesToMove));
237         possibilityPredicates.put(MoveLocation.INSIDE, new MoveInsidePossibilityPredicate(constraint, nodesToMove));
238     }
239 
240     protected void updatePossibleMoveLocations(Item possibleHost) {
241         Set<MoveLocation> possibleLocations = new HashSet<MoveLocation>();
242         if (possibleHost == null) {
243             possibleHost = new NullItem();
244         }
245 
246         Iterator<Entry<MoveLocation, MovePossibilityPredicate>> it = possibilityPredicates.entrySet().iterator();
247         while (it.hasNext()) {
248             Entry<MoveLocation, MovePossibilityPredicate> entry = it.next();
249             if (entry.getValue().isMovePossible(possibleHost)) {
250                 possibleLocations.add(entry.getKey());
251             }
252         }
253 
254         getActionArea().setPossibleMoveLocations(possibleLocations);
255     }
256 
257     private void initActions() {
258         for (MoveLocation location : MoveLocation.values()) {
259             ConfiguredActionDefinition definition = i18nizer.decorate(new MoveNodeActionDefinition(location));
260             definition.setName(location.name());
261             actionMap.put(location, definition);
262         }
263     }
264 
265     private DialogDefinition prepareDialogDefinition() {
266         ConfiguredDialogDefinition def = new ConfiguredDialogDefinition();
267         def = i18nizer.decorate(def);
268         def.setId("ui-contentapp:code:MoveDialogPresenterImpl.moveDialog");
269 
270         for (MoveLocation location : MoveLocation.values()) {
271             def.addAction(actionMap.get(location));
272         }
273 
274         ConfiguredActionDefinition cancelDef = new ConfiguredActionDefinition();
275         cancelDef.setName("cancel");
276         cancelDef.setImplementationClass(MoveCancelledAction.class);
277         def.addAction(cancelDef);
278 
279         ConfiguredEditorActionAreaDefinition actionAreaDefinition = new ConfiguredEditorActionAreaDefinition();
280         actionAreaDefinition.setPresenterClass(MoveDialogActionAreaPresenter.class);
281 
282         List<SecondaryActionDefinition> secondaryActions = new LinkedList<SecondaryActionDefinition>();
283         secondaryActions.add(new SecondaryActionDefinition(MoveLocation.AFTER.name()));
284         secondaryActions.add(new SecondaryActionDefinition(MoveLocation.BEFORE.name()));
285         actionAreaDefinition.setSecondaryActions(secondaryActions);
286 
287         def.setActionArea(actionAreaDefinition);
288         def.setModalityLevel(ModalityLevel.LIGHT);
289         return def;
290     }
291 
292     @Override
293     public MoveDialogActionAreaPresenter getActionArea() {
294         return (MoveDialogActionAreaPresenter) super.getActionArea();
295     }
296 
297     @Override
298     protected DialogActionExecutor getExecutor() {
299         return (DialogActionExecutor) super.getExecutor();
300     }
301 
302     @Override
303     protected void executeAction(String actionName, Object[] actionContextParams) {
304         actionName = getCorrectActionName(actionName);
305         if (actionName != null) {
306             super.executeAction(actionName, actionContextParams);
307         }
308     }
309 
310     public String getCorrectActionName(String actionName) {
311         if (actionName.equals(BaseDialog.COMMIT_ACTION_NAME)) {
312             boolean moveInsideActionEnabled = getView().getActionAreaView().getViewForAction(MoveLocation.INSIDE.name()).asVaadinComponent().isEnabled();
313             actionName = moveInsideActionEnabled ? MoveLocation.INSIDE.name() : null;
314         }
315         return actionName;
316     }
317 
318     private Item getHostCandidate() {
319         if (currentHostCandidate != null) {
320             return currentHostCandidate;
321         } else {
322             try {
323                 String workspace = ((JcrContentConnector) contentConnector).getContentConnectorDefinition().getWorkspace();
324                 return new JcrNodeAdapter(MgnlContext.getJCRSession(workspace).getRootNode());
325             } catch (RepositoryException e) {
326                 return null;
327             }
328         }
329     }
330 
331     @Override
332     protected void onCancel () {
333         if (callback != null) {
334             callback.onMoveCancelled();
335         }
336     }
337 }