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