View Javadoc
1   /**
2    * This file Copyright (c) 2012-2018 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.ui.form.field.factory;
35  
36  import static info.magnolia.ui.field.factory.RichTextFieldFactory.PLUGIN_NAME_MAGNOLIALINK;
37  import static info.magnolia.ui.field.factory.RichTextFieldFactory.PLUGIN_PATH_MAGNOLIALINK;
38  import static info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorConfig.*;
39  import static info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorTextFieldEvents.*;
40  
41  import info.magnolia.i18nsystem.SimpleTranslator;
42  import info.magnolia.repository.RepositoryConstants;
43  import info.magnolia.ui.api.app.AppController;
44  import info.magnolia.ui.api.app.ChooseDialogCallback;
45  import info.magnolia.ui.api.context.UiContext;
46  import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
47  import info.magnolia.ui.form.field.definition.RichTextFieldDefinition;
48  import info.magnolia.ui.field.factory.RichTextFieldFactory.MagnoliaLink;
49  import info.magnolia.ui.field.factory.RichTextFieldFactory.PluginData;
50  import info.magnolia.ui.vaadin.integration.jcr.JcrItemId;
51  import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
52  import info.magnolia.ui.vaadin.richtext.MagnoliaRichTextField;
53  import info.magnolia.ui.vaadin.richtext.MagnoliaRichTextFieldConfig;
54  
55  import java.util.List;
56  
57  import javax.inject.Inject;
58  import javax.jcr.Node;
59  
60  import org.apache.commons.lang3.StringUtils;
61  import org.slf4j.Logger;
62  import org.slf4j.LoggerFactory;
63  
64  import com.google.gson.Gson;
65  import com.vaadin.server.Sizeable.Unit;
66  import com.vaadin.server.VaadinService;
67  import com.vaadin.v7.data.Item;
68  import com.vaadin.v7.ui.Field;
69  
70  /**
71   * Creates and configures a rich-text field based on its definition.
72   */
73  public class RichTextFieldFactory extends AbstractFieldFactory<RichTextFieldDefinition, String> {
74  
75      private static final Logger log = LoggerFactory.getLogger(RichTextFieldFactory.class);
76  
77      protected final AppController appController;
78      protected final UiContext uiContext;
79      protected final SimpleTranslator i18n;
80      protected MagnoliaRichTextField richTextEditor;
81  
82      @Inject
83      public RichTextFieldFactory(RichTextFieldDefinition definition, Item relatedFieldItem, UiContext uiContext, I18NAuthoringSupport i18NAuthoringSupport, AppController appController, SimpleTranslator i18n) {
84          super(definition, relatedFieldItem, uiContext, i18NAuthoringSupport);
85          this.appController = appController;
86          this.uiContext = uiContext;
87          this.i18n = i18n;
88      }
89  
90      @Override
91      protected Field<String> createFieldComponent() {
92  
93          MagnoliaRichTextFieldConfig config = initializeCKEditorConfig();
94          richTextEditor = new MagnoliaRichTextField(config);
95          if (definition.getHeight() > 0) {
96              richTextEditor.setHeight(definition.getHeight(), Unit.PIXELS);
97          }
98  
99          richTextEditor.addListener(new MagnoliaRichTextField.PluginListener() {
100             @Override
101             public void onPluginEvent(String eventName, String value) {
102                 if (eventName.equals(EVENT_GET_MAGNOLIA_LINK)) {
103                     try {
104                         Gson gson = new Gson();
105                         PluginData pluginData = gson.fromJson(value, PluginData.class);
106                         openLinkDialog(pluginData.path, pluginData.workspace);
107                     } catch (Exception e) {
108                         log.error("openLinkDialog failed", e);
109                         richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, i18n.translate("ui-form.richtexteditorexception.opentargetappfailure"));
110                     }
111                 }
112             }
113         });
114 
115         return richTextEditor;
116     }
117 
118     protected MagnoliaRichTextFieldConfig initializeCKEditorConfig() {
119 
120         MagnoliaRichTextFieldConfig config = new MagnoliaRichTextFieldConfig();
121         String path = VaadinService.getCurrentRequest().getContextPath();
122 
123         // MAGNOLIA LINK PLUGIN — may be used with/without customConfig
124         config.addExternalPlugin(PLUGIN_NAME_MAGNOLIALINK, path + PLUGIN_PATH_MAGNOLIALINK);
125         config.addListenedEvent(EVENT_GET_MAGNOLIA_LINK);
126 
127         // CUSTOM CONFIG.JS — bypass further config because it can't be overridden otherwise
128         if (StringUtils.isNotBlank(definition.getConfigJsFile())) {
129             config.addExtraConfig("customConfig", "'" + path + definition.getConfigJsFile() + "'");
130             return config;
131         }
132 
133         // DEFINITION
134         if (!definition.isAlignment()) {
135             config.addToRemovePlugins("justify");
136         }
137         if (!definition.isImages()) {
138             config.addToRemovePlugins("image");
139         }
140         if (!definition.isLists()) {
141             // In CKEditor 4.1.1 enterkey depends on indent which itself depends on list
142             config.addToRemovePlugins("enterkey");
143             config.addToRemovePlugins("indent");
144             config.addToRemovePlugins("list");
145         }
146         if (!definition.isSource()) {
147             config.addToRemovePlugins("sourcearea");
148         }
149         if (!definition.isTables()) {
150             config.addToRemovePlugins("table");
151             config.addToRemovePlugins("tabletools");
152             config.addToRemovePlugins("tableselection");
153         }
154 
155         if (definition.getColors() != null) {
156             config.addExtraConfig("colorButton_colors", "'" + definition.getColors() + "'");
157             config.addExtraConfig("colorButton_enableMore", "false");
158             config.addToRemovePlugins("colordialog");
159         } else {
160             config.addToRemovePlugins("colorbutton");
161             config.addToRemovePlugins("colordialog");
162         }
163         if (definition.getFonts() != null) {
164             config.addExtraConfig("font_names", "'" + definition.getFonts() + "'");
165         } else {
166             config.addExtraConfig("removeButtons", "'Font'");
167         }
168         if (definition.getFontSizes() != null) {
169             config.addExtraConfig("fontSize_sizes", "'" + definition.getFontSizes() + "'");
170         } else {
171             config.addExtraConfig("removeButtons", "'FontSize'");
172         }
173         if (definition.getFonts() == null && definition.getFontSizes() == null) {
174             config.addToRemovePlugins("font");
175             config.addToRemovePlugins("fontSize");
176         }
177 
178         // MAGNOLIA EXTRA CONFIG
179         List<ToolbarGroup> toolbars = initializeToolbarConfig();
180         config.addToolbarLine(toolbars);
181 
182         config.addToExtraPlugins(PLUGIN_NAME_MAGNOLIALINK);
183         config.addToRemovePlugins("elementspath");
184         config.setBaseFloatZIndex(150);
185         config.setResizeEnabled(false);
186 
187         return config;
188     }
189 
190     protected List<ToolbarGroup> initializeToolbarConfig() {
191         return defaultToolbar();
192     }
193 
194     private String mapWorkSpaceToApp(String workspace) {
195         if (workspace.equalsIgnoreCase("dam")) {
196             return "assets";
197         } else if (workspace.equalsIgnoreCase(RepositoryConstants.WEBSITE)) {
198             return "pages";
199         }
200 
201         return "";
202     }
203 
204     private void openLinkDialog(String path, String workspace) {
205         appController.openChooseDialog(mapWorkSpaceToApp(workspace), uiContext, path, new ChooseDialogCallback() {
206             @Override
207             public void onItemChosen(String actionName, Object chosenValue) {
208                 if (!(chosenValue instanceof JcrItemId)) {
209                     richTextEditor.firePluginEvent(EVENT_CANCEL_LINK);
210                     return;
211                 }
212                 try {
213                     javax.jcr.Item jcrItem = JcrItemUtil.getJcrItem((JcrItemId) chosenValue);
214                     if (!jcrItem.isNode()) {
215                         return;
216                     }
217                     final Node selected = (Node) jcrItem;
218                     Gson gson = new Gson();
219                     MagnoliaLink mlink = new MagnoliaLink();
220                     mlink.identifier = selected.getIdentifier();
221                     mlink.repository = selected.getSession().getWorkspace().getName();
222                     mlink.path = selected.getPath();
223                     if (selected.hasProperty("title")) {
224                         mlink.caption = selected.getProperty("title").getString();
225                     } else {
226                         mlink.caption = selected.getName();
227                     }
228 
229                     richTextEditor.firePluginEvent(EVENT_SEND_MAGNOLIA_LINK, gson.toJson(mlink));
230                 } catch (Exception e) {
231                     String error = i18n.translate("ui-form.richtexteditorexception.cannotaccessselecteditem");
232                     log.error(error, e);
233                     richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, error);
234                 }
235             }
236 
237             @Override
238             public void onCancel() {
239                 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK);
240             }
241         });
242     }
243 }