Welcome to AleshaCMS
AleshaCMS is a modern, developer-friendly Content Management System built on top of a modern PHP framework. It gives you a powerful admin panel, flexible content types, a visual page builder, and a theming system — all without the bloat.
New here? Start with Installation, then explore the Content Types and Page Builder.
What is AleshaCMS?
AleshaCMS is a full-featured CMS that runs on your own server. It's designed for developers who want control over their codebase, and for content editors who need a clean, intuitive admin interface.
- Built on a modern PHP framework — familiar conventions, powerful routing, Blade templating.
- Flexible Content Types — define any content structure (posts, pages, products, events…).
- Visual Page Builder — drag-and-drop sections with real-time preview.
- Theme System — swap full themes with color customization per theme.
- Plugin Architecture — extend functionality without touching core files.
- SQLite / MySQL ready — works with SQLite out of the box, production-ready on MySQL.
Quick Links
Installation
Get AleshaCMS running locally in minutes.
Content Types
Define your own data structures and fields.
Page Builder
Build pages visually with drag-and-drop sections.
Theme Development
Create custom themes with Blade templates.
Requirements
AleshaCMS requires a modern PHP environment. The following dependencies must be installed before proceeding.
Server Requirements
| Dependency | Minimum Version | Recommended |
|---|---|---|
| PHP | 8.2 | 8.3+ |
| Composer | 2.x | Latest |
| Node.js | 18.x | 20+ |
| Database | SQLite 3 / MySQL 8 / PostgreSQL 14 | Any |
PHP Extensions
pdo+ your database driver (pdo_sqlite,pdo_mysql)mbstringopensslfileinfogdorimagick— for image processingtokenizer,xml,ctype,bcmath
Run php -m to list all installed PHP extensions. the framework's built-in php artisan about also validates your environment.
Installation
AleshaCMS can be set up in just a few minutes using Composer.
Download & extract the source
Download the ZIP file provided by your administrator, then extract it and rename the folder:
unzip aleshacms.zip
mv aleshacms my-site
cd my-site
On Windows, right-click the ZIP file and choose Extract All, then rename the extracted folder to my-site.
Install PHP dependencies
composer install
Copy environment file & generate key
cp .env.example .env
php artisan key:generate
Configure database in .env
For SQLite (default — zero config):
DB_CONNECTION=sqlite
# DB_DATABASE defaults to database/database.sqlite
For MySQL:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=aleshacms
DB_USERNAME=root
DB_PASSWORD=secret
Run migrations & seed
php artisan migrate --seed
This creates all tables and a default admin user (admin@example.com / password).
Install frontend assets
npm install
npm run build
Start development server
php artisan serve
Visit http://localhost:8000 for the frontend and http://localhost:8000/admin for the admin panel.
Production — change the default admin password immediately and set APP_ENV=production + APP_DEBUG=false in .env.
Configuration
AleshaCMS uses the framework's standard .env file for environment config, plus a database-driven settings system for runtime configuration.
Environment Variables
| Key | Description | Default |
|---|---|---|
APP_NAME | Application name | AleshaCMS |
APP_URL | Full URL of your site | http://localhost |
APP_ENV | local / production | local |
APP_DEBUG | Show debug errors | true |
DB_CONNECTION | Database driver | sqlite |
FILESYSTEM_DISK | Storage driver for uploads | public |
Admin Settings
Runtime settings are managed through Admin → Settings and stored in the database via AleshaSetting::get() / AleshaSetting::set().
| Setting Key | Description |
|---|---|
site_name | Site name displayed in header and browser tab |
site_tagline | Subtitle shown in theme header |
site_favicon | Path to favicon file |
meta_description | Default SEO meta description |
active_theme | Slug of currently active theme |
theme_colors.{ThemeName} | JSON color overrides per theme |
Reading Settings in Code
AleshaCMS registers short class aliases globally — no use import needed anywhere, including Blade templates.
// Get a value with default fallback
$siteName = AleshaSetting::get('site_name', 'My Site');
// Store a value
AleshaSetting::set('site_name', 'New Name');
Directory Structure
AleshaCMS follows the framework's standard directory layout with a few additions for themes and plugins.
aleshacms/ # CMS core (auto-loaded) ├── Http/Controllers/Admin/ # Admin panel controllers ├── Http/Controllers/Frontend/ # Frontend rendering ├── Http/Middleware/ # Custom middleware ├── Models/ │ ├── Post.php # Unified content model │ └── CMS/ # Setting, Menu, AuditLog… └── CMS/ └── PageBuilder/ # Section renderer Themes/ # All themes live here ├── News/ │ ├── theme.json # Theme manifest │ └── views/ │ ├── layouts/ # base.blade.php │ ├── partials/ # header, footer │ ├── components/ │ │ └── builder/ # hero, text, gallery… │ └── pages/ # home, single, archive └── default/ Plugins/ # Drop-in plugins ├── Disqus/ │ ├── plugin.json │ └── … └── … database/migrations/ # Framework migrations resources/ ├── js/admin/ # Admin Alpine.js components └── css/ # Tailwind / theme CSS routes/web.php # All routes
Content Types
Content Types define the structure of your content. Think of them as custom post types — each one can have its own fields, slug pattern, and front-end template.
Built-in Types
AleshaCMS ships with two built-in content types:
| Slug | Name | Description |
|---|---|---|
post | Post | Standard blog articles |
page | Page | Static pages (supports Page Builder) |
Creating a Content Type
Go to Admin → Content Types → New. Each content type has:
- Name — human-readable label (e.g. "Portfolio")
- Slug — URL-safe identifier (e.g.
portfolio) - Has Featured Image — toggle thumbnail support
- Supports Page Builder — enable visual builder for this type
- Custom Fields — JSON-defined extra fields
Custom Fields
Custom fields are stored as JSON in the custom_fields column on the posts table. Access them in templates via $post->custom_fields['key'].
For boolean flags that need reliable unchecked state saving, create a dedicated database column via migration rather than storing in custom_fields.
Posts & Pages
All content in AleshaCMS — posts, pages, and custom types — is stored in the unified posts table and managed through Admin → Posts.
Post Fields
| Field | Description |
|---|---|
title | Content title |
slug | URL-friendly identifier, auto-generated from title |
content | Main body content (rich text) |
excerpt | Short summary for listings |
status | draft / published / scheduled |
published_at | Scheduled or actual publish date |
featured_image | Path to cover image |
content_type | Slug of its Content Type |
page_builder | JSON array of page builder sections |
builder_fullpage | Boolean — disable container padding (edge-to-edge) |
custom_fields | JSON object of extra data |
Querying in Theme Templates
// Latest 6 published posts
$posts = AleshaPost::ofType('post')
->published()
->latest('published_at')
->take(6)
->get();
// Single post by slug
$post = AleshaPost::where('slug', $slug)->firstOrFail();
Page Builder
The Page Builder lets editors assemble pages visually using pre-built sections — no code required. Each section maps to a Blade component in the active theme.
Available Sections
| Type | Description |
|---|---|
hero | Full-width banner with title, subtitle, and CTA button |
text | Rich text content block |
gallery | Image grid with lightbox support |
faq | Accordion-style FAQ list |
cta | Call-to-action banner with button |
slider | Auto-playing image/content slider |
embed | Embed iframes (YouTube, Maps, etc.) |
newsletter | Email subscription form |
Full Width Mode
Each section has a Full Width toggle. When enabled, the section renders edge-to-edge without a centered container. Useful for hero banners and sliders.
The Full Page Builder checkbox at the post level removes the container padding from the entire page, giving the builder full control of the layout.
Drag to Reorder
Sections can be reordered by dragging the ⠿ handle on the left of each section. Order is saved automatically when the post is saved.
Section Data Structure
[
{
"type": "hero",
"full_width": true,
"title": "Welcome to Our Site",
"subtitle": "The best place to start",
"button_text": "Get Started",
"button_url": "/about"
},
{
"type": "text",
"full_width": false,
"content": "<p>Some rich text here…</p>"
}
]
Media Library
The Media Library stores and manages all uploaded files. Images can be browsed from the post editor and page builder sections.
Uploading Files
Go to Admin → Media → Upload. Files are stored in storage/app/public and served via /storage/. Run php artisan storage:link to create the symlink.
Supported Formats
- Images:
jpg,jpeg,png,gif,webp,svg - Documents:
pdf,docx,xlsx - Archives:
zip
Accessing Media in Code
$media = Media::latest()->paginate(20);
// Full URL to file
asset('storage/' . $media->path);
Taxonomies
Taxonomies group content. AleshaCMS ships with Categories and Tags, and supports custom taxonomy types.
Default Taxonomies
| Type | URL Pattern | Description |
|---|---|---|
category | /category/{slug} | Hierarchical grouping |
tag | /tag/{slug} | Flat labels |
Managing Taxonomies
Manage via Admin → Taxonomies. Select a type from the tab bar, then create/edit/delete terms. Each term has a name, slug, and optional description.
Assigning to Posts
In the post editor, select categories and tags from the sidebar panel. A post can belong to multiple terms of any taxonomy type.
Themes
Themes control the visual appearance of your site. AleshaCMS supports multiple themes — activate one from the admin and customize its colors without touching code.
Switching Themes
Go to Admin → Themes. Each installed theme shows a preview card. Click Activate to switch instantly.
Color Customization
Themes that define a colors block in theme.json will show a Colors button on their card. Click it to open the color picker panel, choose custom colors, and save. Changes take effect immediately — no rebuild needed.
Colors are stored in the database under the key theme_colors.{ThemeName} and injected as CSS custom properties in the theme's <head>.
Bundled Themes
| Theme | Description | CSS Variables |
|---|---|---|
| News | Newspaper-style theme with ticker bar, red nav | --news-red, --news-red-dark |
| default | Clean minimal theme with indigo accent | --theme-primary, --theme-primary-dark |
Sliders
Sliders are named collections of slides (image + title + subtitle + link) that can be embedded anywhere in your theme templates.
Creating a Slider
Go to Admin → Sliders → New. Give it a name (used as the identifier in templates), then add slides with images from the Media Library.
Rendering a Slider
$slider = AleshaSlider::where('name', 'homepage')
->with('slides')
->first();
Users & Roles
AleshaCMS uses a simple role system. All users with the admin role can access the admin panel.
Roles
| Role | Access |
|---|---|
admin | Full admin panel access |
editor | Content management only (posts, media) |
user | Frontend only |
Managing Users
Go to Admin → Users to create, edit, and delete users. Password changes take effect immediately.
Default credentials after fresh install are admin@example.com / password. Change this immediately on any non-local environment.
Settings
Site-wide settings are grouped and managed via Admin → Settings. Settings are stored in the database and can be read anywhere via AleshaSetting::get().
Setting Groups
| Group | Settings |
|---|---|
| General | Site name, tagline, favicon, meta description |
| SEO | Default meta title pattern, robots, sitemap |
| Social | Facebook, Twitter, Instagram, YouTube URLs |
| Advanced | Custom CSS / JS injection, analytics code |
Plugins
Plugins extend AleshaCMS without modifying core files. Drop a plugin folder into Plugins/ and enable it from Admin → Plugins.
Bundled Plugins
| Plugin | Description |
|---|---|
| Disqus | Adds Disqus comment threads to post pages |
Plugin State
Each plugin has an is_active flag. Toggle via the admin panel. The CMS checks AleshaPlugin::where('slug', '...')->where('is_active', true)->exists() before loading plugin features.
Audit Logs
AleshaCMS records every significant admin action to the audit_logs table. Logs include the action, user, affected model, IP address, and timestamp.
Viewing Logs
Go to Admin → Logs. Use the search bar to filter by action, username, or IP address. Logs are paginated (20 per page).
Log Fields
| Field | Description |
|---|---|
action | e.g. post.created, theme.activated |
user | Admin user who performed the action |
model_type / model_id | Affected Eloquent model |
ip_address | Request IP |
user_agent | Browser/client string |
Theme Structure
A theme is a folder inside Themes/. Its name is the theme slug. The minimum required files are a theme.json manifest and a base layout.
Minimum Theme Structure
Themes/MyTheme/ ├── theme.json └── views/ ├── layouts/ │ └── base.blade.php # Master layout ├── partials/ │ ├── header.blade.php │ └── footer.blade.php ├── pages/ │ ├── home.blade.php # Homepage │ ├── single.blade.php # Single post/page │ ├── archive.blade.php # Post listing │ └── taxonomy.blade.php # Category/tag archive └── components/ └── builder/ # One file per section type ├── hero.blade.php ├── text.blade.php └── …
View Namespacing
AleshaCMS registers each theme's views under the namespace theme-{ThemeSlug}. Use this in Blade includes:
@include('theme-MyTheme::partials.header')
@extends('theme-MyTheme::layouts.base')
theme.json
Every theme must have a theme.json file at its root. This file declares the theme's metadata, color definitions, and supported features.
Full Example
{
"name": "My Theme",
"slug": "MyTheme",
"version": "1.0.0",
"author": "Your Name",
"description": "A clean, modern theme for AleshaCMS.",
"colors": [
{
"key": "primary",
"label": "Primary Color",
"default": "#4f46e5",
"css_var": "--theme-primary"
},
{
"key": "primary_dark",
"label": "Primary Dark (Hover)",
"default": "#4338ca",
"css_var": "--theme-primary-dark"
}
]
}
Fields
| Field | Required | Description |
|---|---|---|
name | Yes | Human-readable theme name |
slug | Yes | Must match the folder name exactly |
version | No | Semantic version string |
author | No | Theme author name |
description | No | Short description shown in admin |
colors | No | Array of color definitions for the color picker |
Blade Templates
AleshaCMS theme templates are standard framework Blade files. Each file has a specific role — the frontend controller resolves which template to render based on the requested URL. Below is a complete breakdown of every template file and the variables available in each.
Template Resolution Map
| URL Pattern | Template File |
|---|---|
/ | pages/home.blade.php |
/{slug} (page/post) | pages/single.blade.php |
/post (archive listing) | pages/archive.blade.php |
/category/{slug}, /tag/{slug} | pages/taxonomy.blade.php |
layouts/base.blade.php
Master layout inherited by all page templates via @extends. Defines the HTML shell, loads assets, includes header/footer partials, and exposes named slots via @yield.
| Slot | Description |
|---|---|
@yield('title') | Browser tab title |
@yield('description') | Meta description tag |
@yield('head') | Extra CSS/meta injected inside <head> |
@yield('content') | Main page body |
@yield('scripts') | Extra JS before </body> |
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="UTF-8">
<title>@yield('title', AleshaSetting::get('site_name'))</title>
<meta name="description" content="@yield('description')">
@vite(['resources/css/theme.css'])
@yield('head')
</head>
<body>
@include('theme-MyTheme::partials.header')
<main>@yield('content')</main>
@include('theme-MyTheme::partials.footer')
@yield('scripts')
</body>
</html>
partials/header.blade.php
Rendered on every page via @include in the base layout. Responsible for the site logo, navigation bar, and any top-bar elements (breaking news ticker, date bar, etc.).
Variables are fetched directly inside the partial using @php:
@php
$siteName = AleshaSetting::get('site_name', config('app.name'));
$tagline = AleshaSetting::get('site_tagline', '');
$menu = AleshaMenu::where('location', 'primary')
->with('items.children')->first();
$currentUrl = '/' . request()->path();
@endphp
<header>
<a href="/">{{ $siteName }}</a>
@if($menu && $menu->items->count())
@foreach($menu->items as $item)
<a href="{{ $item->url }}"
class="{{ $currentUrl === $item->url ? 'active' : '' }}">
{{ $item->label }}
</a>
@if($item->children->count())
@foreach($item->children as $child)
<a href="{{ $child->url }}">{{ $child->label }}</a>
@endforeach
@endif
@endforeach
@endif
</header>
partials/footer.blade.php
Included at the bottom of every page. Typically contains footer nav links, social links, and copyright. Fetches its own data via @php.
@php
$siteName = AleshaSetting::get('site_name');
$footerMenu = AleshaMenu::where('location', 'footer')
->with('items')->first();
$social = [
'facebook' => AleshaSetting::get('social_facebook'),
'twitter' => AleshaSetting::get('social_twitter'),
'instagram' => AleshaSetting::get('social_instagram'),
];
@endphp
<footer>
<p>© {{ date('Y') }} {{ $siteName }}</p>
@if($footerMenu)
@foreach($footerMenu->items as $item)
<a href="{{ $item->url }}">{{ $item->label }}</a>
@endforeach
@endif
@foreach(array_filter($social) as $platform => $url)
<a href="{{ $url }}" target="_blank">{{ ucfirst($platform) }}</a>
@endforeach
</footer>
pages/home.blade.php
Rendered at /. Receives variables injected by FrontendController::resolve(). Typically displays a slider, featured posts, and category sections.
| Variable | Type | Description |
|---|---|---|
$featuredPosts | Collection<Post> | Latest published posts for homepage grid |
$slider | Slider|null | Active homepage slider with slides |
$categories | Collection<Taxonomy> | All category terms |
@extends('theme-MyTheme::layouts.base')
@section('title', AleshaSetting::get('site_name'))
@section('content')
@if($slider && $slider->slides->count())
@foreach($slider->slides as $slide)
<div style="background-image:url('{{ asset('storage/' . $slide->image) }}')">
<h2>{{ $slide->title }}</h2>
<p>{{ $slide->subtitle }}</p>
@if($slide->link)
<a href="{{ $slide->link }}">{{ $slide->button_text }}</a>
@endif
</div>
@endforeach
@endif
@foreach($featuredPosts as $post)
<article>
@if($post->featured_image)
<img src="{{ asset('storage/' . $post->featured_image) }}"
alt="{{ $post->title }}">
@endif
<h3><a href="/{{ $post->content_type }}/{{ $post->slug }}">
{{ $post->title }}
</a></h3>
<time>{{ $post->published_at?->format('d M Y') }}</time>
<p>{{ Str::limit(strip_tags($post->excerpt ?: $post->content), 120) }}</p>
</article>
@endforeach
@endsection
pages/single.blade.php
Rendered for any individual post or page URL (/{slug} or /{type}/{slug}). Page builder sections are pre-rendered by the controller and passed as $pageBuilderHtml — just output it with {!! !!}.
| Variable | Type | Description |
|---|---|---|
$post | Post | The resolved post/page model |
$pageBuilderHtml | string | Pre-rendered page builder HTML (empty string if not using builder) |
$builderFullpage | bool | True = edge-to-edge layout, no container padding |
$relatedPosts | Collection<Post> | Posts from same category (optional) |
@extends('theme-MyTheme::layouts.base')
@section('title', $post->title)
@section('description', Str::limit(strip_tags($post->excerpt ?: $post->content), 160))
@section('content')
@if($pageBuilderHtml)
{{-- Page Builder mode: output pre-rendered HTML from controller --}}
{!! $pageBuilderHtml !!}
@else
{{-- Standard content mode --}}
<article>
<h1>{{ $post->title }}</h1>
<div class="article-meta">
{{ $post->published_at?->format('d F Y') }}
@if($post->author)
· {{ $post->author->name }}
@endif
</div>
@if($post->featured_image)
<img src="{{ asset('storage/' . $post->featured_image) }}"
alt="{{ $post->title }}">
@endif
<div class="article-body">
{!! $post->content !!}
</div>
@if($post->custom_fields['source_url'] ?? null)
<p>Source: <a href="{{ $post->custom_fields['source_url'] }}">
{{ $post->custom_fields['source_url'] }}
</a></p>
@endif
</article>
@endif
@endsection
pages/archive.blade.php
Rendered at /post (and any custom content type archive). Shows a paginated list of published posts, supports search filtering via ?search= query string.
| Variable | Type | Description |
|---|---|---|
$posts | LengthAwarePaginator | Paginated post collection |
$type | string | Content type slug (e.g. post) |
$search | string|null | Current search query from ?search= |
@extends('theme-MyTheme::layouts.base')
@section('title', 'Latest Posts')
@section('content')
<form action="/post" method="GET">
<input type="text" name="search" value="{{ $search }}"
placeholder="Search...">
</form>
@forelse($posts as $post)
<article>
<h2><a href="/{{ $post->content_type }}/{{ $post->slug }}">
{{ $post->title }}
</a></h2>
<time>{{ $post->published_at?->format('d M Y') }}</time>
</article>
@empty
<p>No posts found.</p>
@endforelse
{{ $posts->withQueryString()->links() }}
@endsection
pages/taxonomy.blade.php
Rendered for category (/category/{slug}) and tag (/tag/{slug}) archives. Shows all posts belonging to that taxonomy term.
| Variable | Type | Description |
|---|---|---|
$taxonomy | Taxonomy | Current term model (name, slug, description) |
$posts | LengthAwarePaginator | Paginated posts in this term |
$type | string | category or tag |
@extends('theme-MyTheme::layouts.base')
@section('title', $taxonomy->name)
@section('content')
<header>
<span>{{ ucfirst($type) }}</span>
<h1>{{ $taxonomy->name }}</h1>
@if($taxonomy->description)
<p>{{ $taxonomy->description }}</p>
@endif
<small>{{ $posts->total() }} article(s)</small>
</header>
@foreach($posts as $post)
<article>
<h2><a href="/{{ $post->content_type }}/{{ $post->slug }}">
{{ $post->title }}
</a></h2>
</article>
@endforeach
{{ $posts->links() }}
@endsection
Using {!! !!} vs {{ }} — always use {{ }} (escaped) for user-entered text like titles and names. Only use {!! !!} (unescaped) for trusted HTML content like $post->content which comes from the admin editor.
Color System
AleshaCMS injects saved color values as CSS custom properties into every page. Use var(--your-var) throughout your theme CSS — users can change them from the admin without touching code.
How It Works
- Define colors in
theme.jsonwithkey,default, andcss_var. - In your base layout, read saved values and emit a
:rootblock:
@php
$colors = AleshaSetting::get('theme_colors.MyTheme', []);
@endphp
<style>
:root {
--theme-primary: {{ $colors['primary'] ?? '#4f46e5' }};
--theme-primary-dark: {{ $colors['primary_dark'] ?? '#4338ca' }};
/* Derived tints using CSS color-mix() */
--theme-light: color-mix(in srgb, var(--theme-primary) 15%, #fff);
--theme-bg: color-mix(in srgb, var(--theme-primary) 8%, #fff);
}
</style>
Use color-mix(in srgb, var(--theme-primary) 35%, #fff) to auto-derive lighter tints — no need to ask users to pick every shade manually.
Never Hardcode Colors
All color values in your theme's CSS and Blade files must reference CSS variables. Never hardcode hex values like #4f46e5 — users won't be able to change them from the admin panel.
Builder Components
Each page builder section type maps to a Blade file in views/components/builder/. AleshaCMS passes section data as variables — your component just renders it.
Received Variables
Every builder component receives these variables automatically:
| Variable | Description |
|---|---|
$full_width | Boolean — remove container padding for edge-to-edge layout |
| Section-specific fields | e.g. $title, $content, $images, etc. |
Full Width Pattern
@php $fw = $full_width ?? false; @endphp
<section style="padding: 60px 0;">
<div style="{{ $fw ? '' : 'max-width:1100px; margin:0 auto; padding:0 20px;' }}">
{{-- your section content --}}
</div>
</section>
Gallery Lightbox
Gallery components should implement a lightbox using the window._glbReg registry pattern so multiple galleries on the same page work independently:
window._glbReg = window._glbReg || {};
window._glbReg['{{ $gid }}'] = @json($imgs);
if (!window.galleryOpen) {
window.galleryOpen = function(id, i) { /* show modal */ };
window.galleryClose = function(id) { /* hide modal */ };
window.galleryNav = function(id, dir) { /* prev/next */ };
}
Plugin Structure
Plugins are self-contained folders inside Plugins/. They are loaded by the CMS when active and can register routes, views, middleware, and service providers.
Minimum Plugin Structure
Plugins/MyPlugin/ ├── plugin.json # Plugin manifest ├── Plugin.php # Main plugin class (optional) └── views/ # Blade views (optional)
plugin.json
{
"name": "My Plugin",
"slug": "MyPlugin",
"version": "1.0.0",
"author": "Your Name",
"description": "What this plugin does."
}
Checking Plugin State in Templates
$active = AleshaPlugin::where('slug', 'MyPlugin')
->where('is_active', true)
->exists();
if ($active) {
// Render plugin output
}
Plugin Settings
Plugins can store their own settings using the Setting model with namespaced keys.
Convention
Prefix all plugin setting keys with the plugin slug to avoid collisions:
// Save
AleshaSetting::set('myplugin_api_key', $request->api_key);
// Read
$key = AleshaSetting::get('myplugin_api_key', '');
Admin UI for Plugin Settings
Register a settings group under Admin → Settings by adding your keys to the settings controller's group map, or provide your own admin route + view within the plugin.
The Disqus plugin uses disqus_shortname as its setting key — a good reference implementation to follow.