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