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.pages.app.editor;
35  
36  import info.magnolia.event.EventBus;
37  import info.magnolia.i18nsystem.SimpleTranslator;
38  import info.magnolia.pages.app.editor.event.ComponentMoveEvent;
39  import info.magnolia.pages.app.editor.event.NodeSelectedEvent;
40  import info.magnolia.pages.app.editor.parameters.PageEditorStatus;
41  import info.magnolia.ui.api.action.ActionExecutionException;
42  import info.magnolia.ui.api.action.ActionExecutor;
43  import info.magnolia.ui.api.app.SubAppContext;
44  import info.magnolia.ui.api.app.SubAppEventBus;
45  import info.magnolia.ui.api.ioc.SubAppScoped;
46  import info.magnolia.ui.api.message.Message;
47  import info.magnolia.ui.api.message.MessageType;
48  import info.magnolia.ui.contentapp.detail.DetailLocation;
49  import info.magnolia.ui.vaadin.editor.PageEditorListener;
50  import info.magnolia.ui.vaadin.editor.PageEditorView;
51  import info.magnolia.ui.vaadin.editor.events.PageEditorNavigationEvent;
52  import info.magnolia.ui.vaadin.gwt.client.shared.AbstractElement;
53  import info.magnolia.ui.vaadin.gwt.client.shared.ComponentElement;
54  import info.magnolia.ui.vaadin.gwt.client.shared.ErrorType;
55  import info.magnolia.ui.vaadin.gwt.client.shared.PageEditorParameters;
56  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
57  
58  import java.util.Arrays;
59  import java.util.function.Function;
60  import java.util.stream.Collectors;
61  import java.util.stream.Stream;
62  
63  import javax.inject.Inject;
64  import javax.inject.Named;
65  
66  import org.slf4j.Logger;
67  import org.slf4j.LoggerFactory;
68  
69  import com.google.common.base.CaseFormat;
70  
71  /**
72   * Presenter for the server side {@link PageEditorView}.
73   * Serves multiple methods for actions triggered from the page editor.
74   */
75  @SubAppScoped
76  public class PageEditorPresenter implements PageEditorListener {
77  
78      private static final Logger log = LoggerFactory.getLogger(PageEditorPresenter.class);
79      private static final String EXTERNAL_PAGE_CAPTION = "pages.edit.external.page.caption";
80      private static final String[] AREAS_I18N_KEYS = {"pages.areas.maxComponentsReached", "pages.areas.newComponent", "pages.areas.newLabelComponent"};
81  
82      private final ActionExecutor actionExecutor;
83      private final PageEditorView view;
84      private final EventBus subAppEventBus;
85      private final SubAppContext subAppContext;
86      private final SimpleTranslator i18n;
87      private final PageEditorStatus pageEditorStatus;
88  
89      private boolean moving = false;
90      private Listener listener;
91  
92      @Inject
93      public PageEditorPresenter(final ActionExecutor actionExecutor, PageEditorView view, final @Named(SubAppEventBus.NAME) EventBus subAppEventBus,
94              SubAppContext subAppContext, SimpleTranslator i18n, PageEditorStatus pageEditorStatus) {
95          this.actionExecutor = actionExecutor;
96          this.view = view;
97          this.subAppEventBus = subAppEventBus;
98          this.subAppContext = subAppContext;
99          this.i18n = i18n;
100         this.pageEditorStatus = pageEditorStatus;
101         registerHandlers();
102     }
103 
104     public PageEditorView start(DetailLocation location) {
105         view.setListener(this);
106         pageEditorStatus.setI18nKeys(Arrays.stream(AREAS_I18N_KEYS).collect(Collectors.toMap(Function.identity(), i18n::translate)));
107         pageEditorStatus.updateStatusFromLocation(location);
108         loadPageEditor();
109         return view;
110     }
111 
112     public void reload(DetailLocation location) {
113         pageEditorStatus.updateStatusFromLocation(location);
114         loadPageEditor();
115     }
116 
117     private void registerHandlers() {
118         subAppEventBus.addHandler(ComponentMoveEvent.class, event -> {
119             moving = event.isStart();
120             if (moving) {
121                 view.startMoveComponent((ComponentElement) pageEditorStatus.getSelectedElement());
122             } else if (event.isServerSide()) {
123                 view.cancelMoveComponent();
124             }
125             listener.onMove();
126         });
127 
128     }
129 
130     @Override
131     public void onElementSelect(AbstractElement selectedElement) {
132         getStatus().setSelectedElement(selectedElement);
133         updateParameters();
134         subAppEventBus.fireEvent(new NodeSelectedEvent(selectedElement));
135     }
136 
137     @Override
138     public void onExternalPageSelect() {
139         listener.updateCaptionForExternalPage(i18n.translate(EXTERNAL_PAGE_CAPTION));
140         listener.deactivateComponents();
141     }
142 
143     /**
144      * Used for executing actions from 'client-side'. These might have a {@link AbstractElement} as part of the arguments, but don't have to.
145      * See {@link info.magnolia.ui.vaadin.gwt.client.rpc.PageEditorServerRpc} which receives the calls from 'client-side'.
146      *
147      * @see PagesEditorSubApp#prepareAndExecutePagesEditorAction(String) for 'server-side' action execution as comparison.
148      */
149     @Override
150     public void onError(ErrorType errorType, String... parameters) {
151         if (errorType == null) {
152             throw new IllegalArgumentException("ErrorType must be one of ErrorType.values().");
153         }
154 
155         String key = String.format("pages.templateErrorAlert.%s.message", CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, errorType.name()));
156         String message = i18n.translate(key, parameters);
157         subAppContext.openAlert(MessageStyleTypeEnum.WARNING, i18n.translate("pages.templateErrorAlert.title"), message, i18n.translate("button.ok"), () -> {
158             // Do nothing.
159         });
160     }
161 
162     @Override
163     public void onNavigation(String url) {
164         subAppEventBus.fireEvent(new PageEditorNavigationEvent(url));
165     }
166 
167     /**
168      * Used for executing actions from 'client-side'. These might have a {@link AbstractElement} as part of the arguments, but don't have to.
169      * See {@link info.magnolia.ui.vaadin.gwt.client.rpc.PageEditorServerRpc} which receives the calls from 'client-side'.
170      *
171      * @see PagesEditorSubApp#prepareAndExecutePagesEditorAction(String) for 'server-side' action execution as comparison.
172      */
173     @Override
174     public void onAction(String actionName, Object... args) {
175         Stream.of(args)
176               .filter(AbstractElement.class::isInstance)
177               .map(AbstractElement.class::cast)
178               .findFirst()
179               .ifPresent(pageEditorStatus::setSelectedElement);
180         try {
181             actionExecutor.execute(actionName, args);
182         } catch (ActionExecutionException e) {
183             Message error = new Message(MessageType.ERROR, i18n.translate("pages.pageEditorPresenter.actionExecutionError.message"), e.getMessage());
184             log.error("An error occurred while executing action [{}]", actionName, e);
185             subAppContext.getAppContext().sendLocalMessage(error);
186         }
187     }
188 
189     public AbstractElement getSelectedElement() {
190         return pageEditorStatus.getSelectedElement();
191     }
192 
193     public void loadPageEditor() {
194         PageEditorParameters pageEditorParameters = pageEditorStatus.getParameters();
195         view.load(pageEditorParameters);
196     }
197 
198     public void updateParameters() {
199         PageEditorParameters pageEditorParameters = pageEditorStatus.getParameters();
200         view.update(pageEditorParameters);
201     }
202 
203     public void refresh() {
204         view.refresh();
205     }
206 
207     public boolean isMoving() {
208         return moving;
209     }
210 
211     public void setListener(Listener listener) {
212         this.listener = listener;
213     }
214 
215     public PageEditorStatus getStatus() {
216         return pageEditorStatus;
217     }
218 
219     /**
220      * Listener interface to call {@link PageEditorPresenter}.
221      */
222     interface Listener {
223         void onMove();
224 
225         void updateCaptionForExternalPage(String title);
226 
227         void deactivateComponents();
228     }
229 }