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.ui.form.field.factory;
35
36 import info.magnolia.i18nsystem.SimpleTranslator;
37 import info.magnolia.repository.RepositoryConstants;
38 import info.magnolia.ui.api.app.AppController;
39 import info.magnolia.ui.api.app.ChooseDialogCallback;
40 import info.magnolia.ui.api.context.UiContext;
41 import info.magnolia.ui.form.field.definition.RichTextFieldDefinition;
42 import info.magnolia.ui.vaadin.integration.jcr.JcrItemId;
43 import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
44 import info.magnolia.ui.vaadin.richtext.MagnoliaRichTextField;
45 import info.magnolia.ui.vaadin.richtext.MagnoliaRichTextFieldConfig;
46 import info.magnolia.ui.vaadin.richtext.MagnoliaRichTextFieldConfig.ToolbarGroup;
47
48 import java.util.ArrayList;
49 import java.util.List;
50
51 import javax.jcr.Node;
52
53 import org.apache.commons.lang3.StringUtils;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import com.google.gson.Gson;
58 import com.google.inject.Inject;
59 import com.vaadin.data.Item;
60 import com.vaadin.server.ClientConnector.AttachEvent;
61 import com.vaadin.server.ClientConnector.AttachListener;
62 import com.vaadin.server.Page;
63 import com.vaadin.server.Sizeable.Unit;
64 import com.vaadin.server.VaadinService;
65 import com.vaadin.server.WebBrowser;
66 import com.vaadin.ui.Field;
67
68
69
70
71 public class RichTextFieldFactory extends AbstractFieldFactory<RichTextFieldDefinition, String> {
72
73 private static final Logger log = LoggerFactory.getLogger(RichTextFieldFactory.class);
74
75 private static final String PLUGIN_NAME_MAGNOLIALINK = "magnolialink";
76
77 private static final String PLUGIN_PATH_MAGNOLIALINK = "/VAADIN/js/magnolialink/";
78
79
80
81
82 public static final String EVENT_SEND_MAGNOLIA_LINK = "mgnlLinkSelected";
83
84
85
86
87
88
89 public static final String EVENT_CANCEL_LINK = "mgnlLinkCancel";
90
91
92
93
94
95 public static final String EVENT_GET_MAGNOLIA_LINK = "mgnlGetLink";
96
97 protected final AppController appController;
98 protected final UiContext uiContext;
99 protected final SimpleTranslator i18n;
100 protected MagnoliaRichTextField richTextEditor;
101
102 @Inject
103 public RichTextFieldFactory(RichTextFieldDefinition definition, Item relatedFieldItem, AppController appController, UiContext uiContext, SimpleTranslator i18n) {
104 super(definition, relatedFieldItem);
105 this.appController = appController;
106 this.uiContext = uiContext;
107 this.i18n = i18n;
108 }
109
110 @Override
111 protected Field<String> createFieldComponent() {
112
113 MagnoliaRichTextFieldConfig config = initializeCKEditorConfig();
114 richTextEditor = new MagnoliaRichTextField(config);
115 if (definition.getHeight() > 0) {
116 richTextEditor.setHeight(definition.getHeight(), Unit.PIXELS);
117 }
118
119 richTextEditor.addAttachListener(new AttachListener() {
120 @Override
121 public void attach(AttachEvent event) {
122 WebBrowser browser = Page.getCurrent().getWebBrowser();
123 if (browser.isIOS() || browser.isAndroid()) {
124
125 richTextEditor.setEnabled(false);
126 richTextEditor.setReadOnly(true);
127 richTextEditor.addStyleName("richtextfield-disabled");
128 }
129 }
130 });
131
132 richTextEditor.addListener(new MagnoliaRichTextField.PluginListener() {
133 @Override
134 public void onPluginEvent(String eventName, String value) {
135 if (eventName.equals(EVENT_GET_MAGNOLIA_LINK)) {
136 try {
137 Gson gson = new Gson();
138 PluginData pluginData = gson.fromJson(value, PluginData.class);
139 openLinkDialog(pluginData.path, pluginData.workspace);
140 } catch (Exception e) {
141 log.error("openLinkDialog failed", e);
142 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, i18n.translate("ui-form.richtexteditorexception.opentargetappfailure"));
143 }
144 }
145 }
146 });
147
148 return richTextEditor;
149 }
150
151 protected MagnoliaRichTextFieldConfig initializeCKEditorConfig() {
152
153 MagnoliaRichTextFieldConfig config = new MagnoliaRichTextFieldConfig();
154 String path = VaadinService.getCurrentRequest().getContextPath();
155
156
157 config.addExternalPlugin(PLUGIN_NAME_MAGNOLIALINK, path + PLUGIN_PATH_MAGNOLIALINK);
158 config.addListenedEvent(EVENT_GET_MAGNOLIA_LINK);
159
160
161 if (StringUtils.isNotBlank(definition.getConfigJsFile())) {
162 config.addExtraConfig("customConfig", "'" + path + definition.getConfigJsFile() + "'");
163 return config;
164 }
165
166
167 if (!definition.isAlignment()) {
168 config.addToRemovePlugins("justify");
169 }
170 if (!definition.isImages()) {
171 config.addToRemovePlugins("image");
172 }
173 if (!definition.isLists()) {
174
175 config.addToRemovePlugins("enterkey");
176 config.addToRemovePlugins("indent");
177 config.addToRemovePlugins("list");
178 }
179 if (!definition.isSource()) {
180 config.addToRemovePlugins("sourcearea");
181 }
182 if (!definition.isTables()) {
183 config.addToRemovePlugins("table");
184 config.addToRemovePlugins("tabletools");
185 }
186
187 if (definition.getColors() != null) {
188 config.addExtraConfig("colorButton_colors", "'" + definition.getColors() + "'");
189 config.addExtraConfig("colorButton_enableMore", "false");
190 config.addToRemovePlugins("colordialog");
191 } else {
192 config.addToRemovePlugins("colorbutton");
193 config.addToRemovePlugins("colordialog");
194 }
195 if (definition.getFonts() != null) {
196 config.addExtraConfig("font_names", "'" + definition.getFonts() + "'");
197 } else {
198 config.addExtraConfig("removeButtons", "'Font'");
199 }
200 if (definition.getFontSizes() != null) {
201 config.addExtraConfig("fontSize_sizes", "'" + definition.getFontSizes() + "'");
202 } else {
203 config.addExtraConfig("removeButtons", "'FontSize'");
204 }
205 if (definition.getFonts() == null && definition.getFontSizes() == null) {
206 config.addToRemovePlugins("font");
207 config.addToRemovePlugins("fontSize");
208 }
209
210
211 List<ToolbarGroup> toolbars = initializeToolbarConfig();
212 config.addToolbarLine(toolbars);
213
214 config.addToExtraPlugins(PLUGIN_NAME_MAGNOLIALINK);
215 config.addToRemovePlugins("elementspath");
216 config.setBaseFloatZIndex(150);
217 config.setResizeEnabled(false);
218
219 return config;
220 }
221
222 protected List<ToolbarGroup> initializeToolbarConfig() {
223 List<ToolbarGroup> toolbars = new ArrayList<ToolbarGroup>();
224 toolbars.add(new ToolbarGroup("basicstyles", new String[] { "Bold", "Italic", "Underline", "SpecialChar" }));
225 toolbars.add(new ToolbarGroup("paragraph", new String[] { "NumberedList", "BulletedList", "JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock", "Image", "Table" }));
226 toolbars.add(new ToolbarGroup("links", new String[] { "Link", "InternalLink", "DamLink", "Unlink" }));
227 toolbars.add(new ToolbarGroup("styles", new String[] { "Font", "FontSize", "TextColor" }));
228 toolbars.add(new ToolbarGroup("clipboard", new String[] { "Cut", "Copy", "Paste", "PasteText", "PasteFromWord" }));
229 toolbars.add(new ToolbarGroup("undo", new String[] { "Undo", "Redo" }));
230 toolbars.add(new ToolbarGroup("tools", new String[] { "Source" }));
231 return toolbars;
232 }
233
234 private String mapWorkSpaceToApp(String workspace) {
235 if (workspace.equalsIgnoreCase("dam")) {
236 return "assets";
237 } else if (workspace.equalsIgnoreCase(RepositoryConstants.WEBSITE)) {
238 return "pages";
239 }
240
241 return "";
242 }
243
244 private void openLinkDialog(String path, String workspace) {
245 appController.openChooseDialog(mapWorkSpaceToApp(workspace), uiContext, null, new ChooseDialogCallback() {
246 @Override
247 public void onItemChosen(String actionName, Object chosenValue) {
248 if (!(chosenValue instanceof JcrItemId)) {
249 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK);
250 return;
251 }
252 try {
253 javax.jcr.Item jcrItem = JcrItemUtil.getJcrItem((JcrItemId) chosenValue);
254 if (!jcrItem.isNode()) {
255 return;
256 }
257 final Node selected = (Node) jcrItem;
258 Gson gson = new Gson();
259 MagnoliaLink mlink = new MagnoliaLink();
260 mlink.identifier = selected.getIdentifier();
261 mlink.repository = selected.getSession().getWorkspace().getName();
262 mlink.path = selected.getPath();
263 if (selected.hasProperty("title")) {
264 mlink.caption = selected.getProperty("title").getString();
265 } else {
266 mlink.caption = selected.getName();
267 }
268
269 richTextEditor.firePluginEvent(EVENT_SEND_MAGNOLIA_LINK, gson.toJson(mlink));
270 } catch (Exception e) {
271 String error = i18n.translate("ui-form.richtexteditorexception.cannotaccessselecteditem");
272 log.error(error, e);
273 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, error);
274 }
275 }
276
277 @Override
278 public void onCancel() {
279 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK);
280 }
281 });
282 }
283
284
285
286
287 protected static class MagnoliaLink {
288
289 public String identifier;
290
291 public String repository;
292
293 public String path;
294
295 public String caption;
296
297 }
298
299
300
301
302 protected static class PluginData {
303 public String workspace;
304 public String path;
305 }
306 }