Skip to content

2022.03.29 - Cooperative content

Today our solution has a new requirement. There are new contribuitors to the Learning contents.

Until today the number of contribuitor where few and all very senior. The new team member are junior and should propose changes to the content instead of changing the content directly.

Two main options are available:

  1. Allow for a new group of fields dealing with proposed changes (with a duplicate fields approach)
  2. Create an auxiliary definition to handle this changes (separate definition)

The first aproach has the advantage of keeping all information and discussion of one content in one single record. The second apprach is more scalable and allows for more details on how to deal with these proposals and distinct permissions In the end we opted for the second approach.

Duplicate fields approach

In this approach we have, within the content data, a group of repeatable fields where you can propose changes and discuss each individually:

Definition

The result is the following:

Interface

Simple and concise. Not very scalable (good for few changes per content), hard to delegate and distribute work (good for very small team), dificult to manage permissions (not a problem if everyone has all the privileges)

Separate definition

In this approach we extract the field of the previous approch and create a seperate definition:

Definition

And the change to the Contents definition:

Definition

The result is the following:

Interface

Extra funcionality

In order to make it simpler to colaborate and comment proposals we added a script that copies every new comment to a log field with the audit data that will make it easiear to decide on proposals.

groovy
import groovy.transform.Field;

@Field FIELD_COMENTARIO = "New Comment";
@Field FIELD_LOG_COMENTARIOS = "Proposal Log";
@Field FIELD_ESTADO = "Proposal State";

if( msg.user != "integrationm"
    && msg.product == "recordm"
    && msg.type == "E-learning ContentsX RFCs"
    && msg.action != "delete"
){
    def updates = [:];
    if( msg.value(FIELD_COMENTARIO) != null || msg.field(FIELD_ESTADO).changed() ){
        updates[FIELD_LOG_COMENTARIOS] = "" + getLogComentariosActualizado(msg.user);
        updates[FIELD_COMENTARIO] = "";
    }

	if(updates) {
        def result = recordm.update(messageMap.type, "recordmInstanceId:" + messageMap.instance.id, updates);
        log.info("[\$log] UPDATE '${msg.type}' id:${msg.instance.id}, updates: ${updates}, result:${result.getStatus()} | ${result.getStatusInfo()} ");
    }
}

String getLogComentariosActualizado(currentUser){
    def logComentarios = msg.value(FIELD_LOG_COMENTARIOS);
    def novoComentario = msg.value(FIELD_COMENTARIO);
    def estadoMsg = getEstadoMessage();

    def entrada = "#### " + (new Date()).format('yyyy-MM-dd HH:mm:ss').toString() + " - " + currentUser;
    entrada += " [$estadoMsg] "
    entrada +="\n\n";
    entrada += novoComentario != null ? novoComentario : "";
    entrada +="\n\n";

    if(logComentarios!=null){
        entrada +=  "======================================================================\n";
    }
    return ( entrada + (logComentarios ?: ""));
}

def getEstadoMessage(){
    String oldEstado = msg.oldInstance.value(FIELD_ESTADO) ?: "";
    String estado = msg.value(FIELD_ESTADO) ?: "";

    if(msg.action == "update" && oldEstado != estado){
        return "$oldEstado -> $estado";
    } else {
        return estado;
    }
}

Permissions

One of the advantages of having a seperate definition is that you can make the access to the contents read only to junior members of the team and allow them full privelages to the proposal that they make.