Blogs — Reference
The Blogs surface is the multi-blog management plane for an SGEN site. It owns the blog records themselves (each one a named container for posts, with its own slug, archive template, and contributor list), the per-blog settings, and the cross-blog routing rules that decide which posts surface in which archive. A site can run a single blog at the conventional /blog URL, or it can run several blogs side by side under different paths — /news, /case-studies, /engineering, /customer-stories — each with its own visual treatment, its own author roster, and its own subscription stream.
This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or mapping content from a legacy multi-blog WordPress install. Customer-facing walkthroughs for creating a blog and configuring contributor access live in the customer docs set; this page describes the shape of the surface, not the editorial flow.
Overview
Blogs live under the Blogs module in SG-Admin, distinct from the Posts module that owns individual entries. The split is intentional: the Blogs module configures containers, the Posts module configures content. A post always belongs to exactly one blog, and the blog record decides where that post is published, who can publish it, and how the archive renders.
The module renders four primary views — the blog list, the blog create / edit form, the per-blog settings panel, and the contributor permissions panel — and exposes a small set of write operations for create, update, archive template assignment, contributor invitation, subscriber export, soft delete, restore, and permanent delete.
The module is paired by convention: one half renders the views and prepares the data, the other half handles writes and returns AJAX responses. The pairing matches the broader SG-Admin convention used across Pages, Posts, Forms, Media, and Users.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Blogs
- URL prefix:
/sg-admin/blogs/ - View templates:
application/views/Admin/Blogs/
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Blogs [+ New blog] │├──────────────────────────────────────────────────────────────────────┤│ Name Slug Posts Contributors Status ││ ──────────────── ──────────────── ────── ───────────── ────────││ Engineering /engineering 48 4 active ││ Customer stories /customer-stories 27 2 active ││ News /news 112 7 active ││ Changelog /changelog 94 1 active ││ Old launch blog /launch-2024 18 0 trash ││ ││ [⋯ Edit] [⋯ Settings] [⋯ Contributors] [⋯ Subscribers] [⋯ Delete] │└──────────────────────────────────────────────────────────────────────┘Actions
The Blogs surface exposes the following operations. Each is described by what it does to the site state, not by its internal method name.
List blogs
Returns the blog records on the site, paginated, with name, slug, post count, contributor count, and status columns. Supports column sort and a free-text filter by name or slug. The default view excludes trashed blogs; a status filter flips them into view.
Create blog
Opens an empty blog form. Required fields at minimum: name, slug, archive template, default author. Optional fields cover description, hero image, RSS feed title, RSS feed description, default category, and SEO metadata for the archive page itself. On submit, the surface validates uniqueness of slug across all blogs on the site, persists the record, creates the archive page at /, and returns the new blog identifier.
Edit blog
Loads an existing blog into the same form shape used for create, pre-populated. Submit replaces the stored configuration. A slug change rewrites the archive URL and triggers a 301 redirect from the old path; existing post permalinks update to match the new slug.
Slug changes are reversible inside a 30-day window — the prior slug is held in a redirect table. After 30 days the prior slug is released and can be reused by another blog or page.
Per-blog settings
A focused settings panel scoped to one blog. Owns the archive template selection, posts-per-archive-page count, default post status (draft or scheduled), comment policy (open / moderated / closed), social-share rendering toggle, RSS feed length, and the default author for posts created without an explicit author assignment.
Manage contributors
Opens the contributor permissions panel. Lists every operator with any capability on this blog, the capability they hold (author / editor / publisher / admin), and the date they were granted access. Supports invite, capability change, and revocation. Contributor invitations send through the Email module using the platform's invitation template.
Export subscribers
Produces a CSV of subscribers attached to the blog's RSS or email-subscription stream. Columns: email, subscription source (RSS-only, email-only, both), subscribed-at timestamp, confirmation status. Used for migration to an external email-service provider or for audit-trail reconciliation.
Soft delete
Marks the blog as trashed. Trashed blogs disappear from the default list, the archive page returns a 410 response, and posts under the blog are hidden from public surfaces. Post records themselves remain intact and re-emerge if the blog is restored.
Restore
Returns a trashed blog to active status. The archive page resumes responding, posts reappear on public surfaces, and the slug is reclaimed if no other blog has taken it during the trash window.
Permanent delete
Hard-removes the blog record. Available only after a soft delete. Posts under the blog are re-assigned to a fallback blog (configured under Settings → General) so individual entries are never destroyed by a blog-level delete. The 301 redirect from the old archive URL drops; subsequent requests return a 410.
Assign archive template
A standalone write that re-points the blog to a different archive template without opening the full edit form. Used when bulk-updating several blogs to a newly designed archive layout. The change applies on the next archive render — no cache flush required.
Reorder blogs
Sets the display order for the blog list view and for any front-end navigation that lists blogs as a set. Drag-handle interface; on submit, the surface writes a sort-order integer per blog.
Data model
A blog record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
name | string | Display name. Unique within the active set. |
slug | string | URL segment. Unique across all blogs on the site. |
description | string | Optional. Renders in the archive header and the RSS feed metadata. |
archive_template_id | integer | Foreign key to the template that renders /. |
default_author_id | integer | Foreign key to a user. Used when a post is created without explicit author. |
default_category_id | integer | Optional. Pre-selected on the new-post form for posts under this blog. |
posts_per_archive_page | integer | Defaults to 12. |
default_post_status | enum | draft or scheduled. Applies to new posts. |
comment_policy | enum | open, moderated, closed. |
social_share_enabled | boolean | Toggles the social-share rendering on the post template. |
rss_feed_length | integer | Number of items in the RSS feed. Defaults to 20. |
hero_image_id | integer | Optional. References a Media library record. |
seo_meta | JSON | Title, description, OG image for the archive page itself. |
sort_order | integer | Display order in lists. |
status | enum | active or trash. |
created_at | timestamp | Immutable. |
updated_at | timestamp | Touched on any edit. |
status = trash is the soft-deleted state. Trashed blogs retain all other fields and all their posts. Permanent delete removes the blog row and re-attributes the posts to the fallback blog.Slug uniqueness scope: unique across all blogs, but does not collide with Pages slugs. A page at /news and a blog at /news is a configuration conflict the surface surfaces at edit time — the second one to save wins, and the first surfaces a warning in the SG-Admin notifications tray.
Contributor list: stored as a separate join table, not on the blog record itself. The join row carries blog_id, user_id, capability_slug, granted_at, and granted_by_user_id. Capabilities are: author (write own draft), editor (edit any draft), publisher (publish), admin (manage contributors).
BLOG RECORD RELATED RECORDS┌─────────────────────────┐│ id │ ──────► POSTS (post.blog_id)│ name, slug │ one-to-many│ description ││ archive_template_id │ ──────► TEMPLATE (one-to-one ref)│ default_author_id │ ──────► USER│ default_category_id │ ──────► CATEGORY│ hero_image_id │ ──────► MEDIA│ seo_meta (JSON) ││ status, sort_order │ ◄────── BLOG_CONTRIBUTORS (join)│ timestamps │ blog_id + user_id + cap└─────────────────────────┘↓ on permanent deleteFALLBACK BLOG (Settings → General)receives all re-attributed postsreceives subscriber stream if migrate-on-delete is onPermissions
Access to the Blogs surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Blogs surface.
Layer 2 — per-action capability. Within SG-Admin, each Blogs action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and a contributor join can additionally grant blog-scoped capabilities. The capability map is:
| Capability | Administrator | Editor | Viewer | Blog admin (per-blog) |
|---|---|---|---|---|
| List blogs | ✔ | ✔ | ✔ | ✔ |
| Create blog | ✔ | — | — | — |
| Edit blog | ✔ | ✔ | — | ✔ (own blog only) |
| Per-blog settings | ✔ | ✔ | — | ✔ (own blog only) |
| Manage contributors | ✔ | — | — | ✔ (own blog only) |
| Export subscribers | ✔ | ✔ | — | ✔ (own blog only) |
| Soft delete | ✔ | — | — | — |
| Restore | ✔ | — | — | — |
| Permanent delete | ✔ | — | — | — |
| Assign archive template | ✔ | ✔ | — | — |
| Reorder blogs | ✔ | ✔ | — | — |
admin capability on that specific blog. The platform-wide Administrator role implicitly holds Blog admin on every blog.Self-protection rules. A site cannot soft-delete its last active blog — the surface returns a structured rejection. The fallback blog (configured under Settings → General) cannot be deleted unless another blog is promoted to fallback first. Slug changes are blocked if the new slug would collide with an active page or another active blog.
Audit. Every write — create, edit, settings change, contributor invite, contributor revoke, soft delete, restore, permanent delete — emits an Activity Log entry. The log records the acting operator, the target blog, and the change shape (field-level diff for edits, capability shape for contributor changes). Activity Log retention follows the site's general settings.
REQUEST → /sg-admin/blogs/...│▼┌────────────────────────┐│ Admin gate │ unauth → reject└──────────┬─────────────┘│ authed▼┌────────────────────────┐│ Role capability check │ role lacks cap → check per-blog│ (platform-wide) │└──────────┬─────────────┘│▼┌────────────────────────┐│ Per-blog contributor │ no grant + no role → reject│ check (scoped writes) │└──────────┬─────────────┘│ allowed▼┌────────────────────────┐│ Self-protection rules │ last-blog / fallback-blog / slug│ │ collision → reject└──────────┬─────────────┘│ passes▼write executes│▼Activity Log entryRelated references
- Posts — Reference. Every post belongs to exactly one blog. The Posts module owns content; Blogs owns the container.
- Pages — Reference. Blog archive pages are rendered as pages internally. Slug collisions between blogs and pages surface as warnings.
- Users — Reference. Contributor grants reference user IDs. Default author and capability map both bind to Users.
- Email — Reference. Subscription routing, RSS-to-email digests, and contributor invitation templates all live in the Email module.
- Settings — Reference. Fallback blog, default comment policy, default post status, RSS feed defaults, and subscriber retention all live in Settings.
- Media — Reference. Hero images and OG images reference Media records. Media deletion cascades to a placeholder.
- SEO — Reference. Archive-page meta and structured data resolve through SEO defaults if the per-blog seo_meta is empty.
/docs/blogs.