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.pages.app.editor;
35
36 import info.magnolia.cms.core.Path;
37 import info.magnolia.cms.core.version.VersionManager;
38 import info.magnolia.cms.i18n.I18nContentSupport;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.event.EventBus;
41 import info.magnolia.i18nsystem.SimpleTranslator;
42 import info.magnolia.jcr.util.NodeTypes;
43 import info.magnolia.jcr.util.PropertyUtil;
44 import info.magnolia.link.LinkUtil;
45 import info.magnolia.objectfactory.Components;
46 import info.magnolia.pages.app.editor.event.NodeSelectedEvent;
47 import info.magnolia.repository.RepositoryConstants;
48 import info.magnolia.ui.actionbar.ActionbarPresenter;
49 import info.magnolia.ui.actionbar.ActionbarView;
50 import info.magnolia.ui.actionbar.definition.ActionbarDefinition;
51 import info.magnolia.ui.actionbar.definition.ActionbarGroupDefinition;
52 import info.magnolia.ui.actionbar.definition.ActionbarItemDefinition;
53 import info.magnolia.ui.actionbar.definition.ActionbarSectionDefinition;
54 import info.magnolia.ui.api.action.ActionDefinition;
55 import info.magnolia.ui.api.action.ActionExecutionException;
56 import info.magnolia.ui.api.action.ActionExecutor;
57 import info.magnolia.ui.api.app.AppContext;
58 import info.magnolia.ui.api.app.SubAppContext;
59 import info.magnolia.ui.api.app.SubAppEventBus;
60 import info.magnolia.ui.api.availability.AvailabilityChecker;
61 import info.magnolia.ui.api.availability.AvailabilityDefinition;
62 import info.magnolia.ui.api.event.AdmincentralEventBus;
63 import info.magnolia.ui.api.event.ContentChangedEvent;
64 import info.magnolia.ui.api.i18n.I18NAuthoringSupport;
65 import info.magnolia.ui.api.location.Location;
66 import info.magnolia.ui.api.location.LocationChangedEvent;
67 import info.magnolia.ui.api.message.Message;
68 import info.magnolia.ui.api.message.MessageType;
69 import info.magnolia.ui.contentapp.definition.ContentSubAppDescriptor;
70 import info.magnolia.ui.contentapp.detail.DetailLocation;
71 import info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor;
72 import info.magnolia.ui.contentapp.detail.DetailView;
73 import info.magnolia.ui.framework.app.BaseSubApp;
74 import info.magnolia.ui.framework.app.SubAppContextImpl;
75 import info.magnolia.ui.framework.i18n.DefaultI18NAuthoringSupport;
76 import info.magnolia.ui.vaadin.editor.PageEditorListener;
77 import info.magnolia.ui.vaadin.editor.gwt.shared.PlatformType;
78 import info.magnolia.ui.vaadin.editor.pagebar.PageBarView;
79 import info.magnolia.ui.vaadin.gwt.client.shared.AbstractElement;
80 import info.magnolia.ui.vaadin.gwt.client.shared.AreaElement;
81 import info.magnolia.ui.vaadin.gwt.client.shared.PageEditorParameters;
82 import info.magnolia.ui.vaadin.gwt.client.shared.PageElement;
83 import info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector;
84 import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnector;
85 import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeItemId;
86 import info.magnolia.ui.workbench.StatusBarView;
87
88 import java.util.Arrays;
89 import java.util.List;
90 import java.util.Locale;
91 import java.util.Map;
92
93 import javax.inject.Inject;
94 import javax.inject.Named;
95 import javax.jcr.Node;
96 import javax.jcr.RepositoryException;
97 import javax.jcr.Session;
98
99 import org.apache.commons.lang3.ObjectUtils;
100 import org.apache.commons.lang3.StringUtils;
101 import org.slf4j.Logger;
102 import org.slf4j.LoggerFactory;
103
104
105
106
107 public class PagesEditorSubApp extends BaseSubApp<PagesEditorSubAppView> implements PagesEditorSubAppView.Listener, ActionbarPresenter.Listener, PageBarView.Listener, PageEditorPresenter.Listener {
108
109 private static final Logger log = LoggerFactory.getLogger(PagesEditorSubApp.class);
110
111 protected static final String PROPERTY_TITLE = "title";
112
113 private final ActionExecutor actionExecutor;
114 private final SubAppContext subAppContext;
115 private final PagesEditorSubAppView view;
116 private final EventBus subAppEventBus;
117 private final EventBus admincentralEventBus;
118 private final PageEditorPresenter pageEditorPresenter;
119 private final ActionbarPresenter actionbarPresenter;
120 private final PageBarView pageBarView;
121 private final I18NAuthoringSupport i18NAuthoringSupport;
122 private final I18nContentSupport i18nContentSupport;
123 private final JcrContentConnector contentConnector;
124 private final AvailabilityChecker availabilityChecker;
125 private final StatusBarView statusBarView;
126 private final String workspace;
127 private final AppContext appContext;
128 private final VersionManager versionManager;
129 private final SimpleTranslator i18n;
130
131 private PageEditorParameters parameters;
132 private PlatformType targetPreviewPlatform = PlatformType.DESKTOP;
133 private Locale currentLocale;
134 private String caption;
135
136 @Inject
137 public PagesEditorSubApp(final ActionExecutor actionExecutor, final SubAppContext subAppContext, final PagesEditorSubAppView view, @Named(AdmincentralEventBus.NAME) EventBus admincentralEventBus,
138 final @Named(SubAppEventBus.NAME) EventBus subAppEventBus, final PageEditorPresenter pageEditorPresenter, final ActionbarPresenter actionbarPresenter, final PageBarView pageBarView,
139 I18NAuthoringSupport i18NAuthoringSupport, I18nContentSupport i18nContentSupport, VersionManager versionManager, final SimpleTranslator i18n, AvailabilityChecker availabilityChecker,
140 ContentConnector contentConnector, StatusBarView statusBarView) {
141 super(subAppContext, view);
142 this.actionExecutor = actionExecutor;
143 this.subAppContext = subAppContext;
144 this.view = view;
145 this.subAppEventBus = subAppEventBus;
146 this.admincentralEventBus = admincentralEventBus;
147 this.pageEditorPresenter = pageEditorPresenter;
148 this.actionbarPresenter = actionbarPresenter;
149 this.pageBarView = pageBarView;
150 this.i18NAuthoringSupport = i18NAuthoringSupport;
151 this.i18nContentSupport = i18nContentSupport;
152 this.availabilityChecker = availabilityChecker;
153 this.statusBarView = statusBarView;
154 this.contentConnector = (JcrContentConnector) contentConnector;
155 this.workspace = this.contentConnector.getContentConnectorDefinition().getWorkspace();
156 this.appContext = subAppContext.getAppContext();
157 this.versionManager = versionManager;
158 this.i18n = i18n;
159 bindHandlers();
160 }
161
162
163
164
165 @Deprecated
166 public PagesEditorSubApp(final ActionExecutor actionExecutor, final SubAppContext subAppContext, final PagesEditorSubAppView view, @Named(AdmincentralEventBus.NAME) EventBus admincentralEventBus,
167 final @Named(SubAppEventBus.NAME) EventBus subAppEventBus, final PageEditorPresenter pageEditorPresenter, final ActionbarPresenter actionbarPresenter, final PageBarView pageBarView,
168 I18NAuthoringSupport i18NAuthoringSupport, I18nContentSupport i18nContentSupport, VersionManager versionManager, final SimpleTranslator i18n, AvailabilityChecker availabilityChecker,
169 ContentConnector contentConnector) {
170 this(actionExecutor, subAppContext, view, admincentralEventBus, subAppEventBus, pageEditorPresenter, actionbarPresenter, pageBarView, i18NAuthoringSupport, i18nContentSupport, versionManager, i18n, availabilityChecker, contentConnector, Components.getComponent(StatusBarView.class));
171 }
172
173 @Override
174 public String getCaption() {
175 return caption;
176 }
177
178 public void updateCaption(DetailLocation location) {
179 this.caption = getPageTitle(location);
180 pageBarView.setPageName(caption, location.getNodePath());
181 }
182
183 @Override
184 public PagesEditorSubAppView start(Location location) {
185 view.setListener(this);
186
187 DetailLocation detailLocation = DetailLocation.wrap(location);
188 super.start(detailLocation);
189
190 ActionbarDefinition actionbarDefinition = ((ContentSubAppDescriptor) getSubAppContext().getSubAppDescriptor()).getActionbar();
191 Map<String, ActionDefinition> actionDefinitions = getSubAppContext().getSubAppDescriptor().getActions();
192 actionbarPresenter.setListener(this);
193 ActionbarView actionbar = actionbarPresenter.start(actionbarDefinition, actionDefinitions);
194
195 pageBarView.setListener(this);
196 pageEditorPresenter.setListener(this);
197
198 view.setActionbarView(actionbar);
199 view.setPageBarView(pageBarView);
200 view.setPageEditorView(pageEditorPresenter.start());
201 view.setStatusBarView(statusBarView);
202
203 goToLocation(detailLocation);
204 return view;
205 }
206
207
208
209
210
211 protected void updateNodePath(String path) {
212 DetailLocation detailLocation = getCurrentLocation();
213 detailLocation.updateNodePath(path);
214 setPageEditorParameters(detailLocation);
215 getAppContext().updateSubAppLocation(getSubAppContext(), detailLocation);
216 view.setStatusBarView(statusBarView);
217 pageEditorPresenter.updateParameters(parameters);
218 }
219
220 @Override
221 public boolean supportsLocation(Location location) {
222 return getCurrentLocation().getNodePath().equals(DetailLocation.wrap(location).getNodePath());
223 }
224
225
226
227
228 @Override
229 public DetailLocation getCurrentLocation() {
230 return DetailLocation.wrap(super.getCurrentLocation());
231 }
232
233 @Override
234 public void locationChanged(Location location) {
235 DetailLocation itemLocation = DetailLocation.wrap(location);
236 super.locationChanged(itemLocation);
237 goToLocation(itemLocation);
238 }
239
240 private void goToLocation(DetailLocation location) {
241 if (isLocationChanged(location)) {
242 doGoToLocation(location);
243 }
244 }
245
246 protected void doGoToLocation(DetailLocation location) {
247 setPageEditorParameters(location);
248 updateActionbar();
249 pageEditorPresenter.loadPageEditor(parameters);
250 updatePageBarAvailableLanguages(location);
251 }
252
253 private void updatePageBarAvailableLanguages(DetailLocation location) {
254 try {
255 Node node = MgnlContext.getJCRSession(workspace).getNode(location.getNodePath());
256 List<Locale> locales = i18NAuthoringSupport.getAvailableLocales(node);
257 pageBarView.setAvailableLanguages(locales);
258 Locale locale = currentLocale != null && locales.contains(currentLocale) ? currentLocale : getDefaultLocale(node);
259 pageBarView.setCurrentLanguage(locale);
260 } catch (RepositoryException e) {
261 log.error("Unable to get node [{}] from workspace [{}]", location.getNodePath(), workspace, e);
262 }
263 }
264
265
266
267
268
269
270 private Locale getDefaultLocale(Node node) {
271 Locale locale;
272 if (i18NAuthoringSupport instanceof DefaultI18NAuthoringSupport) {
273 locale = ((DefaultI18NAuthoringSupport)i18NAuthoringSupport).getDefaultLocale(node);
274 } else {
275 locale = i18nContentSupport.getDefaultLocale();
276 }
277 return locale;
278 }
279
280 private void setPageEditorParameters(DetailLocation location) {
281 DetailView.ViewType action = location.getViewType();
282 String path = location.getNodePath();
283 boolean isPreview = DetailView.ViewType.VIEW.getText().equals(action.getText());
284 this.parameters = new PageEditorParameters(MgnlContext.getContextPath(), path, isPreview);
285 this.parameters.setPlatformType(targetPreviewPlatform);
286 try {
287 Node node = MgnlContext.getJCRSession(workspace).getNode(path);
288 String uri = i18NAuthoringSupport.createI18NURI(node, currentLocale);
289 StringBuffer sb = new StringBuffer(uri);
290
291 if (isPreview) {
292 LinkUtil.addParameter(sb, "mgnlPreview", Boolean.toString(true));
293 } else {
294
295 this.targetPreviewPlatform = PlatformType.DESKTOP;
296 this.parameters.setPlatformType(targetPreviewPlatform);
297 pageBarView.setPlatFormType(targetPreviewPlatform);
298
299 LinkUtil.addParameter(sb, "mgnlPreview", Boolean.toString(false));
300 }
301 LinkUtil.addParameter(sb, "mgnlChannel", targetPreviewPlatform.getId());
302
303 if (location.hasVersion()) {
304 LinkUtil.addParameter(sb, "mgnlVersion", location.getVersion());
305 }
306 uri = sb.toString();
307 this.parameters.setUrl(uri);
308 updateCaption(location);
309 pageBarView.togglePreviewMode(isPreview);
310 } catch (RepositoryException e) {
311 log.error(e.getMessage(), e);
312 }
313 }
314
315 private boolean isLocationChanged(DetailLocation location) {
316 DetailView.ViewType action = location.getViewType();
317 String path = location.getNodePath();
318
319 if (parameters != null && (parameters.getNodePath().equals(path) && parameters.isPreview() == DetailView.ViewType.VIEW.getText().equals(action.getText())) && !location.hasVersion()) {
320 return false;
321 }
322 return true;
323 }
324
325 private String getPageTitle(DetailLocation location) {
326 String caption = StringUtils.EMPTY;
327 try {
328 Session session = MgnlContext.getJCRSession(workspace);
329 Node node = session.getNode(location.getNodePath());
330 if (StringUtils.isNotBlank(location.getVersion())) {
331 node = versionManager.getVersion(node, location.getVersion());
332 caption = i18n.translate("subapp.versioned_page", PropertyUtil.getString(node, PROPERTY_TITLE, node.getName()), location.getVersion());
333 } else {
334 caption = PropertyUtil.getString(node, PROPERTY_TITLE, node.getName());
335 }
336
337 } catch (RepositoryException e) {
338 log.warn("Could not set page Tab Title for item : {}", location.getNodePath(), e);
339 }
340 return caption;
341 }
342
343 private void bindHandlers() {
344
345 admincentralEventBus.addHandler(ContentChangedEvent.class, new ContentChangedEvent.Handler() {
346
347 @Override
348 public void onContentChanged(ContentChangedEvent event) {
349 if (contentConnector.canHandleItem(event.getItemId())) {
350
351 try {
352 String currentNodePath = getCurrentLocation().getNodePath();
353 if (!MgnlContext.getJCRSession(RepositoryConstants.WEBSITE).nodeExists(currentNodePath)) {
354 getSubAppContext().close();
355 }
356 } catch (RepositoryException e) {
357 log.warn("Could not determine if currently edited page exists", e);
358 }
359 }
360 }
361 });
362
363 subAppEventBus.addHandler(NodeSelectedEvent.class, new NodeSelectedEvent.Handler() {
364
365 @Override
366 public void onItemSelected(NodeSelectedEvent event) {
367 AbstractElement element = event.getElement();
368 if (element instanceof PageElement) {
369 String path = element.getPath();
370 if (StringUtils.isEmpty(path)) {
371 path = "/";
372 }
373 if (!path.equals(parameters.getNodePath())) {
374 updateNodePath(path);
375 }
376 }
377 updateActionbar();
378 }
379 });
380
381 subAppEventBus.addHandler(ContentChangedEvent.class, new ContentChangedEvent.Handler() {
382
383 @Override
384 public void onContentChanged(ContentChangedEvent event) {
385 updatePageEditor();
386 }
387 });
388
389 admincentralEventBus.addHandler(ContentChangedEvent.class, new ContentChangedEvent.Handler() {
390
391 @Override
392 public void onContentChanged(ContentChangedEvent event) {
393 updatePageEditor();
394 }
395 });
396
397 admincentralEventBus.addHandler(LocationChangedEvent.class, new LocationChangedEvent.Handler() {
398 @Override
399 public void onLocationChanged(LocationChangedEvent event) {
400 view.setStatusBarView(statusBarView);
401 }
402 });
403 }
404
405 private void updatePageEditor() {
406
407 if (subAppContext instanceof SubAppContextImpl) {
408 Locale authoringLocale = ((SubAppContextImpl) subAppContext).getAuthoringLocale();
409 if (!ObjectUtils.equals(authoringLocale, currentLocale)) {
410
411 currentLocale = authoringLocale;
412 doGoToLocation(getCurrentLocation());
413 } else {
414
415 pageEditorPresenter.refresh();
416 }
417 } else {
418
419 pageEditorPresenter.refresh();
420 }
421 view.setStatusBarView(statusBarView);
422 }
423
424 @Override
425 public void onActionbarItemClicked(String actionName) {
426 prepareAndExecutePagesEditorAction(actionName);
427 }
428
429 protected void prepareAndExecutePagesEditorAction(String actionName) {
430 AbstractElement selectedElement = pageEditorPresenter.getSelectedElement();
431 if (selectedElement == null) {
432 log.warn("Trying to execute action [{}] but no element was selected. Was the page actually loaded?", actionName);
433 return;
434 }
435 try {
436 Object itemId = getItemId(selectedElement);
437 actionExecutor.execute(actionName, contentConnector.getItem(itemId), selectedElement, pageEditorPresenter);
438 } catch (ActionExecutionException e) {
439 Message error = new Message(MessageType.ERROR, i18n.translate("pages.pagesEditorSubapp.actionExecutionException.message"), e.getMessage());
440 log.error("An error occurred while executing action [{}]", actionName, e);
441 appContext.sendLocalMessage(error);
442 }
443 }
444
445 @Override
446 public void languageSelected(Locale locale) {
447 if (locale != null && !locale.equals(currentLocale)) {
448 this.currentLocale = locale;
449
450 if (subAppContext instanceof SubAppContextImpl) {
451 ((SubAppContextImpl) subAppContext).setAuthoringLocale(locale);
452 }
453 doGoToLocation(getCurrentLocation());
454 }
455 }
456
457 @Override
458 public void platformSelected(PlatformType platformType) {
459 if (platformType != null && !platformType.equals(targetPreviewPlatform)) {
460 this.targetPreviewPlatform = platformType;
461 doGoToLocation(getCurrentLocation());
462 }
463 }
464
465
466
467
468 public PageEditorParameters getParameters() {
469 return parameters;
470 }
471
472 public void updateActionbar() {
473 Object itemId = getItemId(pageEditorPresenter.getSelectedElement());
474
475 DetailSubAppDescriptor subAppDescriptor = (DetailSubAppDescriptor) getSubAppContext().getSubAppDescriptor();
476 ActionbarDefinition actionbarDefinition = subAppDescriptor.getActionbar();
477 if (actionbarDefinition == null) {
478 return;
479 }
480 List<ActionbarSectionDefinition> sections = actionbarDefinition.getSections();
481
482 ActionbarSectionDefinition sectionDefinition = getVisibleSection(sections, itemId);
483
484
485 for (ActionbarSectionDefinition section : sections) {
486 actionbarPresenter.hideSection(section.getName());
487 }
488
489 if (sectionDefinition != null) {
490
491 actionbarPresenter.showSection(sectionDefinition.getName());
492
493
494 for (ActionbarGroupDefinition groupDefinition : sectionDefinition.getGroups()) {
495 for (ActionbarItemDefinition itemDefinition : groupDefinition.getItems()) {
496
497 String actionName = itemDefinition.getName();
498 ActionDefinition actionDefinition = actionExecutor.getActionDefinition(actionName);
499 if (actionDefinition != null) {
500 AvailabilityDefinition availability = actionDefinition.getAvailability();
501 if (availabilityChecker.isAvailable(availability, Arrays.asList(itemId))) {
502 actionbarPresenter.enable(actionName);
503 } else {
504 actionbarPresenter.disable(actionName);
505 }
506 }
507 }
508 }
509 }
510 }
511
512 private Object getItemId(AbstractElement element) {
513 if (element == null) {
514 return null;
515 }
516 else if (element instanceof AreaElement && ((AreaElement) element).isOptional() && !((AreaElement) element).isCreated()) {
517
518 try {
519 int index = element.getPath().lastIndexOf("/");
520 String parentPath = element.getPath().substring(0, index);
521 String relPath = element.getPath().substring(index + 1);
522
523 Node parent = MgnlContext.getJCRSession(getWorkspace()).getNode(parentPath);
524
525 JcrNewNodeItemId jcrNewNodeItemId = new JcrNewNodeItemId(parent.getIdentifier(), getWorkspace(), NodeTypes.Area.NAME);
526 jcrNewNodeItemId.setName(Path.getUniqueLabel(parent, relPath));
527 return jcrNewNodeItemId;
528 } catch (RepositoryException e) {
529 log.error("Failed to create new jcr node item id: " + e.getMessage(), e);
530 }
531 return null;
532 }
533 else {
534 return contentConnector.getItemIdByUrlFragment(element.getPath());
535 }
536 }
537
538 private ActionbarSectionDefinition getVisibleSection(List<ActionbarSectionDefinition> sections, Object itemId) {
539 for (ActionbarSectionDefinition section : sections) {
540 if (availabilityChecker.isAvailable(section.getAvailability(), Arrays.asList(itemId)))
541 return section;
542 }
543 return null;
544 }
545
546 @Override
547 public void onEscape() {
548 if (pageEditorPresenter.isMoving()) {
549 pageEditorPresenter.onAction(PageEditorListener.ACTION_CANCEL_MOVE_COMPONENT);
550 } else {
551
552 if (getCurrentLocation().getViewType().equals(DetailView.ViewType.EDIT)) {
553 prepareAndExecutePagesEditorAction(PageEditorListener.ACTION_VIEW_PREVIEW);
554 } else {
555 prepareAndExecutePagesEditorAction(PageEditorListener.ACTION_VIEW_EDIT);
556 }
557 }
558 }
559
560 protected String getWorkspace() {
561 return workspace;
562 }
563
564 public PageEditorPresenter getPageEditorPresenter() {
565 return pageEditorPresenter;
566 }
567
568 @Override
569 public void onMove() {
570 updateActionbar();
571 }
572 }