Plugin Directory


Ignore:
Timestamp:
10/18/2026 11:12:11 PM (10 hours ago)
Author:
yuracode
Message:

Release YuraCode Security 1.0.6

File:
1 edited

Legend:

Unmodified
Added
Removed
  • yuracode-security/trunk/src/Admin/SettingsPage.php

    r3714465 r3753699  
    1010        const OPTION_GROUP = 'yuracode-security_settings_group';
    1111        const PAGE = 'yuracode-security';
     12
     13        // Placeholder tab on subsites of a multisite network: the firewall is
     14        // network-managed, so its real sections collapse into this read-only one.
     15        const NETWORK_FIREWALL_SECTION = 'yuracode-security_network_firewall';
    1216
    1317        public static function init() {
    … …  
    6266                $defaults = Plugin::defaults();
    6367                $input = is_array($input) ? $input : array();
     68                $manages_file = HtaccessManager::current_site_manages_file();
    6469                $out = array();
    6570
    6671                foreach (Schema::fields() as $field) {
    6772                        list($section, $key, $label, $type, $args) = $field;
     73
     74                        // The firewall is network-managed on multisite: subsites don't
     75                        // render its fields, so its keys are dropped here, exactly like
     76                        // Pro keys after a license lapse.
     77                        if (!$manages_file && self::is_firewall_section($section)) {
     78                                continue;
     79                        }
    6880
    6981                        $path = explode('.', $args['path']);
    … …  
    92104
    93105        public static function htaccess_notice() {
    94                 if (!self::on_settings_page()) {
     106                if (!self::on_settings_page() || !HtaccessManager::current_site_manages_file()) {
    95107                        return;
    96108                }
    … …  
    112124
    113125        public static function htaccess_server_notice() {
    114                 if (!self::on_settings_page()) {
     126                if (!self::on_settings_page() || !HtaccessManager::current_site_manages_file()) {
    115127                        return;
    116128                }
    … …  
    147159         */
    148160        public static function rewrite_block_notice() {
    149                 if (!self::on_settings_page() || !current_user_can('manage_options')) {
     161                if (
     162                        !self::on_settings_page()
     163                        || !current_user_can('manage_options')
     164                        || !HtaccessManager::current_site_manages_file()
     165                ) {
    150166                        return;
    151167                }
    … …  
    175191                // Direct links to a tab (?ycs_tab=<id>) only activate known schema
    176192                // sections or extra tabs; anything else falls back to the first tab.
    177                 $known_tabs = array_merge(array_keys(Schema::sections()), array_keys($extra_tabs));
     193                $known_tabs = array_merge(array_keys(self::visible_sections()), array_keys($extra_tabs));
    178194
    179195                if (!in_array($active_tab, $known_tabs, true)) {
    … …  
    211227                $first = true;
    212228
    213                 foreach (Schema::sections() as $id => $section): ?>
     229                foreach (self::visible_sections() as $id => $section): ?>
    214230                        <button
    215231                                type="button"
    … …  
    248264                $first = true;
    249265
    250                 foreach (Schema::sections() as $id => $section): ?>
     266                foreach (self::visible_sections() as $id => $section): ?>
    251267                        <div
    252268                                id="panel-<?php echo esc_attr($id); ?>"
    … …  
    263279                                        </div>
    264280                                        <div class="ycs-fields">
    265                                                 <?php FieldRenderer::render_fields($id); ?>
     281                                                <?php
     282
     283                                                if (self::NETWORK_FIREWALL_SECTION === $id) {
     284                                                        echo
     285                                                                '<p class="description">'
     286                                                                        . esc_html__(
     287                                                                                'The firewall rules live in the one .htaccess file in the WordPress root that every site on this network shares, so they are configured on the main site. Open Settings > YuraCode Security there. The hardening and login-protection settings on this page apply to this site only.',
     288                                                                                'yuracode-security',
     289                                                                        )
     290                                                                        . '</p>';
     291                                                } else {
     292                                                        FieldRenderer::render_fields($id);
     293                                                }
     294                                                ?>
    266295                                        </div>
    267296                                </div>
    … …  
    295324        }
    296325
     326        private static function visible_sections() {
     327                return self::sections_for_context(Schema::sections(), HtaccessManager::current_site_manages_file());
     328        }
     329
     330        /**
     331         * Sections as the current context shows them; pure, so the multisite
     332         * branch stays testable on a single-site install.
     333         *
     334         * On a subsite of a multisite network the firewall sections collapse
     335         * into a single read-only tab: the rules live in the one .htaccess the
     336         * network shares and are managed on the main site.
     337         *
     338         * @param array $sections     Schema sections, id => section.
     339         * @param bool  $site_manages Whether the current site writes the .htaccess.
     340         */
     341        public static function sections_for_context($sections, $site_manages) {
     342                if ($site_manages) {
     343                        return $sections;
     344                }
     345
     346                $out = array();
     347
     348                foreach ((array) $sections as $id => $section) {
     349                        if (self::is_firewall_section($id)) {
     350                                continue;
     351                        }
     352
     353                        $out[$id] = $section;
     354                }
     355
     356                $out[self::NETWORK_FIREWALL_SECTION] = array(
     357                        'title' => __('Firewall', 'yuracode-security'),
     358                        'desc' => __(
     359                                'The firewall protects the entire network and is managed on the main site.',
     360                                'yuracode-security',
     361                        ),
     362                );
     363
     364                return $out;
     365        }
     366
     367        /**
     368         * Whether a schema section belongs to the network-managed firewall.
     369         *
     370         * Add-ons registering firewall sections reuse the `yuracode-security_firewall*`
     371         * prefix, so they collapse on subsites too.
     372         */
     373        private static function is_firewall_section($id) {
     374                return str_starts_with((string) $id, 'yuracode-security_firewall');
     375        }
     376
    297377        private static function schema_tab_active($id, $active_tab, $first) {
    298378                // No tab selected (or an unknown/extra tab): the first schema tab wins.
Note: See TracChangeset for help on using the changeset viewer.