Changeset 3648066
- Timestamp:
- 08/15/2026 12:13:55 AM (2 months ago)
- Location:
- post-visibility-control
- Files:
-
- 18 added
- 8 edited
-
. (modified) (1 prop)
-
tags/1.1.0 (added)
-
tags/1.1.0/assets (added)
-
tags/1.1.0/assets/css (added)
-
tags/1.1.0/assets/css/admin.css (added)
-
tags/1.1.0/assets/js (added)
-
tags/1.1.0/assets/js/admin-metabox.js (added)
-
tags/1.1.0/assets/js/admin-notices.js (added)
-
tags/1.1.0/assets/js/block-editor.js (added)
-
tags/1.1.0/changelog.txt (added)
-
tags/1.1.0/includes (added)
-
tags/1.1.0/includes/admin-metabox.php (added)
-
tags/1.1.0/includes/admin-notices.php (added)
-
tags/1.1.0/includes/block-editor.php (added)
-
tags/1.1.0/includes/frontend-filters.php (added)
-
tags/1.1.0/includes/helpers.php (added)
-
tags/1.1.0/post-visibility-control.php (added)
-
tags/1.1.0/readme.txt (added)
-
trunk/assets/css/admin.css (modified) (1 diff)
-
trunk/assets/js/block-editor.js (modified) (2 diffs)
-
trunk/changelog.txt (added)
-
trunk/includes/block-editor.php (modified) (3 diffs)
-
trunk/includes/frontend-filters.php (modified) (3 diffs)
-
trunk/includes/helpers.php (modified) (1 diff)
-
trunk/post-visibility-control.php (modified) (3 diffs)
-
trunk/readme.txt (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
post-visibility-control
- Property svn:ignore
-
old new 1 1 AUDITORIA-SEGURIDAD-2026-06-01.md 2 AUDITORIA-SEGURIDAD-2026-07-03.md 3 .claude 4 roadmap-post-visibility-control.md
-
- Property svn:ignore
-
post-visibility-control/trunk/assets/css/admin.css
r3441562 r3648066 29 29 } 30 30 31 /* Block Editor - Options container in Status popup */ 32 .ayudawp-pvc-block-options { 33 padding: 0 0 16px 0; 34 border-bottom: 1px solid #e0e0e0; 35 margin-bottom: 16px; 31 /* Block Editor - Options row in the Summary panel (PluginPostStatusInfo) */ 32 .ayudawp-pvc-block-options .ayudawp-pvc-block-options-inner { 33 width: 100%; 36 34 } 37 38 .ayudawp-pvc-block-options .ayudawp-pvc-title {39 font-size: 13px;40 font-weight: 600;41 margin: 0 0 12px;42 color: #1e1e1e;43 }44 45 .ayudawp-pvc-block-options .ayudawp-pvc-description {46 color: #757575;47 font-size: 12px;48 margin: 0 0 12px;49 line-height: 1.4;50 }51 52 .ayudawp-pvc-block-options label {53 display: flex;54 align-items: center;55 margin-bottom: 8px;56 cursor: pointer;57 font-size: 13px;58 }59 60 .ayudawp-pvc-block-options input[type="radio"] {61 margin: 0 8px 0 0;62 } -
post-visibility-control/trunk/assets/js/block-editor.js
r3441562 r3648066 2 2 * Block Editor JavaScript for Post Visibility Control. 3 3 * 4 * Injects visibility options into the Status & Visibility popup. 4 * Registers a panel in the Summary section of the document sidebar 5 * using the official PluginPostStatusInfo API. 5 6 * 6 7 * @package PostVisibilityControl … … 8 9 */ 9 10 10 ( function( ) {11 ( function( wp, config ) { 11 12 'use strict'; 12 13 13 var COMPONENT_ID = 'ayudawp-pvc-visibility-options'; 14 var currentVisibility = 'visible'; 14 if ( ! wp || ! config ) { 15 return; 16 } 17 18 // Prefer the wp.editor slot (WP 6.6+); fall back to wp.editPost on older versions. 19 var slotPackage = ( wp.editor && wp.editor.PluginPostStatusInfo ) ? wp.editor : wp.editPost; 20 21 if ( ! wp.plugins || ! slotPackage || ! slotPackage.PluginPostStatusInfo ) { 22 return; 23 } 24 25 var el = wp.element.createElement; 26 var PluginPostStatusInfo = slotPackage.PluginPostStatusInfo; 27 var RadioControl = wp.components.RadioControl; 28 var compose = wp.compose.compose; 29 var withSelect = wp.data.withSelect; 30 var withDispatch = wp.data.withDispatch; 31 32 var radioOptions = Object.keys( config.options ).map( function( value ) { 33 return { value: value, label: config.options[ value ] }; 34 } ); 15 35 16 36 /** 17 * Get current visibility from post meta. 37 * Render the visibility options row. 38 * 39 * @param {Object} props Component props. 40 * @return {Object} Element. 18 41 */ 19 function getCurrentVisibility() { 20 var meta = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'meta' ); 21 if ( meta && meta[ ayudawpPVCBlock.metaKey ] ) { 22 return meta[ ayudawpPVCBlock.metaKey ]; 23 } 24 return 'visible'; 42 function VisibilityPanel( props ) { 43 return el( 44 PluginPostStatusInfo, 45 { className: 'ayudawp-pvc-block-options' }, 46 el( 47 'div', 48 { className: 'ayudawp-pvc-block-options-inner' }, 49 el( RadioControl, { 50 label: config.strings.title, 51 help: config.strings.description, 52 selected: props.visibility, 53 options: radioOptions, 54 onChange: props.setVisibility 55 } ) 56 ) 57 ); 25 58 } 26 59 27 /** 28 * Update visibility in post meta. 29 */ 30 function updateVisibility( newValue ) { 31 var meta = {}; 32 meta[ ayudawpPVCBlock.metaKey ] = newValue; 33 wp.data.dispatch( 'core/editor' ).editPost( { meta: meta } ); 34 currentVisibility = newValue; 35 } 60 var VisibilityPanelWithData = compose( 61 withSelect( function( select ) { 62 var meta = select( 'core/editor' ).getEditedPostAttribute( 'meta' ); 36 63 37 /** 38 * Create the visibility options HTML. 39 */ 40 function createOptionsElement() { 41 currentVisibility = getCurrentVisibility(); 64 return { 65 visibility: ( meta && meta[ config.metaKey ] ) ? meta[ config.metaKey ] : 'visible' 66 }; 67 } ), 68 withDispatch( function( dispatch ) { 69 return { 70 setVisibility: function( value ) { 71 var meta = {}; 72 meta[ config.metaKey ] = value; 73 dispatch( 'core/editor' ).editPost( { meta: meta } ); 74 } 75 }; 76 } ) 77 )( VisibilityPanel ); 42 78 43 var wrapper = document.createElement( 'div' );44 wrapper.id = COMPONENT_ID;45 wrapper.className = 'ayudawp-pvc-block-options';79 wp.plugins.registerPlugin( 'ayudawp-post-visibility-control', { 80 render: VisibilityPanelWithData 81 } ); 46 82 47 // Title. 48 var title = document.createElement( 'div' ); 49 title.className = 'ayudawp-pvc-title'; 50 title.textContent = ayudawpPVCBlock.strings.title; 51 wrapper.appendChild( title ); 52 53 // Description. 54 var desc = document.createElement( 'p' ); 55 desc.className = 'ayudawp-pvc-description'; 56 desc.textContent = ayudawpPVCBlock.strings.description; 57 wrapper.appendChild( desc ); 58 59 // Radio options. 60 Object.keys( ayudawpPVCBlock.options ).forEach( function( value ) { 61 var label = document.createElement( 'label' ); 62 63 var input = document.createElement( 'input' ); 64 input.type = 'radio'; 65 input.name = 'ayudawp_pvc_visibility'; 66 input.value = value; 67 input.checked = ( value === currentVisibility ); 68 69 input.addEventListener( 'change', function( e ) { 70 if ( e.target.checked ) { 71 updateVisibility( e.target.value ); 72 } 73 } ); 74 75 var text = document.createElement( 'span' ); 76 text.textContent = ayudawpPVCBlock.options[ value ]; 77 78 label.appendChild( input ); 79 label.appendChild( text ); 80 wrapper.appendChild( label ); 81 } ); 82 83 return wrapper; 84 } 85 86 /** 87 * Find the status popover and inject options. 88 */ 89 function injectOptions() { 90 // Already injected? 91 if ( document.getElementById( COMPONENT_ID ) ) { 92 updateRadioSelection(); 93 return; 94 } 95 96 // Find the popover by looking for status radio buttons. 97 // The popover contains radio buttons for Draft, Pending, Private, etc. 98 var statusPopover = null; 99 var popovers = document.querySelectorAll( '.components-popover__content' ); 100 101 popovers.forEach( function( popover ) { 102 // Check if this popover contains the post status options. 103 var hasStatusRadios = popover.querySelector( 'input[type="radio"]' ); 104 var hasPasswordCheckbox = popover.querySelector( 'input[type="checkbox"]' ); 105 106 // The status popover has both radio buttons and checkboxes. 107 if ( hasStatusRadios && hasPasswordCheckbox ) { 108 // Verify it's the status popover by checking for specific elements. 109 var textContent = popover.textContent || ''; 110 if ( textContent.indexOf( 'Protegido' ) !== -1 || 111 textContent.indexOf( 'Password' ) !== -1 || 112 textContent.indexOf( 'Fijo' ) !== -1 || 113 textContent.indexOf( 'Sticky' ) !== -1 ) { 114 statusPopover = popover; 115 } 116 } 117 } ); 118 119 if ( ! statusPopover ) { 120 return; 121 } 122 123 // Find the password protection checkbox container. 124 var checkboxes = statusPopover.querySelectorAll( 'input[type="checkbox"]' ); 125 var passwordCheckbox = null; 126 127 checkboxes.forEach( function( checkbox ) { 128 var container = checkbox.closest( '.components-base-control' ) || checkbox.closest( 'label' ) || checkbox.parentElement; 129 if ( container ) { 130 var text = container.textContent || ''; 131 if ( text.indexOf( 'Protegido' ) !== -1 || text.indexOf( 'Password' ) !== -1 || text.indexOf( 'contraseña' ) !== -1 ) { 132 passwordCheckbox = container; 133 } 134 } 135 } ); 136 137 // If we found the password checkbox, insert before it. 138 if ( passwordCheckbox ) { 139 var optionsElement = createOptionsElement(); 140 passwordCheckbox.parentElement.insertBefore( optionsElement, passwordCheckbox ); 141 return; 142 } 143 144 // Alternative: find the sticky checkbox and insert before it. 145 checkboxes.forEach( function( checkbox ) { 146 var container = checkbox.closest( '.components-base-control' ) || checkbox.closest( 'label' ) || checkbox.parentElement; 147 if ( container ) { 148 var text = container.textContent || ''; 149 if ( text.indexOf( 'Fijo' ) !== -1 || text.indexOf( 'Sticky' ) !== -1 ) { 150 var optionsElement = createOptionsElement(); 151 container.parentElement.insertBefore( optionsElement, container ); 152 } 153 } 154 } ); 155 } 156 157 /** 158 * Update radio selection if visibility changed externally. 159 */ 160 function updateRadioSelection() { 161 var newVisibility = getCurrentVisibility(); 162 if ( newVisibility !== currentVisibility ) { 163 var input = document.querySelector( '#' + COMPONENT_ID + ' input[value="' + newVisibility + '"]' ); 164 if ( input && ! input.checked ) { 165 input.checked = true; 166 currentVisibility = newVisibility; 167 } 168 } 169 } 170 171 /** 172 * Initialize. 173 */ 174 function init() { 175 wp.domReady( function() { 176 // Observe DOM for popover opening. 177 var observer = new MutationObserver( function() { 178 injectOptions(); 179 } ); 180 181 observer.observe( document.body, { 182 childList: true, 183 subtree: true 184 } ); 185 186 // Subscribe to editor changes. 187 wp.data.subscribe( function() { 188 if ( document.getElementById( COMPONENT_ID ) ) { 189 updateRadioSelection(); 190 } 191 } ); 192 } ); 193 } 194 195 init(); 196 197 } )(); 83 } )( window.wp, window.ayudawpPVCBlock ); -
post-visibility-control/trunk/includes/block-editor.php
r3441562 r3648066 3 3 * Block Editor integration for Post Visibility Control. 4 4 * 5 * Adds visibility options to the Status & Visibility modal in Gutenberg. 5 * Adds visibility options to the Summary panel of the document sidebar 6 * through the PluginPostStatusInfo slot. 6 7 * 7 8 * @package PostVisibilityControl … … 53 54 'wp-components', 54 55 'wp-data', 55 'wp-dom-ready', 56 'wp-i18n', 56 'wp-compose', 57 57 ), 58 58 AYUDAWP_PVC_VERSION, … … 73 73 ) 74 74 ); 75 76 // Set translations.77 wp_set_script_translations( 'ayudawp-pvc-block-editor', 'post-visibility-control' );78 75 } 79 76 } -
post-visibility-control/trunk/includes/frontend-filters.php
r3458727 r3648066 22 22 23 23 /** 24 * Internal query var used to mark queries that need filtering. 25 * 26 * @since 1.1.0 27 * @var string 28 */ 29 const CONTEXT_QUERY_VAR = 'ayudawp_pvc_context'; 30 31 /** 24 32 * Constructor. 25 33 * … … 33 41 34 42 add_action( 'pre_get_posts', array( $this, 'filter_queries' ) ); 43 add_filter( 'posts_where', array( $this, 'exclude_posts_where' ), 10, 2 ); 35 44 } 36 45 37 46 /** 38 * Filter posts from search and archive queries. 47 * Mark search and archive queries that need visibility filtering. 48 * 49 * The SQL is appended later by exclude_posts_where(), which only acts on 50 * queries marked here. This keeps the exclusion scoped to the query that 51 * triggered it instead of leaking into every later query of the request. 39 52 * 40 53 * @since 1.0.0 … … 68 81 } 69 82 70 // Build meta query to exclude hidden posts. 71 $visibility_to_exclude = array(); 83 if ( $query->is_search() ) { 84 $query->set( self::CONTEXT_QUERY_VAR, 'search' ); 85 } elseif ( $query->is_archive() || $query->is_home() || $query->is_front_page() ) { 86 $query->set( self::CONTEXT_QUERY_VAR, 'archive' ); 87 } 88 } 72 89 73 if ( $query->is_search() ) { 74 // Exclude posts hidden from search. 75 $visibility_to_exclude = array( 'archive_only', 'hidden' ); 76 } elseif ( $query->is_archive() || $query->is_home() || $query->is_front_page() ) { 77 // Exclude posts hidden from archives. 78 $visibility_to_exclude = array( 'search_only', 'hidden' ); 90 /** 91 * Append the visibility exclusion to queries marked in filter_queries(). 92 * 93 * Both variants are full SQL literals on purpose: Plugin Check 2.0 flags 94 * any PHP variable interpolated inside a SQL string, so the two possible 95 * exclusion lists are written out instead of being built dynamically. 96 * 97 * @since 1.1.0 98 * @param string $where The WHERE clause of the query. 99 * @param WP_Query $query The WordPress query object. 100 * @return string Filtered WHERE clause. 101 */ 102 public function exclude_posts_where( $where, $query ) { 103 $context = $query->get( self::CONTEXT_QUERY_VAR ); 104 105 if ( 'search' !== $context && 'archive' !== $context ) { 106 return $where; 79 107 } 80 108 81 if ( empty( $visibility_to_exclude ) ) { 82 return; 109 global $wpdb; 110 111 if ( 'search' === $context ) { 112 // Exclude posts hidden from search. 113 $where .= " AND {$wpdb->posts}.ID NOT IN ( 114 SELECT post_id 115 FROM {$wpdb->postmeta} 116 WHERE meta_key = '_ayudawp_visibility' 117 AND meta_value IN ( 'archive_only', 'hidden' ) 118 )"; 119 } else { 120 // Exclude posts hidden from archives. 121 $where .= " AND {$wpdb->posts}.ID NOT IN ( 122 SELECT post_id 123 FROM {$wpdb->postmeta} 124 WHERE meta_key = '_ayudawp_visibility' 125 AND meta_value IN ( 'search_only', 'hidden' ) 126 )"; 83 127 } 84 128 85 // Use posts_where filter for better performance. 86 add_filter( 87 'posts_where', 88 function( $where ) use ( $visibility_to_exclude ) { 89 global $wpdb; 90 91 $excluded = "'" . implode( "','", array_map( 'esc_sql', $visibility_to_exclude ) ) . "'"; 92 93 $where .= " AND {$wpdb->posts}.ID NOT IN ( 94 SELECT post_id 95 FROM {$wpdb->postmeta} 96 WHERE meta_key = '_ayudawp_visibility' 97 AND meta_value IN ({$excluded}) 98 )"; 99 100 return $where; 101 }, 102 10, 103 1 104 ); 129 return $where; 105 130 } 106 131 -
post-visibility-control/trunk/includes/helpers.php
r3441562 r3648066 78 78 * 79 79 * @since 1.0.0 80 * @param string $post_type The post type to check.80 * @param string|null $post_type The post type to check. 81 81 * @return bool True if supported, false otherwise. 82 82 */ 83 83 function ayudawp_pvc_is_supported_post_type( $post_type ) { 84 if ( ! is_string( $post_type ) ) { 85 return false; 86 } 87 84 88 $supported = ayudawp_pvc_get_supported_post_types(); 85 89 return isset( $supported[ $post_type ] ); -
post-visibility-control/trunk/post-visibility-control.php
r3492975 r3648066 4 4 * Plugin URI: https://servicios.ayudawp.com 5 5 * Description: Control visibility of posts, pages, and custom post types in archives and search results. 6 * Version: 1. 0.26 * Version: 1.1.0 7 7 * Author: Fernando Tellado 8 8 * Author URI: https://ayudawp.com … … 12 12 * Requires at least: 5.0 13 13 * Requires PHP: 7.4 14 * Tested up to: 7. 014 * Tested up to: 7.1 15 15 * 16 16 * @package PostVisibilityControl … … 23 23 24 24 // Plugin constants. 25 define( 'AYUDAWP_PVC_VERSION', '1. 0.2' );25 define( 'AYUDAWP_PVC_VERSION', '1.1.0' ); 26 26 define( 'AYUDAWP_PVC_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 27 27 define( 'AYUDAWP_PVC_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); -
post-visibility-control/trunk/readme.txt
r3574876 r3648066 3 3 Tags: visibility, exclude, search, archive, hide posts 4 4 Requires at least: 5.0 5 Tested up to: 7. 05 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.27 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 15 15 Post Visibility Control adds visibility options to posts, pages, and custom post types. Control exactly where your content appears without making it private. 16 16 17 The plugin integrates seamlessly into the native WordPress interface. In the Block Editor (Gutenberg), you will find the options in side the Status & Visibility popup when you click on the post status. In the Classic Editor, the options appear within the Visibility section of the Publish metabox, right where you would expect them.17 The plugin integrates seamlessly into the native WordPress interface. In the Block Editor (Gutenberg), you will find the options in the Summary panel of the settings sidebar, together with the rest of the status controls. In the Classic Editor, the options appear within the Visibility section of the Publish metabox, right where you would expect them. 18 18 19 19 **Features:** … … 46 46 47 47 1. Edit any post or page 48 2. In the right sidebar, look for the "Status" section (usually shows "Draft" or "Publish") 49 3. Click on the status to open the Status & Visibility popup 50 4. You will see "Visibility Control" options with four radio buttons 51 5. Select your preferred visibility option 52 6. Save or publish your post 48 2. Open the settings sidebar (gear icon) and select the document tab (Post or Page) 49 3. In the Summary panel you will see "Visibility Control" with four radio buttons 50 4. Select your preferred visibility option 51 5. Save or publish your post 53 52 54 53 **Classic Editor:** … … 65 64 1. Upload the `post-visibility-control` folder to `/wp-content/plugins/` 66 65 2. Activate the plugin through the 'Plugins' menu in WordPress 67 3. Edit any post or page and find the visibility control options in the Visibility section (Classic Editor) or Status & Visibility panel (Block Editor)66 3. Edit any post or page and find the visibility control options in the Visibility section (Classic Editor) or the Summary panel (Block Editor) 68 67 69 68 == Frequently Asked Questions == … … 71 70 = Where do I find the visibility options? = 72 71 73 In the Block Editor (Gutenberg): Edit your post , then click on the "Status" in the right sidebar to open the Status & Visibility popup. The Visibility Control options appear therewith four radio buttons.72 In the Block Editor (Gutenberg): Edit your post and open the settings sidebar. The Visibility Control options appear in the Summary panel with four radio buttons. 74 73 75 74 In the Classic Editor: Edit your post and look at the Publish metabox in the right sidebar. The Visibility Control options appear within the Visibility section, below the standard WordPress visibility settings. … … 102 101 == Changelog == 103 102 104 = 1.0.2 = 105 Tested up to WordPress 7.0 103 = 1.1.0 = 104 * Improved: Block editor integration rebuilt on the official PluginPostStatusInfo API. The options now live in the Summary panel of the settings sidebar instead of being injected into the status popup. 105 * Improved: Tested up to WordPress 7.1. 106 * Fix: Visibility options did not appear in the block editor when the admin language was other than English or Spanish. 107 * Fix: After an archive or search query, the visibility exclusion leaked into every later query of the same request, so hidden posts could vanish from unrelated queries such as featured post blocks. The exclusion is now scoped to the query it targets. 108 * Fix: PHP 8.5 deprecation notice (null used as array offset) on admin screens without a post type context. 106 109 107 = 1.0.1 = 108 * Performance: Optimized database queries to prevent slow queries and server timeouts 109 * Performance: Replaced inefficient meta_query with optimized posts_where filter 110 * Fixed: Potential 504 errors on sites with large post counts 111 112 = 1.0.0 = 113 * Initial release 114 * Support for posts, pages, and custom post types 115 * Classic Editor and Block Editor support 116 * Four visibility options 110 For older changelog entries, please check the [changelog.txt](https://plugins.svn.wordpress.org/post-visibility-control/trunk/changelog.txt) file 117 111 118 112 == Upgrade Notice == 119 113 120 = 1.0.1 = 121 Important performance update. Fixes slow database queries that could cause server timeouts on sites with many posts. 122 123 = 1.0.0 = 124 Initial release. 114 = 1.1.0 = 115 Visibility options now show in the block editor in any admin language, hidden posts no longer vanish from unrelated queries, and a PHP 8.5 deprecation notice is fixed. 125 116 126 117 == Support ==
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)