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