View Javadoc
1   /**
2    * This file Copyright (c) 2013-2015 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
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  * PagesEditorSubApp.
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      * @deprecated since 5.2.4 - use info.magnolia.pages.app.editor.PagesEditorSubApp#PagesEditorSubApp(info.magnolia.ui.api.action.ActionExecutor, info.magnolia.ui.api.app.SubAppContext, info.magnolia.pages.app.editor.PagesEditorSubAppView, info.magnolia.event.EventBus, info.magnolia.event.EventBus, info.magnolia.pages.app.editor.PageEditorPresenter, info.magnolia.ui.actionbar.ActionbarPresenter, info.magnolia.ui.vaadin.editor.pagebar.PageBarView, info.magnolia.ui.api.i18n.I18NAuthoringSupport, info.magnolia.cms.i18n.I18nContentSupport, info.magnolia.cms.core.version.VersionManager, info.magnolia.i18nsystem.SimpleTranslator, info.magnolia.ui.api.availability.AvailabilityChecker, info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector)
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      * Informs the app framework when navigating pages inside the page editor.
205      * Updates the shell fragment, caption and current location.
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); // update page status bar
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      * Wraps the current DefaultLocation in a DetailLocation. Providing getter and setters for used parameters.
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      * Returns the default locale for the given page.
263      *
264      * TODO: Once {@link DefaultI18NAuthoringSupport#getDefaultLocale(javax.jcr.Node)} is added to {@link I18NAuthoringSupport} this method should go.
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                 // reset channel
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                     // Check if the node still exist
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      * This method has package visibility for testing purposes only.
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         // Figure out which section to show, only one
458         ActionbarSectionDefinition sectionDefinition = getVisibleSection(sections, itemId);
459 
460         // Hide all other sections
461         for (ActionbarSectionDefinition section : sections) {
462             actionbarPresenter.hideSection(section.getName());
463         }
464 
465         if (sectionDefinition != null) {
466             // Show our section
467             actionbarPresenter.showSection(sectionDefinition.getName());
468 
469             // Evaluate availability of each action within the section
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             // Toggle preview and edit mode.
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 }