/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2014, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.gses.webui.form;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.vaadin.event.MouseEvents;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.ServiceImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.apache.commons.lang.StringUtils;
import ru.codeinside.adm.AdminServiceProvider;
import ru.codeinside.adm.database.Employee;
import ru.codeinside.adm.database.Organization;
import ru.codeinside.gses.API;
import ru.codeinside.gses.activiti.forms.FormID;
import ru.codeinside.gses.form.FormEntry;
import ru.codeinside.gses.service.BidID;
import ru.codeinside.gses.service.F2;
import ru.codeinside.gses.service.Fn;
import ru.codeinside.gses.service.Functions;
import ru.codeinside.gses.webui.Flash;
import ru.codeinside.gses.webui.components.api.WithTaskId;
import ru.codeinside.gses.webui.eventbus.TaskChanged;
import ru.codeinside.gses.webui.utils.Components;
import ru.codeinside.gses.webui.wizard.Wizard;
import ru.codeinside.gses.webui.wizard.WizardStep;
import ru.codeinside.gses.webui.wizard.event.WizardCancelledEvent;
import ru.codeinside.gses.webui.wizard.event.WizardCompletedEvent;
import ru.codeinside.gses.webui.wizard.event.WizardProgressListener;
import ru.codeinside.gses.webui.wizard.event.WizardStepActivationEvent;
import ru.codeinside.gses.webui.wizard.event.WizardStepSetChangedEvent;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import static com.vaadin.ui.Window.Notification.TYPE_ERROR_MESSAGE;
import static com.vaadin.ui.Window.Notification.TYPE_WARNING_MESSAGE;
final public class TaskForm extends VerticalLayout implements WithTaskId {
private static final long serialVersionUID = 1L;
final static ThemeResource EDITOR_ICON = new ThemeResource("../custom/icon/linedpaperpencil32.png");
final static ThemeResource VIEW_ICON = new ThemeResource("../custom/icon/paper32.png");
private final FormID id;
private final ImmutableList<FormSeq> flow;
private final FormFlow formFlow;
private final CloseListener closeListener;
private final DataAccumulator accumulator;
final Wizard wizard;
private Logger log = Logger.getLogger(TaskForm.class.getName());
public interface CloseListener extends Serializable {
void onFormClose(TaskForm form);
}
final Embedded editorIcon;
final HorizontalLayout header;
final Embedded viewIcon;
Component mainContent;
public TaskForm(final FormDescription formDesc, final CloseListener closeListener, final DataAccumulator accumulator) {
this.closeListener = closeListener;
flow = formDesc.flow;
id = formDesc.id;
formFlow = new FormFlow(id);
this.accumulator = accumulator;
header = new HorizontalLayout();
header.setWidth(100, UNITS_PERCENTAGE);
header.setSpacing(true);
addComponent(header);
editorIcon = new Embedded(null, EDITOR_ICON);
editorIcon.setDescription("Редактирование");
editorIcon.setStyleName("icon-inactive");
editorIcon.setHeight(32, UNITS_PIXELS);
editorIcon.setWidth(32, UNITS_PIXELS);
editorIcon.setImmediate(true);
header.addComponent(editorIcon);
header.setComponentAlignment(editorIcon, Alignment.BOTTOM_CENTER);
if (flow.get(0) instanceof FormDataSource) {
viewIcon = new Embedded(null, VIEW_ICON);
viewIcon.setDescription("Предварительный просмотр");
viewIcon.setStyleName("icon-inactive");
viewIcon.setHeight(32, UNITS_PIXELS);
viewIcon.setWidth(32, UNITS_PIXELS);
viewIcon.setImmediate(true);
header.addComponent(viewIcon);
header.setComponentAlignment(viewIcon, Alignment.BOTTOM_CENTER);
editorIcon.addListener(new MouseEvents.ClickListener() {
@Override
public void click(MouseEvents.ClickEvent event) {
if (mainContent != wizard) {
TaskForm.this.replaceComponent(mainContent, wizard);
TaskForm.this.setExpandRatio(wizard, 1f);
mainContent = wizard;
editorIcon.setStyleName("icon-inactive");
viewIcon.setStyleName("icon-active");
}
}
});
viewIcon.addListener(new MouseEvents.ClickListener() {
@Override
public void click(MouseEvents.ClickEvent event) {
if (mainContent == wizard && flow.get(0) instanceof FormDataSource) {
FormDataSource dataSource = (FormDataSource) flow.get(0);
Map<String, String> response = null;
String serviceLocation = AdminServiceProvider.get().getSystemProperty(API.PRINT_TEMPLATES_SERVICELOCATION);
String json = buildJsonStringWithFormData(dataSource);
boolean responseContainsTypeKey = false;
if (serviceLocation != null &&
!serviceLocation.isEmpty() &&
json != null &&
!json.isEmpty()) {
response = callPrintService(serviceLocation, json);
if (response != null && response.containsKey("type")) {
responseContainsTypeKey = true;
log.info("PRINT SERVICE. Response type: " + response.get("type"));
} else {
log.info("PRINT SERVICE. Response type: null");
}
}
PrintPanel printPanel;
if (responseContainsTypeKey &&
response.get("type").equals("success") &&
response.get("content") != null &&
!response.get("content").isEmpty()) {
printPanel = new PrintPanel(response.get("content"), getApplication());
} else {
printPanel = new PrintPanel(dataSource, getApplication(), formDesc.procedureName, id.taskId);
}
TaskForm.this.replaceComponent(wizard, printPanel);
TaskForm.this.setExpandRatio(printPanel, 1f);
mainContent = printPanel;
editorIcon.setStyleName("icon-active");
viewIcon.setStyleName("icon-inactive");
editorIcon.setVisible(true);
}
}
});
viewIcon.setStyleName("icon-active");
viewIcon.setVisible(true);
} else {
viewIcon = null;
}
VerticalLayout labels = new VerticalLayout();
labels.setSpacing(true);
header.addComponent(labels);
header.setComponentAlignment(labels, Alignment.MIDDLE_LEFT);
header.setExpandRatio(labels, 1f);
addLabel(labels, formDesc.procedureName, "h1");
addLabel(labels, formDesc.processDefinition.getDescription(), null);
if (formDesc.task != null) {
addLabel(labels, formDesc.task.getName(), "h2");
addLabel(labels, formDesc.task.getDescription(), null);
}
wizard = new Wizard(accumulator);
wizard.setImmediate(true);
wizard.addListener(new ProgressActions());
for (FormSeq seq : flow) {
wizard.addStep(new FormStep(seq, formFlow, wizard));
}
mainContent = wizard;
addComponent(wizard);
setExpandRatio(wizard, 1f);
if (formDesc.task != null) {
setMargin(true);
} else {
// у заявителя уже есть обрамление
setMargin(true, false, false, false);
}
setImmediate(true);
setSpacing(true);
setSizeFull();
setExpandRatio(wizard, 1f);
}
private void addLabel(ComponentContainer container, String text, String style) {
text = StringUtils.trimToNull(text);
if (text != null) {
Label label = new Label(text);
if (style != null) {
label.setStyleName(style);
}
container.addComponent(label);
}
}
@Override
public String getTaskId() {
return id.taskId;
}
void complete() {
try {
final boolean processed;
final BidID bidID;
if (id.taskId == null) {
processed = true;
bidID = Functions.withEngine(new StartTaskFormSubmitter(id.processDefinitionId, formFlow.getForms(), accumulator));
} else {
bidID = null;
processed = Functions.withEngine(new TaskFormSubmitter(id.taskId, formFlow.getForms(), accumulator));
}
Flash.fire(new TaskChanged(this, id.taskId));
if (!processed) {
getWindow().showNotification("Ошибка", "Этап уже обработан или передан другому исполнителю", TYPE_ERROR_MESSAGE);
}
if (bidID != null) {
getWindow().showNotification("Заявка " + bidID.bidId + " подана!", TYPE_WARNING_MESSAGE);
}
close();
} catch (Exception e) {
Components.showException(getWindow(), e);
}
}
void close() {
if (closeListener != null) {
closeListener.onFormClose(this);
}
}
final class ProgressActions implements WizardProgressListener {
@Override
public void activeStepChanged(WizardStepActivationEvent event) {
if (viewIcon != null) {
List<WizardStep> steps = event.getWizard().getSteps();
WizardStep step = event.getActivatedStep();
viewIcon.setVisible(steps.indexOf(step) == 0);
}
}
@Override
public void stepSetChanged(WizardStepSetChangedEvent unusedEvent) {
}
@Override
public void wizardCancelled(WizardCancelledEvent unusedEvent) {
close();
}
@Override
public void wizardCompleted(WizardCompletedEvent unusedEvent) {
complete();
}
}
private String buildJsonStringWithFormData(FormDataSource dataSource) {
String taskId = getTaskId();
String procedureCode = getProcedureCode(taskId);
String organizationId = getOrganizationId();
String userTaskId = null;
if (taskId != null && !taskId.isEmpty()) {
userTaskId = getUserTaskId(taskId);
}
List<Map<String, String>> elements = getElements(dataSource);
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("procedure_id", procedureCode);
data.put("organization_id", organizationId);
data.put("task_id", userTaskId);
data.put("elements", elements);
Gson gson = new Gson();
return gson.toJson(data);
}
private List<Map<String, String>> getElements(FormDataSource dataSource) {
List<Map<String, String>> result = new LinkedList<Map<String, String>>();
FormEntry formEntry = dataSource.createFormTree();
FormEntry[] children = formEntry.children;
for (FormEntry childEntry : children) {
Map<String, String> element = new LinkedHashMap<String, String>();
element.put("name", childEntry.id);
element.put("value", (childEntry.value == null || "value".equals(childEntry.value)) ? "" : childEntry.value);
result.add(element);
}
return result;
}
private Map<String, String> callPrintService(String serviceLocation, String json) {
HttpURLConnection connection = null;
Map<String, String> result = null;
try {
URL url = new URL(serviceLocation);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
String postParameters = "data=" + json;
OutputStream os = new BufferedOutputStream(connection.getOutputStream());
os.write(postParameters.getBytes("UTF-8"));
os.flush();
os.close();
BufferedReader input = new BufferedReader(new InputStreamReader(connection.getInputStream()));
log.info("PRINT SERVICE. Response code: " + connection.getResponseCode());
String line;
StringBuffer stringBuffer = new StringBuffer();
while ((line = input.readLine()) != null) {
stringBuffer.append(line);
}
input.close();
result = new Gson().fromJson(stringBuffer.toString(), new TypeToken<Map<String, String>>() {
}.getType());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
return result;
}
private String getProcedureCode(String taskId) {
if (taskId != null && !taskId.isEmpty()) {
return String.valueOf(AdminServiceProvider.get().getBidByTask(taskId).getProcedure().getRegisterCode());
} else if (id.processDefinitionId != null && !id.processDefinitionId.isEmpty()){
return String.valueOf(AdminServiceProvider.get().getProcedureCodeByProcessDefinitionId(id.processDefinitionId));
} else {
return null;
}
}
private String getOrganizationId() {
Employee user = AdminServiceProvider.get().findEmployeeByLogin(Flash.login());
Organization organization = user.getOrganization();
if (organization != null) {
return organization.getId().toString();
} else {
return null;
}
}
private String getUserTaskId(String taskId) {
return Fn.withEngine(new GetUserTaskId(), Flash.login(), taskId);
}
final private static class GetUserTaskId implements F2<String, String, String> {
@Override
public String apply(ProcessEngine engine, String login, String taskId) {
CommandExecutor commandExecutor = ((ServiceImpl) engine.getFormService()).getCommandExecutor();
return String.valueOf(commandExecutor.execute(new GetUserTaskIdCommand(taskId)));
}
final private static class GetUserTaskIdCommand implements Command {
private final String taskId;
GetUserTaskIdCommand(String taskId) {
this.taskId = taskId;
}
@Override
public Object execute(CommandContext commandContext) {
String processInstanceId = AdminServiceProvider.get().getBidByTask(taskId).getProcessInstanceId();
ExecutionEntity execution = Context.getCommandContext()
.getExecutionManager()
.findExecutionById(processInstanceId);
return execution.getActivity().getId();
}
}
}
}