Sim é possível. Tenho isso rodando tanto em widgets quanto em formulários.
Na minha widget funciona assim:
1 - Os dados estão em uma tabela de metadados do RM e são manipulados pelo usuário através de uma widget no fluig.
Como criar um Metadados no RM
http://tdn.totvs.com/display/public/LRM/Metadados+-+Como+incluir+um+projeto
Cadastrar o WebService wsDataServer do RM Host Serviço no Fluig
Criar um Dataset para gravar/modificar um registro no Metadados Criado
function createDataset(fields, constraints, sortFields) {
/* Caminhos do serviço*/
var USUARIO ="ESCREVER O USUÁRIO DE INTEGRAÇÃO DO RM"
var SENHA = "ESCREVER A SENHA";
var NOME_SERVICO = "HostWsDataServer";
var CAMINHO_SERVICO = "com.totvs.WsDataServer";
var CAMINHOSOAP = "com.totvs.IwsDataServer";
var DataServerName = 'RMSPRJ4570112Server'; //COLOCAR O NOME DO PROJETO DE METADADOS CRIADO NO RM
var Contexto = "CODCOLIGADA=1";
var ID = "";
var CODCOLIGADA = "";
var DATA = "";
var ASSUNTO = "";
var ORIGEM = "";
var ACAO = "";
var RESPONSAVEL = "";
var DATAPREVISAO = "";
var DATACONCLUSAO = "";
var ACOMPANHAMENTO= "";
var OBS ="";
if (constraints != null) {
for (var i = 0; i < constraints.length; i++) {
if (constraints[i].fieldName == "ID") {
ID = constraints[i].initialValue;
}
if (constraints[i].fieldName == "CODCOLIGADA") {
CODCOLIGADA = constraints[i].initialValue;
}
if (constraints[i].fieldName == "DATA") {
DATA = constraints[i].initialValue;
}
if (constraints[i].fieldName == "ASSUNTO") {
ASSUNTO = constraints[i].initialValue;
}
if (constraints[i].fieldName == "ORIGEM") {
ORIGEM = constraints[i].initialValue;
}
if (constraints[i].fieldName == "ACAO") {
ACAO = constraints[i].initialValue;
}
if (constraints[i].fieldName == "RESPONSAVEL") {
RESPONSAVEL = constraints[i].initialValue;
}
if (constraints[i].fieldName == "DATAPREVISAO") {
DATAPREVISAO = constraints[i].initialValue;
}
if (constraints[i].fieldName == "DATACONCLUSAO") {
DATACONCLUSAO = constraints[i].initialValue;
}
if (constraints[i].fieldName == "ACOMPANHAMENTO") {
ACOMPANHAMENTO = constraints[i].initialValue;
}
if (constraints[i].fieldName == "OBS") {
OBS = constraints[i].initialValue;
}
}
}
//A FORMA COMO MONTEI O XML NÃO É SEGURA, O METADADOS ESTARÁ VULNERÁVEL A INJECTIONS. PARA O MEU CASO O RISCO É BAIXO, E O O IMPACTO É MENOR AINDA.
var XML = "<NewDataSet>";
XML+="<ZMDPLANOACO>";
XML+="<ID>"+ID+"</ID>";
XML+="<CODCOLIGADA>"+CODCOLIGADA+"</CODCOLIGADA>";
XML+="<DATA>"+DATA+"</DATA>";
XML+="<ASSUNTO>"+ASSUNTO+"</ASSUNTO>";
XML+="<ORIGEM>"+ORIGEM+"</ORIGEM>";
XML+="<ACAO>"+ACAO+"</ACAO>";
XML+="<RESPONSAVEL>"+RESPONSAVEL+"</RESPONSAVEL>";
XML+="<DATAPREVISAO>"+DATAPREVISAO+"</DATAPREVISAO>";
XML+="<DATACONCLUSAO>"+DATACONCLUSAO+"</DATACONCLUSAO>";
XML+="<ACOMPANHAMENTO>"+ACOMPANHAMENTO+"</ACOMPANHAMENTO>";
XML+="<OBS>"+OBS+"</OBS>";
XML+="</ZMDPLANOACO>";
XML+="</NewDataSet>";
var newDataset = DatasetBuilder.newDataset();
newDataset.addColumn("SaveRecordResult");
var properties = {};
properties["basic.authorization"] = "true";
properties["basic.authorization.username"] = USUARIO;
properties["basic.authorization.password"] = SENHA;
properties["disable.chunking"] = "true";
properties["log.soap.messages"] = "true";
properties["receive.timeout"] = "60000";
properties["basic.authorization.type"] = "none";
try{
var servico = ServiceManager.getService(NOME_SERVICO);
var serviceHelper = servico.getBean();
var serviceLocator = serviceHelper.instantiate(CAMINHO_SERVICO);
var service = serviceLocator.getRMIwsDataServer();
var authenticatedService = serviceHelper.getCustomClient(service, CAMINHOSOAP , properties );
var result = authenticatedService.saveRecord(DataServerName, XML,Contexto);
var strRetorno = result;
newDataset.addRow(new Array(strRetorno));
}catch(err){
newDataset.addRow(new Array(err.toString() + ' _ ' + XML ));
}
return newDataset;
2 - No fluig, quando o usuário adiciona um novo registro, o fluig integra com o RM e cria um novo registro no metadados.
Adicionar a biblioteca abaixo em sua widget
<script src='/webdesk/vcXMLRPC.js' type="text/javascript"></script>
:
Salvar / Adicionar um registro. Para um novo registro o ID deve ser -1, para alterar ele o ID deve ser o do registro
salvarRegistro:function(ID, CODCOLIGADA,DATA, ASSUNTO, ORIGEM, RESPONSAVEL,DATAPREVISAO, DATACONCLUSAO, ACAO, ACOMPANHAMENTO,OBS){
var c1 = DatasetFactory.createConstraint("ID",ID, ID, ConstraintType.MUST);
var c2 = DatasetFactory.createConstraint("CODCOLIGADA",CODCOLIGADA, CODCOLIGADA, ConstraintType.MUST);
var c3 = DatasetFactory.createConstraint("DATA",DATA, DATA, ConstraintType.MUST);
var c4 = DatasetFactory.createConstraint("ASSUNTO",ASSUNTO, ASSUNTO, ConstraintType.MUST);
var c5 = DatasetFactory.createConstraint("ORIGEM",ORIGEM, ORIGEM, ConstraintType.MUST);
var c6 = DatasetFactory.createConstraint("RESPONSAVEL",RESPONSAVEL, RESPONSAVEL, ConstraintType.MUST);
var c7 = DatasetFactory.createConstraint("DATAPREVISAO",DATAPREVISAO, DATAPREVISAO, ConstraintType.MUST);
var c8 = DatasetFactory.createConstraint("DATACONCLUSAO",DATACONCLUSAO, DATACONCLUSAO, ConstraintType.MUST);
var c9 = DatasetFactory.createConstraint("ACAO",ACAO, ACAO, ConstraintType.MUST);
var c10 = DatasetFactory.createConstraint("ACOMPANHAMENTO",ACOMPANHAMENTO, ACOMPANHAMENTO, ConstraintType.MUST);
var c11 = DatasetFactory.createConstraint("OBS",OBS, OBS, ConstraintType.MUST);
var restricoes = new Array(c1 , c2, c3, c4, c5, c6, c7, c8, c9, c10, c11);
var dataset = DatasetFactory.getDataset("NOME do Seu Data set criado no item 1", null, restricoes, null);
for (var i in dataset.values){
resultado = dataset.values[i].SaveRecordResult.toString();
}
return resultado;
},
3 - Com o Id desse novo registro criado, obtido com o retorno da requisição, a widget cria uma pasta com o nome igual ao ID do registro.
Criar uma pasta
criarPasta:function(nomePasta,idPai){
var that = this;
var mensagem = "";
$.ajax({
async : true,
method : "POST",
contentType: "application/json",
url : '/api/public/ecm/document/createFolder',
data: JSON.stringify({
"description" : nomePasta,
"parentId" : idPai
}),
error: function(data,status,header,config) {
FLUIGC.toast({
title: 'erro',
message: "Falha ao criar pasta. Será impossível fazer downloads.",
type: 'danger'
});
},
success: function(data) {}
});
},
4 - Quando o usuário faz o upload de um ou mais arquivos para o fluig, ele grava o arquivo dentro dessa pasta criada.
Upload de arquivo, eu uso a biblioteca do jquery fileupload para facilitar a operação de drag and drop
uploadArquivoNovo:function(parentId, fileName, fileDescription, text){
var that = this;
$.ajax({
async : true,
type : "POST",
contentType: "application/json",
url : '/api/public/ecm/document/createDocument',
data: JSON.stringify({
"description": fileDescription,
"parentId": parentId,
"attachments":[{"fileName": fileName}],
"additionalComments": text
}),
error: function(jqXHR, textStatus, errorThrown) {
FLUIGC.toast({
title: '',
message: "Falha ao enviar o arquivo " + fileName + '.'+ errorThrown,
type: 'danger'
});
console.log(jqXHR, textStatus, errorThrown);
return false;
},
success: function(data) {
var idNovo = data.content.id;
var description = data.content.description;
var acoesNovas = '<a href="httsp://fluig.XXXXXXXX.com.br/portal/p/1/ecmnavigation?app_ecm_navigation_doc='+idNovo.toString()+'" target="_blank"><span class="fluigicon fluigicon-download fluigicon-md"></span></a>';
var row ={
id: idNovo,
nomearquivo: description,
acoes: acoesNovas
};
that.adicionarLinhaTabelaArquivos(idNovo, description, acoesNovas);
FLUIGC.toast({
title: '',
message: "Documento publicado - " + fileName,
type: 'info'
});
return true;
},
});
},
5 - Para listar os aquivos, o fluig acessa o dataset de documentos e popula uma tabela com os arquivos que estão na pasta.
Listar arquivos de uma pasta
buscarArquivosPasta:function(idPai){
var that = this;
var retorno = [];
var c1 = DatasetFactory.createConstraint("parentDocumentId",idPai, idPai, ConstraintType.MUST);
var c2 = DatasetFactory.createConstraint("deleted",false, false, ConstraintType.MUST);
var c3 = DatasetFactory.createConstraint("activeVersion",true, true, ConstraintType.MUST);
var sortingFields = new Array("documentDescription");
var constraints = new Array(c1,c2,c3);
var resultado = DatasetFactory.getDataset("document", null, constraints, sortingFields);
if(resultado.values.length > 0){
for(var r = 0; r < resultado.values.length; r++){
var registro = resultado.values[r];
var row={
id:registro['documentPK.documentId'],
nomearquivo: registro['documentDescription'],
acoes: registro['documentPK.documentId']
}
retorno.push(row);
}
}
return retorno;
},
6 - Através de um simples link o usuário acessa os documento pela widget.
Espero ter ajudado.