View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 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.workbench;
35  
36  import info.magnolia.event.EventBus;
37  import info.magnolia.objectfactory.ComponentProvider;
38  import info.magnolia.ui.api.view.View;
39  import info.magnolia.ui.framework.ioc.AdmincentralFlavour;
40  import info.magnolia.ui.imageprovider.definition.ImageProviderDefinition;
41  import info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector;
42  import info.magnolia.ui.workbench.contenttool.ConfiguredContentToolDefinition;
43  import info.magnolia.ui.workbench.contenttool.ContentToolDefinition;
44  import info.magnolia.ui.workbench.contenttool.ContentToolPresenter;
45  import info.magnolia.ui.workbench.contenttool.search.SearchContentToolPresenter;
46  import info.magnolia.ui.workbench.definition.ContentPresenterDefinition;
47  import info.magnolia.ui.workbench.definition.WorkbenchDefinition;
48  import info.magnolia.ui.workbench.event.QueryStatementChangedEvent;
49  import info.magnolia.ui.workbench.event.SelectionChangedEvent;
50  import info.magnolia.ui.workbench.event.ViewTypeChangedEvent;
51  import info.magnolia.ui.workbench.list.ListPresenterDefinition;
52  import info.magnolia.ui.workbench.search.SearchPresenter;
53  import info.magnolia.ui.workbench.search.SearchPresenterDefinition;
54  import info.magnolia.ui.workbench.tree.TreePresenter;
55  
56  import java.util.ArrayList;
57  import java.util.HashSet;
58  import java.util.Iterator;
59  import java.util.LinkedHashMap;
60  import java.util.List;
61  import java.util.Map;
62  
63  import javax.inject.Inject;
64  
65  import org.apache.commons.lang3.StringUtils;
66  
67  /**
68   * The WorkbenchPresenter is responsible for creating, configuring and updating the workbench view, as well as handling its interaction.
69   */
70  public class WorkbenchPresenter implements WorkbenchView.Listener {
71  
72      private final WorkbenchView view;
73  
74      private final ComponentProvider componentProvider;
75  
76      private final Map<String, ContentPresenter> contentPresenters = new LinkedHashMap<String, ContentPresenter>();
77  
78      private ContentPresenter activePresenter;
79  
80      private final WorkbenchStatusBarPresenter statusBarPresenter;
81  
82      private WorkbenchDefinition workbenchDefinition;
83  
84      private EventBus eventBus;
85  
86      protected ContentConnector contentConnector;
87  
88      @Inject
89      public WorkbenchPresenter(WorkbenchView view, ComponentProvider componentProvider, WorkbenchStatusBarPresenter statusBarPresenter, ContentConnector contentConnector) {
90          this.view = view;
91          this.componentProvider = componentProvider;
92          this.statusBarPresenter = statusBarPresenter;
93          this.contentConnector = contentConnector;
94      }
95  
96      public WorkbenchView start(WorkbenchDefinition workbenchDefinition, ImageProviderDefinition imageProviderDefinition, EventBus eventBus) {
97          this.workbenchDefinition = workbenchDefinition;
98          this.eventBus = eventBus;
99  
100         sanityCheck(workbenchDefinition);
101 
102         // find default viewType
103         String defaultViewType = getDefaultViewType();
104 
105         // add content views
106         for (final ContentPresenterDefinition presenterDefinition : workbenchDefinition.getContentViews()) {
107             ContentPresenter presenter;
108             Class<? extends ContentPresenter> presenterClass = presenterDefinition.getImplementationClass();
109             if (presenterClass != null) {
110                 presenter = componentProvider.newInstance(presenterClass);
111                 contentPresenters.put(presenterDefinition.getViewType(), presenter);
112                 ContentView contentView = presenter.start(workbenchDefinition, eventBus, presenterDefinition.getViewType(), contentConnector);
113 
114                 // use new #addContentView (doesn't require the content-view definition anymore)
115                 if (view instanceof WorkbenchViewImpl) {
116                     ((WorkbenchViewImpl) view).addContentView(presenterDefinition.getViewType(), contentView, presenterDefinition.getIcon());
117                 } else {
118                     // will be deprecated
119                     view.addContentView(presenterDefinition.getViewType(), contentView, presenterDefinition);
120                 }
121 
122                 if (presenterDefinition.getViewType().equals(defaultViewType)) {
123                     activePresenter = presenter;
124                     setViewType(presenterDefinition.getViewType());
125                 }
126 
127                 if (presenter instanceof TreePresenter && workbenchDefinition.isDialogWorkbench()) {
128                     ((TreePresenter) presenter).disableDragAndDrop();
129                 }
130             } else {
131                 throw new RuntimeException("The provided view type [" + presenterDefinition.getViewType() + "] is not valid.");
132             }
133 
134         }
135 
136         // add content tools
137         final List<ContentToolDefinition> contentTools = this.workbenchDefinition.getContentTools();
138         for (ContentToolDefinition toolDefinition : contentTools) {
139             Class<? extends ContentToolPresenter> presenterClass = toolDefinition.getPresenterClass();
140             if (presenterClass != null) {
141                 ContentToolPresenter contentToolPresenter = componentProvider.newInstance(presenterClass, toolDefinition, eventBus, this);
142                 View contentToolView = contentToolPresenter.start();
143 
144                 if ((toolDefinition instanceof ConfiguredContentToolDefinition) && (view instanceof WorkbenchViewImpl)) {
145                     final ConfiguredContentToolDefinition configuredContentToolDefinition = (ConfiguredContentToolDefinition) toolDefinition;
146                     // the following methods should become the parts of WorkbenchView/ContentTool inetrfaces as a part of MGNLUI-3709
147                     ((WorkbenchViewImpl) view).addContentTool(contentToolView, configuredContentToolDefinition.getAlignment(), 0);
148                 }
149             }
150         }
151 
152         if (hasViewType(ListPresenterDefinition.VIEW_TYPE) && hasViewType(SearchPresenterDefinition.VIEW_TYPE)
153             && (AdmincentralFlavour.get().isM5() || workbenchDefinition.isDialogWorkbench())) {
154             addSearchContentTool();
155         }
156 
157         // add status bar
158         view.setStatusBarView(statusBarPresenter.start(eventBus, activePresenter));
159 
160         view.setMultiselect(!workbenchDefinition.isDialogWorkbench());
161 
162         view.setListener(this);
163         return view;
164     }
165 
166     /**
167      * Returns the default item id of this workbench.
168      * @deprecated since 5.4.3. Method isn't used anymore.
169      */
170     @Deprecated
171     public Object resolveWorkbenchRoot() {
172         return contentConnector.getDefaultItemId();
173     }
174 
175     protected void sanityCheck(WorkbenchDefinition workbenchDefinition) {
176         if (workbenchDefinition == null) {
177             throw new IllegalArgumentException("Failed to init a workbench: WorkbenchDefinition is null.");
178         } else if (workbenchDefinition.getContentViews() == null || workbenchDefinition.getContentViews().size() == 0) {
179             throw new IllegalArgumentException("Failed to init a workbench: No content-view is configured.");
180         }
181     }
182 
183     protected void addSearchContentTool() {
184         SearchContentToolPresenter searchPresenter = componentProvider.newInstance(SearchContentToolPresenter.class, this, eventBus);
185         View searchView = searchPresenter.start();
186         ((WorkbenchViewImpl) this.view).addContentTool(searchView, ContentToolDefinition.Alignment.LEFT, 0);
187     }
188 
189 
190     /**
191      * Search logic is now implemented in a corresponding tool presenter.
192      *
193      * @see SearchContentToolPresenter
194      */
195     @Override
196     public void onSearch(final String searchExpression) {
197         // deprecated, does nothing
198     }
199 
200     @Override
201     public void onViewTypeChanged(final String viewType) {
202         setViewType(viewType);
203         eventBus.fireEvent(new ViewTypeChangedEvent(viewType));
204     }
205 
206     @Override
207     public void onSearchQueryChange(String searchQuery) {
208         eventBus.fireEvent(new QueryStatementChangedEvent(searchQuery));
209     }
210 
211     private void setViewType(String viewType) {
212         ContentPresenter oldPresenter = activePresenter;
213         List<Object> itemIds = oldPresenter == null ? null : oldPresenter.getSelectedItemIds();
214 
215         activePresenter = contentPresenters.get(viewType);
216         activePresenter.refresh();
217         view.setViewType(viewType);
218 
219         statusBarPresenter.setActivePresenter(activePresenter);
220 
221         // make sure selection is kept when switching views
222         if (itemIds != null) {
223             select(itemIds);
224         }
225     }
226 
227     public List<Object> getSelectedIds() {
228         return activePresenter.getSelectedItemIds();
229     }
230 
231     public void expand(Object itemId) {
232         activePresenter.expand(itemId);
233     }
234 
235     public void select(Object itemId) {
236         List<Object> ids = new ArrayList<Object>(1);
237         ids.add(itemId);
238         select(ids);
239     }
240 
241     public void select(List<Object> itemIds) {
242         final List<Object> selectedIds = filterExistingItems(itemIds);
243         // restore selection
244         if (selectedIds.isEmpty()) {
245             Object workbenchRootItemId = contentConnector.getDefaultItemId();
246             if (workbenchRootItemId != null) {
247                 selectedIds.add(workbenchRootItemId);
248             }
249         }
250 
251         activePresenter.setSelectedItemIds(selectedIds);
252         activePresenter.select(selectedIds);
253         // Only send event if items are not empty (do exist)
254         eventBus.fireEvent(new SelectionChangedEvent(new HashSet<Object>(selectedIds)));
255     }
256 
257     protected List<Object> filterExistingItems(List<Object> itemIds) {
258         List<Object> filteredIds = new ArrayList<Object>();
259         Iterator<Object> it = itemIds.iterator();
260         while (it.hasNext()) {
261             Object itemId = it.next();
262             if (contentConnector.canHandleItem(itemId) && contentConnector.getItem(itemId) != null) {
263                 filteredIds.add(itemId);
264             }
265         }
266         return filteredIds;
267     }
268 
269     public void refresh() {
270         activePresenter.refresh();
271         statusBarPresenter.refresh();
272     }
273 
274     public String getDefaultViewType() {
275         for (ContentPresenterDefinition definition : workbenchDefinition.getContentViews()) {
276             if (definition.isActive()) {
277                 return definition.getViewType();
278             }
279         }
280         return workbenchDefinition.getContentViews().get(0).getViewType();
281     }
282 
283     public boolean hasViewType(String viewType) {
284         return contentPresenters.containsKey(viewType);
285     }
286 
287     public void resynch(final List<Object> itemIds, final String viewType, final String query) {
288         setViewType(viewType);
289         select(itemIds);
290 
291         if (SearchPresenterDefinition.VIEW_TYPE.equals(viewType)) {
292             doSearch(query);
293             // update search field and focus it
294             view.setSearchQuery(query);
295         }
296     }
297 
298     public void doSearch(String searchExpression) {
299         // firing new search forces search view as new view type
300         if (activePresenter != contentPresenters.get(SearchPresenterDefinition.VIEW_TYPE)) {
301             setViewType(SearchPresenterDefinition.VIEW_TYPE);
302         }
303         final SearchPresenter searchPresenter = (SearchPresenter) activePresenter;
304         if (StringUtils.isBlank(searchExpression)) {
305             searchPresenter.clear();
306         } else {
307             searchPresenter.search(searchExpression);
308         }
309     }
310 
311     public final ContentPresenter getActivePresenter() {
312         return activePresenter;
313     }
314 
315     protected final EventBus getEventBus() {
316         return this.eventBus;
317     }
318 
319     protected final WorkbenchDefinition getWorkbenchDefinition() {
320         return workbenchDefinition;
321     }
322 
323     public final ContentPresenter getContentPresenter(String viewType) {
324         return contentPresenters.get(viewType);
325     }
326 }