The DRM system enforces license compliance through escalating admin notices and a full-page interstitial lock. It only affects the WordPress admin area โ front-end functionality is never impacted.
Source: libraries/slswc/client/src/DRM.php
Configuration
Pass a drm array in the $args parameter of Plugin::get_instance():
use SLSWC\Client\Plugin;
$client = Plugin::get_instance(
'https://your-license-server.com/',
__FILE__,
array(
'license_key' => get_option( 'my_license_key', '' ),
'drm' => array(
'enabled' => true,
'product_name' => 'My Plugin Pro',
'parent_menu_slug' => 'my-plugin',
'page_hook_prefix' => 'my-plugin',
'submenu_slugs' => array(
'my-plugin-settings',
'my-plugin-reports',
),
'license_page_url' => admin_url( 'admin.php?page=my-plugin-settings&tab=license' ),
'purchase_url' => 'https://your-store.com/',
'logo_url' => plugins_url( 'assets/logo.svg', __FILE__ ),
'text_domain' => 'my-plugin',
),
)
);
Configuration Options
| Key | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Enable/disable DRM enforcement |
identifier | string | '' | Unique identifier (defaults to the updater's text domain) |
product_name | string | '' | Product name shown in notices |
parent_menu_slug | string | '' | Parent admin menu slug to intercept |
page_hook_prefix | string | '' | WordPress sanitized menu title for load- hooks |
submenu_slugs | array | [] | List of submenu slugs to intercept when locked. The parent menu page is only intercepted when this list is not empty |
license_page_url | string | '' | URL to the license activation page |
purchase_url | string | '' | URL where customers can purchase a license |
logo_url | string | '' | Logo image URL for the interstitial page |
grace_soft_days | int | 2 | Days without a successful license check before grace period notices begin |
grace_lock_days | int | 7 | Days without a successful license check before the full-page lock activates |
text_domain | string | 'slswc-client' | Text domain for translations |
Configuration Precedence
The grace period values are resolved in this order, from lowest to highest priority:
- The built-in defaults (
grace_soft_days2,grace_lock_days7). - Values sent by the license server and saved with the license details during the scheduled license check.
- Values you pass in the
drmarray (only keys you set explicitly). - The
slswc_drm_config_{identifier}filter.
State Machine
The DRM operates on a state machine with four states:
[License Active] -----> 'ok' (no notices)
|
[No License] ----> 'grace_silent' (silent grace period)
|
+--> 'grace_soft' (warning notices with countdown)
|
+--> 'locked' (full-page interstitial)
"Days without license" is counted from the last successful license check (or from when the DRM first ran on a fresh install).
| State | Days Without License | Behavior |
|---|---|---|
ok | N/A (license active) | No notices. Normal operation. |
grace_silent | 0 to grace_soft_days - 1 | No "no license" notice. Grace period begins silently. |
grace_soft | grace_soft_days to grace_lock_days - 1 | Warning admin notices with countdown. |
locked | grace_lock_days and beyond | Full-page interstitial blocks access to the intercepted plugin admin pages. Admin notices still show on other pages. |
Admin Notice Escalation
Notices are shown based on the license status:
| License Status | Notice Type | Description |
|---|---|---|
active | None | License is valid |
expiring | Pre-expiry warning | License will expire soon |
expired | Expired notice | License has expired |
disabled | Disabled notice | License was revoked |
| Other (no license) | Grace period countdown | Shows days remaining. Not shown in the grace_silent state |
The expiring, expired, and disabled notices are shown regardless of the DRM state. Notices are dismissible for 24 hours. Dismissal state is stored in the {identifier}_drm_show_{flag}_notice option.
Full-Page Interstitial
When the DRM state reaches locked, any attempt to load the plugin's admin pages is intercepted. Instead of the normal page content, a full-page interstitial is rendered with:
- Product logo
- Explanation that the license has expired or is missing
- Link to the license activation page
- Link to purchase a license
- "Check License" button (AJAX) to re-verify without page reload
The interstitial intercepts page loads via load-{hook} actions for all configured submenu pages, and for the parent menu page (toplevel_page_{parent_menu_slug}) only when submenu_slugs is not empty. Use the slswc_drm_should_lock_page_{identifier} filter to narrow locking further, for example to specific screens.
Scheduled License Check
The DRM schedules a daily wp-cron job (slswc_drm_license_check_{identifier}) that:
- Sends a
check_licenserequest to the license server. - Updates the license status based on the response.
- Resets the grace period clock if the license is valid.
- Clears all notice dismissals on successful activation.
If the server is unreachable, the current state is preserved (the clock does not advance).
AJAX Handlers
Two AJAX endpoints are registered per DRM instance:
| Action | Description |
|---|---|
wp_ajax_slswc_drm_dismiss_notice_{identifier} | Dismiss a notice for 24 hours |
wp_ajax_slswc_drm_refresh_status_{identifier} | Re-check the license and return the new DRM state |
Both require a valid nonce (slswc_drm_{identifier}) and manage_options capability.
Filters
| Filter | Parameters | Description |
|---|---|---|
slswc_drm_config_{identifier} | (array $config) | Modify the DRM configuration after initialization |
slswc_drm_state_{identifier} | (string $state, DRM $drm) | Override the computed DRM state |
slswc_drm_notice_html_{identifier} | (string $html, string $state, string $license_status, DRM $drm) | Modify or replace notice HTML |
slswc_drm_should_lock_page_{identifier} | (bool $should_lock, DRM $drm) | Prevent the interstitial from showing on the current page, or narrow locking to specific screens |
slswc_drm_interstitial_html_{identifier} | (string $html, DRM $drm) | Modify or replace the interstitial HTML |
Example: Extend the Grace Period
add_filter( 'slswc_drm_config_my-plugin', function ( $config ) {
$config['grace_soft_days'] = 5;
$config['grace_lock_days'] = 14;
return $config;
} );
Example: Disable the Lock for Specific Users
add_filter( 'slswc_drm_should_lock_page_my-plugin', function ( $should_lock, $drm ) {
if ( current_user_can( 'manage_network' ) ) {
return false;
}
return $should_lock;
}, 10, 2 );
Option Keys
The DRM stores state in WordPress options using the pattern {identifier}_drm_{key}. On multisite, license_activated and last_license_check are stored as network (site) options; the notice dismissal flags are always regular options:
| Key | Description |
|---|---|
license_activated | yes or no |
last_license_check | Unix timestamp of last successful check (set to the current time on first run) |
show_no_license_notice | Dismissal expiry timestamp |
show_pre_expiry_notice | Dismissal expiry timestamp |
show_expired_notice | Dismissal expiry timestamp |
show_disabled_notice | Dismissal expiry timestamp |
Related Pages
- Client Library Overview โ Architecture and class reference
- Plugin Integration โ How to pass the
drmconfiguration toPlugin::get_instance() - Auto-Updates โ How update checks interact with license status
- Hooks Reference โ All
slswc_*actions and filters, including DRM-specific hooks