Episode 17: Site Settings & Configuration
Mastering Global Portal Management and Environment Configuration
Introduction
Professional portal deployment requires sophisticated configuration management that supports multiple environments, feature toggles, and global behavior settings. Site settings and configuration management form the backbone of enterprise-grade Power Pages implementations, enabling controlled deployments, A/B testing, and environment-specific customizations while maintaining consistency across development, staging, and production environments.
Key Concepts
- Configuration Management: Centralized control of portal behavior, features, and settings across environments
- Environment Strategy: Systematic approach to development, staging, and production environment configuration
- Feature Toggles: Runtime control over feature availability and portal functionality
- Settings Hierarchy: Understanding precedence and inheritance in Power Pages configuration
- Configuration Deployment: Automated and controlled propagation of settings across environments
Learning Objectives
By the end of this episode, you will be able to:
- Design and implement comprehensive configuration management strategies for enterprise portals
- Create sophisticated environment-specific configuration workflows
- Build advanced feature toggle systems for controlled feature rollouts
- Establish configuration governance and change management processes
- Implement automated configuration deployment and validation systems
Why Site Settings & Configuration Matter
Enterprise portals require sophisticated configuration management to support controlled deployments, environment-specific behaviors, and global feature management. Proper configuration architecture enables rapid deployment cycles, reduces configuration drift, supports A/B testing, and provides the foundation for scalable portal operations across complex organizational structures.
Step-by-Step Guide
1. Advanced Configuration Architecture
Hierarchical Configuration Management:
// Global Configuration Structure
{
"environment": {
"name": "production",
"debug_mode": false,
"logging_level": "error",
"performance_monitoring": true
},
"features": {
"advanced_search": {
"enabled": true,
"version": "2.1",
"rollout_percentage": 100,
"user_segments": ["premium", "enterprise"]
},
"new_dashboard": {
"enabled": false,
"version": "3.0",
"rollout_percentage": 0,
"user_segments": ["beta_testers"]
},
"payment_integration": {
"enabled": true,
"provider": "stripe",
"test_mode": false,
"webhook_url": "https://portal.company.com/webhooks/payment"
}
},
"integrations": {
"crm": {
"provider": "dynamics365",
"endpoint": "https://company.crm.dynamics.com",
"api_version": "9.2",
"timeout": 30000,
"retry_count": 3
},
"email_service": {
"provider": "sendgrid",
"template_version": "v2",
"tracking_enabled": true,
"bounce_handling": true
}
},
"security": {
"session_timeout": 3600,
"password_policy": {
"min_length": 12,
"require_special_chars": true,
"require_numbers": true,
"require_uppercase": true
},
"rate_limiting": {
"api_calls_per_minute": 100,
"login_attempts_per_hour": 5
}
},
"content": {
"default_language": "en-US",
"supported_languages": ["en-US", "es-ES", "fr-FR", "de-DE"],
"content_versioning": true,
"auto_translation": false
}
}
Environment-Specific Configuration Implementation:
<!-- Environment Detection and Configuration Loading -->
{% assign environment = site.environment | default: 'development' %}
{% assign config = site.data.configurations[environment] %}
<!-- Debug Information (Development Only) -->
{% if config.environment.debug_mode %}
<div class="debug-panel" style="position: fixed; bottom: 0; right: 0; background: #000; color: #fff; padding: 10px; z-index: 9999;">
<h4>Debug Information</h4>
<p>Environment: {{ environment }}</p>
<p>Debug Mode: {{ config.environment.debug_mode }}</p>
<p>User ID: {{ user.id }}</p>
<p>Session ID: {{ session.id }}</p>
<p>Current Language: {{ request.language }}</p>
<p>User Roles: {{ user.roles | join: ', ' }}</p>
<p>Performance Monitoring: {{ config.environment.performance_monitoring }}</p>
<details>
<summary>Active Features</summary>
<ul>
{% for feature in config.features %}
{% if feature.enabled %}
<li>{{ feature.name }} - v{{ feature.version }}</li>
{% endif %}
{% endfor %}
</ul>
</details>
<details>
<summary>Configuration Details</summary>
<pre>{{ config | json }}</pre>
</details>
</div>
{% endif %}
<!-- Performance Monitoring Integration -->
{% if config.environment.performance_monitoring %}
<script>
// Initialize performance monitoring
(function() {
const performanceConfig = {
environment: '{{ environment }}',
userId: '{{ user.id }}',
sessionId: '{{ session.id }}',
buildVersion: '{{ site.build_version }}',
enabledFeatures: {{ config.features | where: 'enabled', true | map: 'name' | json }}
};
// Track page load performance
window.addEventListener('load', function() {
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
trackMetric('page_load_time', loadTime, performanceConfig);
});
// Track feature usage
function trackFeatureUsage(featureName, action, metadata = {}) {
trackEvent('feature_usage', {
feature: featureName,
action: action,
...metadata,
...performanceConfig
});
}
window.trackFeatureUsage = trackFeatureUsage;
})();
</script>
{% endif %}
<!-- Error Logging Configuration -->
{% assign logging_level = config.environment.logging_level | default: 'warn' %}
<script>
window.LOGGING_CONFIG = {
level: '{{ logging_level }}',
environment: '{{ environment }}',
userId: '{{ user.id }}',
endpoint: '/api/logging'
};
// Custom error handler
window.onerror = function(message, source, lineno, colno, error) {
if (window.LOGGING_CONFIG.level === 'error' || window.LOGGING_CONFIG.level === 'debug') {
logError({
message: message,
source: source,
line: lineno,
column: colno,
error: error ? error.stack : null,
userAgent: navigator.userAgent,
url: window.location.href,
timestamp: new Date().toISOString()
});
}
};
</script>
2. Advanced Feature Toggle System
Dynamic Feature Management:
<!-- Feature Toggle Engine -->
{% assign user_segments = user.segments | default: array %}
{% assign user_id_hash = user.id | sha256 | slice: 0, 8 %}
<!-- Feature Availability Checker -->
{% capture check_feature_availability %}
{% assign feature_name = include.feature %}
{% assign feature_config = config.features[feature_name] %}
{% assign is_available = false %}
{% if feature_config.enabled %}
<!-- Check user segment eligibility -->
{% assign segment_match = false %}
{% for segment in feature_config.user_segments %}
{% if user_segments contains segment %}
{% assign segment_match = true %}
{% break %}
{% endif %}
{% endfor %}
{% if segment_match or feature_config.user_segments.size == 0 %}
<!-- Check rollout percentage -->
{% assign user_hash_decimal = user_id_hash | hex_to_decimal %}
{% assign user_percentage = user_hash_decimal | modulo: 100 %}
{% if user_percentage < feature_config.rollout_percentage %}
{% assign is_available = true %}
{% endif %}
{% endif %}
{% endif %}
{{ is_available }}
{% endcapture %}
<!-- Advanced Search Feature Implementation -->
{% include 'check_feature_availability', feature: 'advanced_search' %}
{% assign advanced_search_enabled = check_feature_availability %}
{% if advanced_search_enabled == 'true' %}
<div class="advanced-search-container">
<h3>Advanced Search</h3>
<form class="advanced-search-form" method="get" action="/search">
<div class="search-fields">
<div class="field-group">
<label for="keywords">Keywords:</label>
<input type="text" id="keywords" name="q" value="{{ request.params.q }}">
</div>
<div class="field-group">
<label for="category">Category:</label>
<select id="category" name="category">
<option value="">All Categories</option>
{% for category in search_categories %}
<option value="{{ category.value }}"
{% if request.params.category == category.value %}selected{% endif %}>
{{ category.label }}
</option>
{% endfor %}
</select>
</div>
<div class="field-group">
<label for="date_range">Date Range:</label>
<select id="date_range" name="date_range">
<option value="">Any Time</option>
<option value="last_week">Last Week</option>
<option value="last_month">Last Month</option>
<option value="last_year">Last Year</option>
</select>
</div>
<div class="field-group">
<label for="content_type">Content Type:</label>
<select id="content_type" name="content_type">
<option value="">All Types</option>
<option value="articles">Articles</option>
<option value="documents">Documents</option>
<option value="videos">Videos</option>
</select>
</div>
</div>
<div class="search-actions">
<button type="submit" class="btn btn-primary">Search</button>
<button type="button" class="btn btn-secondary" onclick="clearAdvancedSearch()">Clear</button>
</div>
</form>
<!-- Advanced Search Analytics -->
<script>
if (window.trackFeatureUsage) {
document.querySelector('.advanced-search-form').addEventListener('submit', function(e) {
window.trackFeatureUsage('advanced_search', 'search_performed', {
has_category: e.target.category.value !== '',
has_date_range: e.target.date_range.value !== '',
has_content_type: e.target.content_type.value !== ''
});
});
}
</script>
</div>
{% else %}
<!-- Fallback to Basic Search -->
<div class="basic-search-container">
<form class="search-form" method="get" action="/search">
<div class="search-input-group">
<input type="text" name="q" placeholder="Search..." value="{{ request.params.q }}">
<button type="submit" class="btn btn-primary">Search</button>
</div>
</form>
</div>
{% endif %}
<!-- New Dashboard Feature with Beta Access -->
{% include 'check_feature_availability', feature: 'new_dashboard' %}
{% assign new_dashboard_enabled = check_feature_availability %}
{% if new_dashboard_enabled == 'true' %}
<div class="beta-feature-notice">
<div class="notice-header">
<span class="beta-badge">BETA</span>
<h4>New Dashboard Experience</h4>
</div>
<p>You're seeing our new dashboard design. <a href="/feedback/dashboard">Share your feedback</a></p>
</div>
<!-- Enhanced Dashboard Content -->
<div class="dashboard-v3">
<div class="dashboard-grid">
<!-- Modern dashboard implementation -->
<div class="dashboard-card analytics-card">
<h3>Analytics Overview</h3>
<div class="metrics-grid">
<div class="metric">
<span class="metric-value">{{ user.activity.page_views }}</span>
<span class="metric-label">Page Views</span>
</div>
<div class="metric">
<span class="metric-value">{{ user.activity.downloads }}</span>
<span class="metric-label">Downloads</span>
</div>
</div>
</div>
<div class="dashboard-card tasks-card">
<h3>Quick Actions</h3>
<div class="actions-list">
<a href="/upload" class="action-item">
<span class="action-icon">📁</span>
<span class="action-text">Upload Document</span>
</a>
<a href="/requests/new" class="action-item">
<span class="action-icon">📝</span>
<span class="action-text">New Request</span>
</a>
</div>
</div>
</div>
</div>
<script>
// Track beta dashboard usage
if (window.trackFeatureUsage) {
window.trackFeatureUsage('new_dashboard', 'viewed');
// Track interactions with dashboard elements
document.querySelectorAll('.dashboard-card').forEach(card => {
card.addEventListener('click', function() {
window.trackFeatureUsage('new_dashboard', 'card_interaction', {
card_type: this.className.includes('analytics') ? 'analytics' : 'actions'
});
});
});
}
</script>
{% else %}
<!-- Standard Dashboard -->
<div class="dashboard-v2">
<h2>Dashboard</h2>
<div class="dashboard-summary">
<div class="summary-item">
<h4>Recent Activity</h4>
<p>{{ user.activity.recent_count }} recent actions</p>
</div>
<div class="summary-item">
<h4>Notifications</h4>
<p>{{ user.notifications.unread_count }} unread messages</p>
</div>
</div>
</div>
{% endif %}
3. Integration Configuration Management
Dynamic Integration Settings:
<!-- CRM Integration Configuration -->
{% assign crm_config = config.integrations.crm %}
<script>
// CRM Integration Setup
window.CRM_CONFIG = {
provider: '{{ crm_config.provider }}',
endpoint: '{{ crm_config.endpoint }}',
apiVersion: '{{ crm_config.api_version }}',
timeout: {{ crm_config.timeout }},
retryCount: {{ crm_config.retry_count }}
};
// CRM API Client
class CRMClient {
constructor(config) {
this.config = config;
this.baseUrl = config.endpoint;
}
async makeRequest(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const defaultOptions = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'OData-MaxVersion': '4.0',
'OData-Version': '4.0'
},
timeout: this.config.timeout
};
const requestOptions = { ...defaultOptions, ...options };
for (let attempt = 0; attempt < this.config.retryCount; attempt++) {
try {
const response = await fetch(url, requestOptions);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (attempt === this.config.retryCount - 1) {
throw error;
}
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}
async getContact(contactId) {
return this.makeRequest(`/api/data/v${this.config.apiVersion}/contacts(${contactId})`);
}
async updateContact(contactId, data) {
return this.makeRequest(`/api/data/v${this.config.apiVersion}/contacts(${contactId})`, {
method: 'PATCH',
body: JSON.stringify(data)
});
}
}
// Initialize CRM client
window.crmClient = new CRMClient(window.CRM_CONFIG);
</script>
<!-- Email Service Configuration -->
{% assign email_config = config.integrations.email_service %}
<script>
// Email Service Configuration
window.EMAIL_CONFIG = {
provider: '{{ email_config.provider }}',
templateVersion: '{{ email_config.template_version }}',
trackingEnabled: {{ email_config.tracking_enabled }},
bounceHandling: {{ email_config.bounce_handling }}
};
// Email Service Integration
class EmailService {
constructor(config) {
this.config = config;
}
async sendEmail(templateId, recipientEmail, templateData = {}) {
const emailData = {
template_id: templateId,
template_version: this.config.templateVersion,
recipient: recipientEmail,
template_data: templateData,
tracking_enabled: this.config.trackingEnabled,
bounce_handling: this.config.bounceHandling
};
try {
const response = await fetch('/api/email/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(emailData)
});
if (!response.ok) {
throw new Error(`Email send failed: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Email sending error:', error);
throw error;
}
}
async getEmailStatus(messageId) {
const response = await fetch(`/api/email/status/${messageId}`);
return response.json();
}
}
// Initialize email service
window.emailService = new EmailService(window.EMAIL_CONFIG);
</script>
4. Security Configuration Implementation
Advanced Security Settings:
<!-- Security Configuration -->
{% assign security_config = config.security %}
<!-- Session Management -->
<script>
// Session Configuration
window.SESSION_CONFIG = {
timeout: {{ security_config.session_timeout }},
warningTime: {{ security_config.session_timeout | minus: 300 }}, // 5 minutes before timeout
renewEndpoint: '/api/session/renew',
logoutEndpoint: '/api/session/logout'
};
// Session Management System
class SessionManager {
constructor(config) {
this.config = config;
this.warningShown = false;
this.timeoutTimer = null;
this.warningTimer = null;
this.lastActivity = Date.now();
this.setupActivityTracking();
this.startTimers();
}
setupActivityTracking() {
const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
events.forEach(event => {
document.addEventListener(event, () => {
this.updateActivity();
}, { passive: true });
});
}
updateActivity() {
this.lastActivity = Date.now();
this.resetTimers();
}
startTimers() {
// Warning timer
this.warningTimer = setTimeout(() => {
this.showSessionWarning();
}, this.config.warningTime * 1000);
// Timeout timer
this.timeoutTimer = setTimeout(() => {
this.logoutUser();
}, this.config.timeout * 1000);
}
resetTimers() {
clearTimeout(this.warningTimer);
clearTimeout(this.timeoutTimer);
this.warningShown = false;
this.startTimers();
}
showSessionWarning() {
if (this.warningShown) return;
this.warningShown = true;
const timeLeft = Math.floor((this.config.timeout * 1000 - (Date.now() - this.lastActivity)) / 1000);
if (confirm(`Your session will expire in ${Math.floor(timeLeft / 60)} minutes. Would you like to extend your session?`)) {
this.renewSession();
}
}
async renewSession() {
try {
const response = await fetch(this.config.renewEndpoint, {
method: 'POST',
credentials: 'same-origin'
});
if (response.ok) {
this.resetTimers();
} else {
throw new Error('Session renewal failed');
}
} catch (error) {
console.error('Session renewal error:', error);
this.logoutUser();
}
}
logoutUser() {
window.location.href = this.config.logoutEndpoint;
}
}
// Initialize session manager
document.addEventListener('DOMContentLoaded', function() {
window.sessionManager = new SessionManager(window.SESSION_CONFIG);
});
</script>
<!-- Password Policy Enforcement -->
{% assign password_policy = security_config.password_policy %}
<script>
// Password Policy Configuration
window.PASSWORD_POLICY = {
minLength: {{ password_policy.min_length }},
requireSpecialChars: {{ password_policy.require_special_chars }},
requireNumbers: {{ password_policy.require_numbers }},
requireUppercase: {{ password_policy.require_uppercase }}
};
// Password Validation
function validatePassword(password) {
const policy = window.PASSWORD_POLICY;
const errors = [];
if (password.length < policy.minLength) {
errors.push(`Password must be at least ${policy.minLength} characters long`);
}
if (policy.requireSpecialChars && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
errors.push('Password must contain at least one special character');
}
if (policy.requireNumbers && !/\d/.test(password)) {
errors.push('Password must contain at least one number');
}
if (policy.requireUppercase && !/[A-Z]/.test(password)) {
errors.push('Password must contain at least one uppercase letter');
}
return {
isValid: errors.length === 0,
errors: errors
};
}
// Apply password validation to forms
document.addEventListener('DOMContentLoaded', function() {
const passwordFields = document.querySelectorAll('input[type="password"]');
passwordFields.forEach(field => {
if (field.name === 'new_password' || field.name === 'password') {
field.addEventListener('input', function() {
const validation = validatePassword(this.value);
const feedbackElement = this.parentNode.querySelector('.password-feedback');
if (feedbackElement) {
feedbackElement.innerHTML = validation.errors.map(error =>
`<div class="error">${error}</div>`
).join('');
}
});
}
});
});
</script>
<!-- Rate Limiting Display -->
{% assign rate_limiting = security_config.rate_limiting %}
<script>
// Rate Limiting Configuration
window.RATE_LIMITING = {
apiCallsPerMinute: {{ rate_limiting.api_calls_per_minute }},
loginAttemptsPerHour: {{ rate_limiting.login_attempts_per_hour }}
};
// Rate limiting feedback
function handleRateLimitResponse(response) {
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const message = retryAfter
? `Rate limit exceeded. Please try again in ${retryAfter} seconds.`
: 'Rate limit exceeded. Please try again later.';
showMessage(message, 'warning');
return true;
}
return false;
}
// Apply rate limiting handling to API calls
const originalFetch = window.fetch;
window.fetch = function(...args) {
return originalFetch.apply(this, args).then(response => {
handleRateLimitResponse(response);
return response;
});
};
</script>
5. Content Configuration Management
Multi-Language Content Settings:
<!-- Content Configuration -->
{% assign content_config = config.content %}
<!-- Language Management -->
<div class="content-configuration">
<div class="language-settings">
<h3>Language Configuration</h3>
<div class="current-language">
<strong>Current Language:</strong> {{ content_config.default_language }}
</div>
<div class="supported-languages">
<strong>Supported Languages:</strong>
<ul>
{% for language in content_config.supported_languages %}
<li>
<span class="language-code">{{ language }}</span>
{% if language == content_config.default_language %}
<span class="default-badge">Default</span>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% if content_config.content_versioning %}
<div class="versioning-info">
<h4>Content Versioning</h4>
<p>Content versioning is enabled. All content changes are tracked and can be reverted.</p>
<div class="version-controls">
<button class="btn btn-outline-secondary" onclick="showVersionHistory()">
View Version History
</button>
<button class="btn btn-outline-secondary" onclick="showContentDiff()">
Compare Versions
</button>
</div>
</div>
{% endif %}
{% if content_config.auto_translation %}
<div class="auto-translation-info">
<h4>Automatic Translation</h4>
<p>Automatic translation is enabled for supported content types.</p>
<div class="translation-status">
<div class="status-item">
<span class="status-label">Translation Provider:</span>
<span class="status-value">{{ content_config.translation_provider | default: 'Azure Translator' }}</span>
</div>
<div class="status-item">
<span class="status-label">Quality Threshold:</span>
<span class="status-value">{{ content_config.translation_quality_threshold | default: 85 }}%</span>
</div>
</div>
</div>
{% endif %}
</div>
</div>
<!-- Content Management Scripts -->
<script>
// Content Configuration Management
window.CONTENT_CONFIG = {
defaultLanguage: '{{ content_config.default_language }}',
supportedLanguages: {{ content_config.supported_languages | json }},
versioningEnabled: {{ content_config.content_versioning }},
autoTranslation: {{ content_config.auto_translation }}
};
// Version Management Functions
function showVersionHistory() {
// Implementation for showing content version history
window.open('/admin/content/versions', '_blank');
}
function showContentDiff() {
// Implementation for content comparison
window.open('/admin/content/compare', '_blank');
}
// Content Editor Enhancement
function enhanceContentEditor() {
const editors = document.querySelectorAll('.content-editor');
editors.forEach(editor => {
// Add auto-save functionality
let saveTimeout;
editor.addEventListener('input', function() {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
autoSaveContent(this);
}, 2000);
});
// Add translation suggestions if auto-translation is enabled
if (window.CONTENT_CONFIG.autoTranslation) {
addTranslationSuggestions(editor);
}
});
}
function autoSaveContent(editor) {
const contentId = editor.dataset.contentId;
const content = editor.value || editor.innerHTML;
fetch('/api/content/autosave', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
contentId: contentId,
content: content,
language: window.CONTENT_CONFIG.defaultLanguage
})
}).then(response => {
if (response.ok) {
showAutoSaveIndicator();
}
});
}
function showAutoSaveIndicator() {
const indicator = document.querySelector('.autosave-indicator');
if (indicator) {
indicator.textContent = 'Auto-saved at ' + new Date().toLocaleTimeString();
indicator.style.opacity = '1';
setTimeout(() => {
indicator.style.opacity = '0.5';
}, 2000);
}
}
// Initialize content management features
document.addEventListener('DOMContentLoaded', function() {
enhanceContentEditor();
});
</script>
Practical Example: Multi-Environment Portal Configuration
Let’s implement a complete configuration management system for a corporate portal:
<!-- environment-specific-portal.html -->
{% assign env = site.environment %}
{% assign config = site.data.configurations[env] %}
<!DOCTYPE html>
<html lang="{{ config.content.default_language }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ site.title }} - {{ config.environment.name | capitalize }} Environment</title>
<!-- Environment-Specific Stylesheets -->
{% if env == 'development' %}
<link rel="stylesheet" href="/assets/css/app.css">
<link rel="stylesheet" href="/assets/css/debug.css">
{% elsif env == 'staging' %}
<link rel="stylesheet" href="/assets/css/app.min.css">
<link rel="stylesheet" href="/assets/css/staging-banner.css">
{% else %}
<link rel="stylesheet" href="/assets/css/app.min.css">
{% if config.features.cdn_assets.enabled %}
<link rel="preconnect" href="{{ config.features.cdn_assets.domain }}">
{% endif %}
{% endif %}
<!-- Performance Monitoring -->
{% if config.environment.performance_monitoring %}
<script>
window.performance.mark('head-start');
</script>
{% endif %}
</head>
<body class="env-{{ env }}">
<!-- Environment Banner -->
{% unless env == 'production' %}
<div class="environment-banner {{ env }}">
<div class="banner-content">
<span class="env-label">{{ env | upcase }} ENVIRONMENT</span>
<span class="env-warning">This is not the live site</span>
{% if config.environment.debug_mode %}
<span class="debug-indicator">DEBUG MODE ENABLED</span>
{% endif %}
</div>
</div>
{% endunless %}
<!-- Main Navigation with Feature Toggles -->
<nav class="main-navigation">
<div class="nav-container">
<div class="nav-brand">
<a href="/">{{ site.title }}</a>
</div>
<div class="nav-links">
<a href="/">Home</a>
<a href="/products">Products</a>
<a href="/support">Support</a>
<!-- Feature-Gated Navigation Items -->
{% include 'check_feature_availability', feature: 'advanced_search' %}
{% if check_feature_availability == 'true' %}
<a href="/search/advanced">Advanced Search</a>
{% endif %}
{% include 'check_feature_availability', feature: 'new_dashboard' %}
{% if check_feature_availability == 'true' %}
<a href="/dashboard/v3" class="beta-link">
Dashboard
<span class="beta-badge">BETA</span>
</a>
{% else %}
<a href="/dashboard">Dashboard</a>
{% endif %}
<!-- User-Specific Navigation -->
{% if user %}
<div class="user-menu">
<a href="/profile">{{ user.fullname }}</a>
<div class="dropdown">
<a href="/profile">Profile</a>
<a href="/settings">Settings</a>
{% if user.roles contains 'Administrator' %}
<a href="/admin">Administration</a>
{% endif %}
<a href="/logout">Logout</a>
</div>
</div>
{% else %}
<a href="/login">Login</a>
{% endif %}
</div>
</div>
</nav>
<!-- Main Content Area -->
<main class="main-content">
<!-- Feature Toggle Configuration Panel (Admin Only) -->
{% if user.roles contains 'Administrator' and config.environment.debug_mode %}
<div class="admin-feature-panel">
<h4>Feature Toggle Panel</h4>
<div class="feature-toggles">
{% for feature in config.features %}
<div class="feature-toggle">
<label>
<input type="checkbox"
data-feature="{{ feature[0] }}"
{% if feature[1].enabled %}checked{% endif %}
onchange="toggleFeature(this)">
{{ feature[0] | replace: '_', ' ' | capitalize }}
</label>
<span class="rollout-info">{{ feature[1].rollout_percentage }}% rollout</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Dynamic Content Based on Configuration -->
<div class="content-area">
<h1>Welcome to {{ site.title }}</h1>
<!-- Environment-Specific Messaging -->
{% case env %}
{% when 'development' %}
<div class="dev-notice">
<h3>Development Environment</h3>
<p>This environment is for development and testing purposes.</p>
<ul>
<li>Debug mode: {{ config.environment.debug_mode }}</li>
<li>Performance monitoring: {{ config.environment.performance_monitoring }}</li>
<li>Logging level: {{ config.environment.logging_level }}</li>
</ul>
</div>
{% when 'staging' %}
<div class="staging-notice">
<h3>Staging Environment</h3>
<p>This environment mirrors production for final testing.</p>
<p>Data may be reset periodically.</p>
</div>
{% when 'production' %}
<!-- Production-specific content -->
<div class="welcome-section">
<p>Your trusted partner for digital solutions.</p>
</div>
{% endcase %}
<!-- Integration Status Dashboard -->
<div class="integration-status">
<h3>System Status</h3>
<div class="status-grid">
<div class="status-item">
<span class="status-label">CRM Integration:</span>
<span class="status-indicator" data-service="crm">
<span class="loading">Checking...</span>
</span>
</div>
<div class="status-item">
<span class="status-label">Email Service:</span>
<span class="status-indicator" data-service="email">
<span class="loading">Checking...</span>
</span>
</div>
<div class="status-item">
<span class="status-label">Payment Gateway:</span>
<span class="status-indicator" data-service="payment">
<span class="loading">Checking...</span>
</span>
</div>
</div>
</div>
<!-- Content Preview with Versioning -->
{% if config.content.content_versioning %}
<div class="content-preview">
<div class="content-header">
<h3>Latest Content Updates</h3>
<button class="btn btn-outline-primary" onclick="showContentVersions()">
View All Versions
</button>
</div>
<div class="content-items">
{% assign recent_content = site.data.content_updates | sort: 'updated_date' | reverse | limit: 5 %}
{% for content_item in recent_content %}
<div class="content-item">
<h4>{{ content_item.title }}</h4>
<p class="content-meta">
Updated: {{ content_item.updated_date | date: "%B %d, %Y" }}
| Version: {{ content_item.version }}
| Language: {{ content_item.language }}
</p>
<p>{{ content_item.summary }}</p>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</main>
<!-- Configuration-Driven JavaScript -->
<script>
// Global Configuration
window.PORTAL_CONFIG = {
environment: '{{ env }}',
debug: {{ config.environment.debug_mode }},
features: {{ config.features | json }},
integrations: {
crm: {
provider: '{{ config.integrations.crm.provider }}',
timeout: {{ config.integrations.crm.timeout }}
},
email: {
provider: '{{ config.integrations.email_service.provider }}'
}
},
security: {
sessionTimeout: {{ config.security.session_timeout }}
}
};
// Feature Toggle Management (Admin Only)
{% if user.roles contains 'Administrator' %}
function toggleFeature(checkbox) {
const featureName = checkbox.dataset.feature;
const enabled = checkbox.checked;
fetch('/api/admin/features/toggle', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
feature: featureName,
enabled: enabled,
environment: '{{ env }}'
})
}).then(response => {
if (response.ok) {
showMessage(`Feature ${featureName} ${enabled ? 'enabled' : 'disabled'}`, 'success');
// Optionally reload page to reflect changes
if (confirm('Reload page to see changes?')) {
location.reload();
}
} else {
showMessage('Failed to toggle feature', 'error');
checkbox.checked = !enabled; // Revert checkbox
}
});
}
{% endif %}
// Integration Health Checks
async function checkIntegrationHealth() {
const services = ['crm', 'email', 'payment'];
for (const service of services) {
try {
const response = await fetch(`/api/health/${service}`);
const status = response.ok ? 'healthy' : 'unhealthy';
updateStatusIndicator(service, status);
} catch (error) {
updateStatusIndicator(service, 'error');
}
}
}
function updateStatusIndicator(service, status) {
const indicator = document.querySelector(`[data-service="${service}"]`);
if (indicator) {
const statusText = {
'healthy': '✅ Operational',
'unhealthy': '⚠️ Issues Detected',
'error': '❌ Offline'
};
indicator.innerHTML = statusText[status] || '❓ Unknown';
indicator.className = `status-indicator ${status}`;
}
}
// Content Version Management
function showContentVersions() {
window.open('/admin/content/versions', '_blank');
}
// Initialize application
document.addEventListener('DOMContentLoaded', function() {
// Check integration health
checkIntegrationHealth();
// Set up environment-specific features
if (window.PORTAL_CONFIG.debug) {
console.log('Portal Configuration:', window.PORTAL_CONFIG);
}
// Initialize session management
if (window.sessionManager) {
window.sessionManager.init();
}
});
// Environment-specific error handling
{% if env == 'production' %}
window.onerror = function(message, source, lineno, colno, error) {
// Send to error tracking service
fetch('/api/errors/report', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message, source, lineno, colno,
stack: error ? error.stack : null,
userAgent: navigator.userAgent,
url: location.href,
timestamp: new Date().toISOString()
})
});
};
{% else %}
window.onerror = function(message, source, lineno, colno, error) {
console.error('Error:', { message, source, lineno, colno, error });
};
{% endif %}
</script>
<!-- Environment-Specific Analytics -->
{% if env == 'production' and config.environment.performance_monitoring %}
<script>
// Production analytics tracking
(function() {
// Track page performance
window.addEventListener('load', function() {
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
gtag('event', 'page_load_time', {
'value': loadTime,
'custom_parameter': 'milliseconds'
});
});
// Track feature usage
window.trackFeatureUsage = function(feature, action, metadata = {}) {
gtag('event', 'feature_usage', {
'feature_name': feature,
'action': action,
'custom_parameters': metadata
});
};
})();
</script>
{% elsif env == 'development' %}
<script>
// Development analytics (console only)
window.trackFeatureUsage = function(feature, action, metadata = {}) {
console.log('Feature Usage:', { feature, action, metadata });
};
</script>
{% endif %}
</body>
</html>
Troubleshooting Common Issues
Configuration Management Debugging:
- Environment Detection Issues: Verify environment variables and configuration file paths
- Feature Toggle Conflicts: Check for conflicting feature dependencies and rollout percentages
- Integration Failures: Implement comprehensive health checks and fallback mechanisms
- Configuration Drift: Establish automated configuration validation and sync processes
- Performance Impact: Monitor configuration loading and feature toggle evaluation overhead
Configuration Management Best Practices
Establishing Robust Configuration Governance:
- Version Control: Track all configuration changes through version control systems
- Environment Parity: Maintain consistency between development, staging, and production
- Automated Testing: Validate configuration changes through automated test suites
- Rollback Procedures: Implement quick rollback mechanisms for configuration issues
- Documentation: Maintain comprehensive documentation of all configuration options
Key Takeaways
Site settings and configuration management form the foundation of enterprise-grade Power Pages implementations. Focus on hierarchical configuration architecture, robust feature toggle systems, comprehensive integration management, and strong governance processes to build scalable, maintainable portal solutions that support complex organizational requirements and controlled deployment strategies.
In the next episode, you’ll learn about JavaScript Frameworks Integration to enhance your portal with modern front-end technologies and create rich, interactive user experiences.
- Verify functionality across different browsers and devices
- Validate accessibility compliance
- Performance test under load conditions
4. Documentation and Maintenance
Set yourself up for long-term success:
- Document all customizations and their purpose
- Create maintenance procedures and schedules
- Plan for future updates and changes
- Train team members on your customizations
Practical Example
Consider implementing this episode’s techniques for a customer portal where users need enhanced functionality beyond basic templates. The implementation should:
- Provide an intuitive, branded user experience
- Handle complex business logic appropriately
- Perform well under realistic usage conditions
- Be maintainable by your development team
Apply the specific techniques from this episode while following the general principles of good portal development.
Common Implementation Patterns
| Pattern | When to Use | Benefits | Considerations |
|---|---|---|---|
| Progressive Enhancement | Adding advanced features | Ensures basic functionality always works | Requires careful planning |
| Responsive Design | Multi-device support | Consistent experience across devices | May increase complexity |
| Modular Development | Complex customizations | Easier maintenance and updates | Requires good architecture |
| Performance Monitoring | All implementations | Early detection of issues | Ongoing effort required |
Troubleshooting Tips
- Performance Issues: Monitor page load times and optimize images, scripts, and database queries
- Browser Compatibility: Test across different browsers and use progressive enhancement
- Mobile Experience: Ensure all functionality works well on mobile devices
- Maintenance Challenges: Keep good documentation and follow consistent coding standards
Advanced Considerations
As you implement these intermediate techniques, consider:
- Scalability: How will your solution perform as usage grows?
- Security: Do your customizations introduce any security vulnerabilities?
- Integration: How do your changes affect other portal functionality?
- Future Updates: Will your customizations survive platform updates?
Summary
Advanced site configuration enables enterprise-grade portals that perform well, scale effectively, and integrate seamlessly with your IT infrastructure. Document your configurations thoroughly and implement proper change management processes. Next, you’ll add powerful search functionality to help users find information quickly.