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 Value | Content Type | Body Property Contains | Use Case |
|---|
0 | Note | Note text content | Meeting notes, comments, descriptions |
1 | File Attachment | File description | Documents, images, PDFs attached to records |
2 | Bookmark | URL | Links to external resources or documentation |
3 | Snippet | Code or configuration text | JavaScript, 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