001/*
002 * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     arussel
016 */
017package org.nuxeo.ecm.platform.routing.core.impl;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023
024import org.nuxeo.ecm.core.api.CoreSession;
025import org.nuxeo.ecm.core.api.DocumentModel;
026import org.nuxeo.ecm.core.api.DocumentModelList;
027import org.nuxeo.ecm.core.api.DocumentRef;
028import org.nuxeo.ecm.core.api.IdRef;
029import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
030import org.nuxeo.ecm.core.api.security.ACE;
031import org.nuxeo.ecm.core.api.security.ACL;
032import org.nuxeo.ecm.core.api.security.ACP;
033import org.nuxeo.ecm.core.api.security.SecurityConstants;
034import org.nuxeo.ecm.core.api.security.impl.ACPImpl;
035import org.nuxeo.ecm.platform.routing.api.DocumentRoute;
036import org.nuxeo.ecm.platform.routing.api.DocumentRouteElement;
037import org.nuxeo.ecm.platform.routing.api.DocumentRouteStep;
038import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants;
039import org.nuxeo.runtime.api.Framework;
040
041/**
042 * @author arussel
043 */
044public class DocumentRouteElementImpl implements DocumentRouteElement, DocumentRouteStep {
045
046    private static final long serialVersionUID = 1L;
047
048    protected DocumentModel document;
049
050    protected ElementRunner runner;
051
052    public DocumentRouteElementImpl(DocumentModel doc, ElementRunner runner) {
053        this.document = doc;
054        this.runner = runner;
055    }
056
057    @Override
058    public DocumentModelList getAttachedDocuments(CoreSession session) {
059        List<String> docIds = getDocumentRoute(session).getAttachedDocuments();
060        List<DocumentRef> refs = new ArrayList<DocumentRef>();
061        for (String id : docIds) {
062            refs.add(new IdRef(id));
063        }
064        return session.getDocuments(refs.toArray(new DocumentRef[] {}));
065    }
066
067    @Override
068    public void run(CoreSession session) {
069        runner.run(session, this);
070    }
071
072    @Override
073    public void run(CoreSession session, Map<String, Serializable> map) {
074        runner.run(session, this, map);
075    }
076
077    @Override
078    public void resume(CoreSession session, String nodeId, String taskId, Map<String, Object> data, String status) {
079        runner.resume(session, this, nodeId, taskId, data, status);
080    }
081
082    @Override
083    public DocumentRoute getDocumentRoute(CoreSession session) {
084        DocumentModel parent = document;
085        while (true) {
086            if (parent.hasFacet(DocumentRoutingConstants.DOCUMENT_ROUTE_DOCUMENT_FACET)) {
087                break;
088            }
089            parent = session.getParentDocument(parent.getRef());
090        }
091        return parent.getAdapter(DocumentRoute.class);
092    }
093
094    @Override
095    public DocumentModel getDocument() {
096        return document;
097    }
098
099    protected Object getProperty(String propertyName) {
100        return document.getPropertyValue(propertyName);
101    }
102
103    @Override
104    public String getName() {
105        return getDocument().getName();
106    }
107
108    /**
109     * @since 7.2
110     */
111    @Override
112    public String getTitle() {
113        return (String) getProperty(DocumentRoutingConstants.TITLE_PROPERTY_NAME);
114    }
115
116    @Override
117    public boolean isValidated() {
118        return checkLifeCycleState(ElementLifeCycleState.validated);
119    }
120
121    @Override
122    public boolean isReady() {
123        return checkLifeCycleState(ElementLifeCycleState.ready);
124    }
125
126    @Override
127    public boolean isDone() {
128        return checkLifeCycleState(ElementLifeCycleState.done);
129    }
130
131    protected boolean checkLifeCycleState(ElementLifeCycleState state) {
132        return document.getCurrentLifeCycleState().equalsIgnoreCase(state.name());
133    }
134
135    @Override
136    public String getDescription() {
137        return (String) getProperty(DocumentRoutingConstants.DESCRIPTION_PROPERTY_NAME);
138    }
139
140    @Override
141    public boolean isRunning() {
142        return checkLifeCycleState(ElementLifeCycleState.running);
143    }
144
145    @Override
146    public boolean isCanceled() {
147        return checkLifeCycleState(ElementLifeCycleState.canceled);
148    }
149
150    @Override
151    public boolean isDraft() {
152        return checkLifeCycleState(ElementLifeCycleState.draft);
153    }
154
155    @Override
156    public void setRunning(CoreSession session) {
157        followTransition(ElementLifeCycleTransistion.toRunning, session, false);
158    }
159
160    @Override
161    public void followTransition(ElementLifeCycleTransistion transition, CoreSession session, boolean recursive) {
162        if (document.followTransition(transition.name())) {
163            if (Framework.isTestModeSet()) {
164                session.save();
165            }
166            document = session.getDocument(document.getRef());
167        }
168        if (recursive) {
169            DocumentModelList children = session.getChildren(document.getRef());
170            for (DocumentModel child : children) {
171                DocumentRouteElement element = child.getAdapter(DocumentRouteElement.class);
172                element.followTransition(transition, session, recursive);
173            }
174
175        }
176    }
177
178    @Override
179    public void save(CoreSession session) {
180        session.saveDocument(document);
181    }
182
183    @Override
184    public void setDone(CoreSession session) {
185        followTransition(ElementLifeCycleTransistion.toDone, session, false);
186        EventFirer.fireEvent(session, this, null, DocumentRoutingConstants.Events.afterStepRunning.name());
187    }
188
189    @Override
190    public void setValidated(CoreSession session) {
191        followTransition(ElementLifeCycleTransistion.toValidated, session, true);
192    }
193
194    @Override
195    public void setReady(CoreSession session) {
196        followTransition(ElementLifeCycleTransistion.toReady, session, true);
197    }
198
199    @Override
200    public void validate(CoreSession session) {
201        setValidated(session);
202        setReadOnly(session);
203    }
204
205    @Override
206    public void setReadOnly(CoreSession session) {
207        SetDocumentOnReadOnlyUnrestrictedSessionRunner readOnlySetter = new SetDocumentOnReadOnlyUnrestrictedSessionRunner(
208                session, document.getRef());
209        readOnlySetter.runUnrestricted();
210    }
211
212    protected class SetDocumentOnReadOnlyUnrestrictedSessionRunner extends UnrestrictedSessionRunner {
213
214        public SetDocumentOnReadOnlyUnrestrictedSessionRunner(CoreSession session, DocumentRef ref) {
215            super(session);
216            this.ref = ref;
217        }
218
219        protected DocumentRef ref;
220
221        @Override
222        public void run() {
223            DocumentModel doc = session.getDocument(ref);
224            ACP acp = new ACPImpl();
225            // add new ACL to set READ permission to everyone
226            ACL routingACL = acp.getOrCreateACL(DocumentRoutingConstants.DOCUMENT_ROUTING_ACL);
227            routingACL.add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.READ, true));
228            // block rights inheritance
229            ACL localACL = acp.getOrCreateACL(ACL.LOCAL_ACL);
230            localACL.add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false));
231            doc.setACP(acp, true);
232            session.saveDocument(doc);
233        }
234    }
235
236    @Override
237    public boolean canValidateStep(CoreSession session) {
238        return hasPermissionOnDocument(session, SecurityConstants.WRITE_LIFE_CYCLE);
239    }
240
241    protected boolean hasPermissionOnDocument(CoreSession session, String permission) {
242        return session.hasPermission(document.getRef(), permission);
243    }
244
245    @Override
246    public void setCanValidateStep(CoreSession session, String userOrGroup) {
247        setPermissionOnDocument(session, userOrGroup, SecurityConstants.WRITE_LIFE_CYCLE);
248    }
249
250    protected void setPermissionOnDocument(CoreSession session, String userOrGroup, String permission) {
251        ACP acp = document.getACP();
252        ACL routingACL = acp.getOrCreateACL(DocumentRoutingConstants.DOCUMENT_ROUTING_ACL);
253        routingACL.add(new ACE(userOrGroup, permission, true));
254        document.setACP(acp, true);
255        session.saveDocument(document);
256    }
257
258    @Override
259    public boolean canUpdateStep(CoreSession session) {
260        return hasPermissionOnDocument(session, SecurityConstants.WRITE_PROPERTIES);
261    }
262
263    @Override
264    public void setCanUpdateStep(CoreSession session, String userOrGroup) {
265        setPermissionOnDocument(session, userOrGroup, SecurityConstants.WRITE_PROPERTIES);
266    }
267
268    @Override
269    public void setCanReadStep(CoreSession session, String userOrGroup) {
270        setPermissionOnDocument(session, userOrGroup, SecurityConstants.READ);
271    }
272
273    @Override
274    public boolean canDeleteStep(CoreSession session) {
275        return hasPermissionOnDocument(session, SecurityConstants.REMOVE);
276    }
277
278    @Override
279    public void setCanDeleteStep(CoreSession session, String userOrGroup) {
280        setPermissionOnDocument(session, userOrGroup, SecurityConstants.REMOVE);
281    }
282
283    @Override
284    public void backToReady(CoreSession session) {
285        EventFirer.fireEvent(session, this, null, DocumentRoutingConstants.Events.beforeStepBackToReady.name());
286        followTransition(ElementLifeCycleTransistion.backToReady, session, false);
287        EventFirer.fireEvent(session, this, null, DocumentRoutingConstants.Events.afterStepBackToReady.name());
288    }
289
290    @Override
291    public DocumentRouteStep undo(CoreSession session) {
292        runner.undo(session, this);
293        document = session.getDocument(document.getRef());
294        return this;
295    }
296
297    @Override
298    public boolean canUndoStep(CoreSession session) {
299        GetIsParentRunningUnrestricted runner = new GetIsParentRunningUnrestricted(session);
300        runner.runUnrestricted();
301        return runner.isRunning();
302    }
303
304    protected class GetIsParentRunningUnrestricted extends UnrestrictedSessionRunner {
305        public GetIsParentRunningUnrestricted(CoreSession session) {
306            super(session);
307        }
308
309        protected boolean isRunning;
310
311        @Override
312        public void run() {
313            DocumentModel parent = session.getDocument(document.getParentRef());
314            DocumentRouteElement parentElement = parent.getAdapter(DocumentRouteElement.class);
315            isRunning = parentElement.isRunning();
316        }
317
318        public boolean isRunning() {
319            return isRunning;
320        }
321    }
322
323    @Override
324    public void cancel(CoreSession session) {
325        runner.cancel(session, this);
326    }
327
328    @Override
329    public void setCanceled(CoreSession session) {
330        followTransition(ElementLifeCycleTransistion.toCanceled, session, false);
331    }
332
333    @Override
334    public boolean isModifiable() {
335        return (isDraft() || isReady() || isRunning());
336    }
337}