1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.vaadin.gwt.client.connector;
35
36 import info.magnolia.ui.vaadin.editor.PageEditor;
37 import info.magnolia.ui.vaadin.gwt.client.css.PageEditorCssProvider;
38 import info.magnolia.ui.vaadin.gwt.client.editor.dom.CmsNode;
39 import info.magnolia.ui.vaadin.gwt.client.editor.dom.Comment;
40 import info.magnolia.ui.vaadin.gwt.client.editor.dom.MgnlComponent;
41 import info.magnolia.ui.vaadin.gwt.client.editor.dom.MgnlElement;
42 import info.magnolia.ui.vaadin.gwt.client.editor.dom.processor.AbstractMgnlElementProcessor;
43 import info.magnolia.ui.vaadin.gwt.client.editor.dom.processor.CommentProcessor;
44 import info.magnolia.ui.vaadin.gwt.client.editor.dom.processor.ElementProcessor;
45 import info.magnolia.ui.vaadin.gwt.client.editor.dom.processor.MgnlElementProcessorFactory;
46 import info.magnolia.ui.vaadin.gwt.client.editor.dom.processor.ProcessException;
47 import info.magnolia.ui.vaadin.gwt.client.editor.event.ClientActionEvent;
48 import info.magnolia.ui.vaadin.gwt.client.editor.event.ClientActionEventHandler;
49 import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStartMoveEvent;
50 import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStopMoveEvent;
51 import info.magnolia.ui.vaadin.gwt.client.editor.event.EditAreaEvent;
52 import info.magnolia.ui.vaadin.gwt.client.editor.event.EditAreaEventHandler;
53 import info.magnolia.ui.vaadin.gwt.client.editor.event.EditComponentEvent;
54 import info.magnolia.ui.vaadin.gwt.client.editor.event.EditComponentEventHandler;
55 import info.magnolia.ui.vaadin.gwt.client.editor.event.FrameNavigationEvent;
56 import info.magnolia.ui.vaadin.gwt.client.editor.event.FrameNavigationEventHandler;
57 import info.magnolia.ui.vaadin.gwt.client.editor.event.NewAreaEvent;
58 import info.magnolia.ui.vaadin.gwt.client.editor.event.NewAreaEventHandler;
59 import info.magnolia.ui.vaadin.gwt.client.editor.event.NewComponentEvent;
60 import info.magnolia.ui.vaadin.gwt.client.editor.event.NewComponentEventHandler;
61 import info.magnolia.ui.vaadin.gwt.client.editor.event.SelectElementEvent;
62 import info.magnolia.ui.vaadin.gwt.client.editor.event.SelectElementEventHandler;
63 import info.magnolia.ui.vaadin.gwt.client.editor.event.SortComponentEvent;
64 import info.magnolia.ui.vaadin.gwt.client.editor.event.SortComponentEventHandler;
65 import info.magnolia.ui.vaadin.gwt.client.editor.jsni.event.FrameLoadedEvent;
66 import info.magnolia.ui.vaadin.gwt.client.editor.model.Model;
67 import info.magnolia.ui.vaadin.gwt.client.editor.model.ModelImpl;
68 import info.magnolia.ui.vaadin.gwt.client.editor.model.focus.FocusModel;
69 import info.magnolia.ui.vaadin.gwt.client.editor.model.focus.FocusModelImpl;
70 import info.magnolia.ui.vaadin.gwt.client.rpc.PageEditorClientRpc;
71 import info.magnolia.ui.vaadin.gwt.client.rpc.PageEditorServerRpc;
72 import info.magnolia.ui.vaadin.gwt.client.shared.AbstractElement;
73 import info.magnolia.ui.vaadin.gwt.client.shared.AreaElement;
74 import info.magnolia.ui.vaadin.gwt.client.shared.ComponentElement;
75 import info.magnolia.ui.vaadin.gwt.client.shared.PageEditorParameters;
76 import info.magnolia.ui.vaadin.gwt.client.shared.PageElement;
77 import info.magnolia.ui.vaadin.gwt.client.widget.PageEditorView;
78 import info.magnolia.ui.vaadin.gwt.client.widget.PageEditorViewImpl;
79 import info.magnolia.ui.vaadin.gwt.client.widget.dnd.MoveWidget;
80
81 import java.util.List;
82 import java.util.logging.Logger;
83
84 import com.google.gwt.core.client.GWT;
85 import com.google.gwt.core.client.JavaScriptException;
86 import com.google.gwt.dom.client.Document;
87 import com.google.gwt.dom.client.Element;
88 import com.google.gwt.dom.client.HeadElement;
89 import com.google.gwt.dom.client.LinkElement;
90 import com.google.gwt.dom.client.Node;
91 import com.google.gwt.event.shared.EventBus;
92 import com.google.gwt.event.shared.SimpleEventBus;
93 import com.google.gwt.user.client.ui.Widget;
94 import com.vaadin.client.BrowserInfo;
95 import com.vaadin.client.communication.RpcProxy;
96 import com.vaadin.client.communication.StateChangeEvent;
97 import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
98 import com.vaadin.client.ui.AbstractComponentConnector;
99 import com.vaadin.shared.ui.Connect;
100
101
102
103
104 @Connect(PageEditor.class)
105 public class PageEditorConnector extends AbstractComponentConnector implements PageEditorView.Listener {
106
107 private Logger log = Logger.getLogger(getClass().getName());
108
109 private final PageEditorServerRpc rpc = RpcProxy.create(PageEditorServerRpc.class, this);
110
111 private final EventBus eventBus = new SimpleEventBus();
112
113 private PageEditorView view;
114 private MoveWidget moveWidget;
115
116 private Model model;
117
118 private FocusModel focusModel;
119 private ElementProcessor elementProcessor;
120 private CommentProcessor commentProcessor;
121
122 @Override
123 protected void init() {
124 super.init();
125 this.model = new ModelImpl();
126 this.focusModel = new FocusModelImpl(eventBus, model);
127 this.elementProcessor = new ElementProcessor(eventBus, model);
128 this.commentProcessor = new CommentProcessor();
129
130 addStateChangeHandler(new StateChangeHandler() {
131 @Override
132 public void onStateChanged(StateChangeEvent stateChangeEvent) {
133 PageEditorParameters params = getState().parameters;
134 view.setUrl(params.getUrl());
135 if (params.isPreview()) {
136 view.getFrame().addStyleName("iframe-preloader");
137 }
138 }
139 });
140
141 registerRpc(PageEditorClientRpc.class, new PageEditorClientRpc() {
142
143 @Override
144 public void refresh() {
145 view.reload();
146 }
147
148 @Override
149 public void startMoveComponent() {
150 MgnlComponent component = focusModel.getSelectedComponent();
151 if (component != null) {
152 component.doStartMove(false);
153 model.setMoving(true);
154 if (!BrowserInfo.get().isTouchDevice()) {
155 PageEditorConnector.this.moveWidget = component.getMoveWidget();
156 moveWidget.attach(view.getFrame());
157 }
158 }
159 }
160
161 @Override
162 public void cancelMoveComponent() {
163 eventBus.fireEvent(new ComponentStopMoveEvent(null, true));
164 }
165 });
166
167 eventBus.addHandler(FrameLoadedEvent.TYPE, new FrameLoadedEvent.Handler() {
168 @Override
169 public void handle(FrameLoadedEvent event) {
170 model.reset();
171 Document document = null;
172 try {
173 document = event.getFrame().getContentDocument();
174 } catch (JavaScriptException e) {
175 GWT.log("Error getting content document from iframe: " + e.getMessage());
176 }
177 if (document == null) {
178 rpc.selectExternalPage();
179 } else {
180 process(document);
181
182 view.initKeyEventListeners();
183
184 if (!getState().parameters.isPreview()) {
185 view.initDomEventListeners();
186 focusModel.init();
187 } else {
188 focusModel.clearSelection();
189 }
190 }
191 }
192 });
193
194 eventBus.addHandler(FrameNavigationEvent.TYPE, new FrameNavigationEventHandler() {
195 @Override
196 public void onFrameUrlChanged(FrameNavigationEvent frameUrlChangedEvent) {
197 String path = frameUrlChangedEvent.getPath();
198
199 final String platformId = getState().parameters.getPlatformType().getId();
200 path += path.indexOf('?') == -1 ? "?" : "&";
201 path += "mgnlChannel=" + platformId;
202
203 final boolean isPreview = getState().parameters.isPreview();
204 path += "&mgnlPreview=" + isPreview;
205
206 view.setUrl(path);
207 }
208 });
209
210 eventBus.addHandler(SelectElementEvent.TYPE, new SelectElementEventHandler() {
211 @Override
212 public void onSelectElement(SelectElementEvent selectElementEvent) {
213 AbstractElement selectedElement = selectElementEvent.getElement();
214 if (selectedElement instanceof PageElement) {
215 rpc.selectPage((PageElement) selectedElement);
216 }
217 else if (selectedElement instanceof AreaElement) {
218 rpc.selectArea((AreaElement) selectedElement);
219 }
220 else if (selectedElement instanceof ComponentElement) {
221 rpc.selectComponent((ComponentElement) selectedElement);
222 }
223 view.resetScrollTop();
224 }
225 });
226
227 eventBus.addHandler(NewAreaEvent.TYPE, new NewAreaEventHandler() {
228 @Override
229 public void onNewArea(NewAreaEvent newAreaEvent) {
230 rpc.newArea(newAreaEvent.getAreaElement());
231 }
232 });
233
234 eventBus.addHandler(NewComponentEvent.TYPE, new NewComponentEventHandler() {
235 @Override
236 public void onNewComponent(NewComponentEvent newComponentEvent) {
237 rpc.newComponent(newComponentEvent.getParentAreaElement());
238 }
239 });
240
241 eventBus.addHandler(EditAreaEvent.TYPE, new EditAreaEventHandler() {
242 @Override
243 public void onEditArea(EditAreaEvent editAreaEvent) {
244 rpc.editArea(editAreaEvent.getAreaElement());
245 }
246 });
247
248 eventBus.addHandler(EditComponentEvent.TYPE, new EditComponentEventHandler() {
249 @Override
250 public void onEditComponent(EditComponentEvent editComponentEvent) {
251 rpc.editComponent(editComponentEvent.getComponentElement());
252 }
253 });
254
255 eventBus.addHandler(SortComponentEvent.TYPE, new SortComponentEventHandler() {
256 @Override
257 public void onSortComponent(SortComponentEvent sortComponentEvent) {
258 rpc.sortComponent(sortComponentEvent.getAreaElement());
259 }
260 });
261
262 eventBus.addHandler(ComponentStartMoveEvent.TYPE, new ComponentStartMoveEvent.CompnentStartMoveEventHandler() {
263 @Override
264 public void onStart(ComponentStartMoveEvent componentStartMoveEvent) {
265 rpc.startMoveComponent();
266 }
267 });
268
269 eventBus.addHandler(ComponentStopMoveEvent.TYPE, new ComponentStopMoveEvent.ComponentStopMoveEventHandler() {
270 @Override
271 public void onStop(ComponentStopMoveEvent componentStopMoveEvent) {
272 if (!componentStopMoveEvent.isServerSide()) {
273 rpc.stopMoveComponent();
274 }
275 if (moveWidget != null && moveWidget.isAttached()) {
276 moveWidget.detach();
277 }
278 model.setMoving(false);
279 }
280 });
281
282 eventBus.addHandler(ClientActionEvent.TYPE, new ClientActionEventHandler() {
283 @Override
284 public void onAction(ClientActionEvent actionEvent) {
285 rpc.onClientAction(actionEvent.getActionName(), actionEvent.getElement(), actionEvent.getParameters());
286 }
287 });
288 }
289
290 @Override
291 protected Widget createWidget() {
292 this.view = new PageEditorViewImpl(eventBus);
293 this.view.setListener(this);
294 return view.asWidget();
295 }
296
297 @Override
298 public PageEditorState getState() {
299 return (PageEditorState) super.getState();
300 }
301
302 @Override
303 public void selectElement(Element element) {
304 focusModel.selectElement(model.getMgnlElement(element));
305 }
306
307 private void process(final Document document) {
308 try {
309 injectEditorStyles(document);
310 long startTime = System.currentTimeMillis();
311 processDocument(document, null);
312 processMgnlElements();
313 GWT.log("Time spent to process cms comments: " + (System.currentTimeMillis() - startTime) + "ms");
314 } catch (ProcessException e) {
315 rpc.onError(e.getErrorType(), e.getTagName());
316 GWT.log("Error while processing comment: " + e.getTagName() + " due to " + e.getErrorType());
317 consoleLog("Error while processing comment: " + e.getTagName() + " due to " + e.getErrorType());
318 }
319 }
320
321 private void injectEditorStyles(final Document document) {
322 HeadElement head = HeadElement.as(document.getElementsByTagName("head").getItem(0));
323 for (String uri: PageEditorCssProvider.INSTANCE.getCssLinks()) {
324 LinkElement cssLink = createLinkElement(document, getState().parameters.getContextPath() + uri);
325 head.insertFirst(cssLink);
326 }
327 }
328
329 private LinkElement createLinkElement(final Document document, final String href) {
330 LinkElement cssLink = document.createLinkElement();
331 cssLink.setType("text/css");
332 cssLink.setRel("stylesheet");
333 cssLink.setHref(href);
334 return cssLink;
335 }
336
337 private void processDocument(Node node, MgnlElement mgnlElement) throws ProcessException {
338 if (mgnlElement == null && model.getRootPage() != null) {
339 mgnlElement = model.getRootPage();
340 }
341 for (int i = 0; i < node.getChildCount(); i++) {
342 Node childNode = node.getChild(i);
343 if (childNode.getNodeType() == Comment.COMMENT_NODE) {
344 try {
345 mgnlElement = commentProcessor.process(model, eventBus, childNode, mgnlElement);
346 } catch (ProcessException e) {
347 throw e;
348 } catch (IllegalArgumentException e) {
349 GWT.log("Not CMSComment element, skipping: " + e.toString());
350 } catch (Exception e) {
351 GWT.log("Caught undefined exception: " + e.toString());
352 consoleLog("Caught undefined exception: " + e.toString());
353 }
354 } else if (childNode.getNodeType() == Node.ELEMENT_NODE) {
355 Element element = childNode.cast();
356 elementProcessor.process(element, mgnlElement, getState().parameters.isPreview());
357 }
358 processDocument(childNode, mgnlElement);
359 }
360 }
361
362 private void processMgnlElements() {
363 CmsNode root = model.getRootPage();
364 if (model.getRootPage() == null) {
365 log.warning("Could not find any Magnolia cms:page tag, this might be a static page; not injecting page-editor bars.");
366 return;
367 }
368
369 List<CmsNode> elements = root.getDescendants();
370 elements.add(root);
371 for (CmsNode element : elements) {
372 try {
373 AbstractMgnlElementProcessor processor = MgnlElementProcessorFactory.getProcessor(model, element.asMgnlElement());
374 processor.process();
375 } catch (IllegalArgumentException e) {
376 GWT.log("MgnlFactory could not instantiate class. The element is neither an area nor component.");
377 } catch (Exception e) {
378 final String errorMessage = "Error when processing editor components for '" + element.asMgnlElement().getAttribute("path") +
379 "'. It's possible that the template script for this area or for some subcomponent is incorrect. Please check that all HTML tags are closed properly.\n"
380 + e.toString();
381 GWT.log(errorMessage);
382 consoleLog(errorMessage);
383 }
384 }
385 }
386
387 void consoleLog(String message) {
388 log.info("PageEditor: " + message);
389 }
390 }