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