This unmanaged package provides Apex support for easily adding notes and attachments (using the new content library objects) to Salesforce records, as well as bulk note importing. 100% test coverage is included. The package is distributed as Salesforce DX-format source code.
The package is available under the MIT License.
The following steps are required to prepare note content for insertion into Salesforce. Note that it's here assumed that note content is unformatted plain text, not HTML.
- Replace all basic HTML characters (
<>"'&
) with their corresponding entities (&
and friends). - Replace all line breaks with
<br>
(taking care with Windows CRLF/Linux LF/Mac CR) - Replace
'
with'
. - Do not replace Unicode characters with entities. Other entities, including
'
, result in an exception. Unicode should be left as the bare characters. - Ensure that the source content is well-formed Unicode/UTF-8 and does not contain non-printable characters.
- The title must not be
null
, zero-length, or consist only of whitespace. The title need not be escaped.
All but (5) are handled by this package. You are responsible for ensuring that supplied text is well-formed.
Errors that do occur with ContentNote
s usually come in the form of System.UnexpectedException
s, which cannot be caught or handled. Despite the best efforts of this package, it is still possible to trigger these exceptions by attempting to import notes whose text contains non-printable characters or mangled UTF-8. In a bulk note import process, this will cause the failure of the entire batch with no error message recorded on the note proxies. The error can be diagnosed by examining the Apex Jobs log, where the exception will be displayed. The only workaround is to use very small (or even 1) batch sizes to identify the offending note and manually correct its text.
Note that the API reference on ContentNote
incorrectly specifies to use String.escapeHTML4()
to prepare content. This does not work.
Both the Apex methods below and the Note Proxy object used for bulk note imports accept parameters for visibility and sharing settings. When linking notes and attachments to regular Salesforce records, like Contacts, visibility "AllUsers"
and sharing type "I"
(inferred) are appropriate. (Other visibility values will actually cause exceptions). More detail on acceptable values is found in the ContentDocumentLink
API reference. Values other than "AllUsers"
/"I"
may be useful when working with content libraries or communities.
There are Salesforce governor limits on the number of ContentVersion
objects (which includes notes and attachments) that can be inserted in a 24 hour period. For production editions of Salesforce, the limit is 200,000.
For Developer Edition organizations, the ContentVersion
limit is only 2,500. It's very easy to hit this limit in testing within a developer organization. Insofar as the included test suite inserts a large number of ContentVersion
objects) and the fact that hitting this limit results in a System.UnexpectedException
, it's recommended not to run the full test suite frequently, or to use scratch orgs to run tests.
The class DMRNoteAttachmentImporter
provides Apex support. The following methods are available:
Create a note linked to the record whose Id is supplied. ContentNote
titles may not be null, empty, or composed only of whitespace; if such is provided, it will be replaced by the string "Untitled Note". DMRNoteAttachmentImporter
will escape the text provided as required (see above). See the section above for more on the visibility
and shareType
parameters.
void addAttachment(String title, String path, Blob contents, Id linkedTo, String visibility, String shareType)
Create an attachment linked to the record whose Id is supplied. For a plain-text attachment, you can provide Blob.valueOf(aString)
for contents
without going through the preparation steps required for notes.
Insert all of the accumulated notes, attachments, and links. Update the results
list accordingly. Return true
if all inserts succeeded, and false
if any errors occurred. If errors resulted from invalid links, the corresponding notes or attachments aren't retained and the link error appears at the corresponding index in the results
list.
static Boolean addSingleNote(String title, String content, Id linkedTo, String visibility, String shareType)
static Boolean addSingleAttachment(String title, String path, Blob contents, Id linkedTo, String visibility, String shareType)
These static convenience methods simply create the records as specified and immediately insert them into the database. true
or false
is returned for success or failure, but errors aren't available - create an instance if error reporting is needed.
Notes may be added in bulk by importing data to the DMRNoteProxy__c
object using any data loader. If working with very large notes, bear in mind that some data loaders and spreadsheet applications are not able to handle more than 32KB of text within a CSV cell.
List views on the Note Proxies tab provide access to proxies and can initiate a batch Apex process (DMRNoteBulkImporter
) to perform imports. Be aware that, using the Note Proxy object, you can only import notes of lengths up to the limit of a Long Text Area, 131,072 characters (128KB, assuming 1-byte ASCII or Latin-1 characters). While your organization may vary, testing shows that it's possible to process 32KB notes in batches of 200. Stepping up to 64KB notes required a reduction in batch size.
A DMRNoteImporter permission set is provided. It enables full access to all of the package's functionality.