View Javadoc
1   /**
2    * This file Copyright (c) 2012-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.contentapp.detail;
35  
36  import info.magnolia.event.EventBus;
37  import info.magnolia.i18nsystem.SimpleTranslator;
38  import info.magnolia.ui.api.app.SubAppContext;
39  import info.magnolia.ui.api.event.AdmincentralEventBus;
40  import info.magnolia.ui.api.event.ContentChangedEvent;
41  import info.magnolia.ui.api.location.Location;
42  import info.magnolia.ui.api.view.View;
43  import info.magnolia.ui.contentapp.ContentSubAppView;
44  import info.magnolia.ui.framework.app.BaseSubApp;
45  import info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector;
46  import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeItemId;
47  
48  import javax.inject.Inject;
49  import javax.inject.Named;
50  
51  import org.apache.commons.lang3.StringUtils;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  /**
56   * Base implementation of an item subApp. Provides sensible implementation for
57   * services shared by all item subApps. Implementers of this class represent a
58   * tab for viewing and editing items typically opened from an {@link info.magnolia.ui.contentapp.browser.BrowserSubApp}. Subclasses can
59   * augment the default behavior and perform additional tasks by overriding the
60   * following methods:
61   * <ul>
62   * <li>{@link #onSubAppStart()}
63   * <li>{@link #locationChanged(Location)}
64   * </ul>
65   * Currently lacking listeners for {@link info.magnolia.ui.api.event.ContentChangedEvent}. Currently
66   * lacking handling of locationChanged. Related to MGNLUI-154
67   *
68   * @see DetailEditorPresenter
69   * @see info.magnolia.ui.contentapp.ContentSubAppView
70   * @see DetailLocation
71   */
72  public class DetailSubApp extends BaseSubApp<ContentSubAppView> {
73  
74      private static final Logger log = LoggerFactory.getLogger(DetailSubApp.class);
75  
76      private final DetailEditorPresenter presenter;
77      private final EventBus adminCentralEventBus;
78      private final SimpleTranslator i18n;
79  
80      private Object itemId;
81  
82      private String caption;
83  
84      private ContentConnector contentConnector;
85  
86      @Inject
87      protected DetailSubApp(final SubAppContext subAppContext, final ContentSubAppView view, @Named(AdmincentralEventBus.NAME) EventBus adminCentralEventBus,
88              DetailEditorPresenter presenter, SimpleTranslator i18n, ContentConnector contentConnector) {
89          super(subAppContext, view);
90  
91          this.adminCentralEventBus = adminCentralEventBus;
92          this.presenter = presenter;
93          this.i18n = i18n;
94          this.contentConnector = contentConnector;
95          bindHandlers();
96      }
97  
98      /**
99       * Performs some routine tasks needed by all item subApps before the view is displayed.
100      * The tasks are:
101      * <ul>
102      * <li>setting the current location
103      * <li>setting the presenter's view
104      * <li>calling {@link #onSubAppStart()} a hook-up method subclasses can override to perform additional work.
105      * </ul>
106      */
107     @Override
108     public ContentSubAppView start(final Location location) {
109         DetailLocation detailLocation = DetailLocation.wrap(location);
110         super.start(detailLocation);
111         // set caption
112         setCaption(detailLocation);
113         this.itemId = contentConnector.getItemIdByUrlFragment(detailLocation.getNodePath());
114 
115         View view;
116         if (detailLocation.hasVersion()) {
117             view = presenter.start(detailLocation.getNodePath(), detailLocation.getViewType(), contentConnector, detailLocation.getVersion());
118         } else {
119             view = presenter.start(detailLocation.getNodePath(), detailLocation.getViewType(), contentConnector);
120         }
121         getView().setContentView(view);
122         return getView();
123     }
124 
125     /**
126      * Wraps the current DefaultLocation in a ItemLocation. Providing getter and setters for used parameters.
127      */
128     @Override
129     public DetailLocation getCurrentLocation() {
130         return DetailLocation.wrap(super.getCurrentLocation());
131     }
132 
133     @Override
134     public boolean supportsLocation(Location location) {
135         DetailLocation itemLocation = DetailLocation.wrap(location);
136         String currentPath = getCurrentLocation().getNodePath();
137         return currentPath.equals(itemLocation.getNodePath());
138     }
139 
140     /**
141      * On location change, reload the view and tab caption.
142      */
143     @Override
144     public void locationChanged(Location location) {
145         DetailLocation detailLocation = DetailLocation.wrap(location);
146         if (!detailLocation.equals(getCurrentLocation())) {
147             setCaption(detailLocation);
148             View view = presenter.update(detailLocation);
149             getView().setContentView(view);
150         }
151     }
152 
153     @Override
154     public String getCaption() {
155         return caption;
156     }
157 
158     private void bindHandlers() {
159         adminCentralEventBus.addHandler(ContentChangedEvent.class, new ContentChangedEvent.Handler() {
160 
161             @Override
162             public void onContentChanged(ContentChangedEvent event) {
163                 // See if workspaces match
164                 if (contentConnector.canHandleItem(event.getItemId())) {
165                     // New item
166                     if (itemId == null) {
167                         // Check if parent is still existing, close supApp if it doesn't
168                         String currentNodePath = getCurrentLocation().getNodePath();
169 
170                         // resolve parent, removing trailing slash except for root
171                         int splitIndex = currentNodePath.lastIndexOf("/");
172                         if (splitIndex == 0) {
173                             splitIndex = 1;
174                         }
175                         String parentNodePath = currentNodePath.substring(0, splitIndex);
176                         Object parentItemId = contentConnector.getItemIdByUrlFragment(parentNodePath);
177                         if (!contentConnector.canHandleItem(parentItemId)) {
178                             getSubAppContext().close();
179                         }
180                         // Editing existing item
181                     } else {
182                         // Item (or parent) was deleted: close subApp
183                         if (!contentConnector.canHandleItem(itemId)) {
184                             getSubAppContext().close();
185                         }
186                         // Item still exists: update location if necessary
187                         else {
188                             String currentNodePath = getCurrentLocation().getNodePath();
189                             String itemPath = contentConnector.getItemUrlFragment(itemId);
190                             if (!currentNodePath.equals(itemPath)) {
191                                 DetailLocation location = DetailLocation.wrap(getSubAppContext().getLocation());
192                                 location.updateNodePath(itemPath);
193                                 // Update location
194                                 getSubAppContext().setLocation(location);
195                                 // Update Caption
196                                 setCaption(location);
197                             }
198                         }
199                     }
200                 }
201             }
202         });
203     }
204 
205     /**
206      * Set the Tab caption.
207      * If a version is part of the {@link DetailLocation}, add this version information to the Tab caption.
208      */
209     protected void setCaption(DetailLocation location) {
210         String caption = getBaseCaption(location);
211         // Set version information
212         if (StringUtils.isNotBlank(location.getVersion())) {
213             caption = i18n.translate("subapp.versioned_page", caption, location.getVersion() );
214         }
215         this.caption = caption;
216     }
217 
218     @Override
219     public String getIcon(Location location) {
220         if (itemId instanceof JcrNewNodeItemId || !contentConnector.canHandleItem(itemId)) {
221             return DetailView.ViewType.ADD.getIcon();
222         }
223 
224         DetailLocation detailLocation = DetailLocation.wrap(location);
225         return detailLocation.getViewType().getIcon();
226     }
227 
228     /**
229      * Create the base caption string.
230      * Default is the item path.
231      */
232     protected String getBaseCaption(DetailLocation location) {
233         return location.getNodePath();
234     }
235 
236 }