001/*
002 * (C) Copyright 2014 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-2.1.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 *     <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a>
016 *
017 */
018
019package org.nuxeo.ecm.restapi.server.jaxrs.routing.io;
020
021import java.io.IOException;
022import java.io.Serializable;
023import java.util.Map.Entry;
024
025import javax.servlet.http.HttpServletRequest;
026import javax.ws.rs.Produces;
027import javax.ws.rs.core.Context;
028import javax.ws.rs.core.MediaType;
029import javax.ws.rs.core.UriInfo;
030import javax.ws.rs.ext.Provider;
031
032import org.apache.commons.lang.StringUtils;
033import org.codehaus.jackson.JsonGenerationException;
034import org.codehaus.jackson.JsonGenerator;
035import org.jboss.el.ExpressionFactoryImpl;
036import org.nuxeo.ecm.automation.jaxrs.io.EntityWriter;
037import org.nuxeo.ecm.core.api.CoreSession;
038import org.nuxeo.ecm.core.api.NuxeoPrincipal;
039import org.nuxeo.ecm.core.schema.SchemaManager;
040import org.nuxeo.ecm.core.schema.types.Schema;
041import org.nuxeo.ecm.core.schema.utils.DateParser;
042import org.nuxeo.ecm.platform.actions.ActionContext;
043import org.nuxeo.ecm.platform.actions.ELActionContext;
044import org.nuxeo.ecm.platform.actions.ejb.ActionManager;
045import org.nuxeo.ecm.platform.el.ExpressionContext;
046import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants;
047import org.nuxeo.ecm.platform.routing.core.impl.GraphNode;
048import org.nuxeo.ecm.platform.routing.core.impl.GraphNode.Button;
049import org.nuxeo.ecm.platform.routing.core.impl.GraphRoute;
050import org.nuxeo.ecm.platform.task.Task;
051import org.nuxeo.ecm.platform.task.TaskComment;
052import org.nuxeo.ecm.restapi.server.jaxrs.routing.io.util.JsonEncodeDecodeUtils;
053import org.nuxeo.ecm.webengine.jaxrs.session.SessionFactory;
054import org.nuxeo.runtime.api.Framework;
055
056/**
057 * @since 7.2
058 */
059@Provider
060@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON + "+nxentity" })
061public class TaskWriter extends EntityWriter<Task> {
062
063    public static final String ENTITY_TYPE = "task";
064
065    @Context
066    HttpServletRequest request;
067
068    @Context
069    UriInfo uriInfo;
070
071    @Override
072    protected void writeEntityBody(JsonGenerator jg, Task item) throws IOException {
073        writeTask(jg, item, request, uriInfo);
074    }
075
076    public static void writeTask(JsonGenerator jg, Task item, HttpServletRequest request, UriInfo uriInfo)
077            throws JsonGenerationException, IOException {
078        CoreSession session = null;
079        GraphRoute workflowInstance = null;
080        GraphNode node = null;
081        String workflowInstanceId = item.getProcessId();
082        final String nodeId = item.getVariable(DocumentRoutingConstants.TASK_NODE_ID_KEY);
083        if (request != null) {
084            session = SessionFactory.getSession(request);
085        }
086        if (session != null && StringUtils.isNotBlank(workflowInstanceId)) {
087            NodeAccessRunner nodeAccessRunner = new NodeAccessRunner(session, workflowInstanceId, nodeId);
088            nodeAccessRunner.runUnrestricted();
089            workflowInstance = nodeAccessRunner.workflowInstance;
090            node = nodeAccessRunner.node;
091        }
092
093        jg.writeStringField("id", item.getDocument().getId());
094        jg.writeStringField("name", item.getName());
095        jg.writeStringField("workflowInstanceId", workflowInstanceId);
096        if (workflowInstance != null) {
097            jg.writeStringField("workflowModelName", workflowInstance.getModelName());
098        }
099        jg.writeStringField("state", item.getDocument().getCurrentLifeCycleState());
100        jg.writeStringField("directive", item.getDirective());
101        jg.writeStringField("created", DateParser.formatW3CDateTime(item.getCreated()));
102        jg.writeStringField("dueDate", DateParser.formatW3CDateTime(item.getDueDate()));
103        jg.writeStringField("nodeName", item.getVariable(DocumentRoutingConstants.TASK_NODE_ID_KEY));
104
105        jg.writeArrayFieldStart("targetDocumentIds");
106        for (String docId : item.getTargetDocumentsIds()) {
107            jg.writeStartObject();
108            jg.writeStringField("id", docId);
109            jg.writeEndObject();
110        }
111        jg.writeEndArray();
112
113        jg.writeArrayFieldStart("actors");
114        for (String actorId : item.getActors()) {
115            jg.writeStartObject();
116            jg.writeStringField("id", actorId);
117            jg.writeEndObject();
118        }
119        jg.writeEndArray();
120
121        jg.writeArrayFieldStart("comments");
122        for (TaskComment comment : item.getComments()) {
123            jg.writeStartObject();
124            jg.writeStringField("author", comment.getAuthor());
125            jg.writeStringField("text", comment.getText());
126            jg.writeStringField("date", DateParser.formatW3CDateTime(comment.getCreationDate().getTime()));
127            jg.writeEndObject();
128        }
129        jg.writeEndArray();
130
131        jg.writeFieldName("variables");
132        jg.writeStartObject();
133        // add nodeVariables
134        for (Entry<String, Serializable> e : node.getVariables().entrySet()) {
135            JsonEncodeDecodeUtils.encodeVariableEntry(node.getDocument(), GraphNode.PROP_VARIABLES_FACET, e, jg, request);
136        }
137        // add workflow variables
138        if (workflowInstance != null) {
139            final String transientSchemaName =  DocumentRoutingConstants.GLOBAL_VAR_SCHEMA_PREFIX + node.getId();
140            final SchemaManager schemaManager = Framework.getService(SchemaManager.class);
141            final Schema transientSchema = schemaManager.getSchema(transientSchemaName);
142            for (Entry<String, Serializable> e : workflowInstance.getVariables().entrySet()) {
143                if (transientSchema == null || transientSchema.hasField(e.getKey())) {
144                    JsonEncodeDecodeUtils.encodeVariableEntry(workflowInstance.getDocument(), GraphRoute.PROP_VARIABLES_FACET, e, jg, request);
145                }
146            }
147        }
148        jg.writeEndObject();
149
150        if (session != null) {
151            jg.writeFieldName("taskInfo");
152            jg.writeStartObject();
153            final ActionManager actionManager = Framework.getService(ActionManager.class);
154            jg.writeArrayFieldStart("taskActions");
155            for (Button button : node.getTaskButtons()) {
156                if (StringUtils.isBlank(button.getFilter())
157                        || actionManager.checkFilter(button.getFilter(), createActionContext(session))) {
158                    jg.writeStartObject();
159                    jg.writeStringField("name", button.getName());
160                    jg.writeStringField("url", uriInfo.getBaseUri() + "api/v1/task" + item.getDocument().getId() + "/"
161                            + button.getName());
162                    jg.writeStringField("label", button.getLabel());
163                    jg.writeEndObject();
164                }
165            }
166            jg.writeEndArray();
167
168            jg.writeFieldName("layoutResource");
169            jg.writeStartObject();
170            jg.writeStringField("name", node.getTaskLayout());
171            jg.writeStringField("url",
172                    uriInfo.getBaseUri() + "/layout-manager/layouts/?layoutName=" + node.getTaskLayout());
173            jg.writeEndObject();
174
175            jg.writeArrayFieldStart("schemas");
176            for (String schema : node.getDocument().getSchemas()) {
177                // TODO only keep functional schema once adaptation done
178                jg.writeStartObject();
179                jg.writeStringField("name", schema);
180                jg.writeStringField("url", uriInfo.getBaseUri() + "api/v1/config/schemas/" + schema);
181                jg.writeEndObject();
182            }
183            jg.writeEndArray();
184
185            jg.writeEndObject();
186        }
187
188    }
189
190    protected static ActionContext createActionContext(CoreSession session) {
191        ActionContext actionContext = new ELActionContext(new ExpressionContext(), new ExpressionFactoryImpl());
192        actionContext.setDocumentManager(session);
193        actionContext.setCurrentPrincipal((NuxeoPrincipal) session.getPrincipal());
194        return actionContext;
195    }
196
197    @Override
198    protected String getEntityType() {
199        return ENTITY_TYPE;
200    }
201
202}