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.poc;
35  
36  import static com.vaadin.server.Sizeable.Unit.PERCENTAGE;
37  
38  import info.magnolia.icons.MagnoliaIcons;
39  import info.magnolia.poc.task.MagnoliaTask;
40  import info.magnolia.poc.task.MagnoliaTaskService;
41  import info.magnolia.poc.task.TaskStatusRenderer;
42  import info.magnolia.ui.vaadin.actionbar.Actionbar;
43  import info.magnolia.ui.vaadin.dialog.BaseDialog;
44  import info.magnolia.ui.vaadin.gwt.client.actionbar.shared.ActionbarItem;
45  
46  import java.util.Arrays;
47  import java.util.List;
48  
49  import javax.servlet.annotation.WebServlet;
50  
51  import org.jsoup.Jsoup;
52  import org.jsoup.safety.Whitelist;
53  
54  import com.vaadin.annotations.Theme;
55  import com.vaadin.annotations.Title;
56  import com.vaadin.annotations.VaadinServletConfiguration;
57  import com.vaadin.annotations.Widgetset;
58  import com.vaadin.data.provider.ListDataProvider;
59  import com.vaadin.server.ThemeResource;
60  import com.vaadin.server.VaadinRequest;
61  import com.vaadin.server.VaadinServlet;
62  import com.vaadin.shared.ui.ContentMode;
63  import com.vaadin.ui.Button;
64  import com.vaadin.ui.ComboBox;
65  import com.vaadin.ui.Component;
66  import com.vaadin.ui.CssLayout;
67  import com.vaadin.ui.FormLayout;
68  import com.vaadin.ui.Grid;
69  import com.vaadin.ui.HorizontalLayout;
70  import com.vaadin.ui.Image;
71  import com.vaadin.ui.Label;
72  import com.vaadin.ui.Panel;
73  import com.vaadin.ui.TabSheet;
74  import com.vaadin.ui.TextArea;
75  import com.vaadin.ui.TextField;
76  import com.vaadin.ui.UI;
77  import com.vaadin.ui.VerticalLayout;
78  import com.vaadin.ui.components.grid.ItemClickListener;
79  import com.vaadin.ui.renderers.HtmlRenderer;
80  
81  @Theme("poctheme")
82  @Title("Magnolia 6 Resurface")
83  @Widgetset("info.magnolia.poc.Widgetset")
84  public class TasksBrowser extends UI {
85  
86      private HorizontalLayout header = new HorizontalLayout();
87      private CssLayout viewPort = new CssLayout();
88      private TabSheet tabSheetComponent;
89  
90      @Override
91      protected void init(VaadinRequest request) {
92          header.setStyleName("header");
93          header.setSpacing(false);
94  
95          // Create logo
96          CssLayout logoWrapper = createLogoComponent();
97  
98          // Create finder bar
99          CssLayout finderBarContainer = new CssLayout();
100         finderBarContainer.setStyleName("periscope-wrapper");
101 
102         Component finderBar = createFinderBar();
103 
104         finderBarContainer.addComponent(finderBar);
105 
106         // Create tasks indicator
107         CssLayout tasksWrapper = new CssLayout();
108         tasksWrapper.setStyleName("tasks-wrapper");
109         Component taskIndicator = createIndicatorComponent("Tasks", "23", "green");
110         tasksWrapper.addComponent(taskIndicator);
111 
112         // Create message indicator
113         CssLayout messageWrapper = new CssLayout();
114         messageWrapper.setStyleName("messages-wrapper");
115         Component messageIndicator = createIndicatorComponent("Messages", "02", "green");
116         messageWrapper.addComponent(messageIndicator);
117 
118         // Create profile component
119         Component profileWrapper = createProfileComponent();
120         profileWrapper.setStyleName("profile-wrapper");
121 
122         // Create banner component
123         Component bannerWrapper = createBannerComponent(tasksWrapper);
124         bannerWrapper.setStyleName("banner-wrapper");
125         bannerWrapper.setVisible(false);
126 
127         header.addComponents(logoWrapper, finderBarContainer, tasksWrapper, messageWrapper, profileWrapper);
128 
129         viewPort.setStyleName("viewport");
130         viewPort.setSizeFull();
131 
132         Component tabSheet = createTabSheetComponent();
133         viewPort.addComponents(bannerWrapper, tabSheet);
134 
135 
136         final VerticalLayout layout = new VerticalLayout();
137         layout.setStyleName("main-wrapper");
138         layout.setSpacing(false);
139         layout.addComponents(header, viewPort);
140         layout.setMargin(false);
141         layout.setExpandRatio(header, 0f);
142         layout.setExpandRatio(viewPort, 1f);
143         layout.setSizeFull();
144 
145         setContent(layout);
146     }
147 
148     private CssLayout createLogoComponent() {
149         CssLayout logoWrapper = new CssLayout();
150         logoWrapper.setStyleName("logo-wrapper");
151 
152         ThemeResource resource = new ThemeResource("img/logo-magnolia.svg");
153         // Show the image in the application
154         Image image = new Image("Magnolia logo", resource);
155         image.addStyleName("header-component");
156         logoWrapper.addComponent(image);
157         return logoWrapper;
158     }
159 
160     private HorizontalLayout createProfileComponent() {
161         HorizontalLayout profileWrapper = new HorizontalLayout();
162         profileWrapper.setSpacing(false);
163         profileWrapper.setMargin(false);
164 
165         Label avatar = new Label();
166         avatar.setContentMode(ContentMode.HTML);
167         avatar.addStyleName("header-component");
168         avatar.setValue("<span class='indicator number icon-user-role'></span>");
169         profileWrapper.addComponent(avatar);
170 
171         Component profileComponent = createHeaderComponent("public-01", "INT");
172         profileWrapper.addComponent(profileComponent);
173         return profileWrapper;
174     }
175 
176     private Component createBannerComponent(CssLayout tasksWrapper) {
177         CssLayout bannerWrapper = new CssLayout();
178         bannerWrapper.setWidth(100, Unit.PERCENTAGE);
179         bannerWrapper.setHeight(80, Unit.PIXELS);
180 
181         VerticalLayout banner = new VerticalLayout();
182         banner.addStyleNames("banner", "info");
183         banner.setSpacing(false);
184         banner.setMargin(false);
185 
186         Label infoLabel = new Label("Info");
187         infoLabel.addStyleName("header");
188 
189         Label bodyLabel = new Label("Your license will expire in 15 days. Renew now.");
190         bodyLabel.addStyleName("body");
191 
192         Button closeButton = new Button();
193         closeButton.setCaptionAsHtml(true);
194         closeButton.setPrimaryStyleName("icon-close");
195         closeButton.addStyleName("close");
196         closeButton.addClickListener(e -> bannerWrapper.setVisible(false));
197 
198         banner.addComponents(infoLabel, bodyLabel, closeButton);
199         bannerWrapper.addComponent(banner);
200 
201         tasksWrapper.addLayoutClickListener(e -> {
202             if (!bannerWrapper.isVisible()) {
203                 bannerWrapper.setVisible(true);
204             }
205 
206             switch (banner.getStyleName()) {
207             case "banner info":
208                 banner.removeStyleName("info");
209                 banner.addStyleName("warning");
210                 infoLabel.setValue("Warning");
211                 bodyLabel.setValue("Your license will expire tomorrow. Magnolia will stop functioning if no valid license is found. Renew now.");
212                 break;
213             case "banner warning":
214                 banner.removeStyleName("warning");
215                 banner.addStyleName("error");
216                 infoLabel.setValue("Error");
217                 bodyLabel.setValue("The multipart stream ended unexpectedly. More.");
218                 break;
219             case "banner error":
220                 banner.removeStyleName("error");
221                 banner.addStyleName("info");
222                 infoLabel.setValue("Info");
223                 bodyLabel.setValue("Your license will expire in 15 days. Renew now.");
224                 break;
225             }
226         });
227 
228         return bannerWrapper;
229     }
230 
231     private HorizontalLayout createFinderBar() {
232         HorizontalLayout finderBar = new HorizontalLayout();
233         finderBar.addStyleName("header-component");
234         finderBar.setSpacing(false);
235         finderBar.setMargin(false);
236         finderBar.setSizeFull();
237         Button shellButton = new Button();
238         shellButton.addStyleName("btn-shell icon-appslauncher btn-appslauncher");
239         finderBar.addComponent(shellButton);
240         finderBar.setExpandRatio(shellButton, 0f);
241 
242         CssLayout searchField = new CssLayout();
243         searchField.setStyleName("search-field");
244         TextField textField = new TextField();
245         textField.addStyleNames("search-textfield", "heading-2");
246         textField.setPlaceholder("Type to find");
247         searchField.addComponent(textField);
248         Label searchIcon = new Label();
249         searchIcon.setStyleName("icon-search");
250         searchField.addComponent(searchIcon);
251 
252         finderBar.addComponent(searchField);
253         finderBar.setExpandRatio(searchField, 1f);
254 
255 
256         Button voiceButton = new Button();
257         voiceButton.addStyleName("btn-shell icon-target-app btn-voice");
258         finderBar.addComponent(voiceButton);
259         finderBar.setExpandRatio(voiceButton, 0f);
260 
261         return finderBar;
262     }
263 
264     private Component createIndicatorComponent(String label, String value, String status) {
265         return createHeaderComponent(label, value + "<span class='status icon-status-" + status + " color-" + status + "'></span>");
266     }
267 
268     private Component createHeaderComponent(String label, String value) {
269 
270         VerticalLayout tasksComponent = new VerticalLayout();
271         tasksComponent.setSpacing(false);
272         tasksComponent.setMargin(false);
273         tasksComponent.setStyleName("header-component");
274         Label taskNumber = new Label();
275         taskNumber.addStyleName("indicator heading-2");
276         taskNumber.setValue(value);
277         taskNumber.setContentMode(ContentMode.HTML);
278         Label taskLabel = new Label(label);
279         taskLabel.addStyleNames("text-tiny");
280         tasksComponent.addComponents(taskNumber, taskLabel);
281         tasksComponent.addLayoutClickListener(event -> onTasksCounterClick(event));
282 
283         return tasksComponent;
284     }
285 
286     private void onTasksCounterClick(Event event) {
287         int number = (int) (Math.random() * 100);
288         ((Label) ((VerticalLayout) event.getComponent()).getComponent(0)).setValue(String.valueOf(number));
289         tabSheetComponent.getTab(0).setCaption("<span style='word-spacing: 7px;'>Tasks | <a href='#'>New " + number + "</a></span>");
290         ((Label) ((HorizontalLayout) ((VerticalLayout) ((HorizontalLayout) ((VerticalLayout) tabSheetComponent.getTab(0).getComponent()).getComponent(0)).getComponent(0)).getComponent(0)).getComponent(0)).setValue("Tasks | <a href='#'>New (" + number + ")</a>");
291     }
292 
293     private Component createTabSheetComponent() {
294         tabSheetComponent = new TabSheet();
295         tabSheetComponent.addStyleName("apps");
296         tabSheetComponent.setSizeFull();
297         tabSheetComponent.setTabCaptionsAsHtml(true);
298         tabSheetComponent.addTab(createBrowserAppComponent("Tasks | <a href='#'>New (23)</a>"), "<span style='word-spacing: 7px;'>Tasks | <a href='#'>New 23</a></span>", MagnoliaIcons.SELECT);
299         tabSheetComponent.addTab(createBrowserAppComponent("Tasks | Assigned"), "Assigned");
300         tabSheetComponent.addTab(createBrowserAppComponent("Tasks | Resolved"), "Resolved");
301         tabSheetComponent.addTab(createBrowserAppComponent("Tasks | Failed"), "Failed");
302         tabSheetComponent.addTab(createBrowserAppComponent("Tasks | Scheduled"), "Scheduled");
303         tabSheetComponent.addTab(createBrowserAppComponent("Tasks | All tasks"), "All tasks");
304         tabSheetComponent.setSizeFull();
305         tabSheetComponent.setSelectedTab(1);
306         return tabSheetComponent;
307     }
308 
309     private Component createBrowserAppComponent(String label) {
310         VerticalLayout browser = new VerticalLayout();
311         browser.addStyleName("browser");
312         browser.setSizeFull();
313         browser.setMargin(false);
314 
315         VerticalLayout workbench = new VerticalLayout();
316         workbench.addStyleName("workbench");
317         workbench.setSizeFull();
318         workbench.setMargin(false);
319         Component toolBar = buildTabNameBar(label);
320         Component contentView = buildContentView();
321         Component statusBar = buildStatusBar();
322         workbench.addComponents(toolBar, contentView, statusBar);
323         workbench.setExpandRatio(toolBar, 0f);
324         workbench.setExpandRatio(contentView, 1f);
325 
326         Component actionbar = buildActionBar();
327 
328         HorizontalLayout workbenchWrapper = new HorizontalLayout();
329         workbenchWrapper.setSizeFull();
330         workbenchWrapper.addComponents(workbench, actionbar);
331         workbenchWrapper.setExpandRatio(workbench, 1f);
332         workbenchWrapper.setExpandRatio(actionbar, 0f);
333 
334         // TODO Status bar is now moved out of workbench
335         browser.addComponents(workbenchWrapper, statusBar);
336         browser.setExpandRatio(workbenchWrapper, 1f);
337         browser.setExpandRatio(statusBar, 0f);
338 
339 
340         return browser;
341     }
342 
343     private Component buildTabNameBar(String label) {
344         HorizontalLayout tabNameBar = new HorizontalLayout();
345         tabNameBar.setWidth(100, PERCENTAGE);
346         tabNameBar.addStyleName("tabNameBar");
347         tabNameBar.setSpacing(true);
348 
349         Label tabName = new Label(label, ContentMode.HTML);
350 
351         tabNameBar.addComponents(tabName);
352 
353         return tabNameBar;
354     }
355 
356     private Component buildContentView() {
357         Panel keyboardPanel = new Panel();
358         keyboardPanel.setSizeFull();
359         keyboardPanel.addStyleName("keyboard-panel");
360 
361         Grid<MagnoliaTask> grid = new Grid<>();
362 
363         grid.setSizeFull();
364         grid.setBodyRowHeight(75);
365 
366         grid.addColumn(task -> task.getStatus().name(), new TaskStatusRenderer()).setCaption("New").setExpandRatio(1);
367         grid.addColumn(task -> " <div style='height:59px; padding-top:16px'>" + MagnoliaIcons.WORK_ITEM.getHtml() + "</div> <div style='height:59px; padding-top:16px; padding-left:20px'><span style='line-height:normal; padding-top:16px'>" + Jsoup.clean(task.getName(), Whitelist.simpleText()) + "</span><br/><span style='font-size:12px; line-height:normal; padding-top:8px'>" + Jsoup.clean(task.getDescription(), Whitelist.simpleText()) + "</span></div>", new HtmlRenderer()).setCaption("Task").setExpandRatio(5);
368         grid.addColumn(MagnoliaTask::getStatus).setCaption("Status").setExpandRatio(2);
369         grid.addColumn(MagnoliaTask::getSender).setCaption("Sender").setExpandRatio(2);
370         grid.addColumn(task -> MagnoliaIcons.USER_GROUP.getHtml() + " <span style='height:59px; padding-top:16px; padding-left:8px'>" + Jsoup.clean(task.getSendTo(), Whitelist.simpleText()) + "</span>", new HtmlRenderer()).setCaption("Sent to").setExpandRatio(2);
371         grid.addColumn(MagnoliaTask::getAssigned).setCaption("Assigned").setExpandRatio(2);
372         grid.addColumn(MagnoliaTask::getLastChange).setCaption("Last changed").setExpandRatio(2);
373 
374         grid.setSelectionMode(Grid.SelectionMode.MULTI);
375 
376         grid.addItemClickListener((ItemClickListener<MagnoliaTask>) event -> {
377             if (event.getSource().getSelectionModel().isSelected(event.getItem())) {
378                 event.getSource().getSelectionModel().deselect(event.getItem());
379             } else {
380                 event.getSource().getSelectionModel().select(event.getItem());
381             }
382         });
383 
384         // fetch list of items from service and assign it to Grid
385         // in this case it's a list of tasks
386         MagnoliaTaskService service = MagnoliaTaskService.getInstance();
387         List<MagnoliaTask> magnoliaTasks = service.findAll();
388         grid.setItems(magnoliaTasks);
389 
390         keyboardPanel.setContent(grid);
391 
392         return keyboardPanel;
393     }
394 
395     private Component buildStatusBar() {
396         HorizontalLayout statusBar = new HorizontalLayout();
397         statusBar.addStyleName("statusbar");
398         statusBar.setWidth(100, PERCENTAGE);
399 
400         Label label = new Label("Profile images / Directors / Managing directors /");
401         label.setWidth(100, PERCENTAGE);
402 
403         statusBar.addComponent(label);
404 
405         return statusBar;
406     }
407 
408     private Component buildActionBar() {
409         Actionbar actionbar = new Actionbar();
410         actionbar.addSection("main", "Actions");
411         actionbar.addAction(new ActionbarItem("addAction", "Add action", "icon-add-item", "addActions"), "main");
412         actionbar.addAction(new ActionbarItem("deleteAction", "Delete action", "icon-delete", "addActions"), "main");
413         actionbar.addAction(new ActionbarItem("editAction", "Edit action", "icon-edit", "editActions"), "main");
414         CssLayout wrapper = new CssLayout();
415         wrapper.addComponent(actionbar);
416         wrapper.setWidthUndefined();
417         wrapper.setHeight(100, Unit.PERCENTAGE);
418         wrapper.addStyleName("actionbar");
419         return wrapper;
420     }
421 
422     private Component createDetailAppWithBaseDialog() {
423         HorizontalLayout detail = new HorizontalLayout();
424         detail.addStyleName("detail basedialog");
425         detail.setSizeFull();
426 
427         CssLayout detailView = new CssLayout();
428         detailView.addStyleName("detailview");
429         detailView.setSizeFull();
430         detail.addComponent(detailView);
431 
432         BaseDialog baseDialog = new BaseDialog();
433         baseDialog.setCaption("Asset");
434         baseDialog.setDialogDescription("This is description");
435         baseDialog.setDescriptionVisibility(true);
436         baseDialog.setSizeFull();
437 
438         baseDialog.setContent(createDetailForm());
439 
440         CssLayout footer = new CssLayout();
441         footer.setWidth(100, PERCENTAGE);
442         footer.addComponents(createActions());
443         baseDialog.setFooterToolbar(footer);
444 
445         Panel panel = new Panel();
446         panel.setSizeFull();
447         panel.setContent(baseDialog);
448 
449         detailView.addComponent(panel);
450 
451         return detail;
452     }
453 
454     private Component createDetailAppWithVerticalLayout() {
455         VerticalLayout detailLayout = new VerticalLayout();
456         detailLayout.addStyleName("detail");
457         detailLayout.setSizeFull();
458         detailLayout.setMargin(false);
459         detailLayout.setSpacing(false);
460 
461         Label title = new Label();
462         title.addStyleName("heading-1 title");
463         title.setValue("Asset");
464 
465         CssLayout header = new CssLayout();
466         header.addStyleName("detail-header");
467         header.addComponent(title);
468 
469         Component detailForm = createDetailForm();
470         detailForm.addStyleName("detail-content");
471 
472         CssLayout footer = new CssLayout();
473         footer.addStyleName("detail-footer");
474         footer.setWidth(100, PERCENTAGE);
475         footer.addComponents(createActions());
476 
477         detailLayout.addComponents(header, detailForm, footer);
478         detailLayout.setExpandRatio(header, 0f);
479         detailLayout.setExpandRatio(detailForm, 1f);
480         detailLayout.setExpandRatio(footer, 0f);
481 
482         return detailLayout;
483     }
484 
485     private Component createDetailForm() {
486         TabSheet tabSheetComponent = new TabSheet();
487         tabSheetComponent.setSizeFull();
488 
489         tabSheetComponent.addTab(createTab1(), "Meta Data");
490         tabSheetComponent.addTab(createTab2(), "Article Info");
491         tabSheetComponent.addTab(createTab3(), "Output channels");
492 
493         return tabSheetComponent;
494     }
495 
496     private FormLayout createTab1() {
497         FormLayout form = new FormLayout();
498         form.setMargin(true);
499         form.setSizeFull();
500 
501         TextField textField = new TextField("Asset name");
502 
503         TextField textFieldHover = new TextField("File name");
504         textFieldHover.setDescription("This is file name");
505 
506         TextField textFieldFocus = new TextField("Location");
507         textFieldFocus.setValue("Basel ");
508 
509         form.addComponents(textField,
510                 textFieldHover,
511                 textFieldFocus);
512 
513         form.iterator().forEachRemaining(
514                 component -> component.setWidth(100, PERCENTAGE)
515         );
516 
517         return form;
518     }
519 
520     private FormLayout createTab2() {
521         FormLayout form = new FormLayout();
522         form.setMargin(true);
523         form.setSizeFull();
524 
525         TextField disabledTextField = new TextField("Author");
526         disabledTextField.setEnabled(false);
527 
528         TextField textFieldValidation = new TextField("Birthday");
529 
530         form.addComponents(disabledTextField,
531                 textFieldValidation);
532 
533         form.iterator().forEachRemaining(
534                 component -> component.setWidth(100, PERCENTAGE)
535         );
536 
537         return form;
538     }
539 
540     private FormLayout createTab3() {
541         FormLayout form = new FormLayout();
542         form.setMargin(true);
543         form.setSizeFull();
544 
545         TextField textFieldWithPlaceHolder = new TextField("Title");
546         textFieldWithPlaceHolder.setPlaceholder("Add title here...");
547 
548         TextArea expandable = new TextArea("Description");
549         expandable.setRows(1);
550 
551         form.addComponents(textFieldWithPlaceHolder,
552                 expandable);
553 
554         form.iterator().forEachRemaining(
555                 component -> component.setWidth(100, PERCENTAGE)
556         );
557 
558         return form;
559     }
560 
561     private Component[] createActions() {
562         ComboBox<String> languageSelector = new ComboBox<>();
563         ListDataProvider<String> dataProvider = new ListDataProvider<>(Arrays.asList("English", "Việt Nam"));
564         languageSelector.setEmptySelectionAllowed(false);
565         languageSelector.setPlaceholder("Select...");
566         languageSelector.addStyleName("secondary-actions");
567         languageSelector.setDataProvider(dataProvider);
568 
569         Button saveButton = new Button("Save changes");
570         saveButton.addStyleName("commit primary-button primary-actions");
571 
572         Button downloadButton = new Button("Download");
573         downloadButton.addStyleName("download secondary-button primary-actions");
574 
575         Button cancelButton = new Button("Cancel");
576         cancelButton.addStyleName("cancel secondary-button primary-actions");
577 
578         return new Component[]{languageSelector, saveButton, downloadButton, cancelButton};
579     }
580 
581     @Override
582     public boolean equals(Object obj) {
583         return super.equals(obj);
584     }
585 
586     @Override
587     public int hashCode() {
588         return super.hashCode();
589     }
590 
591     @WebServlet(value = "/tasksbrowser/*", asyncSupported = true)
592     @VaadinServletConfiguration(productionMode = false, ui = TasksBrowser.class)
593     public static class Servlet extends VaadinServlet {
594     }
595 }