View Javadoc
1   /**
2    * This file Copyright (c) 2021 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.vaadin.gwt.client.postmessage;
35  
36  import com.google.gwt.core.client.GWT;
37  import com.google.gwt.core.client.JavaScriptObject;
38  import com.google.gwt.event.shared.EventBus;
39  import com.google.gwt.event.shared.EventHandler;
40  import com.google.gwt.event.shared.HandlerRegistration;
41  import com.google.gwt.event.shared.GwtEvent;
42  import com.google.web.bindery.autobean.shared.AutoBean;
43  import com.google.web.bindery.autobean.shared.AutoBeanCodex;
44  import com.google.web.bindery.autobean.shared.AutoBeanUtils;
45  import com.google.web.bindery.event.shared.Event;
46  
47  import info.magnolia.ui.vaadin.editor.PageEditorListener;
48  import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStartMoveEvent;
49  import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStopMoveEvent;
50  import info.magnolia.ui.vaadin.gwt.client.editor.event.EditAreaEvent;
51  import info.magnolia.ui.vaadin.gwt.client.editor.event.EditComponentEvent;
52  import info.magnolia.ui.vaadin.gwt.client.editor.event.NewComponentEvent;
53  import info.magnolia.ui.vaadin.gwt.client.editor.event.SelectElementEvent;
54  import info.magnolia.ui.vaadin.gwt.client.editor.event.SortComponentEvent;
55  import info.magnolia.ui.vaadin.gwt.client.shared.AbstractElement;
56  import info.magnolia.ui.vaadin.gwt.client.shared.PageElement;
57  import info.magnolia.ui.vaadin.gwt.client.shared.AreaElement;
58  import info.magnolia.ui.vaadin.gwt.client.shared.ComponentElement;
59  import info.magnolia.ui.vaadin.gwt.client.shared.EventBeanFactory;
60  import info.magnolia.ui.vaadin.gwt.client.shared.PostMessageEditorElement;
61  import info.magnolia.ui.vaadin.gwt.client.shared.PostMessageEditorEvent;
62  
63  /**
64   * Handles sending and receiving of events through the postMessages.
65   */
66  public class PostMessageHandler {
67  
68      private EventBeanFactory eventBeanFactory;
69  
70      private EventBus eventBus;
71  
72      private JavaScriptObject listener;
73  
74      public PostMessageHandler(EventBus eventBus) {
75          this.listener = addMessageHandler();
76          this.eventBeanFactory = GWT.create(EventBeanFactory.class);
77          this.eventBus = eventBus;
78      }
79  
80      public <H extends EventHandler> HandlerRegistration addHandler(GwtEvent.Type<H> type, H handler) {
81          return this.eventBus.addHandler(type, handler);
82      }
83  
84      public void fireEvent(Event<?> event) {
85          this.eventBus.fireEvent(event);
86      }
87  
88      /**
89       * Deserializes the events from postMessage payload and fires them to eventBus.
90       */
91      protected void handleMessage(String payload, String origin) {
92          PostMessageEditorEvent event = deserialize(payload);
93          AbstractElement element = getElementFromEvent(event);
94          switch (event.getAction()) {
95              case PageEditorListener.ACTION_EDIT_COMPONENT:
96                  fireEvent(new EditComponentEvent((ComponentElement) element));
97                  break;
98              case PageEditorListener.ACTION_START_MOVE_COMPONENT:
99                  fireEvent(new ComponentStartMoveEvent((ComponentElement) element));
100                 break;
101             case PageEditorListener.ACTION_STOP_MOVE_COMPONENT:
102                 fireEvent(new ComponentStopMoveEvent(null, false));
103                 break;
104             case PageEditorListener.ACTION_EDIT_ELEMENT:
105                 fireEvent(new EditAreaEvent((AreaElement) element));
106                 break;
107             case PageEditorListener.ACTION_ADD_COMPONENT:
108                 fireEvent(new NewComponentEvent((AreaElement) element));
109                 break;
110             case PageEditorListener.ACTION_SORT_COMPONENT:
111                 fireEvent(new SortComponentEvent((AreaElement) element));
112                 break;
113             default:
114                 fireEvent(new SelectElementEvent(element));
115                 break;
116         }
117     }
118 
119     private PostMessageEditorEvent deserialize(String payload) {
120         AutoBean<PostMessageEditorEvent> postMessage = AutoBeanCodex.decode(this.eventBeanFactory, PostMessageEditorEvent.class, payload);
121         return postMessage.as();
122     }
123 
124     /**
125      * Resolves the element from the postMessage payload.
126      */
127     private AbstractElement getElementFromEvent(PostMessageEditorEvent event) {
128         AbstractElement element = null;
129         if (event.getElementType().equals("page")) {
130             element = new PageElement(event.getElement());
131         } else if (event.getElementType().equals("component")) {
132             element = new ComponentElement(event.getElement());
133         } else if (event.getElementType().equals("area")) {
134             element = new AreaElement(event.getElement());
135         }
136         return element;
137     }
138 
139     public void remove() {
140         if (listener != null) {
141             removeMessageHandler(listener);
142         }
143     }
144 
145     public void refresh() {
146         PostMessageEditorEvent event = createEventStub("refresh", null);
147         AutoBean<PostMessageEditorEvent> bean = AutoBeanUtils.getAutoBean(event);
148         sendToFrame(AutoBeanCodex.encode(bean).getPayload(), "*");
149     }
150 
151     public void startMoveComponent(ComponentElement component) {
152         PostMessageEditorEvent event = createEventStub(PageEditorListener.ACTION_START_MOVE_COMPONENT, component);
153         AutoBean<PostMessageEditorEvent> bean = AutoBeanUtils.getAutoBean(event);
154         sendToFrame(AutoBeanCodex.encode(bean).getPayload(), "*");
155     }
156     public void cancelMoveComponent() {
157         PostMessageEditorEvent event = createEventStub(PageEditorListener.ACTION_CANCEL_MOVE_COMPONENT, null);
158         AutoBean<PostMessageEditorEvent> bean = AutoBeanUtils.getAutoBean(event);
159         sendToFrame(AutoBeanCodex.encode(bean).getPayload(), "*");
160     }
161 
162     private PostMessageEditorEvent createEventStub(String action, AbstractElement element) {
163         PostMessageEditorEvent event = this.eventBeanFactory.event().as();
164         event.setAction(action);
165         if (element instanceof PageElement) {
166             PostMessageEditorElement newElement = createElementStub(element);
167             event.setElementType("page");
168             event.setElement(newElement);
169         } else if (element instanceof AreaElement) {
170             PostMessageEditorElement newElement = createAreaStub((AreaElement) element);
171             event.setElementType("area");
172             event.setElement(newElement);
173         } else if (element instanceof ComponentElement) {
174             PostMessageEditorElement newElement = createComponentStub((ComponentElement) element);
175             event.setElementType("component");
176             event.setElement(newElement);
177         }
178         return event;
179     }
180 
181     private PostMessageEditorElement createElementStub(AbstractElement element) {
182         PostMessageEditorElement newElement = this.eventBeanFactory.element().as();
183         newElement.setDialog(element.getDialog());
184         newElement.setPath(element.getPath());
185         newElement.setWorkspace(element.getWorkspace());
186         return newElement;
187     }
188 
189     private PostMessageEditorElement createAreaStub(AreaElement element) {
190         PostMessageEditorElement newElement = createElementStub(element);
191         newElement.setAvailableComponents(element.getAvailableComponents());
192         newElement.setCreated(element.isCreated());
193         newElement.setOptional(element.isOptional());
194         newElement.setAddible(element.getAddible());
195         if (element.getSourceComponent() != null) {
196             newElement.setSourceComponent(createComponentStub(element.getSourceComponent()));
197         }
198         if (element.getTargetComponent() != null) {
199             newElement.setTargetComponent(createComponentStub(element.getTargetComponent()));
200         }
201         newElement.setSortOrder(element.getSortOrder());
202         return newElement;
203     }
204 
205     private PostMessageEditorElement createComponentStub(ComponentElement element) {
206         PostMessageEditorElement newElement = createElementStub(element);
207         newElement.setAvailableTemplates(element.getAvailableTemplates());
208         newElement.setMoveable(element.getMoveable());
209         newElement.setDeletable(element.getDeletable());
210         newElement.setEditable(element.getEditable());
211         newElement.setWritable(element.getWritable());
212         return newElement;
213     }
214 
215     private native JavaScriptObject addMessageHandler()
216     /*-{
217         var self = this;
218         var receiver = $entry(function(event)
219         {
220             var origin = event.origin;
221             var source = event.source;
222             var data = event.data;
223             self.@info.magnolia.ui.vaadin.gwt.client.postmessage.PostMessageHandler::handleMessage(Ljava/lang/String;Ljava/lang/String;)(data, origin);
224         });
225         if ($wnd.addEventListener) {
226             $wnd.addEventListener('message', receiver, false);
227         } else {
228             $wnd.attachEvent('onmessage', receiver);
229         }
230         return receiver;
231     }-*/;
232 
233     private native void removeMessageHandler(JavaScriptObject listener)
234     /*-{
235         if ($wnd.removeEventListener) {
236             $wnd.removeEventListener('message', listener, false);
237         } else {
238             $wnd.detachEvent('onmessage', listener);
239         }
240     }-*/;
241 
242     private native void sendToFrame(String message, String origin)
243     /*-{
244         var iframe = $wnd.document.getElementById("mgnl-pageeditor");
245         iframe.contentWindow.postMessage(message, origin);
246     }-*/;
247 
248 }