I'm trying to find a way how to upload content document and link it to Salesforce object (say, an opportunity) programmatically (using SOAP API).
That's what I tried:
- Insert new ContentVersion with base64 encoded VersionData, Title and PathOnClient
- Find ContentDocument related to ContentVersion just created "Select ContentDocumentId FROM ContentVersion WHERE Id = taken from step 1"
- Insert new ContentDocumentLink with ContentDocumentId = "taken from step 2", Share Type = "I" (inherited from Opportunity), LinkedEntityId = "my opportunity id"
All the operations completed successfully. I can see the Opportunity with linked file in Lightning Experience, however, can't see the file in SF Classic and I can't see it in my private library either. But I can navigate to the Content using its Id (so it's definitely created in Salesforce).
- Pic1 shows (LEX) Opportunity and linked files.
- Pic2 shows (SF Classic) same opportunity but no files are visible.
Did I miss any important step?
The source code (C#, Web Reference using Partner WSDL. Login, error handling is omitted):
// create content version
XmlDocument docCreateContentVersion = new XmlDocument();
XmlElement[] xmlCreateContentVersion = new XmlElement[3];
xmlCreateContentVersion[0] = docCreateContentVersion.CreateElement("VersionData");
xmlCreateContentVersion[0].InnerText = System.Convert.ToBase64String(Encoding.ASCII.GetBytes("Hello, World"));
xmlCreateContentVersion[1] = docCreateContentVersion.CreateElement("Title");
xmlCreateContentVersion[1].InnerText = "My Test Document";
xmlCreateContentVersion[2] = docCreateContentVersion.CreateElement("PathOnClient");
xmlCreateContentVersion[2].InnerText = "My Test Document";
sObject createContentVersion = new sObject();
createContentVersion.type = "ContentVersion";
createContentVersion.Any = xmlCreateContentVersion;
SaveResult[] createContentVersionResults = binding.create(new sObject[] { createContentVersion });
// obtain content documents
QueryResult contentDocuments = binding.query("SELECT ContentDocumentId, PublishStatus, OwnerId FROM ContentVersion WHERE Id = '" + createContentVersionResults[0].id + "'");
String contentDocumentId = contentDocuments.records[0].Any[0].FirstChild.Value;
Console.WriteLine("ContentDocumentId="+contentDocumentId);
// create content document link
XmlDocument docCreateContentDocumentLink = new XmlDocument();
XmlElement[] xmlCreateContentDocumentLink = new XmlElement[3];
xmlCreateContentDocumentLink[0] = docCreateContentDocumentLink.CreateElement("ContentDocumentId");
xmlCreateContentDocumentLink[0].InnerText = contentDocumentId;
xmlCreateContentDocumentLink[1] = docCreateContentDocumentLink.CreateElement("LinkedEntityId");
xmlCreateContentDocumentLink[1].InnerText = "006j000000SYHGu";
xmlCreateContentDocumentLink[2] = docCreateContentDocumentLink.CreateElement("ShareType");
xmlCreateContentDocumentLink[2].InnerText = "I";
sObject createContentDocumentLink = new sObject();
createContentDocumentLink.type = "ContentDocumentLink";
createContentDocumentLink.Any = xmlCreateContentDocumentLink;
SaveResult[] createContentDocumentLinkResults = binding.create(new sObject[] { createContentDocumentLink });
Console.WriteLine("ContentDocumentLinkId=" + createContentDocumentLinkResults[0].id);
Best Answer
I used the Salesforce UI to add a File to an Opportunity. Then checked the resulting ContentDocumentLink records with SQOL.
Note how there are two records, one to the Opportunity and one to my user record (this will be why you can't see it in your private library). The one to the Opportunity has the
ShareType
V (Viewer permission) andVisibility
AllUsers