{"id":263578,"date":"2025-12-04T20:52:59","date_gmt":"2025-12-04T20:52:59","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/riaco-content-protector\/"},"modified":"2026-06-12T07:26:50","modified_gmt":"2026-06-12T07:26:50","slug":"riaco-content-protector","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/riaco-content-protector\/","author":8872771,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.0","stable_tag":"1.1.0","tested":"7.0","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"RIACO Content Protector","header_author":"Roberto Iacono","header_description":"Protect portions of WordPress content with passwords using a shortcode. Supports global and per-shortcode passwords.","assets_banners_color":"fefefe","last_updated":"2026-06-12 07:26:50","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":291,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"prototipo88","date":"2025-12-04 20:52:25"},"1.1.0":{"tag":"1.1.0","author":"prototipo88","date":"2026-06-12 07:26:50"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3411405,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3411405,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3411405,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3411405,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3411405,"resolution":"1","location":"assets","locale":"","width":1200,"height":675},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3411405,"resolution":"2","location":"assets","locale":"","width":1366,"height":641},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3411405,"resolution":"3","location":"assets","locale":"","width":1366,"height":641}},"screenshots":{"1":"Frontend password form","2":"Protected content example","3":"Settings page with global password option"}},"plugin_section":[],"plugin_tags":[18193,5237,1930,24604,17051],"plugin_category":[54],"plugin_contributors":[238641],"plugin_business_model":[],"class_list":["post-263578","plugin","type-plugin","status-publish","hentry","plugin_tags-content-protection","plugin_tags-hide-content","plugin_tags-password","plugin_tags-password-protection","plugin_tags-restrict-content","plugin_category-security-and-spam-protection","plugin_contributors-prototipo88","plugin_committers-prototipo88"],"banners":{"banner":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/banner-772x250.png?rev=3411405","banner_2x":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/banner-1544x500.png?rev=3411405","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/icon-128x128.png?rev=3411405","icon_2x":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/icon-256x256.png?rev=3411405","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/screenshot-1.png?rev=3411405","caption":"Frontend password form"},{"src":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/screenshot-2.png?rev=3411405","caption":"Protected content example"},{"src":"https:\/\/ps.w.org\/riaco-content-protector\/assets\/screenshot-3.png?rev=3411405","caption":"Settings page with global password option"}],"raw_content":"<!--section=description-->\n<p><strong>RIACO Content Protector<\/strong> allows you to protect <em>any part<\/em> of your WordPress content using a shortcode.<br \/>\nUnlike the built-in post password protection, this plugin protects only what you wrap, <em>not the whole post<\/em>.<\/p>\n\n<p>Perfect for:<\/p>\n\n<ul>\n<li>Protecting premium blocks of content  <\/li>\n<li>Protecting guides, downloads, links, or sensitive sections   <\/li>\n<li>Paywall-style snippets<\/li>\n<\/ul>\n\n<h3>Features<\/h3>\n\n<ul>\n<li>Protect <strong>only specific content<\/strong> inside posts\/pages<\/li>\n<li>Uses a minimal shortcode:\n  [riaco_content_protector] Hidden text here [\/riaco_content_protector]<\/li>\n<li><strong>Global password<\/strong> stored in plain text, like WordPress page passwords.<\/li>\n<li>AJAX-based form \u2014 no page reload<\/li>\n<li>Unlocks <strong>all protected sections<\/strong> on the site after correct password<\/li>\n<li>Optional cookie persistence (remember unlocked content for a configurable number of days)<\/li>\n<li>Secure implementation using nonces, hashed tokens, and transients<\/li>\n<li>Developer-friendly: filters, actions, and JS custom events at every key point in the unlock flow<\/li>\n<\/ul>\n\n<p><strong>Important:<\/strong><\/p>\n\n<ul>\n<li>The global password is stored in plain text, just like WordPress page passwords. It can be read by user with 'manage_options' ability.<\/li>\n<li>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.<\/li>\n<\/ul>\n\n<h3>How It Works<\/h3>\n\n<p>Wrap content you want to protect:<\/p>\n\n<pre><code>[riaco_content_protector]\nThis text will be hidden until the visitor enters the password.\n[\/riaco_content_protector]\n<\/code><\/pre>\n\n<p>Set the global password under:<\/p>\n\n<p><strong>Settings &gt; Content Protector<\/strong><\/p>\n\n<p>Visitors will see a modern, styled form.<br \/>\nAfter entering the correct password:<\/p>\n\n<ul>\n<li>The content unlocks immediately  <\/li>\n<li>All other protected areas unlock automatically  <\/li>\n<li>An optional cookie can keep everything unlocked for a chosen number of days  <\/li>\n<\/ul>\n\n<h3>Security<\/h3>\n\n<ul>\n<li>Nonces on every request  <\/li>\n<li>Secure HMAC token for cookie authentication  <\/li>\n<li>Sanitized shortcode attributes  <\/li>\n<li>Escaped output  <\/li>\n<li>No sensitive data stored in cookies  <\/li>\n<li>Global password stored in plain text, like WordPress page passwords. <\/li>\n<\/ul>\n\n<h3>Cookie<\/h3>\n\n<p>The default cookie name is <code>riaco_cp_unlocked_global<\/code>. The scope suffix <code>global<\/code> can be changed via the <code>riaco_cp_cookie_scope<\/code> filter, which also changes the cookie name accordingly.<\/p>\n\n<h3>Style<\/h3>\n\n<p>You can style the content protector box.<\/p>\n\n<p>It has this class: <code>.riaco-cp--container<\/code>, so you can add in your <code>style.css<\/code>:<\/p>\n\n<pre><code>.riaco-cp--container {\n    background: #f8f9fa;\n    padding: 20px;\n    border: 1px solid #ddd;\n    border-radius: 6px;\n}\n<\/code><\/pre>\n\n<p>You can replace button classes using:<\/p>\n\n<pre><code>add_filter( 'riaco_cp_button_classes', function( $classes ) {\n    return 'button my-custom-button-class';\n});\n<\/code><\/pre>\n\n<p>Or you can remove button classes:<\/p>\n\n<pre><code>add_filter( 'riaco_cp_button_classes', function( $classes ) {\n    return str_replace( 'wp-element-button', '', $classes );\n});\n<\/code><\/pre>\n\n<h3>For Developers<\/h3>\n\n<p>The plugin exposes actions and filters at every key point so you can extend or customise behaviour without modifying plugin files.<\/p>\n\n<p><strong>Filters<\/strong><\/p>\n\n<pre><code>riaco_cp_default_options \u2014 modify the option defaults written on first activation.\n\n\nadd_filter( 'riaco_cp_default_options', function( $defaults ) {\n    $defaults['remember_days'] = 30;\n    return $defaults;\n});\n\n\nriaco_cp_instance_id \u2014 override the computed instance ID for a protected block.\n\n\nadd_filter( 'riaco_cp_instance_id', function( $id, $atts, $content ) {\n    return ! empty( $atts['id'] ) ? 'my_prefix_' . $atts['id'] : $id;\n}, 10, 3 );\n\n\nriaco_cp_transient_expiry \u2014 change how long protected content is cached (default: `DAY_IN_SECONDS`).\n\n\nadd_filter( 'riaco_cp_transient_expiry', function( $seconds, $instance_id ) {\n    return HOUR_IN_SECONDS * 6;\n}, 10, 2 );\n\n\nriaco_cp_lock_message \u2014 customise the message shown above the password field.\n\n\nadd_filter( 'riaco_cp_lock_message', function( $message, $instance_id ) {\n    return __( 'Members only. Enter your access code:', 'my-theme' );\n}, 10, 2 );\n\n\nriaco_cp_unlocked_content \u2014 wrap or replace the HTML around unlocked content.\n\n\nadd_filter( 'riaco_cp_unlocked_content', function( $html, $content, $instance_id ) {\n    return '&lt;div class=\"my-unlocked-wrapper\"&gt;' . $content . '&lt;\/div&gt;';\n}, 10, 3 );\n\n\nriaco_cp_cookie_args \u2014 modify the options passed to `setcookie()`.\n\n\nadd_filter( 'riaco_cp_cookie_args', function( $args, $cookie_name ) {\n    $args['samesite'] = 'Strict';\n    return $args;\n}, 10, 2 );\n\n\nriaco_cp_max_attempts \u2014 change the brute-force rate-limit threshold (default: 5).\n\n\nadd_filter( 'riaco_cp_max_attempts', function( $max ) {\n    return 10;\n});\n\n\nriaco_cp_rate_limit_window \u2014 change the rate-limit window in seconds (default: 15 minutes).\n\n\nadd_filter( 'riaco_cp_rate_limit_window', function( $seconds ) {\n    return 5 * MINUTE_IN_SECONDS;\n});\n\n\nriaco_cp_content_access \u2014 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.\n\n\nadd_filter( 'riaco_cp_content_access', function( $access, $instance_id, $atts ) {\n    \/\/ Grant access to logged-in users automatically.\n    if ( is_user_logged_in() ) {\n        return true;\n    }\n    return null; \/\/ Fall through to cookie check for guests.\n}, 10, 3 );\n\n\nriaco_cp_locked_html \u2014 replace or wrap the entire locked form HTML.\n\n\nadd_filter( 'riaco_cp_locked_html', function( $html, $instance_id, $atts ) {\n    return '&lt;div class=\"my-paywall\"&gt;Subscribe to see this content.&lt;\/div&gt;';\n}, 10, 3 );\n\n\nriaco_cp_validate_password \u2014 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.\n\n\nadd_filter( 'riaco_cp_validate_password', function( $result, $user_pass, $instance_id ) {\n    \/\/ Accept a secondary password for a specific block.\n    if ( $instance_id === 'riaco_cp_vip' &amp;&amp; $user_pass === 'vip-secret' ) {\n        return true;\n    }\n    return null; \/\/ Defer to global password check.\n}, 10, 3 );\n\n\nriaco_cp_rate_limit_bypass \u2014 bypass the brute-force counter for trusted IP addresses.\n\n\nadd_filter( 'riaco_cp_rate_limit_bypass', function( $bypass, $ip_address ) {\n    $trusted = array( '127.0.0.1', '192.168.1.100' );\n    return in_array( $ip_address, $trusted, true );\n}, 10, 2 );\n\n\nriaco_cp_cookie_scope \u2014 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.\n\n\nadd_filter( 'riaco_cp_cookie_scope', function( $scope, $instance_id ) {\n    \/\/ Use a per-post cookie so each post unlocks independently.\n    return 'post_' . get_the_ID();\n}, 10, 2 );\n\n\nriaco_cp_ajax_response \u2014 inject additional fields into the JSON success response sent after unlock.\n\n\nadd_filter( 'riaco_cp_ajax_response', function( $data, $instance_id ) {\n    $data['redirect'] = home_url( '\/members\/' );\n    return $data;\n}, 10, 2 );\n\n\nriaco_cp_js_data \u2014 add extra data to the `RIACO_CP_Ajax` JavaScript object.\n\n\nadd_filter( 'riaco_cp_js_data', function( $data ) {\n    $data['my_feature_enabled'] = true;\n    return $data;\n});\n<\/code><\/pre>\n\n<p><strong>Actions<\/strong><\/p>\n\n<pre><code>riaco_cp_loaded \u2014 fires after the plugin initialises; receives the `Plugin` instance.\n\n\nadd_action( 'riaco_cp_loaded', function( $plugin ) {\n    \/\/ $plugin-&gt;settings, $plugin-&gt;shortcode, etc. are available here.\n});\n\n\nriaco_cp_form_fields \u2014 inject extra hidden inputs or markup inside the password form.\n\n\nadd_action( 'riaco_cp_form_fields', function( $instance_id ) {\n    echo '&lt;input type=\"hidden\" name=\"my_field\" value=\"' . esc_attr( $instance_id ) . '\"&gt;';\n});\n\n\nriaco_cp_password_correct \u2014 fires when a visitor enters the correct password.\n\n\nadd_action( 'riaco_cp_password_correct', function( $instance_id ) {\n    \/\/ Log unlock event, fire analytics, etc.\n});\n\n\nriaco_cp_password_incorrect \u2014 fires on a failed attempt; `$attempts` is the running total for this IP.\n\n\nadd_action( 'riaco_cp_password_incorrect', function( $instance_id, $attempts ) {\n    if ( $attempts &gt;= 3 ) {\n        \/\/ Send alert, block IP in your firewall, etc.\n    }\n}, 10, 2 );\n\n\nriaco_cp_cookie_set \u2014 fires after the unlock cookie is written.\n\n\nadd_action( 'riaco_cp_cookie_set', function( $cookie_name, $expire ) {\n    \/\/ Record unlock timestamp, sync to a log, etc.\n}, 10, 2 );\n\n\nriaco_cp_settings_page_after \u2014 fires after the free plugin's settings form. Use it to render your own `&lt;form&gt;` block with additional settings fields below the existing form.\n\n\nadd_action( 'riaco_cp_settings_page_after', function() {\n    \/\/ Render a separate settings form here.\n});\n<\/code><\/pre>\n\n<p><strong>JavaScript Custom Events<\/strong><\/p>\n\n<p>The plugin triggers jQuery custom events on the <code>.riaco-cp--container<\/code> element at key moments. Listen from any JavaScript file that loads after the plugin's script.<\/p>\n\n<pre><code>riaco_cp:before_submit \u2014 fires just before the AJAX password call. Call `e.preventDefault()` to cancel the submission (the UI is restored automatically).\n\n\n$(document).on('riaco_cp:before_submit', '.riaco-cp--container', function(e, instanceId, password) {\n    \/\/ e.preventDefault(); \/\/ uncomment to cancel submission\n});\n\n\nriaco_cp:unlock_success \u2014 fires after a successful AJAX response, before the container is replaced with unlocked content.\n\n\n$(document).on('riaco_cp:unlock_success', '.riaco-cp--container', function(e, instanceId, responseData) {\n    console.log('Unlocked:', instanceId, responseData);\n});\n\n\nriaco_cp:unlock_error \u2014 fires on wrong password, rate-limit hit, or network\/server failure.\n\n\n$(document).on('riaco_cp:unlock_error', '.riaco-cp--container', function(e, instanceId, errorMessage) {\n    console.warn('Unlock failed for', instanceId, errorMessage);\n});\n<\/code><\/pre>\n\n<h3>License<\/h3>\n\n<p>This plugin is licensed under GPLv2 or later.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin through <strong>Plugins &gt; Installed Plugins<\/strong><\/li>\n<li>Go to <strong>Settings &gt; Content Protector<\/strong> and configure your global password<\/li>\n<li><p>Add the shortcode to any post or page<\/p>\n\n<p>[riaco_content_protector] This is hidden. [\/riaco_content_protector]<\/p><\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"can%20i%20protect%20multiple%20sections%20on%20the%20same%20page%3F\"><h3>Can I protect multiple sections on the same page?<\/h3><\/dt>\n<dd><p>Yes. All instances use the same global password and unlock together.<\/p><\/dd>\n<dt id=\"does%20this%20protect%20the%20entire%20post%3F\"><h3>Does this protect the entire post?<\/h3><\/dt>\n<dd><p>No \u2014 only the content wrapped in the shortcode.<\/p><\/dd>\n<dt id=\"are%20passwords%20hashed%20in%20the%20database%3F\"><h3>Are passwords hashed in the database?<\/h3><\/dt>\n<dd><p>No, the global password is stored in plain text like WordPress page passwords for easy admin management.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20gutenberg%20%2F%20block%20editor%3F\"><h3>Does this work with Gutenberg \/ block editor?<\/h3><\/dt>\n<dd><p>Yes. It works in both Classic and Block Editor.<br \/>\nYou can insert the shortcode inside Paragraph block or using Shortcode block.<\/p><\/dd>\n<dt id=\"what%20happens%20when%20i%20change%20the%20global%20password%3F\"><h3>What happens when I change the global password?<\/h3><\/dt>\n<dd><p>All previously unlocked content cookies are invalidated. Users must re-enter the new password.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Added <code>riaco_cp_content_access<\/code> filter to override the cookie check (role\/subscription bypass)<\/li>\n<li>Added <code>riaco_cp_locked_html<\/code> filter to replace the full locked-form HTML<\/li>\n<li>Added <code>riaco_cp_validate_password<\/code> filter for custom password validation (per-post passwords, etc.)<\/li>\n<li>Added <code>riaco_cp_rate_limit_bypass<\/code> filter to exempt trusted IPs from the brute-force counter<\/li>\n<li>Added <code>riaco_cp_cookie_scope<\/code> filter for per-post or per-instance independent lock state<\/li>\n<li>Added <code>riaco_cp_ajax_response<\/code> filter to inject extra fields into the unlock JSON response<\/li>\n<li>Added <code>riaco_cp_js_data<\/code> filter to extend the <code>RIACO_CP_Ajax<\/code> JavaScript object<\/li>\n<li>Added <code>riaco_cp_settings_page_after<\/code> action for extensions to add their own settings form<\/li>\n<li>Added JS custom events: <code>riaco_cp:before_submit<\/code>, <code>riaco_cp:unlock_success<\/code>, <code>riaco_cp:unlock_error<\/code><\/li>\n<li>Added admin footer review prompt on the settings page<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>Shortcode protection<\/li>\n<li>Global password<\/li>\n<li>AJAX unlock<\/li>\n<li>Cookie remember feature<\/li>\n<li>Automatic unlock of all instances<\/li>\n<\/ul>","raw_excerpt":"Protect any portion of your WordPress content using a simple shortcode. Includes global password, AJAX unlock, and site-wide instant access.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/263578","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=263578"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/prototipo88"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=263578"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=263578"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=263578"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=263578"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=263578"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=263578"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}