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.contentapp.browser;
35
36 import info.magnolia.cms.security.Permission;
37 import info.magnolia.cms.security.PermissionUtil;
38 import info.magnolia.cms.security.operations.AccessDefinition;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.event.EventBus;
41 import info.magnolia.jcr.util.NodeUtil;
42 import info.magnolia.jcr.util.SessionUtil;
43 import info.magnolia.objectfactory.ComponentProvider;
44 import info.magnolia.ui.actionbar.ActionbarPresenter;
45 import info.magnolia.ui.actionbar.definition.ActionbarDefinition;
46 import info.magnolia.ui.actionbar.definition.ActionbarGroupDefinition;
47 import info.magnolia.ui.actionbar.definition.ActionbarItemDefinition;
48 import info.magnolia.ui.actionbar.definition.ActionbarSectionDefinition;
49 import info.magnolia.ui.api.action.ActionDefinition;
50 import info.magnolia.ui.api.action.ActionExecutor;
51 import info.magnolia.ui.api.app.SubAppContext;
52 import info.magnolia.ui.api.app.SubAppEventBus;
53 import info.magnolia.ui.api.availability.AvailabilityDefinition;
54 import info.magnolia.ui.api.availability.AvailabilityRule;
55 import info.magnolia.ui.api.location.Location;
56 import info.magnolia.ui.contentapp.ContentSubAppView;
57 import info.magnolia.ui.framework.app.BaseSubApp;
58 import info.magnolia.ui.vaadin.actionbar.ActionPopup;
59 import info.magnolia.ui.vaadin.integration.jcr.JcrItemUtil;
60 import info.magnolia.ui.workbench.definition.WorkbenchDefinition;
61 import info.magnolia.ui.workbench.event.ItemRightClickedEvent;
62 import info.magnolia.ui.workbench.event.SearchEvent;
63 import info.magnolia.ui.workbench.event.SelectionChangedEvent;
64 import info.magnolia.ui.workbench.event.ViewTypeChangedEvent;
65 import info.magnolia.ui.workbench.search.SearchPresenterDefinition;
66
67 import java.util.ArrayList;
68 import java.util.List;
69
70 import javax.inject.Inject;
71 import javax.inject.Named;
72 import javax.jcr.Item;
73 import javax.jcr.Node;
74 import javax.jcr.Property;
75 import javax.jcr.RepositoryException;
76
77 import org.apache.commons.lang.StringUtils;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80 import org.vaadin.peter.contextmenu.ContextMenu;
81
82 import com.vaadin.server.ExternalResource;
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public class BrowserSubApp extends BaseSubApp<ContentSubAppView> {
117
118 private static final Logger log = LoggerFactory.getLogger(BrowserSubApp.class);
119
120 private final BrowserPresenter browser;
121 private final EventBus subAppEventBus;
122 private ActionExecutor actionExecutor;
123 private ComponentProvider componentProvider;
124 private String workbenchRoot;
125
126 @Inject
127 public BrowserSubApp(ActionExecutor actionExecutor, final SubAppContext subAppContext, final ContentSubAppView view, final BrowserPresenter browser, final @Named(SubAppEventBus.NAME) EventBus subAppEventBus, final ComponentProvider componentProvider) {
128 super(subAppContext, view);
129 if (subAppContext == null || view == null || browser == null || subAppEventBus == null) {
130 throw new IllegalArgumentException("Constructor does not allow for null args. Found SubAppContext = " + subAppContext + ", ContentSubAppView = " + view + ", BrowserPresenter = " + browser + ", EventBus = " + subAppEventBus);
131 }
132 this.browser = browser;
133 this.subAppEventBus = subAppEventBus;
134 this.actionExecutor = actionExecutor;
135 this.componentProvider = componentProvider;
136 this.workbenchRoot = ((BrowserSubAppDescriptor) subAppContext.getSubAppDescriptor()).getWorkbench().getPath();
137 }
138
139
140
141
142
143
144
145
146
147
148
149 @Override
150 public ContentSubAppView start(final Location location) {
151 BrowserLocation l = BrowserLocation.wrap(location);
152 super.start(l);
153 getView().setContentView(browser.start());
154 restoreBrowser(l);
155 registerSubAppEventsHandlers(subAppEventBus, this);
156
157 return getView();
158 }
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 protected void restoreBrowser(final BrowserLocation location) {
181 String path = ("/".equals(workbenchRoot) ? "" : workbenchRoot) + location.getNodePath();
182 String viewType = location.getViewType();
183
184 if (!getBrowser().hasViewType(viewType)) {
185 if (!StringUtils.isBlank(viewType)) {
186 log.warn("Unknown view type [{}], returning to default view type.", viewType);
187 }
188 viewType = getBrowser().getDefaultViewType();
189 location.updateViewType(viewType);
190 getAppContext().updateSubAppLocation(getSubAppContext(), location);
191 }
192 String query = location.getQuery();
193
194 BrowserSubAppDescriptor subAppDescriptor = (BrowserSubAppDescriptor) getSubAppContext().getSubAppDescriptor();
195 final String workspaceName = subAppDescriptor.getWorkbench().getWorkspace();
196
197 String itemId = null;
198 try {
199 itemId = JcrItemUtil.getItemId(SessionUtil.getNode(workspaceName, path));
200
201
202 if (itemId == null) {
203 itemId = JcrItemUtil.getItemId(SessionUtil.getNode(workspaceName, workbenchRoot));
204 BrowserLocation newLocation = getCurrentLocation();
205 newLocation.updateNodePath("/");
206
207 getAppContext().updateSubAppLocation(getSubAppContext(), newLocation);
208 }
209 } catch (RepositoryException e) {
210 log.warn("Could not retrieve item at path {} in workspace {}", path, workspaceName);
211 }
212 List<String> ids = new ArrayList<String>();
213 if (itemId != null) {
214 ids.add(itemId);
215 }
216 getBrowser().resync(ids, viewType, query);
217 updateActionbar(getBrowser().getActionbarPresenter());
218 }
219
220
221
222
223 public void showActionPopup(String absItemPath, int x, int y) {
224
225
226 BrowserSubAppDescriptor subAppDescriptor = (BrowserSubAppDescriptor) getSubAppContext().getSubAppDescriptor();
227 ActionbarDefinition actionbarDefinition = subAppDescriptor.getActionbar();
228 if (actionbarDefinition == null) {
229 return;
230 }
231
232 ActionPopup actionPopup = browser.getView().getActionPopup();
233
234 updateActionPopup(actionPopup);
235 actionPopup.open(x, y);
236 }
237
238
239
240
241
242 private void updateActionPopup(ActionPopup actionPopup) {
243
244 actionPopup.removeAllItems();
245
246 BrowserSubAppDescriptor subAppDescriptor = (BrowserSubAppDescriptor) getSubAppContext().getSubAppDescriptor();
247 WorkbenchDefinition workbench = subAppDescriptor.getWorkbench();
248 ActionbarDefinition actionbarDefinition = subAppDescriptor.getActionbar();
249 if (actionbarDefinition == null) {
250 return;
251 }
252 List<ActionbarSectionDefinition> sections = actionbarDefinition.getSections();
253
254 try {
255 String workbenchRootItemId = JcrItemUtil.getItemId(workbench.getWorkspace(), workbench.getPath());
256 List<String> selectedItemIds = getBrowser().getSelectedItemIds();
257 List<Item> items = getJcrItemsExceptOne(workbench.getWorkspace(), selectedItemIds, workbenchRootItemId);
258
259
260 ActionbarSectionDefinition sectionDefinition = getVisibleSection(sections, items);
261
262
263 if (sectionDefinition == null) {
264 return;
265 }
266
267
268 ContextMenu.ContextMenuItem menuItem = null;
269 for (ActionbarGroupDefinition groupDefinition : sectionDefinition.getGroups()) {
270 for (ActionbarItemDefinition itemDefinition : groupDefinition.getItems()) {
271
272 String actionName = itemDefinition.getName();
273 menuItem = addActionPopupItem(subAppDescriptor, actionPopup, itemDefinition, items);
274 menuItem.setEnabled(actionExecutor.isAvailable(actionName, items.toArray(new Item[items.size()])));
275 }
276
277
278 if (menuItem != null) {
279 menuItem.setSeparatorVisible(true);
280 }
281 }
282 if (menuItem != null) {
283 menuItem.setSeparatorVisible(false);
284 }
285 } catch (RepositoryException e) {
286 log.error("Failed to updated actionbar", e);
287 }
288 }
289
290
291
292
293 private ContextMenu.ContextMenuItem addActionPopupItem(BrowserSubAppDescriptor subAppDescriptor, ActionPopup actionPopup, ActionbarItemDefinition itemDefinition, List<javax.jcr.Item> items) {
294 String actionName = itemDefinition.getName();
295
296 ActionDefinition action = subAppDescriptor.getActions().get(actionName);
297 String label = action.getLabel();
298 String iconFontCode = ActionPopup.ICON_FONT_CODE + action.getIcon();
299 ExternalResource iconFontResource = new ExternalResource(iconFontCode);
300 ContextMenu.ContextMenuItem menuItem = actionPopup.addItem(label, iconFontResource);
301
302 menuItem.setData(actionName);
303
304 return menuItem;
305 }
306
307
308
309
310
311
312
313
314
315 public void updateActionbar(ActionbarPresenter actionbar) {
316
317 BrowserSubAppDescriptor subAppDescriptor = (BrowserSubAppDescriptor) getSubAppContext().getSubAppDescriptor();
318 WorkbenchDefinition workbench = subAppDescriptor.getWorkbench();
319 ActionbarDefinition actionbarDefinition = subAppDescriptor.getActionbar();
320 if (actionbarDefinition == null) {
321 return;
322 }
323 List<ActionbarSectionDefinition> sections = actionbarDefinition.getSections();
324
325 try {
326 String workbenchRootItemId = JcrItemUtil.getItemId(workbench.getWorkspace(), workbench.getPath());
327 List<String> selectedItemIds = getBrowser().getSelectedItemIds();
328 List<Item> items = getJcrItemsExceptOne(workbench.getWorkspace(), selectedItemIds, workbenchRootItemId);
329
330
331 ActionbarSectionDefinition sectionDefinition = getVisibleSection(sections, items);
332
333
334 if (sectionDefinition == null) {
335 for (ActionbarSectionDefinition section : sections) {
336 actionbar.hideSection(section.getName());
337 }
338 return;
339 }
340
341
342 for (ActionbarSectionDefinition section : sections) {
343 if (section != sectionDefinition) {
344 actionbar.hideSection(section.getName());
345 }
346 }
347
348
349 actionbar.showSection(sectionDefinition.getName());
350
351
352 for (ActionbarGroupDefinition groupDefinition : sectionDefinition.getGroups()) {
353 for (ActionbarItemDefinition itemDefinition : groupDefinition.getItems()) {
354
355 String actionName = itemDefinition.getName();
356 if (actionExecutor.isAvailable(actionName, items.toArray(new Item[items.size()]))) {
357 actionbar.enable(actionName);
358 } else {
359 actionbar.disable(actionName);
360 }
361 }
362 }
363 } catch (RepositoryException e) {
364 log.error("Failed to updated actionbar", e);
365 for (ActionbarSectionDefinition section : sections) {
366 actionbar.hideSection(section.getName());
367 }
368 }
369 }
370
371 private ActionbarSectionDefinition getVisibleSection(List<ActionbarSectionDefinition> sections, List<Item> items) throws RepositoryException {
372 for (ActionbarSectionDefinition section : sections) {
373 if (isSectionVisible(section, items))
374 return section;
375 }
376 return null;
377 }
378
379 private boolean isSectionVisible(ActionbarSectionDefinition section, List<Item> items) throws RepositoryException {
380 AvailabilityDefinition availability = section.getAvailability();
381
382
383 if (!availability.getAccess().hasAccess(MgnlContext.getUser())) {
384 return false;
385 }
386
387 if (items != null) {
388
389 for (Item item : items) {
390 if (!isSectionVisible(section, item)) {
391 return false;
392 }
393 }
394 }
395 return true;
396 }
397
398 private boolean isSectionVisible(ActionbarSectionDefinition section, Item item) throws RepositoryException {
399 AvailabilityDefinition availability = section.getAvailability();
400
401
402 if ((availability.getRuleClass() != null)) {
403
404 AvailabilityRule rule = componentProvider.newInstance(availability.getRuleClass());
405 if (rule == null || !rule.isAvailable(item)) {
406 return false;
407 }
408 }
409
410
411 if (availability.isWritePermissionRequired() && !MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
412 try {
413 Node node = item instanceof Property ? ((Property) item).getParent() : (Node) item;
414 if (!PermissionUtil.isGranted(node, Permission.WRITE)) {
415 return false;
416 }
417 } catch (RepositoryException e) {
418 log.warn("Could not evaluate write permission for {}.", item);
419 }
420 }
421
422
423 if (item == null) {
424 return availability.isRoot();
425 }
426
427
428 if (!item.isNode()) {
429 return availability.isProperties();
430 }
431
432
433 if (availability.isNodes()) {
434
435 if (availability.getNodeTypes().isEmpty()) {
436 return true;
437 }
438
439 for (String nodeType : availability.getNodeTypes()) {
440 if (NodeUtil.isNodeType((Node) item, nodeType)) {
441 return true;
442 }
443 }
444
445 }
446 return false;
447 }
448
449 protected final BrowserPresenter getBrowser() {
450 return browser;
451 }
452
453
454
455
456 @Override
457 public void locationChanged(final Location location) {
458 super.locationChanged(location);
459 restoreBrowser(BrowserLocation.wrap(location));
460 }
461
462
463
464
465 @Override
466 public BrowserLocation getCurrentLocation() {
467 return BrowserLocation.wrap(super.getCurrentLocation());
468 }
469
470
471
472
473
474
475
476
477
478 private void registerSubAppEventsHandlers(final EventBus subAppEventBus, final BrowserSubApp subApp) {
479 final ActionbarPresenter actionbar = subApp.getBrowser().getActionbarPresenter();
480 subAppEventBus.addHandler(SelectionChangedEvent.class, new SelectionChangedEvent.Handler() {
481
482 @Override
483 public void onSelectionChanged(SelectionChangedEvent event) {
484 BrowserLocation location = getCurrentLocation();
485 try {
486 Item selected = JcrItemUtil.getJcrItem(event.getWorkspace(), JcrItemUtil.parseNodeIdentifier(event.getFirstItemId()));
487 if (selected == null) {
488
489 location.updateNodePath("");
490 } else {
491 location.updateNodePath(StringUtils.removeStart(selected.getPath(), "/".equals(workbenchRoot) ? "" : workbenchRoot));
492 }
493 } catch (RepositoryException e) {
494 log.warn("Could not get jcrItem with itemId " + event.getFirstItemId() + " from workspace " + event.getWorkspace(), e);
495 }
496 getAppContext().updateSubAppLocation(getSubAppContext(), location);
497 updateActionbar(actionbar);
498 }
499 });
500
501 subAppEventBus.addHandler(ItemRightClickedEvent.class, new ItemRightClickedEvent.Handler() {
502
503 @Override
504 public void onItemRightClicked(ItemRightClickedEvent event) {
505 String absItemPath;
506 try {
507 absItemPath = event.getItem().getJcrItem().getPath();
508
509 showActionPopup(absItemPath, event.getClickX(), event.getClickY());
510 } catch (RepositoryException e) {
511 log.warn("Could not get jcrItem with itemId " + event.getItemId() + " from workspace " + event.getWorkspace(), e);
512 }
513
514 }
515 });
516
517 subAppEventBus.addHandler(ViewTypeChangedEvent.class, new ViewTypeChangedEvent.Handler() {
518
519 @Override
520 public void onViewChanged(ViewTypeChangedEvent event) {
521 BrowserLocation location = getCurrentLocation();
522
523 if (location.getViewType().equals(SearchPresenterDefinition.VIEW_TYPE)
524 && !event.getViewType().equals(SearchPresenterDefinition.VIEW_TYPE)) {
525 location.updateQuery("");
526 }
527 location.updateViewType(event.getViewType());
528 getAppContext().updateSubAppLocation(getSubAppContext(), location);
529 updateActionbar(actionbar);
530 }
531 });
532
533 subAppEventBus.addHandler(SearchEvent.class, new SearchEvent.Handler() {
534
535 @Override
536 public void onSearch(SearchEvent event) {
537 BrowserLocation location = getCurrentLocation();
538 if (StringUtils.isNotBlank(event.getSearchExpression())) {
539 location.updateViewType(SearchPresenterDefinition.VIEW_TYPE);
540 }
541 location.updateQuery(event.getSearchExpression());
542 getAppContext().updateSubAppLocation(getSubAppContext(), location);
543 updateActionbar(actionbar);
544 }
545 });
546 }
547
548 public static List<Item> getJcrItemsExceptOne(final String workspaceName, List<String> ids, String itemIdToExclude) {
549 List<Item> items = JcrItemUtil.getJcrItems(workspaceName, ids);
550 if (itemIdToExclude == null) {
551 return items;
552 }
553 for (int i = 0; i < items.size(); i++) {
554 try {
555 if (itemIdToExclude.equals(JcrItemUtil.getItemId(items.get(i)))) {
556 items.set(i, null);
557 }
558 } catch (RepositoryException e) {
559 log.debug("Cannot get item ID for item [{}].", items.get(i));
560 }
561 }
562 return items;
563 }
564 }