Signocore Toolkit fires WordPress filters and actions for the cookie banner, maintenance mode, login protection, security headers, social sharing, SVG sanitizing, Optimize WP, user switching and its own lifecycle. Add your callbacks from a child theme's functions.php or a must-use plugin. The PHP hook names start with signocore_toolkit_, except the share buttons filter, signocore_social_share.
A filter must return the type listed for it. For the cookie banner, the Toolkit's consent script also dispatches JavaScript events on window. They are listed in the last group, together with the format of the consent cookie.
No hooks match "".
Cookie consent
These filters run on frontend pages while Enable Cookie Consent is on under Signocore Toolkit → General. See Cookie consent and Consent Mode for how the banner and Google Consent Mode work.
Filters the consent categories shown in the preferences window.
The categories appear in the order of the array. Each one has a label and a description, both shown as plain text, and required. A required category shows Always active instead of a switch and is always stored as true.
Every category is stored in the sctk_consent cookie and sent with the sctk:consent:updated event under its key. Use lowercase letters, numbers and underscores for keys, and avoid v, ts and method, which the cookie uses for itself.
Only analytics and marketing are passed on to Google Consent Mode. Both are always stored, even if you remove them from the array: Accept All then still stores them as true and grants them in Consent Mode, and every other choice stores them as false. Change their labels rather than removing them.
Visitors who made their choice before you added a category are not asked again. Their cookie has no value for the new key, so treat a missing key as not granted.
| Parameter | Type | Description |
|---|---|---|
$categories |
array |
The categories keyed by consent key: necessary, analytics and marketing by default. Each is an array with label (string), description (string) and required (bool). |
Returns array The categories, in the order to show them.
add_filter('signocore_toolkit_consent_categories', function (array $categories): array { $categories['marketing']['label'] = 'Advertising'; $categories['chat'] = [ 'label' => 'Live chat', 'description' => 'Lets you talk to our support team through the chat widget.', 'required' => false, ]; return $categories; });
Filters the markup of the cookie consent banner.
The banner is printed in the footer of every frontend page and stays hidden until the consent script shows it to a visitor without a saved choice. The preferences window and the floating button are separate and not part of this markup.
The result passes through wp_kses() with the tags allowed in post content, plus inline svg, path and circle elements with a limited set of attributes. Anything else, such as script, input or iframe, is removed.
The consent script finds the banner and its buttons by their ids, so keep them. A button without its id does nothing.
sctk-cookie-banneron the outer element, together withstyle="display:none;", which keeps the banner hidden for visitors who have already chosen.sctk-accept-all,sctk-reject-allandsctk-customizeon the three buttons.
| Parameter | Type | Description |
|---|---|---|
$html |
string |
The banner markup, including the banner text and, when one is set, the link to your privacy policy page. |
Returns string The banner markup.
add_filter('signocore_toolkit_consent_banner_html', function (string $html): string { $link = ' <a href="' . esc_url(home_url('/cookie-policy/')) . '" class="sctk-cookie-banner__link">Cookie policy</a>'; // The banner text is the only paragraph in the markup. return str_replace('</p>', $link . '</p>', $html); });
Maintenance and coming soon
The page itself is described in Maintenance and coming soon. The template and the two page actions only run for visitors who get the maintenance or coming soon page, not for users and IP addresses that may see the normal site.
Filters the path of the template for the maintenance and coming soon page.
Receives signocore-toolkit/maintenance.php from the child theme or theme when there is one, otherwise the plugin's own template. Return the absolute path to a PHP file that exists. It is loaded with load_template(), so the file can read the page data in $args.
The status code (503 for maintenance, 200 for coming soon), the no-cache headers and X-Robots-Tag: noindex, nofollow are already sent. Your file prints the whole page: WordPress does not load the theme's header or footer, and wp_head() is not called for you. The signocore_toolkit_maintenance_head and signocore_toolkit_maintenance_footer actions only fire if your template calls them.
| Parameter | Type | Description |
|---|---|---|
$template |
string |
The absolute path to the template file. |
$args |
array |
The page data: mode (maintenance or coming_soon), headline, message (can contain basic HTML), logoUrl (empty when there is no logo), backAt (a Unix timestamp, or null), contactEmail (can be empty) and themeCss (CSS with the active theme's colors and fonts). |
Returns string The absolute path to the template file.
add_filter('signocore_toolkit_maintenance_template', function (string $template, array $args): string { // A separate launch page for coming soon mode. if ($args['mode'] === 'coming_soon') { return get_stylesheet_directory() . '/templates/coming-soon.php'; } return $template; }, 10, 2);
Fires at the end of the <head> of the maintenance and coming soon page.
The page does not call wp_head(), so scripts, styles and the site icon that WordPress normally adds are missing. Print what the page needs from this action. It fires from the plugin's own template, and from your own template only if it calls the action.
| Parameter | Type | Description |
|---|---|---|
$args |
array |
The page data, as described for signocore_toolkit_maintenance_template. |
add_action('signocore_toolkit_maintenance_head', function (array $args): void { $icon = get_site_icon_url(32); if ($icon !== '') { echo '<link rel="icon" href="' . esc_url($icon) . '" sizes="32x32">' . "\n"; } });
Fires at the end of the <body> of the maintenance and coming soon page.
Prints after the page content. It fires from the plugin's own template, and from your own template only if it calls the action.
| Parameter | Type | Description |
|---|---|---|
$args |
array |
The page data, as described for signocore_toolkit_maintenance_template. |
add_action('signocore_toolkit_maintenance_footer', function (array $args): void { if ($args['mode'] !== 'coming_soon') { return; } echo '<p class="launch-follow"><a href="https://www.instagram.com/example/">Follow us on Instagram for the launch date</a></p>'; });
Fires after the maintenance mode changes.
Fires whenever the mode is saved with a new value, from the Maintenance tab, the admin bar or code, after the Toolkit has purged the page caches it knows. Saving the same mode again does not fire it. Use it to purge a cache the Toolkit does not know, or to let someone know.
| Parameter | Type | Description |
|---|---|---|
$new |
string |
The new mode: off, maintenance or coming_soon. |
$old |
string |
The previous mode. |
add_action('signocore_toolkit_maintenance_changed', function (string $new, string $old): void { wp_mail( get_option('admin_email'), 'Maintenance mode changed', sprintf('The maintenance mode on %s changed from %s to %s.', home_url(), $old, $new) ); }, 10, 2);
Login protection
These actions fire only while login protection is on: Limit Login Attempts is ticked on Signocore Toolkit → Security and SCTK_LOGIN_PROTECTION is not set to false in wp-config.php. See Login protection.
Fires for every failed login that login protection counts.
Trusted IP addresses are never counted and always report attempt 1. Attempts made while the address is blocked do not fire the action, and neither do attempts from an address that cannot be detected. When an attempt reaches the limit, this action fires first and signocore_toolkit_login_lockout right after.
| Parameter | Type | Description |
|---|---|---|
$username |
string |
The username or email address that was tried. |
$ip |
string |
The visitor's IP address, as detected with the IP Detection setting. |
$attempt |
int |
The number of failed attempts from this address within the attempt window, this one included. |
$error |
mixed |
The error WordPress passed to wp_login_failed, usually a WP_Error. |
add_action('signocore_toolkit_login_failed', function (string $username, string $ip, int $attempt): void { if ($attempt === 3) { error_log(sprintf('Third failed login from %s, last tried as "%s".', $ip, $username)); } }, 10, 3);
Fires when an IP address is blocked after too many failed logins.
Fires once per block, right after the attempt that reached the limit, and before the Toolkit sends its own email when Email Notifications is on.
| Parameter | Type | Description |
|---|---|---|
$ip |
string |
The blocked IP address. |
$username |
string |
The username or email address of the attempt that caused the block. |
$expiry |
int |
When the block ends, as a Unix timestamp. |
add_action('signocore_toolkit_login_lockout', function (string $ip, string $username, int $expiry): void { error_log(sprintf( 'Blocked %s until %s after failed logins as "%s".', $ip, wp_date('Y-m-d H:i', $expiry), $username )); }, 10, 3);
Security headers
See Security headers for the headers the Toolkit sends and what the default policy blocks.
Filters the security headers sent with frontend pages.
Runs on frontend pages just before WordPress sends the headers, after every other wp_headers callback. The array holds the Content-Security-Policy, Strict-Transport-Security and Referrer-Policy headers the page carries, keyed by header name: the Toolkit's defaults, or a value that another callback set first. Strict-Transport-Security is only there on HTTPS sites.
Change a value to adjust a header. Return an empty value, or unset the key, to drop a header. A key you add is sent as another header. The admin area, the login page and REST API responses do not run the filter.
| Parameter | Type | Description |
|---|---|---|
$headers |
array |
Header values keyed by header name. |
Returns array The headers to send, keyed by header name.
add_filter('signocore_toolkit_security_headers', function (array $headers): array { // Google Tag Manager's Custom JavaScript variables need eval(). if (isset($headers['Content-Security-Policy'])) { $headers['Content-Security-Policy'] = str_replace( "script-src 'self' https: 'unsafe-inline'", "script-src 'self' https: 'unsafe-inline' 'unsafe-eval'", $headers['Content-Security-Policy'] ); } // HSTS for this domain only, not its subdomains. if (isset($headers['Strict-Transport-Security'])) { $headers['Strict-Transport-Security'] = 'max-age=31536000'; } return $headers; });
Social sharing
signocore_social_share
filterFilters the markup of the share buttons for a post.
The buttons are printed after the content of single posts and pages on Kadence and Signocore Slate, and in WooCommerce's share area on product pages, for the post types ticked under Signocore Toolkit → Social → Add social sharing. The markup is printed as it is, without further filtering.
The filtered markup is cached per post for a month. Saving the post clears the cache, so save a post to see a change to your callback. Return an empty string to show no buttons for a post.
| Parameter | Type | Description |
|---|---|---|
$html |
string |
The share buttons: a div.social-share with a label and links for Facebook, X, LinkedIn, Pinterest (when the post has a featured image) and email. |
$post |
WP_Post |
The post being shared. |
Returns string The share buttons markup, or an empty string for none.
add_filter('signocore_social_share', function (string $html, WP_Post $post): string { // No share buttons on posts in the Announcements category. if (has_category('announcements', $post)) { return ''; } return $html; }, 10, 2);
SVG files and database cleanup
See Performance and media for Sanitize existing SVG files and Optimize WP.
Filters how many SVG files Sanitize existing SVG files cleans per request.
Each batch runs in one request, and the Toolkit moves on to the next batch by itself. Lower the number on hosts with a short PHP time limit, or raise it to get through a large media library in fewer requests. Values below 1 count as 1.
| Parameter | Type | Description |
|---|---|---|
$batchSize |
int |
The number of files per batch. Default 100. |
Returns int The number of files per batch.
add_filter('signocore_toolkit_svg_sanitize_batch', function (int $batchSize): int { return 25; });
Fires after Sanitize existing SVG files has worked through the whole media library.
Fires once, at the end of the last batch, before the redirect back to the Security tab.
| Parameter | Type | Description |
|---|---|---|
$stats |
array |
The result: sanitized (int), the number of files cleaned; failed (string[]), the names of files that could not be made safe, at most 50; missing (int), the number of attachments without a file on disk. |
add_action('signocore_toolkit_svg_sanitized', function (array $stats): void { if ($stats['failed'] === []) { return; } wp_mail( get_option('admin_email'), 'SVG files to review', "These SVG files could not be sanitized:\n\n" . implode("\n", $stats['failed']) ); });
signocore_toolkit_optimized
actionFires after Optimize WP has cleaned the database and cleared the caches.
Fires in the request started from the admin bar, just before the redirect back to the screen you came from.
| Parameter | Type | Description |
|---|---|---|
$stats |
array |
What was done, as numbers: revisions_deleted, spam_deleted, transients_deleted, tables_optimized and orphaned_meta_deleted. |
add_action('signocore_toolkit_optimized', function (array $stats): void { error_log(sprintf( 'Optimize WP removed %d revisions, %d spam comments, %d transients and %d orphaned metadata rows, and optimized %d tables.', $stats['revisions_deleted'], $stats['spam_deleted'], $stats['transients_deleted'], $stats['orphaned_meta_deleted'], $stats['tables_optimized'] )); });
User switching
See User switching. The activity log records both switches through these actions.
Fires after an administrator switches to another user.
Fires once the new login is in place and before the redirect to the user's profile, so get_current_user_id() already returns the user switched to.
| Parameter | Type | Description |
|---|---|---|
$targetId |
int |
The ID of the user switched to. |
$currentUserId |
int |
The ID of the administrator who switched. |
add_action('signocore_toolkit_user_switched', function (int $targetId, int $currentUserId): void { error_log(sprintf('User %d switched to user %d.', $currentUserId, $targetId)); }, 10, 2);
Fires after an administrator switches back to their own account.
Fires once the administrator is logged in again and before the redirect, so get_current_user_id() already returns the administrator.
| Parameter | Type | Description |
|---|---|---|
$originalId |
int |
The ID of the administrator. |
$fromId |
int |
The ID of the user switched back from. |
add_action('signocore_toolkit_user_switched_back', function (int $originalId, int $fromId): void { error_log(sprintf('User %d switched back from user %d.', $originalId, $fromId)); }, 10, 2);
Plugin lifecycle
These actions fire while Signocore Toolkit is being activated, deactivated, updated or deleted. Listen from your theme or from another plugin, which are loaded in those requests.
signocore_toolkit_activated
actionFires after Signocore Toolkit is activated.
Fires in the request that activates the plugin, after it has prepared its settings and database tables and cleared its cached data. Network activation on a multisite network fires it once for each site.
add_action('signocore_toolkit_activated', function (): void { // A cached footer that uses the Toolkit's shortcodes needs to be built again. delete_transient('mytheme_footer_html'); });
Fires after Signocore Toolkit is deactivated.
Fires after the plugin has cleared its cached data and its scheduled tasks. Settings and logs are kept. Network deactivation on a multisite network fires it once for each site.
add_action('signocore_toolkit_deactivated', function (): void { delete_transient('mytheme_footer_html'); });
signocore_toolkit_upgraded
actionFires once after Signocore Toolkit is updated to a new version.
Fires on the first request after an update, once the plugin's own upgrade routine has updated its settings and tables, cleared its cached data and refreshed the rewrite rules. A fresh installation does not fire it.
| Parameter | Type | Description |
|---|---|---|
$oldVersion |
string |
The version that was installed before. |
$newVersion |
string |
The version now installed. |
add_action('signocore_toolkit_upgraded', function (string $oldVersion, string $newVersion): void { if (version_compare($oldVersion, '4.0.0', '<')) { // Runs once when a site moves from 3.x to 4.x. delete_transient('mytheme_footer_html'); } }, 10, 2);
Fires after Signocore Toolkit has removed all its data on uninstall.
Fires when the plugin is deleted from the Plugins screen, after its settings, database tables, stored user data and scheduled tasks are gone. The plugin is inactive by then, so only code in your theme, a must-use plugin or another active plugin can listen. Use it to remove data of your own that only made sense with the Toolkit.
add_action('signocore_toolkit_uninstalled', function (): void { delete_option('mytheme_consent_script_ids'); });
JavaScript consent events
The consent script dispatches these events on window as CustomEvent objects, while Enable Cookie Consent is on. Listen with window.addEventListener() in a script that loads on every frontend page. Add the listener at the top level of your script, not inside a DOMContentLoaded handler, so it is in place before the banner appears.
Read the choice of a returning visitor
No event fires when a page loads. A visitor who chose on an earlier visit only has the consent cookie, so read it yourself. The cookie is named sctk_consent, applies to the whole site and holds URL-encoded JSON:
{"v":1,"ts":1759050000,"analytics":true,"marketing":false,"necessary":true,"method":"custom"}
v: the format version, currently1.ts: when the choice was made, as a Unix timestamp in seconds.- One
trueorfalsevalue per category:analyticsandmarketingalways, and for choices made in the banner or the preferences window alsonecessaryand every category added with signocore_toolkit_consent_categories. method: how the choice was made.accept_all,reject_all,customfor Save Preferences,dismisswhen the preferences window was closed before any choice, which counts as rejecting, orlegacy_migrationwhen a cookie from an older version was converted.
Treat a missing cookie or a missing key as not granted. This helper returns the stored choice, or null:
function sctkConsent() { const match = document.cookie.match(/(?:^|;\s*)sctk_consent=([^;]*)/); if (!match) { return null; } try { return JSON.parse(decodeURIComponent(match[1])); } catch (error) { return null; } }
Read the cookie in the browser, not in PHP. A page cache serves the same HTML to every visitor, whatever they chose.
Reopen the preferences window
For a "Cookie settings" link, for example in your footer, have your code click the floating preferences button, #sctk-cookie-floating. It is on every page while Show Preferences Button is on, even when it is hidden. With that setting off, click the banner's Customize button, #sctk-customize, instead, which is on every page too.
document.addEventListener('click', (event) => { if (!event.target.closest('a[href="#cookie-settings"]')) { return; } event.preventDefault(); const button = document.getElementById('sctk-cookie-floating') || document.getElementById('sctk-customize'); if (button) { button.click(); } });
sctk:consent:updated
eventFires when a visitor makes or changes a consent choice.
Fires after the cookie is saved and the Consent Mode update is sent: on Accept All and Reject All, in the banner or the preferences window, on Save Preferences, and when the preferences window is closed before any choice was made. It also fires once when a cookie from an older version is converted. It does not fire on later page views. event.detail holds the same data as the cookie.
| Parameter | Type | Description |
|---|---|---|
detail.v |
number |
The format version, currently 1. |
detail.ts |
number |
When the choice was made, as a Unix timestamp in seconds. |
detail.analytics |
boolean |
Consent for the Analytics category. |
detail.marketing |
boolean |
Consent for the Marketing category. |
detail.necessary |
boolean |
Always true. |
detail.{key} |
boolean |
One value for each category added with signocore_toolkit_consent_categories. Required categories are always true. |
detail.method |
string |
accept_all, reject_all, custom, dismiss or legacy_migration. |
// Load a chat widget once the visitor agrees to the "chat" category. // sctkConsent() is the cookie reader shown at the start of this section. let chatLoaded = false; function loadChat() { if (chatLoaded) { return; } chatLoaded = true; const script = document.createElement('script'); script.src = 'https://chat.example.com/widget.js'; script.async = true; document.head.appendChild(script); } // A returning visitor who agreed on an earlier visit. if (sctkConsent()?.chat === true) { loadChat(); } // A choice made on this page. window.addEventListener('sctk:consent:updated', (event) => { if (event.detail.chat === true) { loadChat(); } });
Fires when the consent banner appears.
Fires once the page has loaded, for visitors without a saved choice. event.detail is an empty object.
window.addEventListener('sctk:consent:banner:shown', () => { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'consent_banner_shown' }); });
Fires when the preferences window opens.
Fires when a visitor clicks Customize in the banner or the floating preferences button, or when your own code clicks one of them. event.detail is an empty object.
// Mark the page while the window is open, for example to hide a chat bubble that would cover it. window.addEventListener('sctk:consent:preferences:opened', () => { document.body.classList.add('consent-window-open'); });
Fires when the preferences window closes.
Fires on Save Preferences and Accept All in the window, on the close button, on a click on the backdrop and on the Escape key. It also fires when a visitor clicks Accept All or Reject All in the banner, even though the window was not open, so do not expect a matching sctk:consent:preferences:opened event. When a choice is saved, sctk:consent:updated fires first. event.detail is an empty object.
window.addEventListener('sctk:consent:preferences:closed', () => { document.body.classList.remove('consent-window-open'); });