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.i18nsystem.SimpleTranslator;
37  import info.magnolia.ui.actionbar.ActionbarPresenter;
38  import info.magnolia.ui.actionbar.ActionbarView;
39  import info.magnolia.ui.api.action.ActionExecutionException;
40  import info.magnolia.ui.api.action.ActionExecutor;
41  import info.magnolia.ui.api.app.AppContext;
42  import info.magnolia.ui.api.app.SubAppContext;
43  import info.magnolia.ui.api.message.Message;
44  import info.magnolia.ui.api.message.MessageType;
45  import info.magnolia.ui.api.overlay.ConfirmationCallback;
46  import info.magnolia.ui.api.view.View;
47  import info.magnolia.ui.contentapp.definition.EditorDefinition;
48  import info.magnolia.ui.framework.app.stub.StubView;
49  import info.magnolia.ui.vaadin.dialog.BaseDialog;
50  import info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector;
51  import info.magnolia.ui.vaadin.integration.contentconnector.SupportsCreation;
52  import info.magnolia.ui.vaadin.integration.contentconnector.SupportsVersions;
53  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
54  
55  import java.util.HashMap;
56  
57  import javax.inject.Inject;
58  
59  import org.apache.commons.lang3.StringUtils;
60  import org.slf4j.Logger;
61  import org.slf4j.LoggerFactory;
62  
63  import com.vaadin.event.ShortcutAction.KeyCode;
64  import com.vaadin.event.ShortcutListener;
65  import com.vaadin.ui.Panel;
66  
67  /**
68   * Presenter for the workbench displayed in the {@link info.magnolia.ui.contentapp.detail.DetailSubApp}.
69   * Contains the {@link ActionbarPresenter} for handling action events and the {@link DetailPresenter} for displaying the actual item.
70   */
71  public class DetailEditorPresenter implements DetailEditorView.Listener, ActionbarPresenter.Listener {
72  
73      private static final Logger log = LoggerFactory.getLogger(DetailEditorPresenter.class);
74  
75      private final ActionExecutor actionExecutor;
76      private final AppContext appContext;
77      private final SubAppContext subAppContext;
78      private final DetailEditorView view;
79      private final DetailPresenter detailPresenter;
80      private final ActionbarPresenter actionbarPresenter;
81      private final DetailSubAppDescriptor subAppDescriptor;
82      private final EditorDefinition editorDefinition;
83      private final SimpleTranslator i18n;
84      private String nodePath;
85      private ContentConnector contentConnector;
86  
87      @Inject
88      public DetailEditorPresenter(final ActionExecutor actionExecutor, final SubAppContext subAppContext, final DetailEditorView view, final DetailPresenter detailPresenter, final ActionbarPresenter actionbarPresenter, final SimpleTranslator i18n) {
89          this.actionExecutor = actionExecutor;
90          this.view = view;
91          this.detailPresenter = detailPresenter;
92          this.actionbarPresenter = actionbarPresenter;
93          this.appContext = subAppContext.getAppContext();
94          this.subAppContext = subAppContext;
95          this.subAppDescriptor = (DetailSubAppDescriptor) subAppContext.getSubAppDescriptor();
96          this.editorDefinition = subAppDescriptor.getEditor();
97          this.i18n = i18n;
98      }
99  
100     public View start(String nodePath, DetailView.ViewType viewType, ContentConnector contentConnector) {
101 
102         return start(nodePath, viewType, contentConnector, null);
103     }
104 
105     public View start(String nodePath, DetailView.ViewType viewType, ContentConnector contentConnector, String versionName) {
106         this.contentConnector = contentConnector;
107         this.nodePath = nodePath;
108 
109         view.setListener(this);
110         Object itemId = contentConnector.getItemIdByUrlFragment(nodePath);
111 
112         if (contentConnector.canHandleItem(itemId)) {
113             if (StringUtils.isNotEmpty(versionName) && DetailView.ViewType.VIEW.equals(viewType) && contentConnector instanceof SupportsVersions) {
114                 itemId = ((SupportsVersions) contentConnector).getItemVersion(itemId, versionName);
115             }
116         } else {
117             if (contentConnector instanceof SupportsCreation) {
118                 Object parentId = contentConnector.getItemIdByUrlFragment(StringUtils.substringBeforeLast(nodePath, "/"));
119                 itemId = ((SupportsCreation) contentConnector).getNewItemId(parentId, editorDefinition.getNodeType().getName());
120             }
121         }
122 
123         // editor
124         if (editorDefinition != null && editorDefinition.getForm() != null) {
125             DetailView itemView = detailPresenter.start(editorDefinition, viewType, itemId);
126             view.setItemView(itemView);
127             detailPresenter.addShortcut(new CloseEditorAfterConfirmationShortcutListener(KeyCode.ESCAPE, itemView));
128             detailPresenter.addShortcut(new CommitDialogShortcutListener(KeyCode.ENTER));
129             if (editorDefinition.isWide()) {
130                 itemView.setWide(true);
131             }
132         } else {
133             log.warn("DetailPresenter expected an editor and a form definition, but no such definitions are currently configured.");
134             view.setItemView(new StubView("icon-warning-l"));
135             return view;
136         }
137 
138         // actionbar
139         actionbarPresenter.setListener(this);
140         if (subAppDescriptor.getActionbar() != null) {
141             ActionbarView actionbar = actionbarPresenter.start(subAppDescriptor.getActionbar(), subAppDescriptor.getActions());
142             view.setActionbarView(actionbar);
143         }
144 
145         return view;
146     }
147 
148     public View update(DetailLocation location) {
149         return this.start(location.getNodePath(), location.getViewType(), contentConnector, location.getVersion());
150     }
151 
152     public String getNodePath() {
153         return nodePath;
154     }
155 
156     public ActionbarPresenter getActionbarPresenter() {
157         return actionbarPresenter;
158     }
159 
160     @Override
161     public void onViewTypeChanged(final DetailView.ViewType viewType) {
162     }
163 
164     @Override
165     public void onActionbarItemClicked(String actionName) {
166         try {
167            actionExecutor.execute(actionName, detailPresenter.getItem());
168         }
169         catch (ActionExecutionException e) {
170             Message error = new Message(MessageType.ERROR, i18n.translate("ui-contentapp.error.action.execution"), e.getMessage());
171             appContext.sendLocalMessage(error);
172         }
173     }
174 
175     /**
176      * A shortcut listener which opens a confirmation to confirm closing the DetailEditor.
177      */
178     protected class CloseEditorAfterConfirmationShortcutListener extends ShortcutListener {
179 
180         private final View itemView;
181 
182         public CloseEditorAfterConfirmationShortcutListener(int keyCode, View itemView, int... modifierKey) {
183             super("", keyCode, modifierKey);
184             this.itemView = itemView;
185         }
186 
187         @Override
188         public void handleAction(Object sender, Object target) {
189             subAppContext.openConfirmation(
190                     MessageStyleTypeEnum.WARNING, i18n.translate("ui-contentapp.detailEditorPresenter.closeConfirmation.title"), i18n.translate("ui-dialog.closeConfirmation.body"), i18n.translate("ui-dialog.closeConfirmation.confirmButton"), i18n.translate("ui-dialog.cancelButton"), false,
191                     new ConfirmationCallback() {
192                         @Override
193                         public void onSuccess() {
194                             detailPresenter.onActionFired(BaseDialog.CANCEL_ACTION_NAME, new HashMap<String, Object>());
195                         }
196 
197                         @Override
198                         public void onCancel() {
199                             if (itemView.asVaadinComponent() instanceof Panel) {
200                                 ((Panel) itemView.asVaadinComponent()).focus();
201                             }
202                         }
203                     });
204         }
205     }
206 
207     /**
208      * A shortcut listener used to commit the DetailEditor if a text area does not have focus.
209      */
210     protected class CommitDialogShortcutListener extends ShortcutListener {
211 
212         public CommitDialogShortcutListener(int keyCode, int... modifierKey) {
213             super("", keyCode, modifierKey);
214         }
215 
216         @Override
217         public void handleAction(Object sender, Object target) {
218             // textareas are excluded on the client-side, see 'EnterFriendlyShortcutActionHandler', used in PanelConnector
219             detailPresenter.onActionFired(BaseDialog.COMMIT_ACTION_NAME, new HashMap<String, Object>());
220         }
221     }
222 
223 }