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