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.field.factory;
35
36 import static info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorConfig.defaultToolbar;
37 import static info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorTextFieldEvents.*;
38
39 import info.magnolia.i18nsystem.SimpleTranslator;
40 import info.magnolia.jcr.util.PropertyUtil;
41 import info.magnolia.jcr.util.SessionUtil;
42 import info.magnolia.objectfactory.ComponentProvider;
43 import info.magnolia.repository.RepositoryConstants;
44 import info.magnolia.ui.api.app.registry.AppDescriptorRegistry;
45 import info.magnolia.ui.chooser.SingleItemWorkbenchChooser;
46 import info.magnolia.ui.chooser.definition.AppAwareWorkbenchChooserDefinition;
47 import info.magnolia.ui.chooser.definition.ChooserDefinition;
48 import info.magnolia.ui.chooser.definition.SingleItemWorkbenchChooserDefinition;
49 import info.magnolia.ui.dialog.DialogBuilder;
50 import info.magnolia.ui.field.RichTextFieldDefinition;
51 import info.magnolia.ui.framework.overlay.ChooserController;
52 import info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorConfig;
53 import info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorConfig.ToolbarGroup;
54 import info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorTextField;
55
56 import java.util.Collections;
57 import java.util.List;
58
59 import javax.inject.Inject;
60 import javax.jcr.Node;
61 import javax.jcr.RepositoryException;
62
63 import org.apache.commons.lang3.StringUtils;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67 import com.google.gson.Gson;
68 import com.vaadin.server.Sizeable.Unit;
69 import com.vaadin.server.VaadinService;
70 import com.vaadin.ui.Button;
71 import com.vaadin.ui.Component;
72 import com.vaadin.ui.Window;
73
74 import lombok.SneakyThrows;
75
76
77
78
79
80 public class RichTextFieldFactory extends AbstractFieldFactory<String, RichTextFieldDefinition> {
81
82 public static final String PLUGIN_NAME_MAGNOLIALINK = "magnolialink";
83 public static final String PLUGIN_PATH_MAGNOLIALINK = "/VAADIN/js/magnolialink/";
84 public static final String PLUGIN_NAME_MAGNOLIAEXPAND = "magnoliaexpand";
85 public static final String PLUGIN_PATH_MAGNOLIAEXPAND = "/VAADIN/js/magnoliaexpand/";
86
87
88
89
90
91
92 protected static final int CKEDITOR_BASE_Z_INDEX = 10000;
93
94 private static final Logger log = LoggerFactory.getLogger(RichTextFieldFactory.class);
95
96 private final SimpleTranslator i18n;
97 private final ChooserController chooserController;
98
99 @Inject
100 public RichTextFieldFactory(RichTextFieldDefinition definition, ComponentProvider componentProvider,
101 SimpleTranslator i18n, ChooserController chooserController,
102 AppDescriptorRegistry appDescriptorRegistry) {
103 super(definition, componentProvider);
104 this.i18n = i18n;
105 this.chooserController = chooserController;
106 }
107
108 @Override
109 protected Component createFieldComponent() {
110 MagnoliaCKEditorConfig config = initializeCKEditorConfig();
111 return createCkEditorField(config);
112 }
113
114 protected MagnoliaCKEditorTextField createCkEditorField(MagnoliaCKEditorConfig config) {
115 MagnoliaCKEditorTextField richTextEditor = new MagnoliaCKEditorTextField(config);
116
117 if (getDefinition().getHeight() > 0) {
118 richTextEditor.setHeight(getDefinition().getHeight(), Unit.PIXELS);
119 }
120
121 richTextEditor.addListener((eventName, value) -> {
122 if (eventName.equals(EVENT_GET_MAGNOLIA_LINK)) {
123 try {
124 Gson gson = new Gson();
125 PluginData pluginData = gson.fromJson(value, PluginData.class);
126 openLinkDialog(richTextEditor, pluginData.path, pluginData.workspace);
127 } catch (Exception e) {
128 log.error("openLinkDialog failed", e);
129 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, i18n.translate("ui-framework.richtexteditorexception.opentargetappfailure"));
130 }
131 }
132 if (eventName.equals(EVENT_EXPAND_TO_DIALOG)) {
133 expandEditorToDialog(richTextEditor);
134 }
135 });
136
137 return richTextEditor;
138 }
139
140 protected ChooserController getChooserController() {
141 return chooserController;
142 }
143
144 protected SimpleTranslator getI18n() {
145 return i18n;
146 }
147
148 private void expandEditorToDialog(MagnoliaCKEditorTextField richTextEditor) {
149 MagnoliaCKEditorConfig config = initializeCKEditorConfig();
150 config.addToRemovePlugins(PLUGIN_NAME_MAGNOLIAEXPAND);
151
152 MagnoliaCKEditorTextField tobeExpandedRichTextEditor = this.createCkEditorField(config);
153 tobeExpandedRichTextEditor.setValue(richTextEditor.getValue());
154
155 buildAndOpenExpandDialog(richTextEditor, tobeExpandedRichTextEditor);
156 }
157
158 private void buildAndOpenExpandDialog(MagnoliaCKEditorTextField richTextEditor, MagnoliaCKEditorTextField tempRichTextEditor) {
159 Button backToForm = new Button(i18n.translate("ui-framework.richTextEditor.expand.dialog.button.backToForm"));
160 backToForm.addStyleName("commit");
161
162 Window dialog = new DialogBuilder().withContent(tempRichTextEditor)
163 .withTitle(richTextEditor.getCaption())
164 .wide(true)
165 .withCloseListener(e -> richTextEditor.setValue(tempRichTextEditor.getValue()))
166 .withActions(Collections.singletonList(backToForm))
167 .buildAndOpen();
168 dialog.addStyleName("expand");
169 backToForm.addClickListener(e -> dialog.close());
170 }
171
172 protected MagnoliaCKEditorConfig initializeCKEditorConfig() {
173
174 MagnoliaCKEditorConfig config = new MagnoliaCKEditorConfig();
175 String path = VaadinService.getCurrentRequest().getContextPath();
176
177
178 config.addExternalPlugin(PLUGIN_NAME_MAGNOLIALINK, path + PLUGIN_PATH_MAGNOLIALINK);
179 config.addExternalPlugin(PLUGIN_NAME_MAGNOLIAEXPAND, path + PLUGIN_PATH_MAGNOLIAEXPAND);
180 config.addListenedEvent(EVENT_GET_MAGNOLIA_LINK);
181 config.addListenedEvent(EVENT_EXPAND_TO_DIALOG);
182
183 config.setBaseFloatZIndex(CKEDITOR_BASE_Z_INDEX);
184
185
186 if (StringUtils.isNotBlank(getDefinition().getConfigJsFile())) {
187 config.addExtraConfig("customConfig", "'" + path + getDefinition().getConfigJsFile() + "'");
188 return config;
189 }
190
191
192 if (!getDefinition().isAlignment()) {
193 config.addToRemovePlugins("justify");
194 }
195 if (!getDefinition().isImages()) {
196 config.addToRemovePlugins("image");
197 }
198 if (!getDefinition().isLists()) {
199
200 config.addToRemovePlugins("enterkey");
201 config.addToRemovePlugins("indent");
202 config.addToRemovePlugins("list");
203 }
204 if (!getDefinition().isSource()) {
205 config.addToRemovePlugins("sourcearea");
206 }
207 if (!getDefinition().isTables()) {
208 config.addToRemovePlugins("table");
209 config.addToRemovePlugins("tabletools");
210 config.addToRemovePlugins("tableselection");
211 }
212
213 if (getDefinition().getColors() != null) {
214 config.addExtraConfig("colorButton_colors", "'" + getDefinition().getColors() + "'");
215 config.addExtraConfig("colorButton_enableMore", "false");
216 config.addToRemovePlugins("colordialog");
217 } else {
218 config.addToRemovePlugins("colorbutton");
219 config.addToRemovePlugins("colordialog");
220 }
221 if (getDefinition().getFonts() != null) {
222 config.addExtraConfig("font_names", "'" + getDefinition().getFonts() + "'");
223 } else {
224 config.addExtraConfig("removeButtons", "'Font'");
225 }
226 if (getDefinition().getFontSizes() != null) {
227 config.addExtraConfig("fontSize_sizes", "'" + getDefinition().getFontSizes() + "'");
228 } else {
229 config.addExtraConfig("removeButtons", "'FontSize'");
230 }
231 if (getDefinition().getFonts() == null && getDefinition().getFontSizes() == null) {
232 config.addToRemovePlugins("font");
233 config.addToRemovePlugins("fontSize");
234 }
235
236
237 List<ToolbarGroup> toolbars = initializeToolbarConfig();
238 config.addToolbarLine(toolbars);
239
240 config.addToExtraPlugins(PLUGIN_NAME_MAGNOLIALINK);
241 config.addToExtraPlugins(PLUGIN_NAME_MAGNOLIAEXPAND);
242 config.addToRemovePlugins("elementspath");
243 config.setResizeEnabled(false);
244 config.setContentsCss(path + "/VAADIN/themes/resurface/richtextfield-contents/styles.css");
245
246 return config;
247 }
248
249 protected List<ToolbarGroup> initializeToolbarConfig() {
250 return defaultToolbar();
251 }
252
253 private String mapWorkspaceToChooserDialog(String workspace) {
254 if (workspace.equalsIgnoreCase("dam")) {
255 return "dam";
256 } else if (workspace.equalsIgnoreCase(RepositoryConstants.WEBSITE)) {
257 return "pages-app";
258 }
259
260 throw new IllegalArgumentException(workspace + " is not a supported workspace by rich text field");
261 }
262
263 private void openLinkDialog(MagnoliaCKEditorTextField richTextEditor, String path, String workspace) {
264 chooserController.openChooser(createChooserDefinition(workspace), SessionUtil.getNode(workspace, path))
265 .whenComplete((ChooserController.ChooseResult<Node> result, Throwable e) -> {
266
267 if (!result.isChosen()) {
268 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK);
269 return;
270 }
271 if (e != null) {
272 String error = i18n.translate("ui-framework.richtexteditorexception.cannotaccessselecteditem");
273 log.error(error, e);
274 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, error);
275 }
276 result.getChoice().ifPresent(node -> {
277 Gson gson = new Gson();
278 MagnoliaLink mlink = createMagnoliaLink(node);
279 richTextEditor.firePluginEvent(EVENT_SEND_MAGNOLIA_LINK, gson.toJson(mlink));
280 });
281 });
282 }
283
284 @SneakyThrows(RepositoryException.class)
285 private MagnoliaLink createMagnoliaLink(Node node) {
286 MagnoliaLink mlink = new MagnoliaLink();
287 mlink.identifier = node.getIdentifier();
288 mlink.repository = node.getSession().getWorkspace().getName();
289 mlink.path = node.getPath();
290 mlink.caption = PropertyUtil.getString(node, "title", node.getName());
291
292 return mlink;
293 }
294
295
296
297
298
299 protected ChooserDefinition<Node, SingleItemWorkbenchChooser<Node>> createChooserDefinition(String workspace) {
300 SingleItemWorkbenchChooserDefinition<Node> definition = new SingleItemWorkbenchChooserDefinition<>();
301 final AppAwareWorkbenchChooserDefinition<Node> workbenchChooser = new AppAwareWorkbenchChooserDefinition<>();
302 workbenchChooser.setAppName(mapWorkspaceToChooserDialog(workspace));
303 definition.setWorkbenchChooser(workbenchChooser);
304 return definition;
305 }
306
307
308
309
310 public static class MagnoliaLink {
311
312 public String identifier;
313
314 public String repository;
315
316 public String path;
317
318 public String caption;
319
320 }
321
322
323
324
325 public static class PluginData {
326 public String workspace;
327 public String path;
328 }
329 }