Admin Templates¶
Overview¶
Admin templates provide the interface for managing Gold Standard Module content, settings, and users. These templates integrate with XOOPS's admin theme while providing a modern, responsive interface for content management.
Admin Template Architecture¶
flowchart TB
subgraph "Admin Layout"
A[Admin Header] --> B[Navigation Menu]
B --> C[Main Content Area]
C --> D[Admin Footer]
end
subgraph "Template Types"
E[Dashboard]
F[List Views]
G[Form Views]
H[Settings]
end
C --> E
C --> F
C --> G
C --> H Main Admin Templates¶
Admin Index/Dashboard (admin/index.tpl)¶
{* goldstandard_admin_index.tpl *}
<div class="goldstandard-admin-dashboard">
{* Quick Stats *}
<section class="dashboard-stats">
<div class="stat-card">
<div class="stat-icon stat-icon--articles">
<svg><!-- article icon --></svg>
</div>
<div class="stat-content">
<span class="stat-value">{$stats.total_articles}</span>
<span class="stat-label">Total Articles</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon stat-icon--published">
<svg><!-- check icon --></svg>
</div>
<div class="stat-content">
<span class="stat-value">{$stats.published_articles}</span>
<span class="stat-label">Published</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon stat-icon--draft">
<svg><!-- draft icon --></svg>
</div>
<div class="stat-content">
<span class="stat-value">{$stats.draft_articles}</span>
<span class="stat-label">Drafts</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon stat-icon--views">
<svg><!-- eye icon --></svg>
</div>
<div class="stat-content">
<span class="stat-value">{$stats.total_views|number_format}</span>
<span class="stat-label">Total Views</span>
</div>
</div>
</section>
{* Quick Actions *}
<section class="quick-actions">
<h2>Quick Actions</h2>
<div class="action-buttons">
<a href="{$admin_url}/article.php?op=new" class="btn btn-primary">
<svg><!-- plus icon --></svg>
New Article
</a>
<a href="{$admin_url}/category.php?op=new" class="btn btn-secondary">
<svg><!-- folder icon --></svg>
New Category
</a>
<a href="{$admin_url}/article.php?filter=pending" class="btn btn-warning">
<svg><!-- clock icon --></svg>
Review Pending ({$stats.pending_articles})
</a>
</div>
</section>
<div class="dashboard-grid">
{* Recent Activity *}
<section class="dashboard-panel recent-activity">
<h2>Recent Activity</h2>
<ul class="activity-list">
{foreach $recent_activity as $activity}
<li class="activity-item activity-item--{$activity.type}">
<span class="activity-icon">
{if $activity.type == 'published'}
<svg><!-- publish icon --></svg>
{elseif $activity.type == 'created'}
<svg><!-- create icon --></svg>
{elseif $activity.type == 'updated'}
<svg><!-- edit icon --></svg>
{/if}
</span>
<div class="activity-content">
<span class="activity-text">
<strong>{$activity.user_name|escape}</strong>
{$activity.action}
<a href="{$activity.item_url}">{$activity.item_title|escape|truncate:40}</a>
</span>
<span class="activity-time">
{$activity.timestamp|date_format:'%b %d, %H:%M'}
</span>
</div>
</li>
{foreachelse}
<li class="activity-item activity-item--empty">
No recent activity
</li>
{/foreach}
</ul>
</section>
{* Draft Articles *}
<section class="dashboard-panel drafts-panel">
<h2>Your Drafts</h2>
{if $my_drafts}
<ul class="draft-list">
{foreach $my_drafts as $draft}
<li class="draft-item">
<a href="{$admin_url}/article.php?op=edit&id={$draft.id}">
{$draft.title|escape|truncate:50}
</a>
<span class="draft-date">
{$draft.updated_at|date_format:'%b %d'}
</span>
</li>
{/foreach}
</ul>
{else}
<p class="empty-message">No drafts in progress</p>
{/if}
</section>
{* Popular Articles Chart *}
<section class="dashboard-panel chart-panel">
<h2>Views This Week</h2>
<canvas id="viewsChart" data-values='{$chart_data|json_encode}'></canvas>
</section>
</div>
</div>
Article List (admin/article_list.tpl)¶
{* goldstandard_admin_article_list.tpl *}
<div class="goldstandard-admin-list">
{* Page Header *}
<header class="page-header">
<h1>Articles</h1>
<a href="{$admin_url}/article.php?op=new" class="btn btn-primary">
Add New Article
</a>
</header>
{* Filters & Search *}
<div class="list-toolbar">
<form action="" method="get" class="filter-form">
{* Search *}
<div class="search-box">
<input type="search"
name="search"
value="{$search|escape}"
placeholder="Search articles...">
<button type="submit" class="btn-search">
<svg><!-- search icon --></svg>
</button>
</div>
{* Status Filter *}
<select name="status" class="filter-select">
<option value="">All Statuses</option>
<option value="published" {if $filter_status == 'published'}selected{/if}>
Published
</option>
<option value="draft" {if $filter_status == 'draft'}selected{/if}>
Draft
</option>
<option value="pending" {if $filter_status == 'pending'}selected{/if}>
Pending Review
</option>
<option value="archived" {if $filter_status == 'archived'}selected{/if}>
Archived
</option>
</select>
{* Category Filter *}
<select name="category" class="filter-select">
<option value="">All Categories</option>
{foreach $categories as $cat}
<option value="{$cat.id}" {if $filter_category == $cat.id}selected{/if}>
{$cat.name|escape}
</option>
{/foreach}
</select>
<button type="submit" class="btn btn-secondary">Filter</button>
{if $search || $filter_status || $filter_category}
<a href="{$admin_url}/article.php" class="btn btn-link">Clear</a>
{/if}
</form>
{* Bulk Actions *}
<div class="bulk-actions">
<select name="bulk_action" id="bulk-action" class="filter-select">
<option value="">Bulk Actions</option>
<option value="publish">Publish</option>
<option value="unpublish">Unpublish</option>
<option value="archive">Archive</option>
<option value="delete">Delete</option>
</select>
<button type="button" id="apply-bulk" class="btn btn-secondary">Apply</button>
</div>
</div>
{* Article Table *}
<div class="table-responsive">
<table class="admin-table" id="articles-table">
<thead>
<tr>
<th class="col-check">
<input type="checkbox" id="select-all" aria-label="Select all">
</th>
<th class="col-title">
<a href="?sort=title&order={if $sort == 'title' && $order == 'asc'}desc{else}asc{/if}">
Title
{if $sort == 'title'}
<span class="sort-indicator">{if $order == 'asc'}↑{else}↓{/if}</span>
{/if}
</a>
</th>
<th class="col-author">Author</th>
<th class="col-category">Category</th>
<th class="col-status">Status</th>
<th class="col-date">
<a href="?sort=date&order={if $sort == 'date' && $order == 'desc'}asc{else}desc{/if}">
Date
{if $sort == 'date'}
<span class="sort-indicator">{if $order == 'asc'}↑{else}↓{/if}</span>
{/if}
</a>
</th>
<th class="col-views">Views</th>
<th class="col-actions">Actions</th>
</tr>
</thead>
<tbody>
{foreach $articles as $article}
<tr class="article-row{if $article.status == 'draft'} row-draft{/if}">
<td class="col-check">
<input type="checkbox"
name="article_ids[]"
value="{$article.id}"
aria-label="Select article">
</td>
<td class="col-title">
<a href="{$admin_url}/article.php?op=edit&id={$article.id}"
class="article-title">
{$article.title|escape|truncate:60}
</a>
{if $article.featured}
<span class="badge badge-featured">Featured</span>
{/if}
</td>
<td class="col-author">
<span class="author-name">{$article.author_name|escape}</span>
</td>
<td class="col-category">
{if $article.categories}
{$article.categories[0].name|escape}
{if count($article.categories) > 1}
<span class="more-categories">+{count($article.categories) - 1}</span>
{/if}
{else}
<span class="no-category">—</span>
{/if}
</td>
<td class="col-status">
<span class="status-badge status-{$article.status}">
{$article.status|capitalize}
</span>
</td>
<td class="col-date">
{if $article.published_at}
{$article.published_at|date_format:'%Y-%m-%d'}
{else}
<span class="date-none">Not published</span>
{/if}
</td>
<td class="col-views">
{$article.view_count|number_format}
</td>
<td class="col-actions">
<div class="action-menu">
<a href="{$article.url}" target="_blank" title="View">
<svg><!-- eye icon --></svg>
</a>
<a href="{$admin_url}/article.php?op=edit&id={$article.id}" title="Edit">
<svg><!-- edit icon --></svg>
</a>
<a href="{$admin_url}/article.php?op=delete&id={$article.id}"
class="action-delete"
title="Delete"
onclick="return confirm('Delete this article?')">
<svg><!-- trash icon --></svg>
</a>
</div>
</td>
</tr>
{foreachelse}
<tr>
<td colspan="8" class="no-results">
<p>No articles found.</p>
<a href="{$admin_url}/article.php?op=new" class="btn btn-primary">
Create your first article
</a>
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
{* Pagination *}
{if $total_pages > 1}
<div class="table-footer">
<span class="showing-info">
Showing {$start_item} - {$end_item} of {$total_items}
</span>
{include file='db:goldstandard_partials_pagination.tpl'
current_page=$current_page
total_pages=$total_pages
base_url="{$admin_url}/article.php?{$query_string}"}
</div>
{/if}
</div>
Article Form (admin/article_form.tpl)¶
{* goldstandard_admin_article_form.tpl *}
<div class="goldstandard-admin-form">
<form action="{$form_action}" method="post" enctype="multipart/form-data" id="article-form">
{$xoops_token}
<header class="form-header">
<h1>{if $article.id}Edit Article{else}New Article{/if}</h1>
<div class="form-actions">
<button type="submit" name="save_draft" class="btn btn-secondary">
Save Draft
</button>
<button type="submit" name="publish" class="btn btn-primary">
{if $article.status == 'published'}Update{else}Publish{/if}
</button>
</div>
</header>
<div class="form-layout">
{* Main Content Column *}
<div class="form-main">
{* Title *}
<div class="form-group">
<label for="title">Title</label>
<input type="text"
id="title"
name="title"
value="{$article.title|escape}"
class="form-control form-control-lg"
required
placeholder="Enter article title">
</div>
{* Slug *}
<div class="form-group">
<label for="slug">
URL Slug
<span class="hint">(auto-generated from title)</span>
</label>
<div class="input-group">
<span class="input-prefix">{$module_url}/</span>
<input type="text"
id="slug"
name="slug"
value="{$article.slug|escape}"
class="form-control"
pattern="[a-z0-9-]+"
placeholder="article-url-slug">
</div>
</div>
{* Content Editor *}
<div class="form-group">
<label for="content">Content</label>
<textarea id="content"
name="content"
class="form-control editor"
rows="20">{$article.content|escape}</textarea>
</div>
{* Excerpt *}
<div class="form-group">
<label for="excerpt">
Excerpt
<span class="hint">(optional - auto-generated if empty)</span>
</label>
<textarea id="excerpt"
name="excerpt"
class="form-control"
rows="3"
maxlength="500">{$article.excerpt|escape}</textarea>
<span class="char-count">
<span id="excerpt-count">{$article.excerpt|count_characters}</span>/500
</span>
</div>
</div>
{* Sidebar *}
<aside class="form-sidebar">
{* Status *}
<div class="sidebar-panel">
<h3>Status</h3>
<div class="form-group">
<select name="status" id="status" class="form-control">
<option value="draft" {if $article.status == 'draft'}selected{/if}>
Draft
</option>
<option value="pending" {if $article.status == 'pending'}selected{/if}>
Pending Review
</option>
<option value="published" {if $article.status == 'published'}selected{/if}>
Published
</option>
<option value="archived" {if $article.status == 'archived'}selected{/if}>
Archived
</option>
</select>
</div>
{if $article.status == 'published'}
<div class="form-group">
<label>Published</label>
<p class="form-static">
{$article.published_at|date_format:'%B %d, %Y at %H:%M'}
</p>
</div>
{/if}
<div class="form-group">
<label>
<input type="checkbox"
name="featured"
value="1"
{if $article.featured}checked{/if}>
Featured Article
</label>
</div>
</div>
{* Categories *}
<div class="sidebar-panel">
<h3>Categories</h3>
<div class="category-checklist">
{foreach $categories as $category}
<label class="checkbox-label" style="padding-left: {$category.depth * 20}px">
<input type="checkbox"
name="category_ids[]"
value="{$category.id}"
{if in_array($category.id, $article.category_ids)}checked{/if}>
{$category.name|escape}
</label>
{/foreach}
</div>
<a href="{$admin_url}/category.php?op=new" class="add-new-link">
+ Add New Category
</a>
</div>
{* Featured Image *}
<div class="sidebar-panel">
<h3>Featured Image</h3>
<div class="image-upload" id="featured-image-upload">
{if $article.featured_image}
<div class="image-preview">
<img src="{$article.featured_image}" alt="Featured image">
<button type="button" class="btn-remove-image">
<svg><!-- x icon --></svg>
</button>
</div>
<input type="hidden" name="featured_image" value="{$article.featured_image}">
{else}
<div class="upload-placeholder">
<svg><!-- upload icon --></svg>
<span>Click or drag to upload</span>
</div>
{/if}
<input type="file"
name="featured_image_file"
accept="image/*"
class="file-input">
</div>
<div class="form-group">
<label for="featured_image_alt">Alt Text</label>
<input type="text"
id="featured_image_alt"
name="featured_image_alt"
value="{$article.featured_image_alt|escape}"
class="form-control">
</div>
</div>
{* Tags *}
<div class="sidebar-panel">
<h3>Tags</h3>
<div class="tag-input-container">
<input type="text"
id="tag-input"
class="form-control"
placeholder="Add tags...">
<div class="tag-list" id="tag-list">
{foreach $article.tags as $tag}
<span class="tag">
{$tag.name|escape}
<button type="button" class="tag-remove">×</button>
<input type="hidden" name="tags[]" value="{$tag.name|escape}">
</span>
{/foreach}
</div>
</div>
<p class="hint">Separate tags with Enter</p>
</div>
{* SEO Settings *}
<div class="sidebar-panel collapsible">
<h3>
SEO Settings
<button type="button" class="btn-collapse">
<svg><!-- chevron icon --></svg>
</button>
</h3>
<div class="panel-content">
<div class="form-group">
<label for="meta_title">Meta Title</label>
<input type="text"
id="meta_title"
name="meta_title"
value="{$article.meta_title|escape}"
class="form-control"
maxlength="60">
<span class="char-count">{$article.meta_title|count_characters}/60</span>
</div>
<div class="form-group">
<label for="meta_description">Meta Description</label>
<textarea id="meta_description"
name="meta_description"
class="form-control"
rows="3"
maxlength="160">{$article.meta_description|escape}</textarea>
<span class="char-count">{$article.meta_description|count_characters}/160</span>
</div>
</div>
</div>
</aside>
</div>
{* Hidden Fields *}
<input type="hidden" name="id" value="{$article.id}">
<input type="hidden" name="op" value="{if $article.id}update{else}create{/if}">
</form>
</div>
Settings Template (admin/settings.tpl)¶
{* goldstandard_admin_settings.tpl *}
<div class="goldstandard-admin-settings">
<header class="page-header">
<h1>Module Settings</h1>
</header>
<form action="{$form_action}" method="post" id="settings-form">
{$xoops_token}
{* Settings Navigation Tabs *}
<div class="settings-tabs">
<nav class="tab-nav" aria-label="Settings sections">
<button type="button" class="tab-btn active" data-tab="general">
General
</button>
<button type="button" class="tab-btn" data-tab="content">
Content
</button>
<button type="button" class="tab-btn" data-tab="display">
Display
</button>
<button type="button" class="tab-btn" data-tab="seo">
SEO
</button>
<button type="button" class="tab-btn" data-tab="notifications">
Notifications
</button>
<button type="button" class="tab-btn" data-tab="api">
API
</button>
</nav>
{* General Settings *}
<div class="tab-panel active" id="panel-general">
<h2>General Settings</h2>
<div class="form-group">
<label for="module_title">Module Title</label>
<input type="text"
id="module_title"
name="config[module_title]"
value="{$config.module_title|escape}"
class="form-control">
<p class="help-text">Displayed in page titles and navigation</p>
</div>
<div class="form-group">
<label for="articles_per_page">Articles Per Page</label>
<input type="number"
id="articles_per_page"
name="config[articles_per_page]"
value="{$config.articles_per_page}"
class="form-control"
min="5"
max="100">
</div>
<div class="form-group">
<label for="date_format">Date Format</label>
<select id="date_format" name="config[date_format]" class="form-control">
<option value="%B %d, %Y" {if $config.date_format == '%B %d, %Y'}selected{/if}>
January 15, 2026
</option>
<option value="%d/%m/%Y" {if $config.date_format == '%d/%m/%Y'}selected{/if}>
15/01/2026
</option>
<option value="%Y-%m-%d" {if $config.date_format == '%Y-%m-%d'}selected{/if}>
2026-01-15
</option>
</select>
</div>
<div class="form-group">
<label>
<input type="checkbox"
name="config[enable_comments]"
value="1"
{if $config.enable_comments}checked{/if}>
Enable Comments
</label>
</div>
</div>
{* Content Settings *}
<div class="tab-panel" id="panel-content">
<h2>Content Settings</h2>
<div class="form-group">
<label for="allowed_html">Allowed HTML Tags</label>
<input type="text"
id="allowed_html"
name="config[allowed_html]"
value="{$config.allowed_html|escape}"
class="form-control">
<p class="help-text">Comma-separated list: p,a,strong,em,ul,ol,li</p>
</div>
<div class="form-group">
<label for="excerpt_length">Excerpt Length (words)</label>
<input type="number"
id="excerpt_length"
name="config[excerpt_length]"
value="{$config.excerpt_length}"
class="form-control"
min="20"
max="500">
</div>
<div class="form-group">
<label for="max_image_size">Max Image Size (MB)</label>
<input type="number"
id="max_image_size"
name="config[max_image_size]"
value="{$config.max_image_size}"
class="form-control"
min="1"
max="50">
</div>
</div>
{* Additional panels... *}
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save Settings</button>
<button type="reset" class="btn btn-secondary">Reset</button>
</div>
</form>
</div>
Admin JavaScript Integration¶
{* Include at bottom of admin templates *}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Bulk actions
document.getElementById('apply-bulk')?.addEventListener('click', function() {
const action = document.getElementById('bulk-action').value;
if (!action) return;
const checked = document.querySelectorAll('input[name="article_ids[]"]:checked');
if (checked.length === 0) {
alert('Please select at least one article');
return;
}
if (action === 'delete' && !confirm('Delete selected articles?')) {
return;
}
// Submit form with bulk action
const form = document.createElement('form');
form.method = 'POST';
form.action = '{$admin_url}/article.php?op=bulk';
// Add action
const actionInput = document.createElement('input');
actionInput.name = 'action';
actionInput.value = action;
form.appendChild(actionInput);
// Add IDs
checked.forEach(cb => {
const input = document.createElement('input');
input.name = 'ids[]';
input.value = cb.value;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
});
// Select all checkbox
document.getElementById('select-all')?.addEventListener('change', function() {
const checkboxes = document.querySelectorAll('input[name="article_ids[]"]');
checkboxes.forEach(cb => cb.checked = this.checked);
});
});
</script>