001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
020 */
021
022package org.nuxeo.ecm.webapp.context;
023
024import static org.jboss.seam.ScopeType.CONVERSATION;
025import static org.jboss.seam.ScopeType.EVENT;
026import static org.jboss.seam.annotations.Install.FRAMEWORK;
027import static org.nuxeo.ecm.webapp.helpers.EventNames.NAVIGATE_TO_DOCUMENT;
028
029import java.io.Serializable;
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.List;
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.jboss.seam.Component;
036import org.jboss.seam.annotations.Begin;
037import org.jboss.seam.annotations.Create;
038import org.jboss.seam.annotations.Factory;
039import org.jboss.seam.annotations.In;
040import org.jboss.seam.annotations.Install;
041import org.jboss.seam.annotations.Name;
042import org.jboss.seam.annotations.Scope;
043import org.jboss.seam.annotations.intercept.BypassInterceptors;
044import org.jboss.seam.annotations.web.RequestParameter;
045import org.jboss.seam.contexts.Context;
046import org.jboss.seam.contexts.Contexts;
047import org.jboss.seam.core.Events;
048import org.nuxeo.common.utils.Path;
049import org.nuxeo.ecm.core.api.CoreSession;
050import org.nuxeo.ecm.core.api.DocumentLocation;
051import org.nuxeo.ecm.core.api.DocumentModel;
052import org.nuxeo.ecm.core.api.DocumentModelList;
053import org.nuxeo.ecm.core.api.DocumentRef;
054import org.nuxeo.ecm.core.api.IdRef;
055import org.nuxeo.ecm.core.api.NuxeoException;
056import org.nuxeo.ecm.core.api.VersionModel;
057import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
058import org.nuxeo.ecm.core.api.security.SecurityConstants;
059import org.nuxeo.ecm.core.schema.FacetNames;
060import org.nuxeo.ecm.core.schema.SchemaManager;
061import org.nuxeo.ecm.platform.types.Type;
062import org.nuxeo.ecm.platform.types.adapter.TypeInfo;
063import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
064import org.nuxeo.ecm.platform.ui.web.api.UserAction;
065import org.nuxeo.ecm.platform.ui.web.pathelements.ArchivedVersionsPathElement;
066import org.nuxeo.ecm.platform.ui.web.pathelements.DocumentPathElement;
067import org.nuxeo.ecm.platform.ui.web.pathelements.HiddenDocumentPathElement;
068import org.nuxeo.ecm.platform.ui.web.pathelements.PathElement;
069import org.nuxeo.ecm.platform.ui.web.pathelements.VersionDocumentPathElement;
070import org.nuxeo.ecm.platform.ui.web.util.BadDocumentUriException;
071import org.nuxeo.ecm.platform.ui.web.util.DocumentLocator;
072import org.nuxeo.ecm.platform.ui.web.util.DocumentsListsUtils;
073import org.nuxeo.ecm.platform.util.RepositoryLocation;
074import org.nuxeo.ecm.webapp.action.TypesTool;
075import org.nuxeo.ecm.webapp.delegate.DocumentManagerBusinessDelegate;
076import org.nuxeo.ecm.webapp.helpers.ApplicationControllerHelper;
077import org.nuxeo.ecm.webapp.helpers.EventManager;
078import org.nuxeo.ecm.webapp.helpers.EventNames;
079import org.nuxeo.runtime.api.Framework;
080
081/**
082 * Implementation for the navigationContext component available on the session.
083 */
084@Name("navigationContext")
085@Scope(CONVERSATION)
086@Install(precedence = FRAMEWORK)
087public class NavigationContextBean implements NavigationContext, Serializable {
088
089    private static final long serialVersionUID = -3708768859028774906L;
090
091    private static final Log log = LogFactory.getLog(NavigationContextBean.class);
092
093    // --------------------------------------------
094    // fields managed by this class
095    // These fields can be accessed by 2 ways
096    // - simple getters
097    // - via the context thanks to @Factory
098
099    private DocumentModel currentDomain;
100
101    private DocumentModel currentContentRoot;
102
103    private DocumentModel currentWorkspace;
104
105    protected DocumentModel currentDocument;
106
107    protected DocumentModel currentSuperSpace;
108
109    protected DocumentModelList currentDocumentChildren;
110
111    protected List<DocumentModel> currentDocumentParents;
112
113    // document model that is not persisted yet (used for creation)
114    private DocumentModel changeableDocument;
115
116    private List<PathElement> parents;
117
118    private SchemaManager schemaManager;
119
120    @In(create = true, required = false)
121    protected transient CoreSession documentManager;
122
123    @Override
124    @Create
125    public void init() {
126        parents = null;
127    }
128
129    @Override
130    @BypassInterceptors
131    public DocumentModel getCurrentDocument() {
132        return currentDocument;
133    }
134
135    @Override
136    public String getCurrentDomainPath() {
137        if (currentDomain != null) {
138            return currentDomain.getPathAsString();
139        }
140        Path path;
141        if (currentDocument != null) {
142            path = currentDocument.getPath();
143        } else {
144            // Find any document, and lookup its domain.
145            DocumentModelList docs = documentManager.query("SELECT * FROM Document", 1);
146            if (docs.size() < 1) {
147                log.debug("Could not find a single document readable by current user.");
148                return null;
149            }
150            path = docs.get(0).getPath();
151        }
152        if (path.segmentCount() > 0) {
153            String[] segs = { path.segment(0) };
154            return Path.createFromSegments(segs).toString();
155        } else {
156            return null;
157        }
158    }
159
160    @Override
161    public void setCurrentDocument(DocumentModel documentModel) {
162        if (log.isDebugEnabled()) {
163            log.debug("Setting current document to " + documentModel);
164        }
165
166        if (!checkIfUpdateNeeded(currentDocument, documentModel)) {
167            if (log.isDebugEnabled()) {
168                log.debug(String.format("Current document already set to %s => give up updates", documentModel));
169            }
170            return;
171        }
172
173        currentSuperSpace = null;
174        currentDocument = documentModel;
175        // update all depending variables
176        updateContextVariables();
177        resetCurrentPath();
178        Contexts.getEventContext().remove("currentDocument");
179
180        EventManager.raiseEventsOnDocumentSelected(currentDocument);
181
182        if (log.isDebugEnabled()) {
183            log.debug("Current document set to: " + changeableDocument);
184        }
185    }
186
187    @Override
188    @BypassInterceptors
189    public DocumentModel getChangeableDocument() {
190        return changeableDocument;
191    }
192
193    @Override
194    public void setChangeableDocument(DocumentModel changeableDocument) {
195        if (log.isDebugEnabled()) {
196            log.debug("Setting changeable document to: " + changeableDocument);
197        }
198        this.changeableDocument = changeableDocument;
199        Contexts.getEventContext().set("changeableDocument", changeableDocument);
200    }
201
202    @Override
203    public DocumentModelList getCurrentPath() {
204        DocumentModelList parentDocsList = new DocumentModelListImpl();
205
206        List<DocumentModel> fromRoot = documentManager.getParentDocuments(currentDocument.getRef());
207        // add in reverse order
208        parentDocsList.addAll(fromRoot);
209        Collections.reverse(parentDocsList);
210
211        return parentDocsList;
212    }
213
214    @Override
215    public DocumentModel getCurrentSuperSpace() {
216        if (currentSuperSpace == null && currentDocument != null) {
217            if (currentDocument.hasFacet(FacetNames.SUPER_SPACE)) {
218                currentSuperSpace = currentDocument;
219            } else if (documentManager != null) {
220                currentSuperSpace = documentManager.getSuperSpace(currentDocument);
221            }
222        }
223        return currentSuperSpace;
224    }
225
226    @Override
227    public void invalidateCurrentDocument() {
228        if (currentDocument != null) {
229            currentDocument = documentManager.getDocument(currentDocument.getRef());
230            updateContextVariables();
231        }
232    }
233
234    @Override
235    @BypassInterceptors
236    public DocumentModel getCurrentDomain() {
237        return currentDomain;
238    }
239
240    @Override
241    public void setCurrentDomain(DocumentModel domainDocModel) {
242        if (!checkIfUpdateNeeded(currentDomain, domainDocModel)) {
243            return;
244        }
245
246        currentDomain = domainDocModel;
247        Contexts.getEventContext().remove("currentDomain");
248
249        if (domainDocModel == null) {
250            Events.instance().raiseEvent(EventNames.DOMAIN_SELECTION_CHANGED, currentDomain);
251            return;
252        }
253
254        if (currentDocument == null) {
255            setCurrentDocument(null);
256        }
257
258        // if we switched branch then realign currentDocument
259        if (currentDocumentParents != null
260                && !DocumentsListsUtils.isDocumentInList(domainDocModel, currentDocumentParents)) {
261            setCurrentDocument(domainDocModel);
262        }
263
264        Events.instance().raiseEvent(EventNames.DOMAIN_SELECTION_CHANGED, currentDomain);
265    }
266
267    protected boolean checkIfUpdateNeeded(DocumentModel ctxDoc, DocumentModel newDoc) {
268        if (log.isDebugEnabled()) {
269            log.debug(String.format("Check if update needed: compare context " + "doc '%s' to new doc '%s'", ctxDoc,
270                    newDoc));
271        }
272        if (ctxDoc == null && newDoc != null || ctxDoc != null && newDoc == null) {
273            return true;
274        }
275        if (ctxDoc == null && newDoc == null) {
276            return false;
277        }
278        if (log.isDebugEnabled()) {
279            log.debug(String.format(
280                    "Check if update needed: compare cache key on " + "context doc '%s' with new doc '%s'",
281                    ctxDoc.getCacheKey(), newDoc.getCacheKey()));
282        }
283        return !ctxDoc.getCacheKey().equals(newDoc.getCacheKey());
284    }
285
286    @Override
287    public void saveCurrentDocument() {
288        if (currentDocument == null) {
289            // cannot call saveDocument with null arg => nasty stateful bean
290            // de-serialization error
291            throw new IllegalStateException("null currentDocument");
292        }
293        currentDocument = documentManager.saveDocument(currentDocument);
294        documentManager.save();
295    }
296
297    @Override
298    public List<PathElement> getCurrentPathList() {
299        if (parents == null) {
300            resetCurrentPath();
301        }
302        return parents;
303    }
304
305    protected ServerContextBean getServerLocator() {
306        return (ServerContextBean) Component.getInstance("serverLocator");
307    }
308
309    @Override
310    public RepositoryLocation getCurrentServerLocation() {
311        return getServerLocator().getCurrentServerLocation();
312    }
313
314    /**
315     * @deprecated use getCurrentServerLocation() instead
316     */
317    @Override
318    @Deprecated
319    public RepositoryLocation getSelectedServerLocation() {
320        return getServerLocator().getCurrentServerLocation();
321    }
322
323    /**
324     * Switches to a new server location by updating the context and updating to the CoreSession (DocumentManager).
325     */
326    @Override
327    public void setCurrentServerLocation(RepositoryLocation serverLocation) {
328        if (serverLocation == null) {
329            log.warn("Setting ServerLocation to null, is this normal ?");
330        }
331
332        RepositoryLocation currentServerLocation = serverLocation;
333        getServerLocator().setRepositoryLocation(serverLocation);
334        resetCurrentContext();
335        Contexts.getEventContext().set("currentServerLocation", currentServerLocation);
336
337        // update the documentManager
338        documentManager = null;
339        documentManager = getOrCreateDocumentManager();
340        Events.instance().raiseEvent(EventNames.LOCATION_SELECTION_CHANGED);
341
342        DocumentModel rootDocument = documentManager.getRootDocument();
343        if (documentManager.hasPermission(rootDocument.getRef(), SecurityConstants.READ)) {
344            currentDocument = rootDocument;
345            updateContextVariables();
346        }
347    }
348
349    /**
350     * Returns the current documentManager if any or create a new session to the current location.
351     */
352    @Override
353    public CoreSession getOrCreateDocumentManager() {
354        if (documentManager != null) {
355            return documentManager;
356        }
357        // protect for unexpected wrong cast
358        Object supposedDocumentManager = Contexts.lookupInStatefulContexts("documentManager");
359        DocumentManagerBusinessDelegate documentManagerBD = null;
360        if (supposedDocumentManager != null) {
361            if (supposedDocumentManager instanceof DocumentManagerBusinessDelegate) {
362                documentManagerBD = (DocumentManagerBusinessDelegate) supposedDocumentManager;
363            } else {
364                log.error("Found the documentManager being " + supposedDocumentManager.getClass()
365                        + " instead of DocumentManagerBusinessDelegate. This is wrong.");
366            }
367        }
368        if (documentManagerBD == null) {
369            // this is the first time we select the location, create a
370            // DocumentManagerBusinessDelegate instance
371            documentManagerBD = new DocumentManagerBusinessDelegate();
372            Contexts.getConversationContext().set("documentManager", documentManagerBD);
373        }
374        documentManager = documentManagerBD.getDocumentManager(getCurrentServerLocation());
375        return documentManager;
376    }
377
378    @Override
379    @BypassInterceptors
380    public DocumentModel getCurrentWorkspace() {
381        return currentWorkspace;
382    }
383
384    // Factories to make navigation related data
385    // available in the context
386
387    @Override
388    @Factory(value = "currentDocument", scope = EVENT)
389    public DocumentModel factoryCurrentDocument() {
390        return currentDocument;
391    }
392
393    @Override
394    @Factory(value = "changeableDocument", scope = EVENT)
395    public DocumentModel factoryChangeableDocument() {
396        return changeableDocument;
397    }
398
399    @Override
400    @Factory(value = "currentDomain", scope = EVENT)
401    public DocumentModel factoryCurrentDomain() {
402        return currentDomain;
403    }
404
405    @Override
406    @Factory(value = "currentWorkspace", scope = EVENT)
407    public DocumentModel factoryCurrentWorkspace() {
408        return currentWorkspace;
409    }
410
411    @Override
412    @Factory(value = "currentContentRoot", scope = EVENT)
413    public DocumentModel factoryCurrentContentRoot() {
414        return currentContentRoot;
415    }
416
417    // @Factory(value = "currentServerLocation", scope = EVENT)
418    @Override
419    public RepositoryLocation factoryCurrentServerLocation() {
420        return getCurrentServerLocation();
421    }
422
423    @Override
424    @Factory(value = "currentSuperSpace", scope = EVENT)
425    public DocumentModel factoryCurrentSuperSpace() {
426        return getCurrentSuperSpace();
427    }
428
429    public void setCurrentWorkspace(DocumentModel workspaceDocModel) {
430
431        if (!checkIfUpdateNeeded(currentWorkspace, workspaceDocModel)) {
432            return;
433        }
434        currentWorkspace = workspaceDocModel;
435
436        if (workspaceDocModel == null) {
437            return;
438        }
439
440        if (currentDocument == null) {
441            setCurrentDocument(workspaceDocModel);
442            return;
443        }
444
445        // if we switched branch then realign currentDocument
446        if (currentDocumentParents != null
447                && !DocumentsListsUtils.isDocumentInList(workspaceDocModel, currentDocumentParents)) {
448            setCurrentDocument(workspaceDocModel);
449            return;
450        }
451    }
452
453    @Override
454    public void updateDocumentContext(DocumentModel doc) {
455        setCurrentDocument(doc);
456    }
457
458    /**
459     * Updates variables according to hierarchy rules and to the new currentDocument.
460     */
461    protected void updateContextVariables() {
462
463        // XXX flush method is not implemented for Event context :)
464        Contexts.getEventContext().set("currentDocument", currentDocument);
465
466        // Don't flush changeable document with a null id (NXP-10732)
467        if ((getChangeableDocument() != null) && (getChangeableDocument().getId() != null)) {
468            setChangeableDocument(null);
469        }
470
471        if (currentDocument == null) {
472            currentDocumentParents = null;
473            return;
474        }
475
476        DocumentRef ref = currentDocument.getRef();
477        if (ref == null) {
478            throw new NuxeoException("DocumentRef is null for currentDocument: " + currentDocument.getName());
479        }
480        // Recompute document parents
481        currentDocumentParents = documentManager.getParentDocuments(ref);
482
483        // iterate in reverse list order to go down the tree
484        // set all navigation variables according to docType
485        // => update to tree
486        String docType;
487        if (currentDocumentParents != null) {
488            for (int i = currentDocumentParents.size() - 1; i >= 0; i--) {
489                DocumentModel docModel = currentDocumentParents.get(i);
490                docType = docModel.getType();
491
492                if (docType != null && hasSuperType(docType, "Workspace")) {
493                    setCurrentWorkspace(docModel);
494                }
495
496                if (docType == null || hasSuperType(docType, "WorkspaceRoot") || hasSuperType(docType, "SectionRoot")) {
497                    setCurrentContentRoot(docModel);
498                }
499
500                if (docType != null && hasSuperType(docType, "Domain")) {
501                    setCurrentDomain(docModel);
502                }
503            }
504        }
505
506        // reinit lower tree
507        docType = currentDocument.getType();
508        if (docType.equals("Root")) {
509            setCurrentDomain(null);
510            setCurrentContentRoot(null);
511            setCurrentWorkspace(null);
512        } else if (hasSuperType(docType, "Domain")) {
513            setCurrentDomain(currentDocument);
514            setCurrentContentRoot(null);
515            setCurrentWorkspace(null);
516        } else if (hasSuperType(docType, "WorkspaceRoot") || hasSuperType(docType, "SectionRoot")) {
517            setCurrentContentRoot(currentDocument);
518            setCurrentWorkspace(null);
519        } else if (hasSuperType(docType, "Workspace")) {
520            setCurrentWorkspace(currentDocument);
521        }
522
523        // lazily recompute some fields
524        currentSuperSpace = null;
525        parents = null;
526    }
527
528    private boolean hasSuperType(String targetDocType, String superType) {
529        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
530        return schemaManager.hasSuperType(targetDocType, superType);
531    }
532
533    @Override
534    public void resetCurrentContext() {
535        // flush event context
536        Context eventContext = Contexts.getEventContext();
537        eventContext.remove("currentDocument");
538        eventContext.remove("changeableDocument");
539        eventContext.remove("currentDocumentChildren");
540        eventContext.remove("currentDomain");
541        eventContext.remove("currentServerLocation");
542        eventContext.remove("currentWorkspace");
543        eventContext.remove("currentContentRoot");
544        eventContext.remove("currentSuperSpace");
545    }
546
547    // XXX AT: we should let each action listener raise specific events
548    // (edition) and decide what's the next view, let's just handle context
549    // setting and redirection + this should be callable from templates i.e use
550    // view as a string.
551    @Override
552    public String getActionResult(DocumentModel doc, UserAction action) {
553
554        TypesTool typesTool = (TypesTool) Component.getInstance("typesTool");
555
556        if (doc == null) {
557            return null;
558        }
559        if (UserAction.CREATE == action) {
560            // the given document is a changeable document
561            setChangeableDocument(doc);
562        } else {
563            updateDocumentContext(doc);
564        }
565
566        final Type type = typesTool.getType(doc.getType());
567
568        final String result;
569        if (UserAction.VIEW == action) {
570            assert currentDocument != null;
571            EventManager.raiseEventsOnDocumentSelected(currentDocument);
572            result = ApplicationControllerHelper.getPageOnSelectedDocumentType(type);
573        } else if (UserAction.EDIT == action) {
574            throw new UnsupportedOperationException("for action " + action);
575        } else if (UserAction.AFTER_EDIT == action) {
576            assert currentDocument != null;
577            EventManager.raiseEventsOnDocumentChange(currentDocument);
578            result = ApplicationControllerHelper.getPageOnEditedDocumentType(type);
579        } else if (UserAction.CREATE == action) {
580            EventManager.raiseEventsOnDocumentCreate(changeableDocument);
581            result = ApplicationControllerHelper.getPageOnCreateDocumentType(type);
582        } else if (UserAction.AFTER_CREATE == action) {
583            assert currentDocument != null;
584            EventManager.raiseEventsOnDocumentSelected(currentDocument);
585            result = ApplicationControllerHelper.getPageOnCreatedDocumentType(type);
586        } else if (UserAction.GO_HOME == action) {
587            EventManager.raiseEventsOnGoingHome();
588            result = "home";
589        } else {
590            log.error(String.format("Unknown action '%s' for navigation on " + "document '%s' with title '%s': ",
591                    action.name(), doc.getId(), doc.getTitle()));
592            result = null;
593        }
594        return result;
595    }
596
597    @Override
598    public String goHome() {
599        resetCurrentContext();
600        EventManager.raiseEventsOnGoingHome();
601        return "home";
602    }
603
604    @Override
605    public String goBack() {
606        if (currentDocument != null) {
607            setChangeableDocument(null);
608            return navigateToDocument(currentDocument);
609        } else {
610            return goHome();
611        }
612    }
613
614    @Override
615    public String navigateToId(String documentId) {
616        if (documentManager == null) {
617            throw new IllegalStateException("documentManager not initialized");
618        }
619        DocumentRef docRef = new IdRef(documentId);
620        final DocumentModel doc = documentManager.getDocument(docRef);
621        return navigateToDocument(doc, "view");
622    }
623
624    @Override
625    public String navigateToRef(DocumentRef docRef) {
626        if (documentManager == null) {
627            throw new IllegalStateException("documentManager not initialized");
628        }
629
630        final DocumentModel doc = documentManager.getDocument(docRef);
631
632        return navigateToDocument(doc, "view");
633    }
634
635    @Override
636    public String navigateToDocument(DocumentModel doc) {
637        return navigateToDocument(doc, "view");
638    }
639
640    @Override
641    public String navigateToDocument(DocumentModel doc, String viewId) {
642        if (doc != null) {
643            updateDocumentContext(doc);
644        }
645        assert currentDocument != null;
646        TypeInfo typeInfo = currentDocument.getAdapter(TypeInfo.class);
647        String chosenView = null;
648        if (typeInfo != null) {
649            String defaultView = typeInfo.getDefaultView();
650            // hardcoded default views
651            if ("view".equals(viewId)) {
652                chosenView = defaultView;
653            } else if ("create".equals(viewId)) {
654                chosenView = typeInfo.getCreateView();
655            } else if ("edit".equals(viewId)) {
656                chosenView = typeInfo.getEditView();
657            } else {
658                chosenView = typeInfo.getView(viewId);
659            }
660            if (chosenView == null) {
661                chosenView = defaultView;
662            }
663        }
664
665        Events.instance().raiseEvent(NAVIGATE_TO_DOCUMENT, currentDocument);
666
667        return chosenView;
668    }
669
670    @Override
671    public String navigateToDocumentWithView(DocumentModel doc, String viewId) {
672        return navigateToDocument(doc, viewId);
673    }
674
675    @Override
676    public String navigateToDocument(DocumentModel docModel, VersionModel versionModel) {
677        DocumentModel docVersion = documentManager.getDocumentWithVersion(docModel.getRef(), versionModel);
678        return navigateToDocument(docVersion);
679    }
680
681    @Override
682    public void selectionChanged() {
683        resetCurrentPath();
684    }
685
686    @Override
687    public String getCurrentDocumentUrl() {
688        if (currentDocument == null) {
689            log.error("current document is null");
690            return null;
691        }
692        return DocumentLocator.getDocumentUrl(getCurrentServerLocation(), currentDocument.getRef());
693    }
694
695    @Override
696    public String getCurrentDocumentFullUrl() {
697        if (currentDocument == null) {
698            log.error("current document is null");
699            return null;
700        }
701        return DocumentLocator.getFullDocumentUrl(getCurrentServerLocation(), currentDocument.getRef());
702    }
703
704    // start a new conversation if needed, join main if possible
705    @Override
706    @Begin(id = "#{conversationIdGenerator.currentOrNewMainConversationId}", join = true)
707    public String navigateTo(RepositoryLocation serverLocation, DocumentRef docRef) {
708        // re-connect only if there is another repository specified
709        if (!serverLocation.equals(getCurrentServerLocation())) {
710            setCurrentServerLocation(serverLocation);
711        }
712        return navigateToRef(docRef);
713    }
714
715    // start a new conversation if needed, join main if possible
716    @Override
717    @Begin(id = "#{conversationIdGenerator.currentOrNewMainConversationId}", join = true)
718    public String navigateToURL(String documentUrl) {
719        final DocumentLocation docLoc;
720        try {
721            docLoc = DocumentLocator.parseDocRef(documentUrl);
722        } catch (BadDocumentUriException e) {
723            log.error("Cannot get document ref from uri " + documentUrl + ". " + e.getMessage(), e);
724            return null;
725        }
726        final DocumentRef docRef = docLoc.getDocRef();
727        RepositoryLocation repLoc = new RepositoryLocation(docLoc.getServerName());
728        return navigateTo(repLoc, docRef);
729    }
730
731    @RequestParameter
732    String docRef;
733
734    /**
735     * @see NavigationContext#navigateToURL()
736     */
737    @Override
738    @Begin(id = "#{conversationIdGenerator.currentOrNewMainConversationId}", join = true)
739    public String navigateToURL() {
740        if (docRef == null) {
741            return null;
742        }
743        return navigateToURL(docRef);
744    }
745
746    protected void resetCurrentPath() {
747        final String logPrefix = "<resetCurrentPath> ";
748
749        parents = new ArrayList<PathElement>();
750
751        if (documentManager == null) {
752            log.error(logPrefix + "documentManager not initialized");
753            return;
754        }
755
756        if (currentDocument != null) {
757            if (currentDocument.isVersion()) {
758                DocumentModel sourceDocument = documentManager.getSourceDocument(currentDocument.getRef());
759
760                List<DocumentModel> parentList = documentManager.getParentDocuments(sourceDocument.getRef());
761                for (DocumentModel docModel : parentList) {
762                    parents.add(getDocumentPathElement(docModel));
763                }
764
765                parents.add(new ArchivedVersionsPathElement(sourceDocument));
766                parents.add(new VersionDocumentPathElement(currentDocument));
767            } else {
768                if (currentDocumentParents != null) {
769                    for (DocumentModel docModel : currentDocumentParents) {
770                        parents.add(getDocumentPathElement(docModel));
771                    }
772                }
773            }
774        }
775    }
776
777    protected PathElement getDocumentPathElement(DocumentModel doc) {
778        if (doc != null && doc.hasFacet(FacetNames.HIDDEN_IN_NAVIGATION)) {
779            return new HiddenDocumentPathElement(doc);
780        }
781        return new DocumentPathElement(doc);
782    }
783
784    @Override
785    public DocumentModel getCurrentContentRoot() {
786        return currentContentRoot;
787    }
788
789    @Override
790    public void setCurrentContentRoot(DocumentModel crDocumentModel) {
791
792        if (!checkIfUpdateNeeded(currentContentRoot, crDocumentModel)) {
793            return;
794        }
795        currentContentRoot = crDocumentModel;
796        Contexts.getEventContext().remove("currentContentRoot");
797
798        if (crDocumentModel == null) {
799            return;
800        }
801
802        if (currentDocument == null) {
803            setCurrentDocument(null);
804            return;
805        }
806
807        // if we switched branch then realign currentDocument
808        if (currentDocumentParents != null
809                && !DocumentsListsUtils.isDocumentInList(crDocumentModel, currentDocumentParents)) {
810            setCurrentDocument(crDocumentModel);
811        }
812    }
813
814}