View Javadoc

1   /**
2    * This file Copyright (c) 2011 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.templating.editor.client;
35  
36  
37  import java.util.Iterator;
38  import java.util.LinkedList;
39  import java.util.List;
40  
41  import info.magnolia.templating.editor.client.dom.CMSComment;
42  import info.magnolia.templating.editor.client.dom.Comment;
43  import info.magnolia.templating.editor.client.dom.MgnlElement;
44  import info.magnolia.templating.editor.client.jsni.LegacyJavascript;
45  import info.magnolia.templating.editor.client.model.ModelStorage;
46  
47  import com.google.gwt.core.client.EntryPoint;
48  import com.google.gwt.core.client.GWT;
49  import com.google.gwt.core.client.Scheduler;
50  import com.google.gwt.core.client.Scheduler.ScheduledCommand;
51  import com.google.gwt.dom.client.Document;
52  import com.google.gwt.dom.client.Element;
53  import com.google.gwt.dom.client.MetaElement;
54  import com.google.gwt.dom.client.Node;
55  import com.google.gwt.dom.client.NodeList;
56  import com.google.gwt.event.dom.client.MouseUpEvent;
57  import com.google.gwt.event.dom.client.MouseUpHandler;
58  import com.google.gwt.http.client.Request;
59  import com.google.gwt.http.client.RequestBuilder;
60  import com.google.gwt.http.client.RequestCallback;
61  import com.google.gwt.http.client.RequestException;
62  import com.google.gwt.http.client.Response;
63  import com.google.gwt.http.client.URL;
64  import com.google.gwt.http.client.UrlBuilder;
65  import com.google.gwt.i18n.client.Dictionary;
66  import com.google.gwt.user.client.Event;
67  import com.google.gwt.user.client.EventListener;
68  import com.google.gwt.user.client.Window;
69  import com.google.gwt.user.client.ui.HTML;
70  import com.google.gwt.user.client.ui.RootPanel;
71  
72  /**
73   * Client side implementation of the page editor. Outputs ui widgets inside document element (typically the {@code <html>} element).
74   *
75   * @version $Id$
76   */
77  public class PageEditor extends HTML implements EventListener, EntryPoint {
78  
79      private boolean pageEditBarAlreadyProcessed = false;
80      private String locale;
81      private static Dictionary dictionary;
82      private static ModelStorage storage = ModelStorage.getInstance();
83  
84      private LinkedList<MgnlElement> mgnlElements = new LinkedList<MgnlElement>();
85  
86      @Override
87      public void onModuleLoad() {
88          Element documentElement = Document.get().getDocumentElement();
89  
90          locale = detectCurrentLocale(documentElement);
91          //TODO move messages we need to this module?
92          LegacyJavascript.exposeMgnlMessagesToGwtDictionary("info.magnolia.module.admininterface.messages");
93          dictionary = Dictionary.getDictionary("mgnlGwtMessages");
94  
95  
96  
97          Scheduler.get().scheduleDeferred(new ScheduledCommand() {
98              @Override
99              public void execute() {
100                 long stime = System.currentTimeMillis();
101                 processCmsComments(Document.get().getDocumentElement(), null);
102                 long etime= System.currentTimeMillis();
103                 GWT.log("Time: " + String.valueOf(etime -stime));
104                 cleanRootElements();
105                 storage.getFocusModel().reset();
106 
107             }
108         });
109 
110         RootPanel.get().addDomHandler(new MouseUpHandler() {
111             @Override
112             public void onMouseUp(MouseUpEvent event) {
113 
114                 storage.getFocusModel().handleClick((Element)event.getNativeEvent().getEventTarget().cast());
115                 event.stopPropagation();
116             }
117         }, MouseUpEvent.getType());
118 
119     }
120 
121     @Override
122     public void onBrowserEvent(Event event) {
123         super.onBrowserEvent(event);
124     }
125 
126     /**
127      * TODO: rename and/or remove arguments no longer needed (collectionName, nodeName).
128      */
129     public void openDialog(String dialog, String workspace, String path, String collectionName, String nodeName) {
130         if (collectionName == null) {
131             collectionName = "";
132         }
133         if (nodeName == null) {
134             nodeName = "";
135         }
136 
137         LegacyJavascript.mgnlOpenDialog(path, collectionName, nodeName, dialog, workspace, "", "", "", locale);
138     }
139 
140     public void moveComponentStart(String id) {
141         LegacyJavascript.mgnlMoveNodeStart(id);
142     }
143 
144     public void moveComponentEnd(AbstractBarWidget source, String path) {
145         LegacyJavascript.mgnlMoveNodeEnd(source.getElement(), path);
146     }
147 
148     public void moveComponentOver(AbstractBarWidget source) {
149         LegacyJavascript.mgnlMoveNodeHigh(source.getElement());
150     }
151 
152     public void moveComponentOut(AbstractBarWidget source) {
153         LegacyJavascript.mgnlMoveNodeReset(source.getElement());
154     }
155 
156     public void deleteComponent(String path) {
157         LegacyJavascript.mgnlDeleteNode(path);
158     }
159 
160     public void addComponent(String workspace, String path, String collectionName, String nodeName, String availableComponents) {
161         if (collectionName == null) {
162             collectionName = "";
163         }
164         if (nodeName == null) {
165             nodeName = "mgnlNew";
166         }
167         if (availableComponents == null) {
168             availableComponents = "";
169         }
170         LegacyJavascript.mgnlOpenDialog(path, collectionName, nodeName, availableComponents, workspace, ".magnolia/dialogs/selectParagraph.html", "", "", locale);
171     }
172 
173     public void preview(boolean isPreview) {
174         LegacyJavascript.mgnlPreview(isPreview);
175     }
176 
177     public void showTree(String workspace, String path) {
178         LegacyJavascript.showTree(workspace, path);
179 
180     }
181 
182     public void createComponent(String workspace, String parent, String relPath, String itemType) {
183         GWT.log("Creating [" + itemType + "] in workspace [" + workspace + "] at path [" + parent + "/" + relPath + "]");
184 
185         final StringBuilder url = new StringBuilder();
186         url.append(LegacyJavascript.getContextPath() + ".magnolia/pageeditor/PageEditorServlet");
187         url.append("?action=create");
188         url.append("&workspace=" + workspace);
189         url.append("&parent=" + parent);
190         url.append("&relPath=" + relPath);
191         url.append("&itemType=" + itemType);
192 
193         RequestBuilder req = new RequestBuilder(RequestBuilder.GET, URL.encode(url.toString()));
194         req.setCallback(new RequestCallback() {
195 
196             @Override
197             public void onResponseReceived(Request request, Response response) {
198                 int status = response.getStatusCode();
199                 String responseText = "";
200                 boolean reload = false;
201 
202                 switch (status) {
203                     case Response.SC_OK:
204                         reload = true;
205                         break;
206                     case Response.SC_UNAUTHORIZED:
207                         responseText = "Is your session expired? Please, try to login again.";
208                         break;
209                     default:
210                         responseText = "See logs for more details.";
211                 }
212 
213                 if (reload) {
214                     UrlBuilder urlBuilder = Window.Location.createUrlBuilder();
215 
216                     urlBuilder.removeParameter("mgnlIntercept");
217                     urlBuilder.removeParameter("mgnlPath");
218 
219                     Window.Location.replace(urlBuilder.buildString());
220                 } else {
221                     Window.alert("An error occurred on the server: response status code is " + status + "\n" + responseText);
222                 }
223             }
224 
225             @Override
226             public void onError(Request request, Throwable exception) {
227                 Window.alert(exception.getMessage());
228             }
229         });
230         try {
231             req.send();
232         } catch (RequestException e) {
233             Window.alert("An error occurred while trying to send a request to the server: " + e.getMessage());
234         }
235 
236     }
237 
238     /**
239      * Provides dynamic string lookup of key/value string pairs defined in a module's host HTML page.
240      */
241     public static Dictionary getDictionary() {
242         return dictionary;
243     }
244 
245     /**
246      * A String representing the value for the GWT meta property whose content is <em>locale</em>.
247      * See also <a href='http://code.google.com/webtoolkit/doc/latest/DevGuideI18nLocale.html#LocaleSpecifying'>GWT Dev Guide to i18n</a>
248      */
249     private String detectCurrentLocale(Element element) {
250         final NodeList<Element> meta = element.getOwnerDocument().getElementsByTagName("meta");
251         for (int i = 0; i < meta.getLength(); i++) {
252             MetaElement metaTag = ((MetaElement) meta.getItem(i));
253             if ("gwt:property".equals(metaTag.getName()) && metaTag.getContent().contains("locale")) {
254                 String[] split = metaTag.getContent().split("=");
255                 locale = split.length == 2 ? split[1] : "en";
256                 GWT.log("Detected Locale " + locale);
257                 break;
258             }
259         }
260         return locale;
261     }
262 
263     private void processCmsComments(Node node, MgnlElement mgnlElement) {
264         for (int i = 0; i < node.getChildCount(); i++) {
265             Node childNode = node.getChild(i);
266             if (childNode.getNodeType() == Comment.COMMENT_NODE) {
267 
268                 try {
269                     CMSComment comment = new CMSComment(((Comment)childNode.cast()).getData());
270 
271                     // this should be refactored after cms:edit tag is gone
272 
273                     if (comment.getTagName().equals("cms:edit") && !comment.isClosing()) {
274 
275                         GWT.log("processing comment " + comment);
276                         //We assume the first cms:edit we encounter in DOM is the page edit bar.
277                         if (!pageEditBarAlreadyProcessed) {
278                             GWT.log("element was detected as page edit bar. Injecting it...");
279                             PageBarWidget pageBarWidget = new PageBarWidget(this, comment);
280                             pageBarWidget.attach(childNode);
281                             pageEditBarAlreadyProcessed = true;
282 
283                             storage.addEditBar(mgnlElement, pageBarWidget);
284 
285 
286                             if (pageBarWidget.isPreviewMode()) {
287                                 //we just need the preview bar here
288                                 GWT.log("We're in preview mode, stop processing DOM.");
289                                 break;
290                             }
291                         }
292                         else if (mgnlElement != null && mgnlElement.isComponent()) {
293                             GWT.log("element is a plain edit bar. Injecting it...");
294                             EditBarWidget editBarWidget = new EditBarWidget(mgnlElement, comment, this);
295 
296                             editBarWidget.attach(childNode);
297                             storage.addEditBar(mgnlElement, editBarWidget);
298 
299                         }
300                         else if (mgnlElement != null && mgnlElement.isArea()) {
301                             GWT.log("element was detected as area edit bar. Injecting it...");
302 
303                             AreaBarWidget areaBarWidget = new AreaBarWidget(mgnlElement, comment, this);
304                             if (areaBarWidget.hasControls) {
305                                 areaBarWidget.attach(childNode);
306                                 storage.addEditBar(mgnlElement, areaBarWidget);
307                             }
308                             AbstractOverlayWidget overlay = new AreaOverlayWidget(mgnlElement);
309                             overlay.attach();
310                             storage.addOverlay(mgnlElement, overlay);
311 
312                         }
313                     }
314                     else if (mgnlElement != null && comment.isClosing() && !comment.getTagName().equals("cms:edit")) {
315 
316                             mgnlElement = mgnlElement.getParent();
317                     }
318                     else {
319                         try {
320                             mgnlElement = new MgnlElement(comment, mgnlElement);
321 
322                             if (mgnlElement.getParent() == null) {
323                                 storage.addRoot(mgnlElement);
324                             }
325                             else {
326                                 mgnlElement.getParent().getChildren().add(mgnlElement);
327                             }
328 
329                         }
330                         catch (IllegalArgumentException e) {
331                             GWT.log("Not MgnlElement, skipping: " + e.toString());
332                         }
333                     }
334                 }
335                 catch (IllegalArgumentException e) {
336                     GWT.log("Not CMSComment element, skipping: " + e.toString());
337 
338                 }
339             }
340             else if (childNode.getNodeType() == Element.ELEMENT_NODE && mgnlElement != null) {
341 
342                 Element element = childNode.cast();
343                 if (element.hasTagName("A")) {
344                     disableLink(element);
345                 }
346                     storage.addElement(mgnlElement, element);
347 
348                     if (!element.getClassName().equals("mgnlAreaEditBar") && !element.getClassName().equals("mgnlEditBar")) {
349                         MgnlElement area = mgnlElement;
350                         if (mgnlElement.isComponent()) {
351                             area = mgnlElement.getParentArea();
352                         }
353                         if (area != null) {
354                             if (area.getFirstElement() == null) {
355                                 area.setFirstElement(element);
356                             }
357                             if (area.getLastElement() == null || !area.getLastElement().isOrHasChild(element)) {
358                                 area.setLastElement(element);
359                             }
360                         }
361                     }
362 
363             }
364 
365             processCmsComments(childNode, mgnlElement);
366         }
367 
368     }
369 
370     public native void disableLink(Element element) /*-{
371         if (element.onclick == null) {
372             element.onclick = function() {
373               return false;
374             };
375         }
376       }-*/;
377 
378     private void cleanRootElements() {
379 
380 
381         List<MgnlElement> newRoots = new LinkedList<MgnlElement>();
382         GWT.log(String.valueOf(storage.rootElements.size()));
383         Iterator<MgnlElement> it = storage.rootElements.iterator();
384         while (it.hasNext()) {
385             MgnlElement root = it.next();
386             if (storage.getEditBar(root) == null) {
387                 for (MgnlElement child : root.getChildren()) {
388                         child.setParent(null);
389                         newRoots.add(child);
390                 }
391                 it.remove();
392             }
393         }
394         GWT.log(String.valueOf(storage.rootElements.size()));
395 
396         storage.rootElements.addAll(newRoots);
397         GWT.log(String.valueOf(storage.rootElements.size()));
398 
399     }
400 
401 }