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  import info.magnolia.templating.editor.client.dom.CMSBoundary;
37  import info.magnolia.templating.editor.client.dom.CMSComment;
38  import info.magnolia.templating.editor.client.dom.Comment;
39  import info.magnolia.templating.editor.client.jsni.LegacyJavascript;
40  
41  import com.google.gwt.core.client.EntryPoint;
42  import com.google.gwt.core.client.GWT;
43  import com.google.gwt.dom.client.Document;
44  import com.google.gwt.dom.client.Element;
45  import com.google.gwt.dom.client.MetaElement;
46  import com.google.gwt.dom.client.Node;
47  import com.google.gwt.dom.client.NodeList;
48  import com.google.gwt.http.client.Request;
49  import com.google.gwt.http.client.RequestBuilder;
50  import com.google.gwt.http.client.RequestCallback;
51  import com.google.gwt.http.client.RequestException;
52  import com.google.gwt.http.client.Response;
53  import com.google.gwt.http.client.URL;
54  import com.google.gwt.http.client.UrlBuilder;
55  import com.google.gwt.i18n.client.Dictionary;
56  import com.google.gwt.user.client.Event;
57  import com.google.gwt.user.client.EventListener;
58  import com.google.gwt.user.client.Window;
59  import com.google.gwt.user.client.ui.AbsolutePanel;
60  import com.google.gwt.user.client.ui.HTML;
61  
62  /**
63   * Client side implementation of the page editor. Outputs ui widgets inside document element (typically the {@code <html>} element).
64   *
65   * @version $Id$
66   */
67  public class PageEditor extends HTML implements EventListener, EntryPoint {
68  
69      public static final String MARKER_EDIT = "cms:edit";
70      public static final String MARKER_AREA = "cms:area";
71      public static final String MARKER_COMPONENT = "cms:component";
72  
73  
74      private boolean pageEditBarAlreadyProcessed = false;
75      private String locale;
76      private static Dictionary dictionary;
77  
78      @Override
79      public void onModuleLoad() {
80          Element documentElement = Document.get().getDocumentElement();
81  
82          locale = detectCurrentLocale(documentElement);
83          //TODO move messages we need to this module?
84          LegacyJavascript.exposeMgnlMessagesToGwtDictionary("info.magnolia.module.admininterface.messages");
85          dictionary = Dictionary.getDictionary("mgnlGwtMessages");
86  
87          processCmsComments(documentElement, null);
88  
89          AbsolutePanel boundaryOverlay = new AbsolutePanel();
90          boundaryOverlay.getElement().setId("mgnlBoundary");
91          documentElement.getElementsByTagName("body").getItem(0).appendChild(boundaryOverlay.getElement());
92      }
93  
94      @Override
95      public void onBrowserEvent(Event event) {
96          super.onBrowserEvent(event);
97      }
98  
99      /**
100      * TODO: rename and/or remove arguments no longer needed (collectionName, nodeName).
101      */
102     public void openDialog(String dialog, String workspace, String path, String collectionName, String nodeName) {
103         if (collectionName == null) {
104             collectionName = "";
105         }
106         if (nodeName == null) {
107             nodeName = "";
108         }
109 
110         LegacyJavascript.mgnlOpenDialog(path, collectionName, nodeName, dialog, workspace, "", "", "", locale);
111     }
112 
113     public void moveComponentStart(String id) {
114         LegacyJavascript.mgnlMoveNodeStart(id);
115     }
116 
117     public void moveComponentEnd(AbstractBarWidget source, String path) {
118         LegacyJavascript.mgnlMoveNodeEnd(source.getElement(), path);
119     }
120 
121     public void moveComponentOver(AbstractBarWidget source) {
122         LegacyJavascript.mgnlMoveNodeHigh(source.getElement());
123     }
124 
125     public void moveComponentOut(AbstractBarWidget source) {
126         LegacyJavascript.mgnlMoveNodeReset(source.getElement());
127     }
128 
129     public void deleteComponent(String path) {
130         LegacyJavascript.mgnlDeleteNode(path);
131     }
132 
133     public void addComponent(String workspace, String path, String collectionName, String nodeName, String availableComponents) {
134         if (collectionName == null) {
135             collectionName = "";
136         }
137         if (nodeName == null) {
138             nodeName = "mgnlNew";
139         }
140         if (availableComponents == null) {
141             availableComponents = "";
142         }
143         LegacyJavascript.mgnlOpenDialog(path, collectionName, nodeName, availableComponents, workspace, ".magnolia/dialogs/selectParagraph.html", "", "", locale);
144     }
145 
146     public void preview(boolean isPreview) {
147         LegacyJavascript.mgnlPreview(isPreview);
148     }
149 
150     public void showTree(String workspace, String path) {
151         LegacyJavascript.showTree(workspace, path);
152 
153     }
154 
155     public void createComponent(String workspace, String parent, String relPath, String itemType) {
156         GWT.log("Creating [" + itemType + "] in workspace [" + workspace + "] at path [" + parent + "/" + relPath + "]");
157 
158         final StringBuilder url = new StringBuilder();
159         url.append(LegacyJavascript.getContextPath() + ".magnolia/pageeditor/PageEditorServlet");
160         url.append("?action=create");
161         url.append("&workspace=" + workspace);
162         url.append("&parent=" + parent);
163         url.append("&relPath=" + relPath);
164         url.append("&itemType=" + itemType);
165 
166         RequestBuilder req = new RequestBuilder(RequestBuilder.GET, URL.encode(url.toString()));
167         req.setCallback(new RequestCallback() {
168 
169             @Override
170             public void onResponseReceived(Request request, Response response) {
171                 int status = response.getStatusCode();
172                 String responseText = "";
173                 boolean reload = false;
174 
175                 switch (status) {
176                     case Response.SC_OK:
177                         reload = true;
178                         break;
179                     case Response.SC_UNAUTHORIZED:
180                         responseText = "Is your session expired? Please, try to login again.";
181                         break;
182                     default:
183                         responseText = "See logs for more details.";
184                 }
185 
186                 if (reload) {
187                     UrlBuilder urlBuilder = Window.Location.createUrlBuilder();
188 
189                     urlBuilder.removeParameter("mgnlIntercept");
190                     urlBuilder.removeParameter("mgnlPath");
191 
192                     Window.Location.replace(urlBuilder.buildString());
193                 } else {
194                     Window.alert("An error occurred on the server: response status code is " + status + "\n" + responseText);
195                 }
196             }
197 
198             @Override
199             public void onError(Request request, Throwable exception) {
200                 Window.alert(exception.getMessage());
201             }
202         });
203         try {
204             req.send();
205         } catch (RequestException e) {
206             Window.alert("An error occurred while trying to send a request to the server: " + e.getMessage());
207         }
208 
209     }
210 
211     /**
212      * Provides dynamic string lookup of key/value string pairs defined in a module's host HTML page.
213      */
214     public static Dictionary getDictionary() {
215         return dictionary;
216     }
217 
218     /**
219      * A String representing the value for the GWT meta property whose content is <em>locale</em>.
220      * See also <a href='http://code.google.com/webtoolkit/doc/latest/DevGuideI18nLocale.html#LocaleSpecifying'>GWT Dev Guide to i18n</a>
221      */
222     private String detectCurrentLocale(Element element) {
223         final NodeList<Element> meta = element.getOwnerDocument().getElementsByTagName("meta");
224         for (int i = 0; i < meta.getLength(); i++) {
225             MetaElement metaTag = ((MetaElement) meta.getItem(i));
226             if ("gwt:property".equals(metaTag.getName()) && metaTag.getContent().contains("locale")) {
227                 String[] split = metaTag.getContent().split("=");
228                 locale = split.length == 2 ? split[1] : "en";
229                 GWT.log("Detected Locale " + locale);
230                 break;
231             }
232         }
233         return locale;
234     }
235 
236     private void processCmsComments(Node node, CMSBoundary boundary) {
237         for (int i = 0; i < node.getChildCount(); i++) {
238             Node childNode = node.getChild(i);
239             if (childNode.getNodeType() == Comment.COMMENT_NODE) {
240 
241                 try {
242                     CMSComment comment = new CMSComment(((Comment)childNode.cast()).getData());
243 
244                     if (comment.isClosing()) {
245                         if (boundary != null) {
246                             boundary = boundary.getParentBoundary();
247                         }
248                         continue;
249                     }
250                     try {
251                         boundary = new CMSBoundary(comment, boundary);
252 
253                         if( boundary.getParentBoundary() != null)
254                             boundary.getParentBoundary().getChildBoundaries().add(boundary);
255 /*
256                         if (boundary.isArea()) {
257 
258                             Node sibling = childNode.getNextSibling();
259                             while (sibling != null) {
260                                 if (sibling.getNodeType() == Node.ELEMENT_NODE && boundary.getFirstElement() == null) {
261                                     boundary.setFirstElement((Element)sibling);
262                                     break;
263                                 }
264                                 sibling = sibling.getNextSibling();
265                             }
266 
267                         }*/
268 
269                         if (boundary.isEdit()) {
270                             GWT.log("processing comment " + comment);
271                             //We assume the first cms:edit we encounter in DOM is the page edit bar.
272                             if (!pageEditBarAlreadyProcessed) {
273                                 GWT.log("element was detected as page edit bar. Injecting it...");
274                                 PageBarWidget pageBarWidget = new PageBarWidget(this, comment);
275                                 pageBarWidget.attach(childNode);
276                                 pageEditBarAlreadyProcessed = true;
277 
278                                 boundary.setWidget(pageBarWidget);
279 
280 
281                                 if (pageBarWidget.isPreviewMode()) {
282                                     //we just need the preview bar here
283                                     GWT.log("We're in preview mode, stop processing DOM.");
284                                     break;
285                                 }
286                             }
287                             else if (boundary.getParentBoundary().isComponent()) {
288                                 GWT.log("element is a plain edit bar. Injecting it...");
289                                 EditBarWidget editBarWidget = new EditBarWidget(boundary, this);
290 
291                                 editBarWidget.attach(childNode);
292 
293                                 boundary.setWidget(editBarWidget);
294 
295                             }
296                             else if (boundary.getParentBoundary().isArea()) {
297                                 GWT.log("element was detected as area edit bar. Injecting it...");
298                                 AreaBarWidget areaBarWidget = new AreaBarWidget(boundary, this);
299                                 areaBarWidget.attach(childNode);
300 
301                                 boundary.setWidget(areaBarWidget);
302                             }
303                         }
304                     }
305                     catch (IllegalArgumentException e) {
306                         GWT.log("Not CMSBoundary element, skipping: " + e.toString());
307                     }
308                 }
309                 catch (IllegalArgumentException e) {
310                     GWT.log("Not CMSComment element, skipping: " + e.toString());
311 
312                 }
313             }
314 
315             processCmsComments(childNode, boundary);
316         }
317 
318     }
319 
320 }