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.templating.editor.client;
35
36
37 import info.magnolia.templating.editor.client.dom.Comment;
38 import info.magnolia.templating.editor.client.dom.MgnlElement;
39 import info.magnolia.templating.editor.client.dom.processor.CommentProcessor;
40 import info.magnolia.templating.editor.client.dom.processor.ElementProcessor;
41 import info.magnolia.templating.editor.client.dom.processor.MgnlElementProcessor;
42 import info.magnolia.templating.editor.client.dom.processor.MgnlElementProcessorFactory;
43 import info.magnolia.templating.editor.client.jsni.JavascriptUtils;
44 import info.magnolia.templating.editor.client.model.ModelStorage;
45 import info.magnolia.templating.editor.client.widget.PreviewChannel;
46 import info.magnolia.templating.editor.client.widget.PreviewChannel.Orientation;
47 import info.magnolia.templating.editor.client.widget.dnd.LegacyDragAndDrop;
48
49 import java.util.LinkedList;
50 import java.util.List;
51
52 import com.google.gwt.core.client.EntryPoint;
53 import com.google.gwt.core.client.GWT;
54 import com.google.gwt.dom.client.AnchorElement;
55 import com.google.gwt.dom.client.Document;
56 import com.google.gwt.dom.client.Element;
57 import com.google.gwt.dom.client.FormElement;
58 import com.google.gwt.dom.client.Node;
59 import com.google.gwt.dom.client.NodeList;
60 import com.google.gwt.dom.client.Style.Unit;
61 import com.google.gwt.event.dom.client.KeyCodes;
62 import com.google.gwt.event.dom.client.KeyDownEvent;
63 import com.google.gwt.event.dom.client.KeyDownHandler;
64 import com.google.gwt.event.dom.client.MouseMoveEvent;
65 import com.google.gwt.event.dom.client.MouseMoveHandler;
66 import com.google.gwt.event.dom.client.MouseUpEvent;
67 import com.google.gwt.event.dom.client.MouseUpHandler;
68 import com.google.gwt.http.client.Request;
69 import com.google.gwt.http.client.RequestBuilder;
70 import com.google.gwt.http.client.RequestCallback;
71 import com.google.gwt.http.client.RequestException;
72 import com.google.gwt.http.client.Response;
73 import com.google.gwt.http.client.URL;
74 import com.google.gwt.http.client.UrlBuilder;
75 import com.google.gwt.user.client.Window;
76 import com.google.gwt.user.client.Window.ScrollEvent;
77 import com.google.gwt.user.client.Window.ScrollHandler;
78 import com.google.gwt.user.client.ui.HTML;
79 import com.google.gwt.user.client.ui.RootPanel;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public class PageEditor extends HTML implements EntryPoint {
100
101 private static final String MGNL_CHANNEL_PARAMETER = "mgnlChannel";
102 private static final String MGNL_PREVIEW_PARAMETER = "mgnlPreview";
103 private static final String MGNL_INTERCEPT_PARAMETER = "mgnlIntercept";
104 private static final String MGNL_VERSION_PARAMETER = "mgnlVersion";
105
106 private static String locale;
107 public final static ModelStorage model = ModelStorage.getInstance();
108 private LinkedList<MgnlElement> mgnlElements = new LinkedList<MgnlElement>();
109
110
111 public static boolean process = true;
112 private static boolean isPreview = false;
113
114
115 @Override
116 public void onModuleLoad() {
117
118 String mgnlVersion = Window.Location.getParameter(MGNL_VERSION_PARAMETER);
119 if(mgnlVersion != null) {
120 return;
121 }
122
123 String mgnlChannel = Window.Location.getParameter(MGNL_CHANNEL_PARAMETER);
124 boolean isMobile = "smartphone".equals(mgnlChannel) || "tablet".equals(mgnlChannel);
125
126 if(isMobile) {
127 GWT.log("Found " + mgnlChannel + " in request, post processing links...");
128 postProcessLinksOnMobilePreview(Document.get().getDocumentElement(), mgnlChannel);
129 return;
130 }
131
132 JavascriptUtils.setWindowLocation(Window.Location.getPath());
133
134 Window.addWindowScrollHandler(new ScrollHandler() {
135
136 @Override
137 public void onWindowScroll(ScrollEvent event) {
138 String value = event.getScrollLeft() + ":" + event.getScrollTop();
139 JavascriptUtils.setEditorPositionCookie(value);
140 }
141 });
142
143 JavascriptUtils.getCookiePosition();
144
145 locale = JavascriptUtils.detectCurrentLocale();
146
147 long startTime = System.currentTimeMillis();
148 processDocument(Document.get().getDocumentElement(), null);
149 processMgnlElements();
150
151 GWT.log("Time spent to process cms comments: " + (System.currentTimeMillis() - startTime) + "ms");
152
153 JavascriptUtils.getCookieContentId();
154
155 RootPanel.get().addDomHandler(new MouseUpHandler() {
156 @Override
157 public void onMouseUp(MouseUpEvent event) {
158
159 model.getFocusModel().onMouseUp((Element)event.getNativeEvent().getEventTarget().cast());
160 event.stopPropagation();
161 }
162 }, MouseUpEvent.getType());
163
164 RootPanel.get().addDomHandler(new KeyDownHandler() {
165 @Override
166 public void onKeyDown(KeyDownEvent event) {
167 if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
168
169 if(LegacyDragAndDrop.isMoving()) {
170 LegacyDragAndDrop.moveComponentReset();
171 } else {
172 PageEditor.enablePreview(!isPreview);
173 }
174 event.preventDefault();
175 }
176 }
177 }, KeyDownEvent.getType());
178
179
180
181 RootPanel.get().addDomHandler(new MouseMoveHandler() {
182
183 @Override
184 public void onMouseMove(MouseMoveEvent event) {
185
186 Element moveElement = Document.get().getElementById("mgnlEditorMoveDiv");
187
188 if (moveElement != null) {
189 int x = event.getClientX() + Window.getScrollLeft();
190 int y = event.getClientY() + 15 + Window.getScrollTop();
191 moveElement.getStyle().setTop(y, Unit.PX);
192 moveElement.getStyle().setLeft(x, Unit.PX);
193 }
194 }
195 }, MouseMoveEvent.getType());
196
197 JavascriptUtils.resetEditorCookies();
198
199 GWT.log("Running onPageEditorReady callbacks...");
200 onPageEditorReady();
201 }
202
203 public static void openDialog(String dialog, String workspace, String path) {
204 JavascriptUtils.mgnlOpenDialog(path, "", "", dialog, workspace, "", "", "", locale);
205 }
206
207 public static void deleteComponent(String path) {
208 JavascriptUtils.mgnlDeleteNode(path);
209 }
210
211 public static void addComponent(String workspace, String path, String nodeName, String availableComponents) {
212
213 String collectionName = null;
214
215 if (nodeName == null) {
216 nodeName = "mgnlNew";
217 }
218 if (availableComponents == null) {
219 availableComponents = "";
220 }
221 if (availableComponents.contains(",")) {
222 JavascriptUtils.mgnlOpenDialog(path, collectionName, nodeName, availableComponents, workspace, ".magnolia/dialogs/selectParagraph.html", "", "", locale);
223 } else if (!availableComponents.isEmpty()) {
224 JavascriptUtils.mgnlOpenDialog(path, collectionName, nodeName, availableComponents, workspace, ".magnolia/dialogs/editParagraph.html", "", "", locale);
225 }
226 }
227
228 public static void showTree(String workspace, String path) {
229 JavascriptUtils.showTree(workspace, path);
230
231 }
232
233 public static void createComponent(String workspace, String path, String itemType) {
234 GWT.log("Creating [" + itemType + "] in workspace [" + workspace + "] at path [" + path + "]");
235
236 final StringBuilder url = new StringBuilder();
237 url.append(JavascriptUtils.getContextPath() + ".magnolia/pageeditor/PageEditorServlet");
238 url.append("?action=create");
239 url.append("&workspace=" + workspace);
240 url.append("&path=" + path);
241 url.append("&itemType=" + itemType);
242
243 RequestBuilder req = new RequestBuilder(RequestBuilder.GET, URL.encode(url.toString()));
244 req.setCallback(new RequestCallback() {
245
246 @Override
247 public void onResponseReceived(Request request, Response response) {
248 int status = response.getStatusCode();
249 String responseText = "";
250 boolean reload = false;
251
252 switch (status) {
253 case Response.SC_OK:
254 reload = true;
255 break;
256 case Response.SC_UNAUTHORIZED:
257 responseText = "Is your session expired? Please, try to login again.";
258 break;
259 default:
260 responseText = "See logs for more details.";
261 }
262
263 if (reload) {
264 UrlBuilder urlBuilder = Window.Location.createUrlBuilder();
265
266 urlBuilder.removeParameter("mgnlIntercept");
267 urlBuilder.removeParameter("mgnlPath");
268
269 Window.Location.replace(urlBuilder.buildString());
270 } else {
271 Window.alert("An error occurred on the server: response status code is " + status + "\n" + responseText);
272 }
273 }
274
275 @Override
276 public void onError(Request request, Throwable exception) {
277 Window.alert(exception.getMessage());
278 }
279 });
280 try {
281 req.send();
282 } catch (RequestException e) {
283 Window.alert("An error occurred while trying to send a request to the server: " + e.getMessage());
284 }
285
286 }
287
288 public static void createChannelPreview(final String channelName, final Orientation orientation) {
289 setPreview(true);
290 GWT.log("Creating preview for channel type [" + channelName + "] ");
291
292 final UrlBuilder urlBuilder = Window.Location.createUrlBuilder();
293
294
295 urlBuilder.removeParameter(MGNL_PREVIEW_PARAMETER);
296 urlBuilder.removeParameter(MGNL_INTERCEPT_PARAMETER);
297 urlBuilder.removeParameter(MGNL_CHANNEL_PARAMETER);
298
299 urlBuilder.setParameter(MGNL_PREVIEW_PARAMETER, String.valueOf(isPreview()));
300 urlBuilder.setParameter(MGNL_CHANNEL_PARAMETER, channelName);
301 final PreviewChannel previewChannelWidget = new PreviewChannel(urlBuilder.buildString(), orientation, channelName);
302
303 previewChannelWidget.center();
304 }
305
306 private void processDocument(Node node, MgnlElement mgnlElement) {
307 if(!process) {
308 return;
309 }
310 boolean proceed = true;
311 for (int i = 0; i < node.getChildCount(); i++) {
312 Node childNode = node.getChild(i);
313 if (childNode.getNodeType() == Comment.COMMENT_NODE) {
314 try {
315 mgnlElement = CommentProcessor.process(childNode, mgnlElement);
316 }
317 catch (IllegalArgumentException e) {
318 GWT.log("Not CMSComment element, skipping: " + e.toString());
319 }
320 catch (Exception e) {
321 GWT.log("Caught undefined exception: " + e.toString());
322 }
323 }
324 else if (childNode.getNodeType() == Node.ELEMENT_NODE && mgnlElement != null) {
325 proceed = ElementProcessor.process(childNode, mgnlElement);
326 }
327 if (proceed) {
328 processDocument(childNode, mgnlElement);
329 }
330 }
331 }
332
333 private void processMgnlElements() {
334 List<MgnlElement> rootElements = new LinkedList<MgnlElement>(model.getRootElements());
335 for (MgnlElement root : rootElements) {
336 LinkedList<MgnlElement> elements = new LinkedList<MgnlElement>();
337 elements.add(root);
338 elements.addAll(root.getDescendants());
339
340 for (MgnlElement mgnlElement : elements) {
341 try {
342 MgnlElementProcessor processor = MgnlElementProcessorFactory.getProcessor(mgnlElement);
343 processor.process();
344 }
345 catch (IllegalArgumentException e) {
346 GWT.log("MgnlFactory could not instantiate class. The element is neither an area nor component.");
347 }
348 }
349 }
350
351 }
352
353
354 private void postProcessLinksOnMobilePreview(Element root, String channel) {
355 NodeList<Element> anchors = root.getElementsByTagName("a");
356
357 final String mobilePreviewParams = MGNL_CHANNEL_PARAMETER+"="+channel+"&"+ MGNL_PREVIEW_PARAMETER+"=true";
358
359 for (int i = 0; i < anchors.getLength(); i++) {
360 AnchorElement anchor = AnchorElement.as(anchors.getItem(i));
361
362 GWT.log("Starting to process link " + anchor.getHref());
363
364 if(JavascriptUtils.isEmpty(anchor.getHref())) {
365 continue;
366 }
367 String manipulatedHref = anchor.getHref().replaceFirst(Window.Location.getProtocol() + "//" + Window.Location.getHost(), "");
368 String queryString = Window.Location.getQueryString() != null ? Window.Location.getQueryString() : "";
369
370 GWT.log("query string is " + queryString);
371
372 String queryStringRegex = queryString.replaceFirst("\\?", "\\\\?");
373 manipulatedHref = manipulatedHref.replaceFirst(queryStringRegex, "");
374 int indexOfHash = manipulatedHref.indexOf("#");
375
376 if(indexOfHash != -1) {
377 manipulatedHref = manipulatedHref.substring(indexOfHash);
378 } else {
379 if(!queryString.contains(mobilePreviewParams)) {
380 if(queryString.startsWith("?")) {
381 queryString += "&" + mobilePreviewParams;
382 } else {
383 queryString = "?" + mobilePreviewParams;
384 }
385 }
386 manipulatedHref += queryString;
387 }
388 GWT.log("Resulting link is " + manipulatedHref);
389 anchor.setHref(manipulatedHref);
390 }
391 NodeList<Element> forms = root.getElementsByTagName("form");
392
393 for (int i = 0; i < forms.getLength(); i++) {
394 FormElement form = FormElement.as(forms.getItem(i));
395 form.setAction(form.getAction().concat("?"+ mobilePreviewParams));
396 }
397 }
398
399
400 private native void onPageEditorReady()
401
402
403
404
405
406
407 ;
408
409
410
411
412 public static void enablePreview(boolean preview) {
413 setPreview(preview);
414 final UrlBuilder urlBuilder = Window.Location.createUrlBuilder();
415 GWT.log("Current url is [" + urlBuilder.buildString() + "], setting preview to " + isPreview());
416
417
418 urlBuilder.removeParameter(MGNL_PREVIEW_PARAMETER);
419 urlBuilder.removeParameter(MGNL_INTERCEPT_PARAMETER);
420 urlBuilder.removeParameter(MGNL_CHANNEL_PARAMETER);
421
422 urlBuilder.setParameter(MGNL_INTERCEPT_PARAMETER, "PREVIEW");
423 urlBuilder.setParameter(MGNL_PREVIEW_PARAMETER, String.valueOf(isPreview()));
424
425 if(isPreview()) {
426 urlBuilder.setParameter(MGNL_CHANNEL_PARAMETER, "desktop");
427 } else {
428 urlBuilder.setParameter(MGNL_CHANNEL_PARAMETER, "all");
429 }
430
431 final String newUrl = urlBuilder.buildString();
432 GWT.log("New url is [" + newUrl + "]");
433
434 Window.Location.replace(newUrl);
435 }
436
437
438
439
440 public static boolean isPreview() {
441 return isPreview;
442 }
443
444 public static void setPreview(boolean preview) {
445 isPreview = preview;
446 }
447
448 }