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