Developer Library

This guide describes the current SCPress extension points and the path a request takes through the platform. It reflects the implementation in this repository; a hook is available only when the relevant component runs.

Platform map

PathResponsibility
index.phpFront controller for public requests.
sc-core/bootstrap.phpLoads core services, connects the database, starts sessions, loads active plugins, initializes the active theme, and applies security settings.
sc-core/Router.phpRoutes public pages, admin paths, and /api/*; fires sc_before_dispatch before matching a route.
sc-core/Hooks.phpIn-process action and filter callback registry.
sc-core/helpers.phpPublic template helpers, hooks, and shared content functions.
sc-core/PluginManager.phpDiscovers plugin folders and manifests, loads active plugin entry files, and fires plugin lifecycle actions.
sc-core/ThemeManager.phpLoads core templates and current content, and initializes visual theme settings.
sc-core/WidgetManager.phpRegisters widget types and zones, then renders assigned widgets.
sc-admin/Signed-in administration screens. Plugins can add menu items and editor tools through documented actions.
sc-content/plugins/Installed plugin code. Each plugin should use its own folder and unique prefix/namespace.
sc-content/themes/Theme packages and StyleFinder child-theme overrides. SCPress core owns page operations and templates.

Request and extension lifecycle

  1. sc-core/bootstrap.php loads core classes and helper functions.
  2. The database and session are initialized.
  3. Active plugin entry files are required from sc-content/plugins/<plugin-id>/; plugins register actions and filters while loading.
  4. SCPress fires sc_plugins_loaded, then initializes the active theme and fires sc_theme_init($activeTheme).
  5. The router fires sc_before_dispatch($path), then dispatches the requested page or API route.
  6. Frontend templates call shared helpers such as sc_header(), sc_the_content(), widget rendering, and sc_footer().

Actions and filters

Register with the SCPress helpers or the WordPress-compatible aliases:

sc_add_action('sc_head', 'my_plugin_add_markup', 10);
sc_add_filter('the_content', 'my_plugin_add_note', 20);

function my_plugin_add_markup(): void {
    echo '<meta name="my-plugin" content="enabled">';
}

function my_plugin_add_note(string $content, ?array $post = null): string {
    return $content . '<p class="my-plugin-note">A note added by my plugin.</p>';
}

Equivalent aliases include add_action, do_action, add_filter, and apply_filters. SCPress callbacks run in ascending priority order (lower number first; default 10). Actions receive the arguments passed by the caller. Filters receive the value first and must return the updated value.

The compatibility bridge currently accepts an $accepted_args parameter for add_action() and add_filter(), but SCPress's hook dispatcher does not use it to trim callback arguments. Write callbacks to accept the arguments documented for the hook and avoid relying on WordPress-only hook behavior that is not listed here.

Core action hooks

HookArgumentsRuns when
sc_before_dispatch$pathBefore a URL is routed; a callback that short-circuits routing must complete the response itself.
sc_plugins_loadednoneAfter active plugin entry files have been included.
sc_plugin_activated$pluginIdAfter a plugin is activated.
sc_plugin_deactivated$pluginIdAfter a plugin is deactivated.
sc_theme_init$activeThemeWhen the active theme settings and core widget zones are initialized.
sc_get_header$nameWhen sc_header($name) is called.
sc_get_footer$nameWhen sc_footer($name) is called.
sc_headnoneAt the frontend head extension point.
sc_footernoneAt the frontend footer extension point.
sc_admin_headnoneIn the admin document head.
sc_admin_menunoneIn the admin sidebar; callbacks may output their own menu links.
sc_admin_post_editor_tools$post, $actionIn the post editor tools area.
sc_api_center_ai_settingsnoneAt the API Center AI settings extension point.
sc_subscription_signup_fieldsnoneIn the registration form extension point.
sc_api_{endpoint}noneFor /api/{endpoint} requests; replace {endpoint} with the route name.

Core filter hooks

HookArgumentsReturn
the_content$content, sometimes $postUpdated HTML/content string.
sc_page_templates$templatesUpdated associative array of page template IDs and labels.
sc_widget_render$output, $widget, $settingsUpdated widget HTML.
sc_render_zone_output$output, $zoneId, $widgetsUpdated rendered widget-zone HTML.
sc_notification_can_send$canReceive, $userId, $type, $payloadBoolean allow/deny value.
sc_validate_user_registration$result, $_POSTResult array, normally ['valid' => bool, 'error' => string].
sc_user_registration_redirect$redirect, $userId, $_POSTRedirect URL string.

Example: register a page template label without replacing the core template system:

sc_add_filter('sc_page_templates', static function (array $templates): array {
    $templates['my-landing'] = 'My Landing Page';
    return $templates;
});

This filter adds the label to the page editor. At present ThemeManager::loadTemplate() loads files from sc-core/templates/ only, so adding a label does not give a plugin a separate template loader. For plugin-specific page output, use a shortcode, a content filter, or a route action until a plugin template API is added.

Building a plugin

Create a unique folder and entry file:

sc-content/plugins/my-plugin/
├── plugin.json
└── my-plugin.php

Minimal manifest:

{
  "name": "My Plugin",
  "slug": "my-plugin",
  "version": "1.0.0",
  "author": "Your name",
  "description": "A short description of what it adds.",
  "entry": "my-plugin.php"
}

Minimal entry file:

<?php
declare(strict_types=1);

if (!defined('SC_PATH')) {
    exit;
}

sc_add_action('sc_plugins_loaded', static function (): void {
    // Register plugin services after active plugin files are loaded.
});

Use namespaced classes or a distinctive prefix for functions, classes, database tables, option keys, routes, and asset handles. Escape output at the point of display, validate and sanitize incoming values, check permissions on admin operations, and use CSRF protection for state-changing requests. Do not assume an uploaded plugin is trusted merely because it is installed.

Template helpers

Common helpers in sc-core/helpers.php include:

sc_header();                  // Render the SCPress header and shell.
sc_footer();                  // Render the SCPress footer and shell.
sc_the_title();               // Print the current title, escaped.
sc_the_content();             // Print the filtered current page/post content.
sc_site_title();              // Return the configured site title.
sc_site_tagline();            // Return the configured site tagline.
sc_theme_url('assets/app.css'); // Resolve a theme asset URL.
sc_render_widgets('sidebar_left'); // Render a registered widget zone.
sc_render_page_footer();      // Render the configurable page footer.

the_content is applied before page content is printed. Use the current-post helpers or ThemeManager where a plugin needs the current record; do not query by a hard-coded page ID.

Data, APIs, widgets, and compatibility

Source of truth

Hook names and signatures are taken from call sites in sc-core/ and sc-admin/. They can change as the platform evolves. Before depending on a hook, verify the call site for the SCPress version you target and keep plugins compatible with the minimum version they declare.