Table of Contents


Creating Document Library Bookmarks and Marketing Links

Create bookmarks and marketing links in the Document Library to organize references to external resources, websites, and dynamic URLs. Unlike NoteAndAttachment bookmarks that attach to specific records, Document Library bookmarks are organized in folders and accessible through the Document Library module.


Overview

The Document Library supports two types of URL references:

Bookmarks - Static links to external resources, documentation, or websites

  • Available to all security roles
  • Static URLs without personalization
  • Organized in Document Library folders
  • Can be published publicly

Marketing Links - Dynamic links with merge field support

  • Available to employee security roles only
  • Support merge fields for personalization
  • URLs customized per user viewing the link
  • Ideal for personalized campaign tracking

Key Characteristics:

  • Stored as Document records with Type = "URL" (bookmarks) or "Marketing Link"
  • Organized in folders like regular files
  • Support folder sharing and permissions
  • Can be published publicly for unauthenticated access
  • Searchable via keywords and descriptions

Document Types for Links

Bookmark (Type = "URL")

Standard bookmarks reference static URLs.

var bookmark = new Document
{
    Name = "Company Documentation",
    Body = "https://docs.example.com/product",
    Type = "URL",
    FolderId = folderId,
    Published = false,
    Description = "Product documentation portal",
    Keywords = "documentation, help, support"
};

Database.Insert(bookmark);

Marketing Link (Type = "Marketing Link")

Marketing links support merge fields for personalized URLs.

var marketingLink = new Document
{
    Name = "Personalized Campaign Link",
    Body = "https://example.com/campaign?user={!$User.Email}&account={!$User.Account.Name}",
    Type = "Marketing Link",
    FolderId = folderId,
    Published = false,
    Description = "Q4 marketing campaign with user tracking",
    Keywords = "marketing, campaign, tracking"
};

Database.Insert(marketingLink);

Creating Bookmarks

Bookmarks provide static links to external resources organized in the Document Library.

Basic Bookmark Creation

[HttpPost]
public ActionResponse CreateBookmark(string folderId, string bookmarkName, 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 Document
    {
        Name = bookmarkName,
        Body = url,
        Type = "URL",
        FolderId = folderId,
        Published = false
    };
    
    try
    {
        Database.Insert(bookmark);
        AspxPage.AddMessage($"Bookmark '{bookmarkName}' created successfully!");
        return RedirectToAction("FolderContents", new { folderId = folderId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        SystemInfo.Debug($"Bookmark creation error: {ex.Message}");
        return View();
    }
}

Bookmark with Description and Keywords

[HttpPost]
public ActionResponse CreateDetailedBookmark(
    string folderId, 
    string name, 
    string url, 
    string description, 
    string keywords)
{
    var bookmark = new Document
    {
        Name = name,
        Body = url,
        Type = "URL",
        FolderId = folderId,
        Published = false,
        Description = description,
        Keywords = keywords
    };
    
    try
    {
        Database.Insert(bookmark);
        AspxPage.AddMessage("Bookmark created with metadata!");
        return RedirectToAction("FolderContents", new { folderId = folderId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Public Bookmark

[HttpPost]
public ActionResponse CreatePublicBookmark(string folderId, string name, string url)
{
    var bookmark = new Document
    {
        Name = name,
        Body = url,
        Type = "URL",
        FolderId = folderId,
        Published = true,  // Publicly accessible
        Description = "Public resource link"
    };
    
    try
    {
        Database.Insert(bookmark);
        AspxPage.AddMessage("Public bookmark created!");
        return RedirectToAction("FolderContents", new { folderId = folderId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Creating Marketing Links

Marketing links support merge fields to create personalized URLs for each user.

Basic Marketing Link

[HttpPost]
public ActionResponse CreateMarketingLink(string folderId, string name, string url)
{
    // Validate employee security role
    if (!SystemInfo.IsEmployeeUser)
    {
        AspxPage.AddError("Only employees can create marketing links.");
        return RedirectToAction("Index");
    }
    
    var marketingLink = new Document
    {
        Name = name,
        Body = url,
        Type = "Marketing Link",
        FolderId = folderId,
        Published = false
    };
    
    try
    {
        Database.Insert(marketingLink);
        AspxPage.AddMessage("Marketing link created!");
        return RedirectToAction("FolderContents", new { folderId = folderId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Marketing Link with User Tracking

[HttpPost]
public ActionResponse CreateTrackingLink(string folderId, string campaignName, string baseUrl)
{
    if (!SystemInfo.IsEmployeeUser)
    {
        AspxPage.AddError("Only employees can create marketing links.");
        return RedirectToAction("Index");
    }
    
    // Build URL with merge fields for tracking
    string trackingUrl = $"{baseUrl}?email={{!$User.Email}}&userId={{!$User.Id}}&campaign={campaignName}";
    
    var marketingLink = new Document
    {
        Name = $"Campaign: {campaignName}",
        Body = trackingUrl,
        Type = "Marketing Link",
        FolderId = folderId,
        Description = $"Marketing campaign link with user tracking for {campaignName}",
        Keywords = $"marketing, campaign, {campaignName}, tracking"
    };
    
    try
    {
        Database.Insert(marketingLink);
        AspxPage.AddMessage("Tracking link created!");
        return RedirectToAction("FolderContents", new { folderId = folderId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Marketing Link with Account Information

[HttpPost]
public ActionResponse CreateAccountLink(string folderId, string linkName, string portalUrl)
{
    if (!SystemInfo.IsEmployeeUser)
    {
        AspxPage.AddError("Only employees can create marketing links.");
        return RedirectToAction("Index");
    }
    
    // Create URL with account-specific parameters
    string accountUrl = $"{portalUrl}/portal?account={{!$User.AccountId}}&user={{!$User.FirstName}}&company={{!$User.Account.Name}}";
    
    var marketingLink = new Document
    {
        Name = linkName,
        Body = accountUrl,
        Type = "Marketing Link",
        FolderId = folderId,
        Description = "Personalized portal link with account information",
        Keywords = "portal, account, personalized"
    };
    
    try
    {
        Database.Insert(marketingLink);
        AspxPage.AddMessage("Account-specific link created!");
        return RedirectToAction("FolderContents", new { folderId = folderId });
    }
    catch (DatabaseException ex)
    {
        AspxPage.AddError(ex.Message);
        return View();
    }
}

Supported Merge Fields

Marketing links support merge fields from the User object to personalize URLs.

Common User Merge Fields

Merge FieldDescriptionExample Value
{!$User.Id}User record ID"abc123-def456"
{!$User.Email}User email address"john.doe@example.com"
{!$User.FirstName}User first name"John"
{!$User.LastName}User last name"Doe"
{!$User.UserName}Username"jdoe"
{!$User.AccountId}Associated Account ID"acc-123"
{!$User.Account.Name}Associated Account name"Acme Corporation"
{!$User.Title}User job title"Sales Manager"
{!$User.Phone}User phone number"+1-555-0123"

Merge Field Usage Examples

// Email tracking
"https://example.com/campaign?email={!$User.Email}"

// User identification
"https://portal.example.com/login?user={!$User.Id}&name={!$User.FirstName}"

// Account-based routing
"https://resources.example.com?account={!$User.AccountId}&company={!$User.Account.Name}"

// Multi-parameter tracking
"https://example.com/offer?email={!$User.Email}&id={!$User.Id}&source=portal"

Complete Usage Examples

Example 1: Resource Library Setup

public class ResourceLibraryController : AspxController
{
    public ActionResponse SetupResourceFolder(string parentFolderId)
    {
        try
        {
            // Create Resources folder
            var resourceFolder = new Folder
            {
                Name = "External Resources",
                ParentId = parentFolderId,
                OwnerId = SystemInfo.UserId
            };
            
            Database.Insert(resourceFolder);
            
            // Create bookmarks for common resources
            var resources = new[]
            {
                new { Name = "Product Documentation", Url = "https://docs.example.com", Desc = "Complete product documentation", Keywords = "documentation, help, guide" },
                new { Name = "API Reference", Url = "https://api.example.com/docs", Desc = "API documentation and examples", Keywords = "api, reference, development" },
                new { Name = "Support Portal", Url = "https://support.example.com", Desc = "Customer support portal", Keywords = "support, help, tickets" },
                new { Name = "Training Videos", Url = "https://training.example.com", Desc = "Product training resources", Keywords = "training, videos, tutorials" }
            };

            var docResources = new List<Document>();

            foreach (var resource in resources)
            {
                var bookmark = new Document
                {
                    Name = resource.Name,
                    Body = resource.Url,
                    Type = "URL",
                    FolderId = resourceFolder.Id,
                    Description = resource.Desc,
                    Keywords = resource.Keywords,
                    Published = false
                };

                docResources.Add(bookmark);
            }
            Database.Insert(docResources);         
            AspxPage.AddMessage($"Resource library created with {resources.Length} bookmarks!");
            return RedirectToAction("FolderContents", new { folderId = resourceFolder.Id });
        }
        catch (Exception ex)
        {
            SystemInfo.Debug($"Setup error: {ex.Message}");
            AspxPage.AddError("Failed to setup resource library.");
            return View();
        }
    }
}

Example 2: Marketing Campaign Links

public class CampaignLinksController : AspxController
{
    [HttpPost]
    public ActionResponse CreateCampaignLinks(string folderId, string campaignName)
    {
        if (!SystemInfo.IsEmployeeUser)
        {
            AspxPage.AddError("Only employees can create marketing links.");
            return RedirectToAction("Index");
        }
        
        try
        {
            // Create campaign subfolder
            var campaignFolder = new Folder
            {
                Name = $"Campaign: {campaignName}",
                ParentId = folderId,
                OwnerId = SystemInfo.UserId
            };
            
            Database.Insert(campaignFolder);
            
            // Create marketing links with different tracking parameters
            var links = new[]
            {
                new
                {
                    Name = "Email Campaign Link",
                    Url = $"https://example.com/offer?campaign={campaignName}&email={{!$User.Email}}&source=email",
                    Desc = "Link for email campaign distribution"
                },
                new
                {
                    Name = "Portal Banner Link",
                    Url = $"https://example.com/offer?campaign={campaignName}&user={{!$User.Id}}&source=portal",
                    Desc = "Link for portal banner clicks"
                },
                new
                {
                    Name = "Account Dashboard Link",
                    Url = $"https://example.com/offer?campaign={campaignName}&account={{!$User.AccountId}}&company={{!$User.Account.Name}}",
                    Desc = "Link for account dashboard widget"
                }
            };
            
            foreach (var link in links)
            {
                var marketingLink = new Document
                {
                    Name = link.Name,
                    Body = link.Url,
                    Type = "Marketing Link",
                    FolderId = campaignFolder.Id,
                    Description = link.Desc,
                    Keywords = $"marketing, campaign, {campaignName}, tracking"
                };
                
                Database.Insert(marketingLink);
            }
            
            AspxPage.AddMessage($"Campaign links created for {campaignName}!");
            return RedirectToAction("FolderContents", new { folderId = campaignFolder.Id });
        }
        catch (Exception ex)
        {
            SystemInfo.Debug($"Campaign setup error: {ex.Message}");
            AspxPage.AddError("Failed to create campaign links.");
            return View();
        }
    }
}

Example 3: Partner Portal Resources

public class PartnerResourcesController : AspxController
{
    public ActionResponse SetupPartnerResources(string partnerFolderId)
    {
        try
        {
            // Create partner resource bookmarks
            var bookmarks = new[]
            {
                new
                {
                    Name = "Partner Portal",
                    Url = "https://partners.example.com",
                    Desc = "Main partner portal",
                    Public = false
                },
                new
                {
                    Name = "Sales Resources",
                    Url = "https://partners.example.com/sales",
                    Desc = "Sales collateral and resources",
                    Public = false
                },
                new
                {
                    Name = "Technical Documentation",
                    Url = "https://partners.example.com/docs",
                    Desc = "Technical integration guides",
                    Public = false
                },
                new
                {
                    Name = "Public Product Page",
                    Url = "https://www.example.com/products",
                    Desc = "Public-facing product information",
                    Public = true
                }
            };
            
            foreach (var bookmark in bookmarks)
            {
                var doc = new Document
                {
                    Name = bookmark.Name,
                    Body = bookmark.Url,
                    Type = "URL",
                    FolderId = partnerFolderId,
                    Description = bookmark.Desc,
                    Published = bookmark.Public,
                    Keywords = "partner, resources"
                };
                
                Database.Insert(doc);
            }
            
            AspxPage.AddMessage("Partner resources created!");
            return RedirectToAction("FolderContents", new { folderId = partnerFolderId });
        }
        catch (Exception ex)
        {
            SystemInfo.Debug($"Partner setup error: {ex.Message}");
            AspxPage.AddError("Failed to setup partner resources.");
            return View();
        }
    }
}

Example 4: Dynamic Link Management

public class LinkManagementController : AspxController
{
    // Create bookmark from user input
    [HttpPost]
    public ActionResponse CreateLink(
        string folderId,
        string linkType,
        string name,
        string url,
        string description,
        string keywords,
        bool published)
    {
        try
        {
            // Validate link type
            if (linkType == "Marketing Link" && !SystemInfo.IsEmployeeUser)
            {
                AspxPage.AddError("Only employees can create marketing links.");
                return View();
            }
            
            // 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 document = new Document
            {
                Name = name,
                Body = url,
                Type = linkType, // "URL" or "Marketing Link"
                FolderId = folderId,
                Description = description,
                Keywords = keywords,
                Published = published
            };
            
            Database.Insert(document);
            
            string linkTypeName = linkType == "Marketing Link" ? "Marketing link" : "Bookmark";
            AspxPage.AddMessage($"{linkTypeName} '{name}' created successfully!");
            return RedirectToAction("FolderContents", new { folderId = folderId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Update existing link
    [HttpPost]
    public ActionResponse UpdateLink(
        string documentId,
        string name,
        string url,
        string description,
        string keywords,
        bool published)
    {
        try
        {
            var document = Database.Retrieve<Document>(documentId);
            
            if (document == null)
            {
                return PageNotFound();
            }
            
            // Validate it's a link
            if (document.Type != "URL" && document.Type != "Marketing Link")
            {
                AspxPage.AddError("This document is not a link.");
                return RedirectToAction("Index");
            }
            
            // Update properties
            document.Name = name;
            document.Body = url;
            document.Description = description;
            document.Keywords = keywords;
            document.Published = published;
            
            Database.Update(document);
            
            AspxPage.AddMessage("Link updated successfully!");
            return RedirectToAction("Details", new { id = documentId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return View();
        }
    }
    
    // Delete link
    [HttpPost]
    public ActionResponse DeleteLink(string documentId)
    {
        try
        {
            var document = Database.Retrieve<Document>(documentId);
            
            if (document == null)
            {
                return PageNotFound();
            }
            
            string folderId = document.FolderId;
            
            Database.Delete(documentId);
            
            AspxPage.AddMessage("Link deleted successfully!");
            return RedirectToAction("FolderContents", new { folderId = folderId });
        }
        catch (DatabaseException ex)
        {
            AspxPage.AddError(ex.Message);
            return RedirectToAction("Index");
        }
    }
}

Retrieving Bookmarks and Marketing Links

Get All Links in Folder

public ActionResponse GetFolderLinks(string folderId)
{
    var links = Database.Query<Document>()
        .Where(d => d.FolderId == folderId && 
                   (d.Type == "URL" || d.Type == "Marketing Link"))
        .OrderBy(d => d.Name)
        .ToList();
    
    return View(links);
}

Filter by Link Type

public ActionResponse GetBookmarks(string folderId)
{
    var bookmarks = Database.Query<Document>()
        .Where(d => d.FolderId == folderId && d.Type == "URL")
        .OrderBy(d => d.Name)
        .ToList();
    
    return View(bookmarks);
}

public ActionResponse GetMarketingLinks(string folderId)
{
    var marketingLinks = Database.Query<Document>()
        .Where(d => d.FolderId == folderId && d.Type == "Marketing Link")
        .OrderBy(d => d.Name)
        .ToList();
    
    return View(marketingLinks);
}

Search Links by Keyword

public ActionResponse SearchLinks(string keyword)
{
    var links = Database.Query<Document>()
        .Where(d => (d.Type == "URL" || d.Type == "Marketing Link") &&
                   (d.Keywords.Contains(keyword) || 
                    d.Name.Contains(keyword) ||
                    d.Description.Contains(keyword)))
        .OrderBy(d => d.Name)
        .ToList();
    
    return View(links);
}

Best Practices

URL Validation

Always validate URLs

// Validate URL format
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
{
    AspxPage.AddError("URL must start with http:// or https://");
    return View();
}

// Validate URL is not empty
if (string.IsNullOrWhiteSpace(url))
{
    AspxPage.AddError("URL is required.");
    return View();
}

Security Role Validation

Validate employee role for marketing links

// Marketing links require employee role
if (linkType == "Marketing Link" && !SystemInfo.IsEmployeeUser)
{
    AspxPage.AddError("Only employees can create marketing links.");
    return RedirectToAction("Index");
}

Merge Field Usage

Use proper merge field syntax

// Good: Proper merge field syntax
string url = "https://example.com?email={!$User.Email}&id={!$User.Id}";

// Bad: Wrong syntax
string url = "https://example.com?email=${User.Email}&id=${User.Id}";

Descriptive Metadata

Include descriptions and keywords

var bookmark = new Document
{
    Name = "API Documentation",
    Body = "https://api.example.com/docs",
    Type = "URL",
    FolderId = folderId,
    Description = "Complete API reference and code examples", // Helpful
    Keywords = "api, documentation, reference, development"  // Searchable
};

Link Organization

Organize links in folders by purpose

// Create themed folders
var folders = new[]
{
    "External Resources",
    "Training Materials",
    "Support Links",
    "Marketing Campaigns"
};

foreach (var folderName in folders)
{
    var folder = new Folder
    {
        Name = folderName,
        ParentId = parentFolderId,
        OwnerId = SystemInfo.UserId
    };
    Database.Insert(folder);
}

Common Pitfalls

Pitfall 1: Wrong document type

// Bad: Using "File" type for bookmark
var bookmark = new Document
{
    Body = "https://example.com",
    Type = "File" // Wrong
};

// Good: Use "URL" type
var bookmark = new Document
{
    Body = "https://example.com",
    Type = "URL" // Correct
};

Pitfall 2: Creating marketing links without employee role check

// Bad: No role validation
var marketingLink = new Document
{
    Type = "Marketing Link" // May fail for non-employees
};

// Good: Validate role first
if (!SystemInfo.IsEmployeeUser)
{
    AspxPage.AddError("Only employees can create marketing links.");
    return RedirectToAction("Index");
}

Pitfall 3: Invalid merge field syntax

// Bad: Wrong syntax
"https://example.com?email=${User.Email}"        // Wrong
"https://example.com?email={{User.Email}}"       // Wrong
"https://example.com?email=[User.Email]"         // Wrong

// Good: Correct syntax
"https://example.com?email={!$User.Email}"        // Correct

Pitfall 4: Forgetting FolderId

// Bad: No FolderId
var bookmark = new Document
{
    Name = "Link",
    Body = "https://example.com",
    Type = "URL"
    // Missing FolderId - will fail
};

// Good: Include FolderId
var bookmark = new Document
{
    Name = "Link",
    Body = "https://example.com",
    Type = "URL",
    FolderId = folderId // Required
};

Bookmark vs NoteAndAttachment Bookmark

Document Library Bookmarks (this page):

  • Stored in Document Library folders
  • Type = "URL" or "Marketing Link"
  • Accessible through Document Library module
  • Support folder sharing and organization
  • Can be published publicly

NoteAndAttachment Bookmarks (see guide):

  • Attached to specific records
  • Type = 2
  • Visible in record's Notes & Attachments section
  • Linked via ReferenceId
  • Different use case and visibility

Related Topics