RIACO Content Protector

Description

RIACO Content Protector allows you to protect any part of your WordPress content using a shortcode.
Unlike the built-in post password protection, this plugin protects only what you wrap, not the whole post.

Perfect for:

  • Protecting premium blocks of content
  • Protecting guides, downloads, links, or sensitive sections
  • Paywall-style snippets

Features

  • Protect only specific content inside posts/pages
  • Uses a minimal shortcode:
    [riaco_content_protector] Hidden text here [/riaco_content_protector]
  • Global password stored in plain text, like WordPress page passwords.
  • AJAX-based form — no page reload
  • Unlocks all protected sections on the site after correct password
  • Optional cookie persistence (remember unlocked content for a configurable number of days)
  • Secure implementation using nonces, hashed tokens, and transients
  • Developer-friendly: filters, actions, and JS custom events at every key point in the unlock flow

Important:

  • The global password is stored in plain text, just like WordPress page passwords. It can be read by user with ‘manage_options’ ability.
  • If the global password or “Remember Unlocked” duration is changed in settings, all existing unlock cookies are invalidated. Users will need to re-enter the new password to access protected content.

How It Works

Wrap content you want to protect:

[riaco_content_protector]
This text will be hidden until the visitor enters the password.
[/riaco_content_protector]

Set the global password under:

Settings > Content Protector

Visitors will see a modern, styled form.
After entering the correct password:

  • The content unlocks immediately
  • All other protected areas unlock automatically
  • An optional cookie can keep everything unlocked for a chosen number of days

Security

  • Nonces on every request
  • Secure HMAC token for cookie authentication
  • Sanitized shortcode attributes
  • Escaped output
  • No sensitive data stored in cookies
  • Global password stored in plain text, like WordPress page passwords.

Cookie

The default cookie name is riaco_cp_unlocked_global. The scope suffix global can be changed via the riaco_cp_cookie_scope filter, which also changes the cookie name accordingly.

Style

You can style the content protector box.

It has this class: .riaco-cp--container, so you can add in your style.css:

.riaco-cp--container {
    background: #f8f9fa;
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 6px;
}

You can replace button classes using:

add_filter( 'riaco_cp_button_classes', function( $classes ) {
    return 'button my-custom-button-class';
});

Or you can remove button classes:

add_filter( 'riaco_cp_button_classes', function( $classes ) {
    return str_replace( 'wp-element-button', '', $classes );
});

For Developers

The plugin exposes actions and filters at every key point so you can extend or customise behaviour without modifying plugin files.

Filters

riaco_cp_default_options — modify the option defaults written on first activation.


add_filter( 'riaco_cp_default_options', function( $defaults ) {
    $defaults['remember_days'] = 30;
    return $defaults;
});


riaco_cp_instance_id — override the computed instance ID for a protected block.


add_filter( 'riaco_cp_instance_id', function( $id, $atts, $content ) {
    return ! empty( $atts['id'] ) ? 'my_prefix_' . $atts['id'] : $id;
}, 10, 3 );


riaco_cp_transient_expiry — change how long protected content is cached (default: `DAY_IN_SECONDS`).


add_filter( 'riaco_cp_transient_expiry', function( $seconds, $instance_id ) {
    return HOUR_IN_SECONDS * 6;
}, 10, 2 );


riaco_cp_lock_message — customise the message shown above the password field.


add_filter( 'riaco_cp_lock_message', function( $message, $instance_id ) {
    return __( 'Members only. Enter your access code:', 'my-theme' );
}, 10, 2 );


riaco_cp_unlocked_content — wrap or replace the HTML around unlocked content.


add_filter( 'riaco_cp_unlocked_content', function( $html, $content, $instance_id ) {
    return '<div class="my-unlocked-wrapper">' . $content . '</div>';
}, 10, 3 );


riaco_cp_cookie_args — modify the options passed to `setcookie()`.


add_filter( 'riaco_cp_cookie_args', function( $args, $cookie_name ) {
    $args['samesite'] = 'Strict';
    return $args;
}, 10, 2 );


riaco_cp_max_attempts — change the brute-force rate-limit threshold (default: 5).


add_filter( 'riaco_cp_max_attempts', function( $max ) {
    return 10;
});


riaco_cp_rate_limit_window — change the rate-limit window in seconds (default: 15 minutes).


add_filter( 'riaco_cp_rate_limit_window', function( $seconds ) {
    return 5 * MINUTE_IN_SECONDS;
});


riaco_cp_content_access — override the cookie check before protected content is rendered. Return `null` to use the default cookie check, `true` to force-grant access, or `false` to force the lock form.


add_filter( 'riaco_cp_content_access', function( $access, $instance_id, $atts ) {
    // Grant access to logged-in users automatically.
    if ( is_user_logged_in() ) {
        return true;
    }
    return null; // Fall through to cookie check for guests.
}, 10, 3 );


riaco_cp_locked_html — replace or wrap the entire locked form HTML.


add_filter( 'riaco_cp_locked_html', function( $html, $instance_id, $atts ) {
    return '<div class="my-paywall">Subscribe to see this content.</div>';
}, 10, 3 );


riaco_cp_validate_password — override password validation. Return `null` to use the default `hash_equals` check, `true` to grant access, or `false` to deny. When non-null, the global password is not consulted.


add_filter( 'riaco_cp_validate_password', function( $result, $user_pass, $instance_id ) {
    // Accept a secondary password for a specific block.
    if ( $instance_id === 'riaco_cp_vip' && $user_pass === 'vip-secret' ) {
        return true;
    }
    return null; // Defer to global password check.
}, 10, 3 );


riaco_cp_rate_limit_bypass — bypass the brute-force counter for trusted IP addresses.


add_filter( 'riaco_cp_rate_limit_bypass', function( $bypass, $ip_address ) {
    $trusted = array( '127.0.0.1', '192.168.1.100' );
    return in_array( $ip_address, $trusted, true );
}, 10, 2 );


riaco_cp_cookie_scope — change the cookie scope suffix (default: `'global'`). The cookie name becomes `riaco_cp_unlocked_{scope}`. Use this to implement per-post or per-instance independent lock states.


add_filter( 'riaco_cp_cookie_scope', function( $scope, $instance_id ) {
    // Use a per-post cookie so each post unlocks independently.
    return 'post_' . get_the_ID();
}, 10, 2 );


riaco_cp_ajax_response — inject additional fields into the JSON success response sent after unlock.


add_filter( 'riaco_cp_ajax_response', function( $data, $instance_id ) {
    $data['redirect'] = home_url( '/members/' );
    return $data;
}, 10, 2 );


riaco_cp_js_data — add extra data to the `RIACO_CP_Ajax` JavaScript object.


add_filter( 'riaco_cp_js_data', function( $data ) {
    $data['my_feature_enabled'] = true;
    return $data;
});

Actions

riaco_cp_loaded — fires after the plugin initialises; receives the `Plugin` instance.


add_action( 'riaco_cp_loaded', function( $plugin ) {
    // $plugin->settings, $plugin->shortcode, etc. are available here.
});


riaco_cp_form_fields — inject extra hidden inputs or markup inside the password form.


add_action( 'riaco_cp_form_fields', function( $instance_id ) {
    echo '<input type="hidden" name="my_field" value="' . esc_attr( $instance_id ) . '">';
});


riaco_cp_password_correct — fires when a visitor enters the correct password.


add_action( 'riaco_cp_password_correct', function( $instance_id ) {
    // Log unlock event, fire analytics, etc.
});


riaco_cp_password_incorrect — fires on a failed attempt; `$attempts` is the running total for this IP.


add_action( 'riaco_cp_password_incorrect', function( $instance_id, $attempts ) {
    if ( $attempts >= 3 ) {
        // Send alert, block IP in your firewall, etc.
    }
}, 10, 2 );


riaco_cp_cookie_set — fires after the unlock cookie is written.


add_action( 'riaco_cp_cookie_set', function( $cookie_name, $expire ) {
    // Record unlock timestamp, sync to a log, etc.
}, 10, 2 );


riaco_cp_settings_page_after — fires after the free plugin's settings form. Use it to render your own `<form>` block with additional settings fields below the existing form.


add_action( 'riaco_cp_settings_page_after', function() {
    // Render a separate settings form here.
});

JavaScript Custom Events

The plugin triggers jQuery custom events on the .riaco-cp--container element at key moments. Listen from any JavaScript file that loads after the plugin’s script.

riaco_cp:before_submit — fires just before the AJAX password call. Call `e.preventDefault()` to cancel the submission (the UI is restored automatically).


$(document).on('riaco_cp:before_submit', '.riaco-cp--container', function(e, instanceId, password) {
    // e.preventDefault(); // uncomment to cancel submission
});


riaco_cp:unlock_success — fires after a successful AJAX response, before the container is replaced with unlocked content.


$(document).on('riaco_cp:unlock_success', '.riaco-cp--container', function(e, instanceId, responseData) {
    console.log('Unlocked:', instanceId, responseData);
});


riaco_cp:unlock_error — fires on wrong password, rate-limit hit, or network/server failure.


$(document).on('riaco_cp:unlock_error', '.riaco-cp--container', function(e, instanceId, errorMessage) {
    console.warn('Unlock failed for', instanceId, errorMessage);
});

License

This plugin is licensed under GPLv2 or later.

Screenshots

Installation

  1. Upload the plugin folder to /wp-content/plugins/
  2. Activate the plugin through Plugins > Installed Plugins
  3. Go to Settings > Content Protector and configure your global password
  4. Add the shortcode to any post or page

    [riaco_content_protector] This is hidden. [/riaco_content_protector]

FAQ

Can I protect multiple sections on the same page?

Yes. All instances use the same global password and unlock together.

Does this protect the entire post?

No — only the content wrapped in the shortcode.

Are passwords hashed in the database?

No, the global password is stored in plain text like WordPress page passwords for easy admin management.

Does this work with Gutenberg / block editor?

Yes. It works in both Classic and Block Editor.
You can insert the shortcode inside Paragraph block or using Shortcode block.

What happens when I change the global password?

All previously unlocked content cookies are invalidated. Users must re-enter the new password.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“RIACO Content Protector” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

1.1.0

  • Added riaco_cp_content_access filter to override the cookie check (role/subscription bypass)
  • Added riaco_cp_locked_html filter to replace the full locked-form HTML
  • Added riaco_cp_validate_password filter for custom password validation (per-post passwords, etc.)
  • Added riaco_cp_rate_limit_bypass filter to exempt trusted IPs from the brute-force counter
  • Added riaco_cp_cookie_scope filter for per-post or per-instance independent lock state
  • Added riaco_cp_ajax_response filter to inject extra fields into the unlock JSON response
  • Added riaco_cp_js_data filter to extend the RIACO_CP_Ajax JavaScript object
  • Added riaco_cp_settings_page_after action for extensions to add their own settings form
  • Added JS custom events: riaco_cp:before_submit, riaco_cp:unlock_success, riaco_cp:unlock_error
  • Added admin footer review prompt on the settings page

1.0.0

  • Initial release
  • Shortcode protection
  • Global password
  • AJAX unlock
  • Cookie remember feature
  • Automatic unlock of all instances