External Links Feature
Version Implemented: 0.229.001
Overview
The External Links feature provides administrators with the ability to configure and display custom navigation links to external resources, systems, and services directly within the SimpleChat interface, creating a unified portal experience for users.
Purpose
External Links enable organizations to:
- Integrate SimpleChat with existing organizational tools and systems
- Provide quick access to frequently used external resources
- Create a unified navigation experience across multiple platforms
- Reduce context switching between different applications
- Customize the user experience with organization-specific links
Technical Specifications
Architecture Overview
- Admin Configuration: Dynamic link management through admin interface
- Database Storage: External links stored in Cosmos DB settings container
- Security Validation: URL validation and security checks for external links
- Responsive Design: Links adapt to different screen sizes and navigation contexts
Database Schema
{
"external_links": {
"enabled": true,
"links": [
{
"id": "link-uuid-1",
"title": "Company Intranet",
"url": "https://intranet.company.com",
"description": "Access company policies and procedures",
"icon": "fas fa-building",
"target": "_blank",
"order": 1,
"visible_to_roles": ["all"],
"category": "company",
"enabled": true
},
{
"id": "link-uuid-2",
"title": "IT Service Desk",
"url": "https://servicedesk.company.com",
"description": "Submit IT support requests",
"icon": "fas fa-headset",
"target": "_blank",
"order": 2,
"visible_to_roles": ["all"],
"category": "support",
"enabled": true
}
],
"display_location": ["navbar", "sidebar"],
"max_links": 10
}
}
Link Validation
class ExternalLinkValidator {
static validateUrl(url) {
const errors = [];
// Basic URL format validation
try {
const urlObj = new URL(url);
// Ensure HTTPS for security
if (urlObj.protocol !== 'https:' && urlObj.protocol !== 'http:') {
errors.push('URL must use HTTP or HTTPS protocol');
}
// Prevent localhost and internal IPs in production
if (this.isInternalUrl(urlObj.hostname)) {
errors.push('Internal URLs are not allowed');
}
} catch (e) {
errors.push('Invalid URL format');
}
return errors;
}
static isInternalUrl(hostname) {
const internalPatterns = [
/^localhost$/,
/^127\./,
/^192\.168\./,
/^10\./,
/^172\.(1[6-9]|2[0-9]|3[0-1])\./
];
return internalPatterns.some(pattern => pattern.test(hostname));
}
}
Configuration Options
Admin Settings Interface
<div class="external-links-section">
<h5>External Links Configuration</h5>
<div class="form-group">
<div class="form-check">
<input type="checkbox" id="enable-external-links" class="form-check-input">
<label class="form-check-label">Enable External Links</label>
</div>
</div>
<div class="links-configuration" id="links-config">
<div class="form-group">
<label>Display Locations</label>
<div class="form-check">
<input type="checkbox" id="display-navbar" class="form-check-input" value="navbar">
<label class="form-check-label">Navigation Bar</label>
</div>
<div class="form-check">
<input type="checkbox" id="display-sidebar" class="form-check-input" value="sidebar">
<label class="form-check-label">Left Sidebar</label>
</div>
</div>
<div class="links-list">
<h6>Configured Links</h6>
<div id="external-links-container">
<!-- Dynamic link entries -->
</div>
<button type="button" class="btn btn-primary" id="add-external-link">
<i class="fas fa-plus"></i> Add External Link
</button>
</div>
</div>
</div>
Link Entry Form
<div class="external-link-entry" data-link-id="">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Link Title <span class="text-danger">*</span></label>
<input type="text" class="form-control link-title" maxlength="50" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>URL <span class="text-danger">*</span></label>
<input type="url" class="form-control link-url" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>Icon (Font Awesome)</label>
<input type="text" class="form-control link-icon" placeholder="fas fa-external-link-alt">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Category</label>
<select class="form-control link-category">
<option value="general">General</option>
<option value="company">Company</option>
<option value="support">Support</option>
<option value="tools">Tools</option>
<option value="documentation">Documentation</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Display Order</label>
<input type="number" class="form-control link-order" min="1" max="10" value="1">
</div>
</div>
</div>
<div class="form-group">
<label>Description (Optional)</label>
<input type="text" class="form-control link-description" maxlength="100"
placeholder="Brief description for tooltips">
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Visible to Roles</label>
<select class="form-control link-roles" multiple>
<option value="all" selected>All Users</option>
<option value="Admin">Administrators</option>
<option value="DocumentManager">Document Managers</option>
<option value="User">Regular Users</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Target</label>
<select class="form-control link-target">
<option value="_blank">New Tab/Window</option>
<option value="_self">Same Tab</option>
</select>
</div>
</div>
</div>
<div class="link-actions">
<button type="button" class="btn btn-sm btn-success test-link">
<i class="fas fa-external-link-alt"></i> Test Link
</button>
<button type="button" class="btn btn-sm btn-danger remove-link">
<i class="fas fa-trash"></i> Remove
</button>
</div>
</div>
Usage Instructions
For Administrators
Adding External Links
- Access Admin Settings: Navigate to Admin Settings → UI Configuration → External Links
- Enable Feature: Check "Enable External Links" to activate the feature
- Choose Display Locations: Select where links should appear (navbar, sidebar, or both)
- Add Links: Click "Add External Link" to create new link entries
- Configure Link Details:
- Title: Short, descriptive name (max 50 characters)
- URL: Full URL including https:// protocol
- Icon: Font Awesome icon class (optional)
- Category: Logical grouping for organization
- Description: Tooltip text for additional context
- Display Order: Numerical order for link arrangement
- Visible to Roles: Control which user roles can see the link
- Target: Whether link opens in new tab or same tab
Link Management Best Practices
const linkManagementGuidelines = {
urlSecurity: {
alwaysUseHttps: "Prefer HTTPS URLs for security",
validateDomains: "Verify links point to trusted domains",
testRegularly: "Test all links periodically for availability"
},
userExperience: {
clearTitles: "Use descriptive, concise link titles",
consistentIcons: "Choose icons that clearly represent the destination",
logicalOrder: "Arrange links in order of frequency of use",
appropriateTarget: "Use new tabs for external sites, same tab for internal tools"
},
organization: {
categorization: "Group related links using categories",
roleBasedAccess: "Show only relevant links to each user role",
regularReview: "Review and update links quarterly"
}
};
Testing External Links
function testExternalLink(url) {
// Client-side validation
try {
const testWindow = window.open(url, '_blank', 'width=800,height=600');
setTimeout(() => {
if (testWindow && !testWindow.closed) {
console.log('Link test successful - page loaded');
testWindow.close();
} else {
console.warn('Link test failed - page may have blocked popup');
}
}, 3000);
} catch (error) {
console.error('Link test error:', error);
}
}
For End Users
Accessing External Links
From Navigation Bar:
- External links appear as additional menu items in the main navigation
- Click any link to open the external resource
- Hover over links to see descriptions in tooltips
From Left Sidebar:
- External links appear in a dedicated "Quick Links" section
- Icons provide visual identification of link types
- Links are organized by category when multiple links exist
Link Interaction
<!-- Navbar external link example -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">
Quick Links
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="https://intranet.company.com" target="_blank"
title="Access company policies and procedures">
<i class="fas fa-building"></i> Company Intranet
</a>
</li>
<li>
<a class="dropdown-item" href="https://servicedesk.company.com" target="_blank"
title="Submit IT support requests">
<i class="fas fa-headset"></i> IT Service Desk
</a>
</li>
</ul>
</li>
<!-- Sidebar external link example -->
<div class="sidebar-section external-links">
<h6 class="sidebar-heading">Quick Links</h6>
<ul class="nav nav-pills flex-column">
<li class="nav-item">
<a class="nav-link" href="https://intranet.company.com" target="_blank"
title="Access company policies and procedures">
<i class="fas fa-building"></i> Company Intranet
</a>
</li>
</ul>
</div>
Integration Points
Authentication Integration
- Links respect user role-based visibility settings
- Authentication status affects which links are displayed
- Integration with organizational SSO for seamless access to external systems
Theme Integration
.external-links {
.nav-link {
color: var(--nav-link-color);
&:hover {
color: var(--nav-link-hover-color);
background-color: var(--nav-link-hover-bg);
}
.icon {
color: var(--icon-color);
margin-right: 8px;
}
}
}
[data-bs-theme="dark"] .external-links {
.nav-link {
color: var(--nav-link-color-dark);
&:hover {
color: var(--nav-link-hover-color-dark);
background-color: var(--nav-link-hover-bg-dark);
}
}
}
Analytics Integration
class ExternalLinkAnalytics {
static trackLinkClick(linkId, linkTitle, linkUrl) {
// Track external link usage for analytics
if (typeof gtag !== 'undefined') {
gtag('event', 'external_link_click', {
link_id: linkId,
link_title: linkTitle,
link_url: linkUrl,
source_page: window.location.pathname
});
}
// Internal analytics tracking
fetch('/api/analytics/external-link-click', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
linkId,
linkTitle,
linkUrl,
timestamp: new Date().toISOString()
})
});
}
}
Security Considerations
URL Validation and Sanitization
import re
from urllib.parse import urlparse
class ExternalLinkSecurity:
ALLOWED_PROTOCOLS = ['http', 'https']
BLOCKED_DOMAINS = ['localhost', '127.0.0.1']
@staticmethod
def validate_external_url(url):
try:
parsed = urlparse(url)
# Check protocol
if parsed.scheme not in ExternalLinkSecurity.ALLOWED_PROTOCOLS:
return False, "Only HTTP and HTTPS URLs are allowed"
# Check for blocked domains
if parsed.hostname in ExternalLinkSecurity.BLOCKED_DOMAINS:
return False, "Internal URLs are not permitted"
# Check for private IP ranges
if ExternalLinkSecurity.is_private_ip(parsed.hostname):
return False, "Private IP addresses are not allowed"
return True, "URL is valid"
except Exception as e:
return False, f"Invalid URL format: {str(e)}"
@staticmethod
def is_private_ip(hostname):
# Basic private IP detection
private_patterns = [
r'^192\.168\.',
r'^10\.',
r'^172\.(1[6-9]|2[0-9]|3[0-1])\.'
]
return any(re.match(pattern, hostname) for pattern in private_patterns)
Content Security Policy Integration
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
frame-ancestors 'none';
form-action 'self' https://trusted-external-domain.com;">
Testing and Validation
Functional Testing
- Verify link creation, editing, and deletion through admin interface
- Test role-based visibility for different user types
- Validate URL validation and security checks
- Confirm proper rendering in both navbar and sidebar locations
Security Testing
- Test URL validation with various malicious input patterns
- Verify blocked internal URLs cannot be saved
- Confirm role-based access controls work correctly
- Test link behavior with different Content Security Policy settings
User Experience Testing
- Verify responsive behavior across different screen sizes
- Test link accessibility with keyboard navigation
- Confirm tooltip functionality for link descriptions
- Validate theme integration in both light and dark modes
Known Limitations
- Maximum of 10 external links to prevent navigation clutter
- Links must use HTTP or HTTPS protocols only
- Internal/private IP addresses are blocked for security
- Link validation is performed client-side and server-side but cannot guarantee external site availability
Future Enhancements
- Link health monitoring and broken link detection
- Advanced analytics and usage reporting for external links
- Link categorization with collapsible sections
- Integration with bookmark management systems
- Dynamic link updates based on user preferences or usage patterns
- Support for authenticated external links with token passing