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.field.RichTextFieldDefinition;
50 import info.magnolia.ui.framework.overlay.ChooserController;
51 import info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorConfig;
52 import info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorConfig.ToolbarGroup;
53 import info.magnolia.ui.vaadin.ckeditor.MagnoliaCKEditorTextField;
54
55 import java.util.List;
56
57 import javax.inject.Inject;
58 import javax.jcr.Node;
59 import javax.jcr.RepositoryException;
60
61 import org.apache.commons.lang3.StringUtils;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 import com.google.gson.Gson;
66 import com.vaadin.server.Sizeable.Unit;
67 import com.vaadin.server.VaadinService;
68 import com.vaadin.ui.Component;
69
70 import lombok.SneakyThrows;
71
72
73
74
75
76 public class RichTextFieldFactory extends AbstractFieldFactory<String, RichTextFieldDefinition> {
77
78 public static final String PLUGIN_NAME_MAGNOLIALINK = "magnolialink";
79 public static final String PLUGIN_PATH_MAGNOLIALINK = "/VAADIN/js/magnolialink/";
80
81 private static final Logger log = LoggerFactory.getLogger(RichTextFieldFactory.class);
82
83 private final SimpleTranslator i18n;
84 private final ChooserController chooserController;
85 private final AppDescriptorRegistry appDescriptorRegistry;
86
87 private MagnoliaCKEditorTextField richTextEditor;
88
89 @Inject
90 public RichTextFieldFactory(RichTextFieldDefinition definition, ComponentProvider componentProvider,
91 SimpleTranslator i18n, ChooserController chooserController,
92 AppDescriptorRegistry appDescriptorRegistry) {
93 super(definition, componentProvider);
94 this.i18n = i18n;
95 this.chooserController = chooserController;
96 this.appDescriptorRegistry = appDescriptorRegistry;
97 }
98
99 @Override
100 protected Component createFieldComponent() {
101 MagnoliaCKEditorConfig config = initializeCKEditorConfig();
102 richTextEditor = new MagnoliaCKEditorTextField(config);
103
104 if (getDefinition().getHeight() > 0) {
105 richTextEditor.setHeight(getDefinition().getHeight(), Unit.PIXELS);
106 }
107
108 richTextEditor.addListener((eventName, value) -> {
109 if (eventName.equals(EVENT_GET_MAGNOLIA_LINK)) {
110 try {
111 Gson gson = new Gson();
112 PluginData pluginData = gson.fromJson(value, PluginData.class);
113 openLinkDialog(pluginData.path, pluginData.workspace);
114 } catch (Exception e) {
115 log.error("openLinkDialog failed", e);
116 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, i18n.translate("ui-framework.richtexteditorexception.opentargetappfailure"));
117 }
118 }
119 });
120
121 return richTextEditor;
122 }
123
124 protected MagnoliaCKEditorConfig initializeCKEditorConfig() {
125
126 MagnoliaCKEditorConfig config = new MagnoliaCKEditorConfig();
127 String path = VaadinService.getCurrentRequest().getContextPath();
128
129
130 config.addExternalPlugin(PLUGIN_NAME_MAGNOLIALINK, path + PLUGIN_PATH_MAGNOLIALINK);
131 config.addListenedEvent(EVENT_GET_MAGNOLIA_LINK);
132
133
134 if (StringUtils.isNotBlank(getDefinition().getConfigJsFile())) {
135 config.addExtraConfig("customConfig", "'" + path + getDefinition().getConfigJsFile() + "'");
136 return config;
137 }
138
139
140 if (!getDefinition().isAlignment()) {
141 config.addToRemovePlugins("justify");
142 }
143 if (!getDefinition().isImages()) {
144 config.addToRemovePlugins("image");
145 }
146 if (!getDefinition().isLists()) {
147
148 config.addToRemovePlugins("enterkey");
149 config.addToRemovePlugins("indent");
150 config.addToRemovePlugins("list");
151 }
152 if (!getDefinition().isSource()) {
153 config.addToRemovePlugins("sourcearea");
154 }
155 if (!getDefinition().isTables()) {
156 config.addToRemovePlugins("table");
157 config.addToRemovePlugins("tabletools");
158 config.addToRemovePlugins("tableselection");
159 }
160
161 if (getDefinition().getColors() != null) {
162 config.addExtraConfig("colorButton_colors", "'" + getDefinition().getColors() + "'");
163 config.addExtraConfig("colorButton_enableMore", "false");
164 config.addToRemovePlugins("colordialog");
165 } else {
166 config.addToRemovePlugins("colorbutton");
167 config.addToRemovePlugins("colordialog");
168 }
169 if (getDefinition().getFonts() != null) {
170 config.addExtraConfig("font_names", "'" + getDefinition().getFonts() + "'");
171 } else {
172 config.addExtraConfig("removeButtons", "'Font'");
173 }
174 if (getDefinition().getFontSizes() != null) {
175 config.addExtraConfig("fontSize_sizes", "'" + getDefinition().getFontSizes() + "'");
176 } else {
177 config.addExtraConfig("removeButtons", "'FontSize'");
178 }
179 if (getDefinition().getFonts() == null && getDefinition().getFontSizes() == null) {
180 config.addToRemovePlugins("font");
181 config.addToRemovePlugins("fontSize");
182 }
183
184
185 List<ToolbarGroup> toolbars = initializeToolbarConfig();
186 config.addToolbarLine(toolbars);
187
188 config.addToExtraPlugins(PLUGIN_NAME_MAGNOLIALINK);
189 config.addToRemovePlugins("elementspath");
190 config.setBaseFloatZIndex(10000);
191 config.setResizeEnabled(false);
192 config.setContentsCss(path + "/VAADIN/themes/resurface/richtextfield-contents/styles.css");
193
194 return config;
195 }
196
197 protected List<ToolbarGroup> initializeToolbarConfig() {
198 return defaultToolbar();
199 }
200
201 private String mapWorkspaceToApp(String workspace) {
202 if (workspace.equalsIgnoreCase("dam")) {
203 return "dam";
204 } else if (workspace.equalsIgnoreCase(RepositoryConstants.WEBSITE)) {
205 return "pages-app";
206 }
207
208 throw new IllegalArgumentException(workspace + " is not a supported workspace by rich text field");
209 }
210
211 private void openLinkDialog(String path, String workspace) {
212 chooserController.openChooser(createChooserDefinition(workspace), SessionUtil.getNode(path, workspace))
213 .whenComplete((ChooserController.ChooseResult<Node> result, Throwable e) -> {
214
215 if (!result.isChosen()) {
216 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK);
217 return;
218 }
219 if (e != null) {
220 String error = i18n.translate("ui-framework.richtexteditorexception.cannotaccessselecteditem");
221 log.error(error, e);
222 richTextEditor.firePluginEvent(EVENT_CANCEL_LINK, error);
223 }
224 result.getChoice().ifPresent(node -> {
225 Gson gson = new Gson();
226 MagnoliaLink mlink = createMagnoliaLink(node);
227 richTextEditor.firePluginEvent(EVENT_SEND_MAGNOLIA_LINK, gson.toJson(mlink));
228 });
229 });
230 }
231
232 @SneakyThrows(RepositoryException.class)
233 private MagnoliaLink createMagnoliaLink(Node node) {
234 MagnoliaLink mlink = new MagnoliaLink();
235 mlink.identifier = node.getIdentifier();
236 mlink.repository = node.getSession().getWorkspace().getName();
237 mlink.path = node.getPath();
238 mlink.caption = PropertyUtil.getString(node, "title", node.getName());
239
240 return mlink;
241 }
242
243 private ChooserDefinition<Node, SingleItemWorkbenchChooser<Node>> createChooserDefinition(String workspace) {
244 SingleItemWorkbenchChooserDefinition<Node> definition = new SingleItemWorkbenchChooserDefinition<>();
245 AppAwareWorkbenchChooserDefinition<Node> workbenchChooserDefinition = new AppAwareWorkbenchChooserDefinition<>();
246 workbenchChooserDefinition.setAppName(mapWorkspaceToApp(workspace));
247 definition.setWorkbenchChooser(workbenchChooserDefinition);
248 return definition;
249 }
250
251
252
253
254 public static class MagnoliaLink {
255
256 public String identifier;
257
258 public String repository;
259
260 public String path;
261
262 public String caption;
263
264 }
265
266
267
268
269 public static class PluginData {
270 public String workspace;
271 public String path;
272 }
273 }