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.framework.action.service;
35
36 import info.magnolia.cms.core.version.VersionManager;
37 import info.magnolia.event.EventBus;
38 import info.magnolia.objectfactory.ComponentProvider;
39 import info.magnolia.objectfactory.Components;
40 import info.magnolia.ui.api.action.ActionExecutor;
41 import info.magnolia.ui.api.app.SubApp;
42 import info.magnolia.ui.api.app.SubAppContext;
43 import info.magnolia.ui.api.availability.AvailabilityChecker;
44 import info.magnolia.ui.api.availability.AvailabilityDefinition;
45 import info.magnolia.ui.api.event.AdmincentralEventBus;
46 import info.magnolia.ui.api.event.ContentChangedEvent;
47 import info.magnolia.ui.api.ioc.UiContextScoped;
48 import info.magnolia.ui.contentapp.browser.ActionExecutionService;
49 import info.magnolia.ui.contentapp.browser.context.ValueContext;
50 import info.magnolia.ui.contentapp.detail.ContentDetailSubApp;
51 import info.magnolia.ui.datasource.jcr.JcrDatasourceDefinition;
52 import info.magnolia.ui.dialog.formdialog.FormDialogPresenter;
53 import info.magnolia.ui.form.EditorCallback;
54 import info.magnolia.ui.form.EditorValidator;
55 import info.magnolia.ui.framework.UiFrameworkView;
56 import info.magnolia.ui.framework.databinding.ItemProviderStrategy;
57 import info.magnolia.ui.framework.databinding.view.EditorView;
58 import info.magnolia.ui.framework.message.MessagesManager;
59 import info.magnolia.ui.vaadin.integration.contentconnector.ConfiguredJcrContentConnectorDefinition;
60 import info.magnolia.ui.vaadin.integration.contentconnector.ConfiguredNodeTypeDefinition;
61 import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnector;
62 import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
63 import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
64 import info.magnolia.util.OptionalConsumer;
65
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.Collection;
69 import java.util.List;
70 import java.util.Optional;
71 import java.util.Set;
72 import java.util.concurrent.Callable;
73 import java.util.function.Predicate;
74 import java.util.stream.Collectors;
75 import java.util.stream.Stream;
76
77 import javax.inject.Inject;
78 import javax.inject.Named;
79 import javax.inject.Provider;
80 import javax.jcr.Item;
81 import javax.jcr.Node;
82 import javax.jcr.RepositoryException;
83
84 import com.google.common.collect.Sets;
85 import com.vaadin.data.BinderValidationStatus;
86
87
88
89
90
91
92
93
94
95 @UiContextScoped
96 public class CompatibilityActionExecutionService extends ActionExecutionService {
97
98 private final ComponentProvider componentProvider;
99 private final EventBus eventBus;
100
101 @Inject
102 public CompatibilityActionExecutionService(ActionExecutor actionExecutor, MessagesManager messagesManager, AvailabilityChecker availabilityChecker, ComponentProvider componentProvider, @Named(AdmincentralEventBus.NAME) EventBus eventBus) {
103 super(actionExecutor, messagesManager, availabilityChecker);
104 this.componentProvider = componentProvider;
105 this.eventBus = eventBus;
106 }
107
108 @Override
109 public void executeAction(String actionName, Object... parameters) {
110 Collection<Object> compatibilityParameters = new ArrayList<>(Arrays.asList(parameters));
111
112 Optional<ValueContextProvider> valueContextProviderOptional = invokeComponentProviderSafely(() -> componentProvider.newInstance(ValueContextProvider.class));
113
114 Optional<Collection> selectedItems = valueContextProviderOptional
115 .map(InjectedComponentProvider::get)
116 .map(ValueContext::get)
117 .map(nodeStream -> nodeStream.collect(Collectors.toList()));
118
119 OptionalConsumer.of(Stream.of(parameters)
120 .filter(FormDialogPresenter.class::isInstance)
121 .findFirst())
122 .ifNotPresent(() -> invokeComponentProviderSafely(() -> Components.getComponent(FormDialogPresenter.class))
123 .ifPresent(compatibilityParameters::add));
124
125 Optional<SubAppContext> subAppContext = invokeComponentProviderSafely(() -> componentProvider.newInstance((SubAppContext.class)));
126 Optional<EditorView> editorView = subAppContext
127 .map(SubAppContext::getSubApp)
128 .map(SubApp::getView)
129 .filter(UiFrameworkView.class::isInstance)
130 .map(view -> ((UiFrameworkView) view).accessViewBeanStore().get(EditorView.class));
131
132 Set<Node> jcrNodes = findJcrNodes(selectedItems);
133
134 if (jcrNodes.size() == 0) {
135 Optional<ItemProviderStrategyProvider> itemProviderStrategyProvider = invokeComponentProviderSafely(() -> componentProvider.newInstance(ItemProviderStrategyProvider.class));
136 itemProviderStrategyProvider
137 .map(InjectedComponentProvider::get)
138 .map(ItemProviderStrategy::read)
139 .filter(Optional::isPresent)
140 .filter(v -> Node.class.isInstance(v.get()))
141 .map(Optional::get)
142 .map(Node.class::cast)
143 .ifPresent(jcrNodes::add);
144 }
145
146 if (jcrNodes.size() > 0) {
147 compatibilityParameters.add(wrapForLegacyActionExecution(jcrNodes.iterator().next(), editorView));
148 compatibilityParameters.add(jcrNodes.stream()
149 .map(node -> wrapForLegacyActionExecution(node, editorView))
150 .collect(Collectors.toList())
151 );
152 mimicContentConnector(compatibilityParameters);
153 mimicEditor(compatibilityParameters, jcrNodes, subAppContext, editorView, invokeComponentProviderSafely(() -> componentProvider.newInstance(ValueContextProvider.class)));
154 } else {
155 selectedItems.ifPresent(o -> o.stream().findFirst().ifPresent(compatibilityParameters::add));
156 }
157 super.executeAction(actionName, compatibilityParameters.toArray(new Object[0]));
158 }
159
160 @Override
161 public <T> Predicate<AvailabilityDefinition> getAvailabilityFilter(Stream<T> selectedItems) {
162 final Stream<Object> wrappedLegacyItems = selectedItems.map(this::wrapForLegacyAvailabilityChecking);
163
164 return super.getAvailabilityFilter(wrappedLegacyItems);
165 }
166
167 private void mimicEditor(Collection<Object> compatibilityParameters, Set<Node> jcrNodes, Optional<SubAppContext> subAppContext, Optional<EditorView> editorView, Optional<ValueContextProvider> valueContextProviderOptional) {
168 editorView.ifPresent(nodeEditorView -> compatibilityParameters.add(new EditorValidator() {
169 @Override
170 public void showValidation(boolean visible) {
171
172 }
173
174 @Override
175 public boolean isValid() {
176 List<BinderValidationStatus<?>> validate = nodeEditorView.validate();
177 return validate.stream().allMatch(BinderValidationStatus::isOk);
178 }
179 }));
180
181 compatibilityParameters.add(new EditorCallback() {
182 @Override
183 public void onCancel() {
184 subAppContext.ifPresent(subAppContext -> {
185 if (subAppContext.getSubApp() instanceof ContentDetailSubApp) {
186 subAppContext.close();
187 }
188 });
189 }
190
191 @Override
192 public void onSuccess(String actionName) {
193 onCancel();
194 }
195 });
196
197 valueContextProviderOptional
198 .map(InjectedComponentProvider::get)
199 .ifPresent(context -> eventBus.addHandler(ContentChangedEvent.class, event -> context.set(jcrNodes)));
200 }
201
202 private void mimicContentConnector(Collection<Object> compatibilityParameters) {
203 invokeComponentProviderSafely(() -> componentProvider.newInstance(JcrDatasourceDefinitionProvider.class))
204 .map(InjectedComponentProvider::get)
205 .ifPresent(datasourceDefinition -> {
206 ConfiguredJcrContentConnectorDefinition contentConnectorDefinition = new ConfiguredJcrContentConnectorDefinition();
207 contentConnectorDefinition.setWorkspace(datasourceDefinition.getWorkspace());
208 contentConnectorDefinition.setRootPath(datasourceDefinition.getRootPath());
209 contentConnectorDefinition.setNodeTypes(datasourceDefinition.getAllowedNodeTypes()
210 .stream()
211 .map(nodeType -> {
212 ConfiguredNodeTypeDefinition nodeTypeDefinition = new ConfiguredNodeTypeDefinition();
213 nodeTypeDefinition.setName(nodeType);
214 return nodeTypeDefinition;
215 })
216 .collect(Collectors.toList()));
217 compatibilityParameters.add(new JcrContentConnector(componentProvider.getComponent(VersionManager.class), contentConnectorDefinition));
218 });
219 }
220
221 private Set<Node> findJcrNodes(Optional<Collection> selectedItems) {
222 return (Set<Node>) selectedItems.map(items -> items.stream()
223 .filter(Node.class::isInstance)
224 .map(Node.class::cast)
225 .collect(Collectors.toSet()))
226 .orElse(Sets.newHashSet());
227 }
228
229 private Object wrapForLegacyAvailabilityChecking(Object potentialJcrItem) {
230 try {
231 if (potentialJcrItem instanceof Item) {
232 return JcrItemUtil.getItemId((Item) potentialJcrItem);
233 } else {
234 return potentialJcrItem;
235 }
236 } catch (RepositoryException e) {
237 throw new RuntimeException(e);
238 }
239 }
240
241 private JcrNodeAdapter wrapForLegacyActionExecution(Object potentialJcrItem, Optional<EditorView> editorView) {
242 return new JcrNodeAdapter((Node) potentialJcrItem) {
243 @Override
244 public Node applyChanges() throws RepositoryException {
245 editorView.ifPresent(editorView -> editorView.write(getJcrItem()));
246 return super.applyChanges();
247 }
248 };
249 }
250
251 private <C> Optional<C> invokeComponentProviderSafely(Callable<C> callable) {
252 try {
253 return Optional.of(callable.call());
254 } catch (Exception e) {
255 return Optional.empty();
256 }
257 }
258
259 private static class ItemProviderStrategyProvider extends InjectedComponentProvider<ItemProviderStrategy<Node>> {
260 @Inject
261 private ItemProviderStrategyProvider(ItemProviderStrategy<Node> strategy) {
262 super(strategy);
263 }
264 }
265
266 private static class ValueContextProvider extends InjectedComponentProvider<ValueContext<Node>> {
267 @Inject
268 private ValueContextProvider(ValueContext<Node> valueContext) {
269 super(valueContext);
270 }
271 }
272
273 private static class JcrDatasourceDefinitionProvider extends InjectedComponentProvider<JcrDatasourceDefinition> {
274 @Inject
275 private JcrDatasourceDefinitionProvider(JcrDatasourceDefinition datasourceDefinition) {
276 super(datasourceDefinition);
277 }
278 }
279
280
281
282
283 private static class InjectedComponentProvider<T> implements Provider<T> {
284
285 private final T component;
286
287 private InjectedComponentProvider(T component) {
288 this.component = component;
289 }
290
291 @Override
292 public T get() {
293 return component;
294 }
295 }
296 }