View Javadoc
1   /**
2    * This file Copyright (c) 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.admincentral;
35  
36  import info.magnolia.event.EventBus;
37  import info.magnolia.icons.MagnoliaAppIcon;
38  import info.magnolia.icons.MagnoliaIcons;
39  import info.magnolia.ui.api.app.AppView;
40  import info.magnolia.ui.api.event.AdmincentralEventBus;
41  import info.magnolia.ui.api.shell.CloseAppEvent;
42  import info.magnolia.ui.api.view.View;
43  import info.magnolia.ui.vaadin.extension.CloseIcon;
44  
45  import javax.inject.Named;
46  
47  import org.apache.commons.lang3.StringUtils;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  import com.google.inject.Inject;
52  import com.vaadin.server.KeyMapper;
53  import com.vaadin.server.Page;
54  import com.vaadin.server.ThemeResource;
55  import com.vaadin.shared.Registration;
56  import com.vaadin.ui.Component;
57  import com.vaadin.ui.Label;
58  import com.vaadin.ui.TabSheet;
59  import com.vaadin.ui.themes.ValoTheme;
60  
61  /**
62   * The Admincentral app view consists of a plain Vaadin TabSheet component, where each sub-app turns into a new tab..
63   *
64   * @see info.magnolia.ui.api.app.AppView
65   */
66  public class AdmincentralAppView implements AppView {
67      private static final Logger log = LoggerFactory.getLogger(AdmincentralAppView.class);
68  
69      // TODO candidate for extraction to ResurfaceTheme constants
70      private static final String TABSHEET_APP = "app";
71  
72      private final TabSheet tabsheet;
73  
74      private Listener listener;
75      private KeyMapper<TabSheet.Tab> mapper = new KeyMapper<>();
76  
77      @Inject
78      public AdmincentralAppView(@Named(AdmincentralEventBus.NAME) EventBus eventBus) {
79          TabSheet tabsheet = new TabSheet();
80          tabsheet.setSizeFull();
81          tabsheet.addStyleNames(ValoTheme.TABSHEET_FRAMED, TABSHEET_APP);
82  
83          Registration registration = tabsheet.addSelectedTabChangeListener(event -> {
84              if (event.isUserOriginated()) {
85                  Component selectedTabContent = event.getTabSheet().getSelectedTab();
86                  String instanceId = mapper.key(tabsheet.getTab(selectedTabContent));
87                  log.debug("FOCUS " + instanceId);
88                  listener.onFocus(instanceId);
89              }
90          });
91  
92          tabsheet.setCloseHandler((ts, tabContent) -> {
93              TabSheet.Tab tab = ts.getTab(tabContent);
94              String instanceId = mapper.key(tab);
95              log.debug("CLOSE " + instanceId);
96  
97              // Move to the main tab if active tab is about to be closed
98              if (instanceId.equals(getActiveSubAppView())) {
99                  tabsheet.setSelectedTab(tabsheet.getTab(0));
100             }
101 
102             // if close handler was set, then tabsheet should remove its tab manually
103             ts.removeTab(tab);
104             listener.onClose(instanceId);
105         });
106 
107         this.tabsheet = tabsheet;
108         CloseIcon.extend(this.tabsheet, () -> {
109             // remove listener when closing an app
110             // to prevent multiple location change events to be fired by the corresponding sub apps
111             // firing those events for each sub app does not make sense because we're closing whole app
112             // it is enough to just destroy sub app views and call location changed event only for the parent app
113             registration.remove();
114             eventBus.fireEvent(new CloseAppEvent());
115         });
116     }
117 
118     @Override
119     public String addSubAppView(View view, String caption, boolean closable) {
120         return addSubAppView(view, caption, "", closable);
121     }
122 
123     @Override
124     public String addSubAppView(View view, String caption, String icon, boolean closable) {
125         if (view == null) {
126             view = () -> new Label("No sub-app view to display");
127         }
128 
129         TabSheet.Tab tab = tabsheet.addTab(view.asVaadinComponent(), caption);
130         MagnoliaIcons.forCssClass(icon).ifPresent(tab::setIcon);
131 
132         tab.setClosable(closable);
133         tabsheet.setSelectedTab(tab);
134 
135         /* TODO check what for and evolve AppView contract */
136         return mapper.key(tab);
137     }
138 
139     @Override
140     public void setActiveSubAppView(String instanceId) {
141         tabsheet.setSelectedTab(mapper.get(instanceId));
142     }
143 
144     @Override
145     public String getActiveSubAppView() {
146         Component selectedTabContent = tabsheet.getSelectedTab();
147         return mapper.key(tabsheet.getTab(selectedTabContent));
148     }
149 
150     @Override
151     public void updateCaption(String instanceId, String caption) {
152         TabSheet.Tab tab = mapper.get(instanceId);
153         tab.setCaption(caption);
154     }
155 
156     @Override
157     public void setTheme(String themeName) {
158         String stylename = String.format("app-%s", themeName);
159         final String themeUrl = String.format("../%s/styles.css", themeName);
160 
161         final Component vaadinComponent = asVaadinComponent();
162         vaadinComponent.addStyleName(stylename);
163         final ThemeResource res = new ThemeResource(themeUrl);
164 
165         if (vaadinComponent.isAttached()) {
166             Page.getCurrent().getStyles().add(res);
167         } else {
168             vaadinComponent.addAttachListener(event -> Page.getCurrent().getStyles().add(res));
169         }
170     }
171 
172     @Override
173     public void setListener(Listener listener) {
174         this.listener = listener;
175     }
176 
177     @Override
178     public View getSubAppViewContainer(String instanceId) {
179         TabSheet.Tab tab = mapper.get(instanceId);
180         return tab::getComponent;
181     }
182 
183     @Override
184     public void closeSubAppView(String instanceId) {
185         TabSheet.Tab tab = mapper.get(instanceId);
186         if (tab != null) {
187             tabsheet.removeTab(tab);
188         }
189     }
190 
191     @Override
192     public void setAppLogo(String logo) {
193         TabSheet.Tab mainTab = tabsheet.getTab(0);
194         MagnoliaAppIcon.forName(logo)
195                 .or(MagnoliaIcons::forCssClass)
196                 .or(MagnoliaIcons.APP)
197                 .ifPresent(mainTab::setIcon);
198     }
199 
200     @Override
201     public void setAppLogo(String logo, String bgcolor) {
202         setAppLogo(logo);
203     }
204 
205     // TODO Deprecate?
206     @Override
207     public void setAppName(String appName) {
208         TabSheet.Tab tab = tabsheet.getTab(0);
209         if (tab != null && StringUtils.isEmpty(tab.getCaption())) {
210             tab.setCaption(appName);
211         }
212     }
213 
214     @Override
215     public void setTabCaptionsAsHtml() {
216         tabsheet.setTabCaptionsAsHtml(true);
217     }
218 
219     @Override
220     public TabSheet asVaadinComponent() {
221         return tabsheet;
222     }
223 }