Loading

Acceso de funciones a objetos de contenido mediante el uso compartido programático en ContentDocumentLink

Fecha de publicación: Oct 13, 2022
Descripción

Terminología

Documento de contenido: representa un documento que se cargó en una biblioteca en Salesforce CRM Content o Salesforce Files.

Versión de contenido: representa una versión específica de un documento en Salesforce CRM Content o Salesforce Files. Un documento de contenido puede tener muchas versiones de contenido.

Vínculo al documento de contenido:
representa el vínculo entre un documento de Salesforce CRM Content o de Salesforce Files y dónde se comparte. Un archivo puede compartirse con otros usuarios, grupos, registros y bibliotecas de Salesforce CRM Content.
 

Acceso al contenido y creación del vínculo al documento

Existen dos tipos de acceso al contenido:
  1. Funciones que acceden a su propio contenido
  2. Funciones que acceden al contenido de otro usuario
Cuando una función crea contenido nuevo, el usuario de integración de la nube será el propietario de dicho contenido, por lo cual las funciones podrán acceder a él. Este es el escenario más simple. El más común es cuando el contenido ya existe y la función debe acceder a él.
 

Funciones que acceden a su propio contenido

Una función puede crear contenido e insertarlo mediante el extremo de la API de REST aquí:
services/data/vXX.X/sobjects/ContentVersion

De este modo, el usuario de integración de la nube se vuelve el propietario del contenido de manera automática, y la función (al igual que otros usuarios) podrá acceder a dicho contenido. Los vínculos a documentos se insertan automáticamente, por lo que no es necesario seguir ningún otro paso.

Funciones que acceden al contenido de otro usuario

Debemos crear un registro en ContentDocumentLink que vincule el contenido con el usuario con quien queremos compartirlo. En este caso, el usuario será el usuario de integración de la nube. Se puede hacer mediante Apex una vez creado el contenido o mediante DataLoader una vez cargado.

A continuación, incluimos un ejemplo de código Apex utilizado para crear contenido (es decir, un registro ContentVersion) y vincularlo con ContentDocumentLink (es decir, un registro ContentDocumentLink).
 
//Crear un documento
//Nota: En este ejemplo, el contenido se crea en Apex.
//También podríamos cargar contenido mediante Data Loader.
ContentVersion cv = new ContentVersion();
cv.Title = 'Test Document';
cv.PathOnClient = 'TestDocument.pdf';
cv.VersionData = Blob.valueOf('Test Content');
cv.IsMajorVersion = true;
Insert cv;
 
//Obtener los documentos de contenido
Id conDocId = [SELECT ContentDocumentId FROM ContentVersion WHERE Id =:cv.Id].ContentDocumentId;
 
//Crear ContentDocumentLink 
ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = conDocId;
//La etiqueta contiene el Id. de usuario del usuario de integración de la nube (obtenido de forma independiente).
cdl.LinkedEntityId = Label.Platform_User_Id;
//V - Permiso de Viewer (Visor). C - Permiso de colaborador I - Permiso inferido
cdl.ShareType = 'V';
Insert cdl;

Aquí, ContentDocumentId se obtiene de ContentVersion (la versión del documento que se creó antes; no se muestra aquí).

LinkedEntityId se refiere al Id. del usuario de integración de la nube. Se debe obtener antes e incluirlo en este código, o se debe poder acceder a él desde este código como una variable global (o mediante la caché de la plataforma).

Una vez creada la entrada en ContentDocumentLink (como se muestra arriba), una función podrá acceder al contenido normalmente desde el SDK.

Obtener el Id. de usuario de integración de la nube
Debido a que el usuario de integración de la nube no está expuesto en Salesforce como los demás usuarios, se puede obtener el Id. de este usuario de una de las siguientes maneras:

1. Podemos consultar el objeto Usuario y averiguar el nombre de usuario de CloudIntegrationUser. El nombre del usuario de integración de la nube es cloud@ + 18 caracteres orgId en minúsculas. Vea la consulta SOQL que figura a continuación. 
SELECT Id, UserName from User WHERE username like 'cloud@00d%'

2. Podemos hacer que la función inserte un registro. El Id. del usuario que insertó este registro será el del usuario de integración de la nube.

Una vez obtenido el Id., se puede almacenar para su uso en el código Apex, como se mostró anteriormente.
El código que aparece a continuación muestra una función que accede a un determinado documento de contenido después de que se estableció el uso compartido como se mostró anteriormente.
// Deliberadamente se selecciona un archivo cuyo propietario NO es el usuario de integración de la plataforma.
// (Id. codificado del usuario de integración de la plataforma reemplazado por un Id. más dinámico. 
// Combinarlo en la consulta.)
let contentQuery = "SELECT Id 
                    FROM ContentVersion 
                    WHERE OwnerId != '0055f0000057ROYAA2' 
                    ORDER BY CreatedDate DESC LIMIT 1";
let queryResults = await context.org.dataApi.query(contentQuery);
let contId = "";
for (const downItem of queryResults.records) {
    contId = downItem.fields["Id"];
    break;
}

// Id. del archivo para su descarga
logger.info("contId: " + JSON.parse(JSON.stringify(contId)));

// Imprimir
logger.info("DOWNLOAD LATEST FILE NOT OWNED BY FUNCTIONS USER");

await request
    .get(
        context.org.baseUrl +
        "/services/data/v54.0/sobjects/ContentVersion/" +
        contId +
        "/VersionData"
    )
    .auth(null, null, true, context.org.dataApi.accessToken)
    .on("error", function (err) {
        logger.info("Exception: " + err);
    })
    .pipe(
        fs.createWriteStream("./tempFiles/downFile.csv", { encoding: "utf8" })
    )
    .on("finish", doSomethingAfterDownload);    
}

Nota 1

Deliberadamente, el código intenta acceder al contenido creado por un usuario que no es el usuario de integración de la nube. Si el contenido se compartió correctamente en ContentDocumentLink (como se mostró arriba), esta función podrá acceder al contenido.

Nota 2

Si el código de la función accediese al contenido creado por el usuario de integración de la nube, no sería necesario compartirlo mediante ContentDocumentLink (ya que este se habría creado automáticamente). En este caso, el acceso al contenido es directo.

Creación de un vínculo automatizado al documento

Es posible automatizar la creación de los registros de vínculos a documentos cada vez que se crea contenido nuevo. En el siguiente ejemplo se muestra cómo hacerlo.

Código del desencadenador de muestra

El código que figura a continuación corresponde al desencadenador que crea un registro ContentDocumentLink cuando se crea un registro ContentVersion (requiere Platform_User_Id del ejemplo anterior que es necesario para la creación del vínculo al documento).
 
trigger ContentVersionTrigger on ContentVersion (after insert) {
    ContentVersionTriggerHelper.triggerHelper(
        Trigger.operationType, 
        Trigger.new, 
        Trigger.oldMap);
}
public with sharing class ContentVersionTriggerHelper {
    public static void triggerHelper(
        System.TriggerOperation operationType,
        List<ContentVersion> newList,
        Map<Id, ContentVersion> oldMap
    ) {
        switch on operationType {
            when AFTER_INSERT{
                List<ContentVersion> docsToProcess = getDocsToShare(newList);
                if(!docsToProcess.isEmpty()){
                    shareWithIntegrationUser(docsToProcess);
                }
            }
        }
    }

    public static List<ContentVersion> getDocsToShare(List<ContentVersion> allDocuments){
        List<ContentVersion> docsToProcess = new List<ContentVersion>();
        for(ContentVersion cVer : allDocuments){
            if(cDoc.OwnerId != Label.Platform_User_Id){
                docsToProcess.add(cVer);
            }
        }
        return docsToProcess;
    } 

    public static void shareWithIntegrationUser(List<ContentVersion> documents){
        List<ContentDocumentLink> shareLinks = new List<ContentDocumentLink>();
        for(ContentVersion cVer : documents){
            ContentDocumentLink cdl = new ContentDocumentLink();
            cdl.ContentDocumentId = cVer.ContentDocumentId;
            cdl.LinkedEntityId = Label.Platform_User_Id;
            cdl.ShareType = 'V';
            shareLinks.add(cdl);
        }
        Database.insert(shareLinks,false);
    }
}
 

Consideraciones y riesgos

  • Una vez compartido el vínculo a un documento de contenido, cualquier función puede acceder a él. Para evitar que se acceda al documento en el futuro, se debe anular el uso compartido de forma explícita en ContentDocumentLink. Este enfoque otorga acceso al documento a todas las funciones o a ninguna, porque todas las funciones se ejecutan como el usuario de integración de la nube.
  • El Id. del usuario de integración de la nube debe obtenerse de manera manual e insertarse en el desencadenador de Apex. Para ello, se debe almacenar el Id. en un parámetro de configuración, una etiqueta o un objeto personalizados. También puede almacenarse en la caché de la plataforma y acceder desde allí. El Id. de usuario podría cambiar después de una migración de la organización y se deberá volver a crear el uso compartido con el Id. nuevo.
  • Los documentos deben compartirse de manera individual mediante la creación de un registro de uso compartido para cada uno. Esto podría generar problemas de escalabilidad si gestionamos una gran cantidad de documentos.
Número del artículo de conocimiento

000393095

 
Cargando
Salesforce Help | Article