Table of Contents


Creating Notes, Attachments, Bookmarks, and Snippets for Records

Magentrix allows developers to programmatically create notes, attach files, add bookmarks, and store code snippets to any record in the platform. These capabilities enable you to build custom workflows, automate documentation, and enhance record management through the NoteAndAttachment class.


Overview

The NoteAndAttachment class provides a unified way to attach content to records across all entities in Magentrix. Whether you're working with Accounts, Contacts, Opportunities, or custom entities, you can use the same approach to add:

  • Notes - Text-based content and comments
  • File Attachments - Documents, images, and other files
  • Bookmarks - Links to external resources
  • Snippets - Code files and configuration content (JavaScript, C#, CSS, JSON, XML, SQL)

Key Characteristics:

  • Single class handles four content types (controlled by Type property)
  • Content is linked to parent records via ReferenceId
  • Files are automatically stored in cloud storage
  • Visible in the record's Notes & Attachments section
  • Support for the Load() helper method for file uploads

Understanding NoteAndAttachment Types

The Type property determines what kind of content you're creating:

Type ValueContent TypeBody Property ContainsUse Case
0NoteNote text contentMeeting notes, comments, descriptions
1File AttachmentFile descriptionDocuments, images, PDFs attached to records
2BookmarkURLLinks to external resources or documentation
3SnippetCode or configuration textJavaScript, C#, CSS, JSON, XML, SQL code

Creating Notes

Notes are text-based content attached to records for documentation, comments, or tracking information.

Basic Note Creation

public ActionResponse CreateNote(string recordId, string title, string content)
{
    var note = new NoteAndAttachment
    {
        Name = title,
        Body = content,
        ReferenceId = recordId,
        Type = 0  // Note type
    };
    
    try
    {
        Database.Insert(note);
        AspxPage.AddMessage("Note created successfully!");
        return RedirectToAction("ViewRecord", new { id = recordId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        SystemInfo.Debug($"Note creation error: {ex.Message}");
        return View();
    }
}

Creating Meeting Notes

[HttpPost]
public ActionResponse AddMeetingNotes(string accountId, string meetingDate, string notes)
{
    var note = new NoteAndAttachment
    {
        Name = $"Meeting Notes - {meetingDate}",
        Body = notes,
        ReferenceId = accountId,
        Type = 0
    };
    
    try
    {
        Database.Insert(note);
        AspxPage.AddMessage("Meeting notes saved successfully!");
        return RedirectToAction("AccountDetails", new { id = accountId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError($"Failed to save notes: {ex.Message}");
        return View();
    }
}

Creating Follow-Up Notes

public ActionResponse AddFollowUpNote(string opportunityId)
{
    var opportunity = Database.Retrieve<Opportunity>(opportunityId);
    
    if (opportunity == null)
    {
        return PageNotFound();
    }
    
    var note = new NoteAndAttachment
    {
        Name = $"Follow-up Required - {DateTime.Now:MM/dd/yyyy}",
        Body = $"Follow-up scheduled for {opportunity.Name}. " +
               $"Next contact date: {opportunity.NextContactDate:MM/dd/yyyy}. " +
               $"Action items: Review proposal, schedule demo call, send pricing.",
        ReferenceId = opportunityId,
        Type = 0
    };
    
    try
    {
        Database.Insert(note);
        AspxPage.AddMessage("Follow-up note created!");
        return RedirectToAction("OpportunityDetails", new { id = opportunityId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Attaching Files to Records

File attachments link documents, images, and other files to specific records.

Using NoteAndAttachment.Load() (Recommended)

The Load() method automatically populates file properties from the uploaded file.

[HttpPost]
public ActionResponse UploadAttachment(HttpPostedFile myFile, string parentRecordId)
{
    // Load file details into NoteAndAttachment
    var attachment = NoteAndAttachment.Load(myFile);
    
    if (attachment == null)
    {
        AspxPage.AddError("Please upload a file to continue.");
        return View();
    }
    
    // Link to parent record
    attachment.ReferenceId = parentRecordId;
    
    try
    {
        Database.Insert(attachment);
        AspxPage.AddMessage($"File '{attachment.Name}' attached successfully!");
        return RedirectToAction("ViewRecord", new { id = parentRecordId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        SystemInfo.Debug($"Attachment error: {ex.Message}");
        return View();
    }
}

Manual File Attachment Creation

Create attachments by manually setting properties.

[HttpPost]
public ActionResponse AttachFileManually(HttpPostedFile myFile, string parentRecordId)
{
    // Read file bytes
    byte[] fileBytes = new byte[myFile.ContentLength];
    myFile.InputStream.Read(fileBytes, 0, myFile.ContentLength);
    
    // Convert to Base64
    string fileContents = Convert.ToBase64String(fileBytes);
    
    // Create attachment
    var attachment = new NoteAndAttachment
    {
        Name = myFile.FileName,
        Body = "Uploaded document",  // File description
        ReferenceId = parentRecordId,
        Type = 1,  // File attachment type
        FileContents = fileContents,
        ContentType = myFile.ContentType
    };
    
    try
    {
        Database.Insert(attachment);
        AspxPage.AddMessage($"File '{attachment.Name}' attached successfully!");
        return RedirectToAction("ViewRecord", new { id = parentRecordId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Attaching Files with Descriptions

[HttpPost]
public ActionResponse AttachWithDescription(HttpPostedFile file, string parentId, string description)
{
    var attachment = NoteAndAttachment.Load(file);
    
    if (attachment == null)
    {
        AspxPage.AddError("No file uploaded.");
        return View();
    }
    
    // Set parent record and description
    attachment.ReferenceId = parentId;
    attachment.Body = description;  // Custom description
    
    try
    {
        Database.Insert(attachment);
        AspxPage.AddMessage($"File '{attachment.Name}' attached with description!");
        return RedirectToAction("ViewRecord", new { id = parentId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Attaching Multiple Files

[HttpPost]
public ActionResponse AttachMultipleFiles(string parentRecordId)
{
    if (Request.Files.Count == 0)
    {
        AspxPage.AddError("No files were uploaded.");
        return View();
    }
    
    var successCount = 0;
    var errorCount = 0;
    var noteAndAttachments = new List<NoteAndAttachment>()

    for (var i = 0; i < Request.Files.Count; i++)
    {
        HttpPostedFile file = Request.Files[i];
        
        if (file.ContentLength == 0)
        {
            continue;
        }
        
        try
        {
            var attachment = NoteAndAttachment.Load(file);
            attachment.ReferenceId = parentRecordId;

            noteAndAttachments.Add(attachment)
        }
        catch (Exception ex)
        {
            SystemInfo.Debug($"Error attaching {file.FileName}: {ex.Message}");
            errorCount++;
        }
    }
    Database.Insert(noteAndAttachments);
    AspxPage.AddMessage($"Successfully attached {successCount} file(s).");
    
    return RedirectToAction("ViewRecord", new { id = parentRecordId });
}

Creating Bookmarks for Records

Bookmarks link records to external URLs or resources.

Basic Bookmark Creation

public ActionResponse CreateBookmark(string parentRecordId, string bookmarkName, string url)
{
    var bookmark = new NoteAndAttachment
    {
        Name = bookmarkName,
        Body = url,  // URL stored in Body property
        Type = 2,  // Bookmark type
        ReferenceId = parentRecordId
    };
    
    try
    {
        Database.Insert(bookmark);
        AspxPage.AddMessage($"Bookmark '{bookmarkName}' created successfully!");
        return RedirectToAction("ViewRecord", new { id = parentRecordId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Creating Documentation Links

[HttpPost]
public ActionResponse AddDocumentationLink(string accountId, string linkName, string url)
{
    // Validate URL
    if (string.IsNullOrWhiteSpace(url))
    {
        AspxPage.AddError("URL is required.");
        return View();
    }
    
    if (!url.StartsWith("http://") && !url.StartsWith("https://"))
    {
        AspxPage.AddError("URL must start with http:// or https://");
        return View();
    }
    
    var bookmark = new NoteAndAttachment
    {
        Name = linkName,
        Body = url,
        Type = 2,
        ReferenceId = accountId
    };
    
    try
    {
        Database.Insert(bookmark);
        AspxPage.AddMessage("Documentation link added successfully!");
        return RedirectToAction("AccountDetails", new { id = accountId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Creating Product Resource Links

public ActionResponse AddProductResources(string opportunityId)
{
    var opportunity = Database.Retrieve<Opportunity>(opportunityId);
    
    if (opportunity == null)
    {
        return PageNotFound();
    }
    
    // Create multiple bookmarks for product resources
    var bookmarks = new List<NoteAndAttachment>
    {
        new NoteAndAttachment
        {
            Name = "Product Documentation",
            Body = "https://docs.example.com/product",
            Type = 2,
            ReferenceId = opportunityId
        },
        new NoteAndAttachment
        {
            Name = "Product Demo Video",
            Body = "https://videos.example.com/demo",
            Type = 2,
            ReferenceId = opportunityId
        },
        new NoteAndAttachment
        {
            Name = "Pricing Information",
            Body = "https://example.com/pricing",
            Type = 2,
            ReferenceId = opportunityId
        }
    };
    
    try
    {
        Database.Insert(bookmarks);
        
        AspxPage.AddMessage("Product resource links added successfully!");
        return RedirectToAction("OpportunityDetails", new { id = opportunityId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Creating Code Snippets for Records

Snippets are code files and configuration content attached to records for technical documentation and development purposes.

Creating JavaScript Snippet

[HttpPost]
public ActionResponse CreateJavaScriptSnippet(string recordId, string snippetName, string code)
{
    var snippet = new NoteAndAttachment
    {
        Name = snippetName,
        FileContents = code,  // JavaScript code content
        Type = 3,  // Snippet type
        ReferenceId = recordId
    };
    
    try
    {
        Database.Insert(snippet);
        AspxPage.AddMessage($"JavaScript snippet '{snippetName}' created successfully!");
        return RedirectToAction("ViewRecord", new { id = recordId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        SystemInfo.Debug($"Snippet error: {ex.Message}");
        return View();
    }
}

Creating SQL Query Snippet

[HttpPost]
public ActionResponse CreateSQLSnippet(string projectId, string queryName, string sqlQuery)
{
    var snippet = new NoteAndAttachment
    {
        Name = queryName,
        Body = sqlQuery,  // SQL query content
        Type = 3,
        ReferenceId = projectId
    };
    
    try
    {
        Database.Insert(snippet);
        AspxPage.AddMessage("SQL query snippet saved!");
        return RedirectToAction("ProjectDetails", new { id = projectId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Creating JSON Configuration Snippet

[HttpPost]
public ActionResponse CreateConfigSnippet(string recordId, string configName, string jsonConfig)
{
    // Validate JSON
    try
    {
        JsonHelper.Deserialize<object>(jsonConfig);
    }
    catch
    {
        AspxPage.AddError("Invalid JSON format.");
        return View();
    }
    
    var snippet = new NoteAndAttachment
    {
        Name = configName,
        Body = jsonConfig,  // JSON configuration content
        Type = 3,
        ReferenceId = recordId
    };
    
    try
    {
        Database.Insert(snippet);
        AspxPage.AddMessage("Configuration snippet created!");
        return RedirectToAction("ViewRecord", new { id = recordId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Creating CSS Snippet

[HttpPost]
public ActionResponse CreateCSSSnippet(string projectId, string styleName, string cssCode)
{
    var snippet = new NoteAndAttachment
    {
        Name = styleName,
        Body = cssCode,  // CSS code content
        Type = 3,
        ReferenceId = projectId
    };
    
    try
    {
        Database.Insert(snippet);
        AspxPage.AddMessage("CSS snippet created successfully!");
        return RedirectToAction("ProjectDetails", new { id = projectId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Complete Usage Examples

Example 1: Account Activity Tracker

public class AccountActivityController : AspxController
{
    // Add activity note
    [HttpPost]
    public ActionResponse LogActivity(string accountId, string activityType, string details)
    {
        var note = new NoteAndAttachment
        {
            Name = $"{activityType} - {DateTime.Now:MM/dd/yyyy hh:mm tt}",
            Body = $"Activity Type: {activityType}\n\n{details}\n\nLogged by: {SystemInfo.UserName}",
            ReferenceId = accountId,
            Type = 0
        };
        
        try
        {
            Database.Insert(note);
            AspxPage.AddMessage("Activity logged successfully!");
            return RedirectToAction("AccountDetails", new { id = accountId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Attach contract document
    [HttpPost]
    public ActionResponse AttachContract(HttpPostedFile contractFile, string accountId)
    {
        if (contractFile == null || contractFile.ContentLength == 0)
        {
            AspxPage.AddError("Please select a contract file.");
            return View();
        }
        
        // Validate file type
        string[] allowedExtensions = { ".pdf", ".docx", ".doc" };
        string extension = Path.GetExtension(contractFile.FileName).ToLower();
        
        if (!allowedExtensions.Contains(extension))
        {
            AspxPage.AddError("Contract must be PDF or Word document.");
            return View();
        }
        
        var attachment = NoteAndAttachment.Load(contractFile);
        attachment.ReferenceId = accountId;
        attachment.Body = "Contract Agreement";
        
        try
        {
            Database.Insert(attachment);
            AspxPage.AddMessage("Contract attached successfully!");
            return RedirectToAction("AccountDetails", new { id = accountId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Add support resource link
    [HttpPost]
    public ActionResponse AddSupportLink(string accountId)
    {
        var account = Database.Retrieve<Account>(accountId);
        
        if (account == null)
        {
            return PageNotFound();
        }
        
        var bookmark = new NoteAndAttachment
        {
            Name = "Customer Support Portal",
            Body = $"https://support.example.com/account/{account.Id}",
            Type = 2,
            ReferenceId = accountId
        };
        
        try
        {
            Database.Insert(bookmark);
            AspxPage.AddMessage("Support link added!");
            return RedirectToAction("AccountDetails", new { id = accountId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
}

Example 2: Development Project Documentation

public class ProjectDocumentationController : AspxController
{
    // Store API integration code
    [HttpPost]
    public ActionResponse StoreIntegrationCode(string projectId, string codeName, string code)
    {
        var snippet = new NoteAndAttachment
        {
            Name = codeName,
            Body = code,
            Type = 3,  // Code snippet
            ReferenceId = projectId
        };
        
        try
        {
            Database.Insert(snippet);
            AspxPage.AddMessage("Integration code saved!");
            return RedirectToAction("ProjectDetails", new { id = projectId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Store API documentation link
    [HttpPost]
    public ActionResponse StoreAPIDocLink(string projectId, string apiName, string docUrl)
    {
        var bookmark = new NoteAndAttachment
        {
            Name = $"{apiName} Documentation",
            Body = docUrl,
            Type = 2,  // Bookmark
            ReferenceId = projectId
        };
        
        try
        {
            Database.Insert(bookmark);
            AspxPage.AddMessage("API documentation link added!");
            return RedirectToAction("ProjectDetails", new { id = projectId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Store implementation notes
    [HttpPost]
    public ActionResponse StoreImplementationNotes(string projectId, string notes)
    {
        var note = new NoteAndAttachment
        {
            Name = $"Implementation Notes - {DateTime.Now:MM/dd/yyyy}",
            Body = notes,
            Type = 0,  // Note
            ReferenceId = projectId
        };
        
        try
        {
            Database.Insert(note);
            AspxPage.AddMessage("Implementation notes saved!");
            return RedirectToAction("ProjectDetails", new { id = projectId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
}

Example 3: Case Management System

public class CaseManagementController : AspxController
{
    // Log case update
    [HttpPost]
    public ActionResponse LogCaseUpdate(string caseId, string updateDetails)
    {
        var note = new NoteAndAttachment
        {
            Name = $"Case Update - {DateTime.Now:MM/dd/yyyy hh:mm tt}",
            Body = $"Update by {SystemInfo.UserName}:\n\n{updateDetails}",
            ReferenceId = caseId,
            Type = 0
        };
        
        try
        {
            Database.Insert(note);
            AspxPage.AddMessage("Case update logged!");
            return RedirectToAction("CaseDetails", new { id = caseId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Attach screenshot
    [HttpPost]
    public ActionResponse AttachScreenshot(HttpPostedFile screenshot, string caseId)
    {
        if (screenshot == null || screenshot.ContentLength == 0)
        {
            AspxPage.AddError("Please select a screenshot.");
            return View();
        }
        
        // Validate image file
        string[] imageTypes = { ".png", ".jpg", ".jpeg", ".gif" };
        string extension = Path.GetExtension(screenshot.FileName).ToLower();
        
        if (!imageTypes.Contains(extension))
        {
            AspxPage.AddError("File must be an image (PNG, JPG, or GIF).");
            return View();
        }
        
        var attachment = NoteAndAttachment.Load(screenshot);
        attachment.ReferenceId = caseId;
        attachment.Body = "Screenshot for case documentation";
        
        try
        {
            Database.Insert(attachment);
            AspxPage.AddMessage("Screenshot attached!");
            return RedirectToAction("CaseDetails", new { id = caseId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Add knowledge base link
    [HttpPost]
    public ActionResponse AddKnowledgeBaseLink(string caseId, string articleUrl, string articleTitle)
    {
        var bookmark = new NoteAndAttachment
        {
            Name = $"KB Article: {articleTitle}",
            Body = articleUrl,
            Type = 2,
            ReferenceId = caseId
        };
        
        try
        {
            Database.Insert(bookmark);
            AspxPage.AddMessage("Knowledge base article linked!");
            return RedirectToAction("CaseDetails", new { id = caseId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
}

Retrieving Notes and Attachments

Get All Items for a Record

public ActionResponse GetRecordAttachments(string recordId)
{
    var items = Database.Query<NoteAndAttachment>()
        .Where(a => a.ReferenceId == recordId)
        .OrderByDescending(a => a.CreatedOn)
        .ToList();
    
    return View(items);
}

Filter by Type

public ActionResponse GetNotes(string recordId)
{
    var notes = Database.Query<NoteAndAttachment>()
        .Where(a => a.ReferenceId == recordId && a.Type == 0)
        .OrderByDescending(a => a.CreatedOn)
        .ToList();
    
    return View(notes);
}

public ActionResponse GetFiles(string recordId)
{
    var files = Database.Query<NoteAndAttachment>()
        .Where(a => a.ReferenceId == recordId && a.Type == 1)
        .OrderByDescending(a => a.CreatedOn)
        .ToList();
    
    return View(files);
}

public ActionResponse GetBookmarks(string recordId)
{
    var bookmarks = Database.Query<NoteAndAttachment>()
        .Where(a => a.ReferenceId == recordId && a.Type == 2)
        .OrderBy(a => a.Name)
        .ToList();
    
    return View(bookmarks);
}

public ActionResponse GetSnippets(string recordId)
{
    var snippets = Database.Query<NoteAndAttachment>()
        .Where(a => a.ReferenceId == recordId && a.Type == 3)
        .OrderBy(a => a.Name)
        .ToList();
    
    return View(snippets);
}

Best Practices

File Validation

Always validate uploaded files

// Check file exists
if (file == null || file.ContentLength == 0)
{
    AspxPage.AddError("Please select a file.");
    return View();
}

// Check file size
if (file.ContentLength > 10 * 1024 * 1024) // 10MB
{
    AspxPage.AddError("File must be less than 10MB.");
    return View();
}

// Check file type
string[] allowed = { ".pdf", ".docx", ".png" };
string ext = Path.GetExtension(file.FileName).ToLower();
if (!allowed.Contains(ext))
{
    AspxPage.AddError("File type not allowed.");
    return View();
}

ReferenceId Requirement

Always set ReferenceId

// Good: Links to parent record
attachment.ReferenceId = accountId;

// Bad: No parent link
var attachment = new NoteAndAttachment { Name = "File" };
// Will fail without ReferenceId

Using Load() Method

Prefer NoteAndAttachment.Load() for file uploads

// Good: Automatic property population
var attachment = NoteAndAttachment.Load(file);
attachment.ReferenceId = parentId;

// Also good: Manual but more work
byte[] bytes = new byte[file.ContentLength];
file.InputStream.Read(bytes, 0, file.ContentLength);
var attachment = new NoteAndAttachment
{
    Name = file.FileName,
    Type = 1,
    FileContents = Convert.ToBase64String(bytes),
    ContentType = file.ContentType,
    ReferenceId = parentId
};

Error Handling

Handle database exceptions

try
{
    Database.Insert(attachment);
}
catch (DatabaseException ex)
{
    AspxPage.AddError($"Failed to attach file: {ex.Message}");
    SystemInfo.Debug($"Attachment error: {ex.ToString()}");
    return View();
}

Type Property Usage

Use correct Type values

Type = 0  // Note
Type = 1  // File Attachment
Type = 2  // Bookmark
Type = 3  // Snippet

Common Pitfalls

Pitfall 1: Forgetting ReferenceId

// Bad: No ReferenceId
var note = new NoteAndAttachment
{
    Name = "My Note",
    Body = "Content",
    Type = 0
};
Database.Insert(note); // Will fail validation

// Good: Include ReferenceId
note.ReferenceId = accountId;

Pitfall 2: Wrong Type value

// Bad: Wrong Type for file
var attachment = NoteAndAttachment.Load(file);
attachment.Type = 0; // Should be 1 for files

// Good: Type is auto-set by Load()
var attachment = NoteAndAttachment.Load(file);
// Type is already 1

Pitfall 3: Confusing NoteAndAttachment snippets with Document snippets

// NoteAndAttachment Snippet (Type = 3)
// - Attached to records via ReferenceId
// - Body contains code text (persisted in database)
// - No Extension field
var snippet = new NoteAndAttachment
{
    Type = 3,
    Body = "function test() { }",
    ReferenceId = projectId
};

// Document Snippet (Type = "Snippet")
// - Stored in Document Library folders
// - Body is transient (stored in cloud)
// - Has Extension field ("js", "css", etc.)
var docSnippet = new Document
{
    Type = "Snippet",
    Extension = "js",
    Body = base64Code,
    FolderId = folderId
};

Related Topics