Plugin Directory

Changeset 3761091


Ignore:
Timestamp:
10/23/2026 07:24:04 AM (68 minutes ago)
Author:
fullworks
Message:

Release version 1.7.10

Location:
stop-user-enumeration
Files:
2 added
24 edited
1 copied

Legend:

Unmodified
Added
Removed
  • stop-user-enumeration/tags/1.7.10/changelog.txt

    r3724500 r3761091  
    11== Changelog ==
     2= 1.7.10 =
     3* Fixed: the settings notice could not be dismissed and kept coming back on every page load
     4* Fixed: on sites running other Fullworks free plugins, the opt-in notice and menu could belong to the wrong plugin
     5* Fixed: leaving the opt-in screen without choosing left the notice up for good; visiting settings now shows the opt-in screen again until you click Allow or Skip
     6* Changed: the notice now links straight to the opt-in screen instead of the settings page
     7* Changed: updated the bundled opt-in library to 1.3.2
     8
    29= 1.7.9 =
    310* Fixed two database writes on every page load: the plugin and its bundled opt-in library both registered an uninstall hook and overwrote each other's entry in the uninstall_plugins option. Uninstall now runs from uninstall.php instead
  • stop-user-enumeration/tags/1.7.10/includes/vendor/alanef/free_plugin_lib/src/Assets/scripts/main.js

    r3259418 r3761091  
    2929                action: 'ffpl_handle_optin',
    3030                email: email,
     31                shortname: ffplData.shortname,
    3132                nonce: ffplData.nonce
    3233            },
  • stop-user-enumeration/tags/1.7.10/includes/vendor/alanef/free_plugin_lib/src/Classes/Email.php

    r3679961 r3761091  
    44
    55class Email {
    6         private static $plugin_shortname;
     6        private $plugin_shortname;
    77
    88        // Plugin shortname to ID mapping
    … …  
    2020
    2121        public function __construct($plugin_shortname) {
    22                 self::$plugin_shortname = $plugin_shortname;
     22                $this->plugin_shortname = $plugin_shortname;
    2323        }
    2424
    … …  
    3333
    3434                // Get plugin ID from map
    35                 $plugin_id = $plugin_map[self::$plugin_shortname] ?? null;
     35                $plugin_id = $plugin_map[$this->plugin_shortname] ?? null;
    3636                if (!$plugin_id) {
    3737                        return false;
  • stop-user-enumeration/tags/1.7.10/includes/vendor/alanef/free_plugin_lib/src/Main.php

    r3724500 r3761091  
    77class Main {
    88        /**
     9         * Shortname of the most recently constructed instance, used only as the default for
     10         * plugin_uninstall(). Every plugin bundling this library shares one copy of the class,
     11         * so per-plugin state must live on the instance, never here.
     12         *
    913         * @var mixed
    1014         */
    11         private static $plugin_shortname;
     15        private static $last_plugin_shortname;
     16        /**
     17         * @var mixed
     18         */
     19        private $plugin_shortname;
    1220        /**
    1321         * @var string
    1422         */
    15         private static $version = '1.3.0';
    16         /**
     23        private static $version = '1.3.2';
     24        /**
     25         * The host plugin's settings page as passed: a menu slug ('stop-user-enumeration'), a hook
     26         * suffix ('settings_page_contact-form-settings') or '' for a plugin with no settings page.
     27         *
    1728         * @var mixed
    1829         */
    1930        private $page;
     31        /**
     32         * The menu slug part of $page, compared with $_GET['page'].
     33         *
     34         * @var string
     35         */
     36        private $page_slug;
    2037        /**
    2138         * @var \WP_Screen|null
    … …  
    3350
    3451        public function __construct($plugin_file, $settings_page, $plugin_shortname, $page, $plugin_name) {
    35                 self::$plugin_shortname = $plugin_shortname;
     52                $this->plugin_shortname = $plugin_shortname;
     53                self::$last_plugin_shortname = $plugin_shortname;
    3654                $this->page = $page;
     55                $this->page_slug = (string) preg_replace('/^.*_page_/', '', (string) $page);
    3756                $this->plugin_file = $plugin_file;
    3857                $this->settings_page = $settings_page;
    … …  
    6786         */
    6887        public static function plugin_uninstall($plugin_shortname = null) {
    69                 $plugin_shortname = null === $plugin_shortname ? self::$plugin_shortname : $plugin_shortname;
     88                $plugin_shortname = null === $plugin_shortname ? self::$last_plugin_shortname : $plugin_shortname;
    7089                if (empty($plugin_shortname)) {
    7190                        return;
    … …  
    81100                // Verify nonce - the nonce IS the ffpl_skip value
    82101                $nonce = sanitize_text_field(wp_unslash($_GET['ffpl_skip']));
    83                 if (!wp_verify_nonce($nonce, 'ffpl_skip_' . self::$plugin_shortname)) {
     102                if (!wp_verify_nonce($nonce, 'ffpl_skip_' . $this->plugin_shortname)) {
    84103                        return;
    85104                }
    86105
    87106                // Set status to optout - user explicitly skipped
    88                 update_site_option(self::$plugin_shortname . '_form_rendered', 'optout');
     107                update_site_option($this->plugin_shortname . '_form_rendered', 'optout');
    89108
    90109                // Redirect to clean URL (remove the skip param)
    … …  
    94113
    95114        public function handle_optin_page() {
     115                // admin_init also runs for admin-ajax.php, and a redirect there breaks every AJAX call on the site
     116                if (wp_doing_ajax() || '' === $this->page_slug || !current_user_can('manage_options')) {
     117                        return;
     118                }
     119
    96120                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is page routing, not form processing
    97121                $current_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
    98                 $option = get_site_option(self::$plugin_shortname . '_form_rendered');
    99                 if ('pending' === $option && $current_page === $this->page) {
    100                         update_site_option(self::$plugin_shortname . '_form_rendered', 'rendering');
    101                         wp_safe_redirect(admin_url('options-general.php?page=ffpl-opt-in-' . self::$plugin_shortname));
     122                $option = get_site_option($this->plugin_shortname . '_form_rendered');
     123                // 'rendering' means the opt-in screen was shown but left without a choice, so ask again
     124                if (in_array($option, array('pending', 'rendering'), true) && $current_page === $this->page_slug) {
     125                        update_site_option($this->plugin_shortname . '_form_rendered', 'rendering');
     126                        wp_safe_redirect(admin_url('options-general.php?page=ffpl-opt-in-' . $this->plugin_shortname));
    102127                        exit;
    103128                }
    … …  
    110135                        $settings_link
    111136                );
    112                 if ('optout' === get_site_option(self::$plugin_shortname . '_form_rendered', 'optout')) {
    113                         $settings_link = '<a href="' . esc_url(admin_url('options-general.php?page=ffpl-opt-in-'.self::$plugin_shortname )) . '" style="font-weight:900; font-size: 110%; color: #b32d2e;">' . esc_html($this->translate('Opt In')) . '</a>';
     137                if ('optout' === get_site_option($this->plugin_shortname . '_form_rendered', 'optout')) {
     138                        $settings_link = '<a href="' . esc_url(admin_url('options-general.php?page=ffpl-opt-in-'.$this->plugin_shortname )) . '" style="font-weight:900; font-size: 110%; color: #b32d2e;">' . esc_html($this->translate('Opt In')) . '</a>';
    114139                        array_unshift(
    115140                                $links,
    … …  
    123148        public function add_settings_page() {
    124149                // First-run detection - if option doesn't exist, set to pending
    125                 $option = get_site_option(self::$plugin_shortname . '_form_rendered');
     150                $option = get_site_option($this->plugin_shortname . '_form_rendered');
    126151                if (false === $option) {
    127                         update_site_option(self::$plugin_shortname . '_form_rendered', 'pending');
     152                        update_site_option($this->plugin_shortname . '_form_rendered', 'pending');
    128153                        $option = 'pending';
    129154                }
    … …  
    135160                                esc_html($this->translate('Opt In ') . esc_html( $this->plugin_name) ), // Menu title
    136161                                'manage_options', // Capability
    137                                 'ffpl-opt-in-'.self::$plugin_shortname, // Menu slug
     162                                'ffpl-opt-in-'.$this->plugin_shortname, // Menu slug
    138163                                array($this, 'render_opt_in_page') // Callback function
    139164                        );
    … …  
    200225                            </div>
    201226                            <div class="button-2">
    202                                 <a href="<?php echo esc_url(add_query_arg('ffpl_skip', wp_create_nonce('ffpl_skip_' . self::$plugin_shortname), $this->settings_page)); ?>"
     227                                <a href="<?php echo esc_url(add_query_arg('ffpl_skip', wp_create_nonce('ffpl_skip_' . $this->plugin_shortname), $this->settings_page)); ?>"
    203228                                   class="button button-secondary btn-skip" name="action" value="skip"
    204229                                   tabindex="2">
    … …  
    241266        public function conditional_enqueue_assets($hook) {
    242267                // Only load on our specific page
    243                 if ($hook !== 'settings_page_ffpl-opt-in-'.self::$plugin_shortname) {
     268                if ($hook !== 'settings_page_ffpl-opt-in-'.$this->plugin_shortname) {
    244269                        return;
    245270                }
    … …  
    260285                        'ajaxurl' => admin_url('admin-ajax.php'),
    261286                        'nonce' => wp_create_nonce('ffpl_optin_nonce'),
     287                        'shortname' => $this->plugin_shortname,
    262288                        'settings_page' => $this->settings_page
    263289                ));
    … …  
    265291
    266292        public function handle_optin_ajax() {
     293                // Every plugin bundling the library registers this handler; leave other plugins' requests to them
     294                if (isset($_POST['shortname']) && !$this->is_own_shortname(sanitize_key(wp_unslash($_POST['shortname'])))) {
     295                        return;
     296                }
     297
    267298                // Verify request method
    268299                if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
    … …  
    297328                $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
    298329
    299                 $email_handler = new Email(self::$plugin_shortname);
     330                $email_handler = new Email($this->plugin_shortname);
    300331                $result = $email_handler->handle_optin_submission($email);
    301332
    302333                if ($result) {
    303                         update_site_option(self::$plugin_shortname . '_form_rendered', 'optin');
     334                        update_site_option($this->plugin_shortname . '_form_rendered', 'optin');
    304335                        wp_send_json_success(array(
    305336                                'redirect_url' => $this->settings_page
    306337                        ));
    307338                } else {
    308                         update_site_option(self::$plugin_shortname . '_form_rendered', 'optout');
     339                        update_site_option($this->plugin_shortname . '_form_rendered', 'optout');
    309340                        wp_send_json_success(array(
    310341                                'redirect_url' => $this->settings_page
    … …  
    325356                }
    326357
    327                 $option = get_site_option(self::$plugin_shortname . '_form_rendered');
     358                $option = get_site_option($this->plugin_shortname . '_form_rendered');
    328359
    329360                // Only show notice if no decision has been made yet (pending or rendering)
    … …  
    334365
    335366                // Don't show if user dismissed the notice
    336                 if (get_user_meta(get_current_user_id(), self::$plugin_shortname . '_notice_dismissed', true)) {
     367                if (get_user_meta(get_current_user_id(), $this->plugin_shortname . '_notice_dismissed', true)) {
    337368                        return;
    338369                }
    … …  
    345376                        'tools',
    346377                        'options-general',
    347                         'settings_page_' . $this->page,
    348378                );
     379                if ('' !== $this->page_slug) {
     380                        // A hook suffix is already the screen base; a menu slug is assumed to be under Settings
     381                        $display_on_pages[] = $this->page === $this->page_slug ? 'settings_page_' . $this->page : $this->page;
     382                }
    349383
    350384                if (!in_array($page, $display_on_pages, true)) {
    … …  
    352386                }
    353387
    354                 $opt_in_url = admin_url('options-general.php?page=ffpl-opt-in-' . self::$plugin_shortname);
     388                $opt_in_url = admin_url('options-general.php?page=ffpl-opt-in-' . $this->plugin_shortname);
    355389                ?>
    356                 <div class="notice notice-info is-dismissible ffpl-setup-notice" data-shortname="<?php echo esc_attr(self::$plugin_shortname); ?>">
     390                <div class="notice notice-info is-dismissible ffpl-setup-notice" data-shortname="<?php echo esc_attr($this->plugin_shortname); ?>">
    357391                        <p>
    358392                                <strong><?php echo esc_html($this->plugin_name); ?>:</strong>
    359393                                <?php
    360394                                printf(
    361                                         '%s <a href="%s"><strong>%s</strong></a> %s',
    362                                         esc_html($this->translate('You haven\'t visited settings yet.')),
    363                                         esc_url($this->settings_page),
    364                                         esc_html($this->translate('Please check your settings')),
    365                                         esc_html($this->translate('for optimal configuration and opt in for security updates, tips and occasional offers.'))
     395                                        '%s <a href="%s"><strong>%s</strong></a>',
     396                                        esc_html($this->translate('Choose whether to receive security updates, tips and occasional offers.')),
     397                                        esc_url($opt_in_url),
     398                                        esc_html($this->translate('Opt in or skip'))
    366399                                );
    367400                                ?>
    … …  
    370403                <script>
    371404                jQuery(document).ready(function($) {
    372                         $('.ffpl-setup-notice').on('click', '.notice-dismiss', function() {
    373                                 var shortname = $(this).closest('.ffpl-setup-notice').data('shortname');
     405                        $('.ffpl-setup-notice[data-shortname="<?php echo esc_js($this->plugin_shortname); ?>"]').on('click', '.notice-dismiss', function() {
    374406                                $.post(ajaxurl, {
    375407                                        action: 'ffpl_dismiss_notice',
    376                                         shortname: shortname,
     408                                        shortname: '<?php echo esc_js($this->plugin_shortname); ?>',
    377409                                        nonce: '<?php echo esc_js(wp_create_nonce('ffpl_dismiss_notice')); ?>'
    378410                                });
    … …  
    394426                }
    395427
    396                 $shortname = isset($_POST['shortname']) ? sanitize_key($_POST['shortname']) : '';
    397                 if ($shortname === self::$plugin_shortname) {
    398                         update_user_meta(get_current_user_id(), self::$plugin_shortname . '_notice_dismissed', true);
    399                         wp_send_json_success();
    400                 }
    401 
    402                 wp_send_json_error();
     428                // Every plugin bundling the library registers this handler; leave other plugins' requests to them
     429                $shortname = isset($_POST['shortname']) ? sanitize_key(wp_unslash($_POST['shortname'])) : '';
     430                if (!$this->is_own_shortname($shortname)) {
     431                        return;
     432                }
     433
     434                // The meta key keeps the shortname's original case so existing dismissals stay readable
     435                update_user_meta(get_current_user_id(), $this->plugin_shortname . '_notice_dismissed', true);
     436                wp_send_json_success();
    403437                wp_die();
     438        }
     439
     440        /**
     441         * sanitize_key() lowercases, and host plugins pass mixed-case shortnames such as 'SUE'.
     442         *
     443         * @param string $shortname A shortname that has been through sanitize_key().
     444         */
     445        private function is_own_shortname($shortname) {
     446                return '' !== $shortname && $shortname === sanitize_key($this->plugin_shortname);
    404447        }
    405448
  • stop-user-enumeration/tags/1.7.10/includes/vendor/autoload.php

    r3724500 r3761091  
    2020require_once __DIR__ . '/composer/autoload_real.php';
    2121
    22 return ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee::getLoader();
     22return ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963::getLoader();
  • stop-user-enumeration/tags/1.7.10/includes/vendor/composer/autoload_real.php

    r3724500 r3761091  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee
     5class ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963
    66{
    77    private static $loader;
    … …  
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::getInitializer($loader));
    3333
    3434        $loader->register(true);
  • stop-user-enumeration/tags/1.7.10/includes/vendor/composer/autoload_static.php

    r3724500 r3761091  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee
     7class ComposerStaticInit3836c91313ab2ce66e7903b7eb493963
    88{
    99    public static $prefixLengthsPsr4 = array (
    … …  
    8888    {
    8989        return \Closure::bind(function () use ($loader) {
    90             $loader->prefixLengthsPsr4 = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$prefixLengthsPsr4;
    91             $loader->prefixDirsPsr4 = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$prefixDirsPsr4;
    92             $loader->prefixesPsr0 = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$prefixesPsr0;
    93             $loader->classMap = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$classMap;
     90            $loader->prefixLengthsPsr4 = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$prefixLengthsPsr4;
     91            $loader->prefixDirsPsr4 = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$prefixDirsPsr4;
     92            $loader->prefixesPsr0 = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$prefixesPsr0;
     93            $loader->classMap = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$classMap;
    9494
    9595        }, null, ClassLoader::class);
  • stop-user-enumeration/tags/1.7.10/includes/vendor/composer/installed.json

    r3724500 r3761091  
    33        {
    44            "name": "alanef/free_plugin_lib",
    5             "version": "1.3.0",
    6             "version_normalized": "1.3.0.0",
     5            "version": "1.3.2",
     6            "version_normalized": "1.3.2.0",
    77            "source": {
    88                "type": "git",
    99                "url": "https://github.com/alanef/free_plugin_lib.git",
    10                 "reference": "6dfce2995dde31a1d12e6f8113c4b420ebd83fe9"
     10                "reference": "9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a"
    1111            },
    1212            "dist": {
    1313                "type": "zip",
    14                 "url": "https://api.github.com/repos/alanef/free_plugin_lib/zipball/6dfce2995dde31a1d12e6f8113c4b420ebd83fe9",
    15                 "reference": "6dfce2995dde31a1d12e6f8113c4b420ebd83fe9",
     14                "url": "https://api.github.com/repos/alanef/free_plugin_lib/zipball/9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a",
     15                "reference": "9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a",
    1616                "shasum": ""
    1717            },
    … …  
    2525                "yoast/wp-test-utils": "^1.2"
    2626            },
    27             "time": "2026-10-02T09:11:20+00:00",
     27            "time": "2026-10-23T06:44:46+00:00",
    2828            "type": "library",
    2929            "installation-source": "dist",
    … …  
    4646            "support": {
    4747                "issues": "https://github.com/alanef/free_plugin_lib/issues",
    48                 "source": "https://github.com/alanef/free_plugin_lib/tree/v1.3.0"
     48                "source": "https://github.com/alanef/free_plugin_lib/tree/v1.3.2"
    4949            },
    5050            "install-path": "../alanef/free_plugin_lib"
  • stop-user-enumeration/tags/1.7.10/includes/vendor/composer/installed.php

    r3724500 r3761091  
    22    'root' => array(
    33        'name' => 'fullworks/stop-user-enumeration',
    4         'pretty_version' => 'v1.7.9',
    5         'version' => '1.7.9.0',
    6         'reference' => 'a87eb0b6cac8051b239c5ad7294599b74bdbe768',
     4        'pretty_version' => 'v1.7.10',
     5        'version' => '1.7.10.0',
     6        'reference' => 'eb7d4253c3576c49fdff00384dbac79813ec744a',
    77        'type' => 'wordpress-plugin',
    88        'install_path' => __DIR__ . '/../../../',
    … …  
    1212    'versions' => array(
    1313        'alanef/free_plugin_lib' => array(
    14             'pretty_version' => '1.3.0',
    15             'version' => '1.3.0.0',
    16             'reference' => '6dfce2995dde31a1d12e6f8113c4b420ebd83fe9',
     14            'pretty_version' => '1.3.2',
     15            'version' => '1.3.2.0',
     16            'reference' => '9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a',
    1717            'type' => 'library',
    1818            'install_path' => __DIR__ . '/../alanef/free_plugin_lib',
    … …  
    3030        ),
    3131        'fullworks/stop-user-enumeration' => array(
    32             'pretty_version' => 'v1.7.9',
    33             'version' => '1.7.9.0',
    34             'reference' => 'a87eb0b6cac8051b239c5ad7294599b74bdbe768',
     32            'pretty_version' => 'v1.7.10',
     33            'version' => '1.7.10.0',
     34            'reference' => 'eb7d4253c3576c49fdff00384dbac79813ec744a',
    3535            'type' => 'wordpress-plugin',
    3636            'install_path' => __DIR__ . '/../../../',
  • stop-user-enumeration/tags/1.7.10/languages/stop-user-enumeration.pot

    r3724500 r3761091  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Stop User Enumeration 1.7.9\n"
     5"Project-Id-Version: Stop User Enumeration 1.7.10\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/stop-user-enumeration\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
  • stop-user-enumeration/tags/1.7.10/readme.txt

    r3724500 r3761091  
    66Tested up to: 7.1
    77Requires PHP: 7.4
    8 Stable tag: 1.7.9
     8Stable tag: 1.7.10
     9Type: free
    910License: GPLv2 or later
    1011License URI: http://www.gnu.org/licenses/gpl-2.0.html
  • stop-user-enumeration/tags/1.7.10/stop-user-enumeration.php

    r3724500 r3761091  
    44Plugin URI: https://fullworksplugins.com/products/stop-user-enumeration/
    55Description: Helps secure your site against hacking attacks through detecting  User Enumeration
    6 Version: 1.7.9
     6Version: 1.7.10
    77Author: Fullworks
    88Requires at least: 6.3
    … …  
    4343
    4444// Define the plugin version constant.
    45 define( 'STOP_USER_ENUMERATION_PLUGIN_VERSION', '1.7.9' );
     45define( 'STOP_USER_ENUMERATION_PLUGIN_VERSION', '1.7.10' );
    4646
    4747// Include the autoloader to dynamically include the classes.
  • stop-user-enumeration/trunk/changelog.txt

    r3724500 r3761091  
    11== Changelog ==
     2= 1.7.10 =
     3* Fixed: the settings notice could not be dismissed and kept coming back on every page load
     4* Fixed: on sites running other Fullworks free plugins, the opt-in notice and menu could belong to the wrong plugin
     5* Fixed: leaving the opt-in screen without choosing left the notice up for good; visiting settings now shows the opt-in screen again until you click Allow or Skip
     6* Changed: the notice now links straight to the opt-in screen instead of the settings page
     7* Changed: updated the bundled opt-in library to 1.3.2
     8
    29= 1.7.9 =
    310* Fixed two database writes on every page load: the plugin and its bundled opt-in library both registered an uninstall hook and overwrote each other's entry in the uninstall_plugins option. Uninstall now runs from uninstall.php instead
  • stop-user-enumeration/trunk/includes/vendor/alanef/free_plugin_lib/src/Assets/scripts/main.js

    r3259418 r3761091  
    2929                action: 'ffpl_handle_optin',
    3030                email: email,
     31                shortname: ffplData.shortname,
    3132                nonce: ffplData.nonce
    3233            },
  • stop-user-enumeration/trunk/includes/vendor/alanef/free_plugin_lib/src/Classes/Email.php

    r3679961 r3761091  
    44
    55class Email {
    6         private static $plugin_shortname;
     6        private $plugin_shortname;
    77
    88        // Plugin shortname to ID mapping
    … …  
    2020
    2121        public function __construct($plugin_shortname) {
    22                 self::$plugin_shortname = $plugin_shortname;
     22                $this->plugin_shortname = $plugin_shortname;
    2323        }
    2424
    … …  
    3333
    3434                // Get plugin ID from map
    35                 $plugin_id = $plugin_map[self::$plugin_shortname] ?? null;
     35                $plugin_id = $plugin_map[$this->plugin_shortname] ?? null;
    3636                if (!$plugin_id) {
    3737                        return false;
  • stop-user-enumeration/trunk/includes/vendor/alanef/free_plugin_lib/src/Main.php

    r3724500 r3761091  
    77class Main {
    88        /**
     9         * Shortname of the most recently constructed instance, used only as the default for
     10         * plugin_uninstall(). Every plugin bundling this library shares one copy of the class,
     11         * so per-plugin state must live on the instance, never here.
     12         *
    913         * @var mixed
    1014         */
    11         private static $plugin_shortname;
     15        private static $last_plugin_shortname;
     16        /**
     17         * @var mixed
     18         */
     19        private $plugin_shortname;
    1220        /**
    1321         * @var string
    1422         */
    15         private static $version = '1.3.0';
    16         /**
     23        private static $version = '1.3.2';
     24        /**
     25         * The host plugin's settings page as passed: a menu slug ('stop-user-enumeration'), a hook
     26         * suffix ('settings_page_contact-form-settings') or '' for a plugin with no settings page.
     27         *
    1728         * @var mixed
    1829         */
    1930        private $page;
     31        /**
     32         * The menu slug part of $page, compared with $_GET['page'].
     33         *
     34         * @var string
     35         */
     36        private $page_slug;
    2037        /**
    2138         * @var \WP_Screen|null
    … …  
    3350
    3451        public function __construct($plugin_file, $settings_page, $plugin_shortname, $page, $plugin_name) {
    35                 self::$plugin_shortname = $plugin_shortname;
     52                $this->plugin_shortname = $plugin_shortname;
     53                self::$last_plugin_shortname = $plugin_shortname;
    3654                $this->page = $page;
     55                $this->page_slug = (string) preg_replace('/^.*_page_/', '', (string) $page);
    3756                $this->plugin_file = $plugin_file;
    3857                $this->settings_page = $settings_page;
    … …  
    6786         */
    6887        public static function plugin_uninstall($plugin_shortname = null) {
    69                 $plugin_shortname = null === $plugin_shortname ? self::$plugin_shortname : $plugin_shortname;
     88                $plugin_shortname = null === $plugin_shortname ? self::$last_plugin_shortname : $plugin_shortname;
    7089                if (empty($plugin_shortname)) {
    7190                        return;
    … …  
    81100                // Verify nonce - the nonce IS the ffpl_skip value
    82101                $nonce = sanitize_text_field(wp_unslash($_GET['ffpl_skip']));
    83                 if (!wp_verify_nonce($nonce, 'ffpl_skip_' . self::$plugin_shortname)) {
     102                if (!wp_verify_nonce($nonce, 'ffpl_skip_' . $this->plugin_shortname)) {
    84103                        return;
    85104                }
    86105
    87106                // Set status to optout - user explicitly skipped
    88                 update_site_option(self::$plugin_shortname . '_form_rendered', 'optout');
     107                update_site_option($this->plugin_shortname . '_form_rendered', 'optout');
    89108
    90109                // Redirect to clean URL (remove the skip param)
    … …  
    94113
    95114        public function handle_optin_page() {
     115                // admin_init also runs for admin-ajax.php, and a redirect there breaks every AJAX call on the site
     116                if (wp_doing_ajax() || '' === $this->page_slug || !current_user_can('manage_options')) {
     117                        return;
     118                }
     119
    96120                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is page routing, not form processing
    97121                $current_page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
    98                 $option = get_site_option(self::$plugin_shortname . '_form_rendered');
    99                 if ('pending' === $option && $current_page === $this->page) {
    100                         update_site_option(self::$plugin_shortname . '_form_rendered', 'rendering');
    101                         wp_safe_redirect(admin_url('options-general.php?page=ffpl-opt-in-' . self::$plugin_shortname));
     122                $option = get_site_option($this->plugin_shortname . '_form_rendered');
     123                // 'rendering' means the opt-in screen was shown but left without a choice, so ask again
     124                if (in_array($option, array('pending', 'rendering'), true) && $current_page === $this->page_slug) {
     125                        update_site_option($this->plugin_shortname . '_form_rendered', 'rendering');
     126                        wp_safe_redirect(admin_url('options-general.php?page=ffpl-opt-in-' . $this->plugin_shortname));
    102127                        exit;
    103128                }
    … …  
    110135                        $settings_link
    111136                );
    112                 if ('optout' === get_site_option(self::$plugin_shortname . '_form_rendered', 'optout')) {
    113                         $settings_link = '<a href="' . esc_url(admin_url('options-general.php?page=ffpl-opt-in-'.self::$plugin_shortname )) . '" style="font-weight:900; font-size: 110%; color: #b32d2e;">' . esc_html($this->translate('Opt In')) . '</a>';
     137                if ('optout' === get_site_option($this->plugin_shortname . '_form_rendered', 'optout')) {
     138                        $settings_link = '<a href="' . esc_url(admin_url('options-general.php?page=ffpl-opt-in-'.$this->plugin_shortname )) . '" style="font-weight:900; font-size: 110%; color: #b32d2e;">' . esc_html($this->translate('Opt In')) . '</a>';
    114139                        array_unshift(
    115140                                $links,
    … …  
    123148        public function add_settings_page() {
    124149                // First-run detection - if option doesn't exist, set to pending
    125                 $option = get_site_option(self::$plugin_shortname . '_form_rendered');
     150                $option = get_site_option($this->plugin_shortname . '_form_rendered');
    126151                if (false === $option) {
    127                         update_site_option(self::$plugin_shortname . '_form_rendered', 'pending');
     152                        update_site_option($this->plugin_shortname . '_form_rendered', 'pending');
    128153                        $option = 'pending';
    129154                }
    … …  
    135160                                esc_html($this->translate('Opt In ') . esc_html( $this->plugin_name) ), // Menu title
    136161                                'manage_options', // Capability
    137                                 'ffpl-opt-in-'.self::$plugin_shortname, // Menu slug
     162                                'ffpl-opt-in-'.$this->plugin_shortname, // Menu slug
    138163                                array($this, 'render_opt_in_page') // Callback function
    139164                        );
    … …  
    200225                            </div>
    201226                            <div class="button-2">
    202                                 <a href="<?php echo esc_url(add_query_arg('ffpl_skip', wp_create_nonce('ffpl_skip_' . self::$plugin_shortname), $this->settings_page)); ?>"
     227                                <a href="<?php echo esc_url(add_query_arg('ffpl_skip', wp_create_nonce('ffpl_skip_' . $this->plugin_shortname), $this->settings_page)); ?>"
    203228                                   class="button button-secondary btn-skip" name="action" value="skip"
    204229                                   tabindex="2">
    … …  
    241266        public function conditional_enqueue_assets($hook) {
    242267                // Only load on our specific page
    243                 if ($hook !== 'settings_page_ffpl-opt-in-'.self::$plugin_shortname) {
     268                if ($hook !== 'settings_page_ffpl-opt-in-'.$this->plugin_shortname) {
    244269                        return;
    245270                }
    … …  
    260285                        'ajaxurl' => admin_url('admin-ajax.php'),
    261286                        'nonce' => wp_create_nonce('ffpl_optin_nonce'),
     287                        'shortname' => $this->plugin_shortname,
    262288                        'settings_page' => $this->settings_page
    263289                ));
    … …  
    265291
    266292        public function handle_optin_ajax() {
     293                // Every plugin bundling the library registers this handler; leave other plugins' requests to them
     294                if (isset($_POST['shortname']) && !$this->is_own_shortname(sanitize_key(wp_unslash($_POST['shortname'])))) {
     295                        return;
     296                }
     297
    267298                // Verify request method
    268299                if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
    … …  
    297328                $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
    298329
    299                 $email_handler = new Email(self::$plugin_shortname);
     330                $email_handler = new Email($this->plugin_shortname);
    300331                $result = $email_handler->handle_optin_submission($email);
    301332
    302333                if ($result) {
    303                         update_site_option(self::$plugin_shortname . '_form_rendered', 'optin');
     334                        update_site_option($this->plugin_shortname . '_form_rendered', 'optin');
    304335                        wp_send_json_success(array(
    305336                                'redirect_url' => $this->settings_page
    306337                        ));
    307338                } else {
    308                         update_site_option(self::$plugin_shortname . '_form_rendered', 'optout');
     339                        update_site_option($this->plugin_shortname . '_form_rendered', 'optout');
    309340                        wp_send_json_success(array(
    310341                                'redirect_url' => $this->settings_page
    … …  
    325356                }
    326357
    327                 $option = get_site_option(self::$plugin_shortname . '_form_rendered');
     358                $option = get_site_option($this->plugin_shortname . '_form_rendered');
    328359
    329360                // Only show notice if no decision has been made yet (pending or rendering)
    … …  
    334365
    335366                // Don't show if user dismissed the notice
    336                 if (get_user_meta(get_current_user_id(), self::$plugin_shortname . '_notice_dismissed', true)) {
     367                if (get_user_meta(get_current_user_id(), $this->plugin_shortname . '_notice_dismissed', true)) {
    337368                        return;
    338369                }
    … …  
    345376                        'tools',
    346377                        'options-general',
    347                         'settings_page_' . $this->page,
    348378                );
     379                if ('' !== $this->page_slug) {
     380                        // A hook suffix is already the screen base; a menu slug is assumed to be under Settings
     381                        $display_on_pages[] = $this->page === $this->page_slug ? 'settings_page_' . $this->page : $this->page;
     382                }
    349383
    350384                if (!in_array($page, $display_on_pages, true)) {
    … …  
    352386                }
    353387
    354                 $opt_in_url = admin_url('options-general.php?page=ffpl-opt-in-' . self::$plugin_shortname);
     388                $opt_in_url = admin_url('options-general.php?page=ffpl-opt-in-' . $this->plugin_shortname);
    355389                ?>
    356                 <div class="notice notice-info is-dismissible ffpl-setup-notice" data-shortname="<?php echo esc_attr(self::$plugin_shortname); ?>">
     390                <div class="notice notice-info is-dismissible ffpl-setup-notice" data-shortname="<?php echo esc_attr($this->plugin_shortname); ?>">
    357391                        <p>
    358392                                <strong><?php echo esc_html($this->plugin_name); ?>:</strong>
    359393                                <?php
    360394                                printf(
    361                                         '%s <a href="%s"><strong>%s</strong></a> %s',
    362                                         esc_html($this->translate('You haven\'t visited settings yet.')),
    363                                         esc_url($this->settings_page),
    364                                         esc_html($this->translate('Please check your settings')),
    365                                         esc_html($this->translate('for optimal configuration and opt in for security updates, tips and occasional offers.'))
     395                                        '%s <a href="%s"><strong>%s</strong></a>',
     396                                        esc_html($this->translate('Choose whether to receive security updates, tips and occasional offers.')),
     397                                        esc_url($opt_in_url),
     398                                        esc_html($this->translate('Opt in or skip'))
    366399                                );
    367400                                ?>
    … …  
    370403                <script>
    371404                jQuery(document).ready(function($) {
    372                         $('.ffpl-setup-notice').on('click', '.notice-dismiss', function() {
    373                                 var shortname = $(this).closest('.ffpl-setup-notice').data('shortname');
     405                        $('.ffpl-setup-notice[data-shortname="<?php echo esc_js($this->plugin_shortname); ?>"]').on('click', '.notice-dismiss', function() {
    374406                                $.post(ajaxurl, {
    375407                                        action: 'ffpl_dismiss_notice',
    376                                         shortname: shortname,
     408                                        shortname: '<?php echo esc_js($this->plugin_shortname); ?>',
    377409                                        nonce: '<?php echo esc_js(wp_create_nonce('ffpl_dismiss_notice')); ?>'
    378410                                });
    … …  
    394426                }
    395427
    396                 $shortname = isset($_POST['shortname']) ? sanitize_key($_POST['shortname']) : '';
    397                 if ($shortname === self::$plugin_shortname) {
    398                         update_user_meta(get_current_user_id(), self::$plugin_shortname . '_notice_dismissed', true);
    399                         wp_send_json_success();
    400                 }
    401 
    402                 wp_send_json_error();
     428                // Every plugin bundling the library registers this handler; leave other plugins' requests to them
     429                $shortname = isset($_POST['shortname']) ? sanitize_key(wp_unslash($_POST['shortname'])) : '';
     430                if (!$this->is_own_shortname($shortname)) {
     431                        return;
     432                }
     433
     434                // The meta key keeps the shortname's original case so existing dismissals stay readable
     435                update_user_meta(get_current_user_id(), $this->plugin_shortname . '_notice_dismissed', true);
     436                wp_send_json_success();
    403437                wp_die();
     438        }
     439
     440        /**
     441         * sanitize_key() lowercases, and host plugins pass mixed-case shortnames such as 'SUE'.
     442         *
     443         * @param string $shortname A shortname that has been through sanitize_key().
     444         */
     445        private function is_own_shortname($shortname) {
     446                return '' !== $shortname && $shortname === sanitize_key($this->plugin_shortname);
    404447        }
    405448
  • stop-user-enumeration/trunk/includes/vendor/autoload.php

    r3724500 r3761091  
    2020require_once __DIR__ . '/composer/autoload_real.php';
    2121
    22 return ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee::getLoader();
     22return ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963::getLoader();
  • stop-user-enumeration/trunk/includes/vendor/composer/autoload_real.php

    r3724500 r3761091  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee
     5class ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963
    66{
    77    private static $loader;
    … …  
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit09347b3d36fb9de9d34777796c5e08ee', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInit3836c91313ab2ce66e7903b7eb493963', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::getInitializer($loader));
    3333
    3434        $loader->register(true);
  • stop-user-enumeration/trunk/includes/vendor/composer/autoload_static.php

    r3724500 r3761091  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee
     7class ComposerStaticInit3836c91313ab2ce66e7903b7eb493963
    88{
    99    public static $prefixLengthsPsr4 = array (
    … …  
    8888    {
    8989        return \Closure::bind(function () use ($loader) {
    90             $loader->prefixLengthsPsr4 = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$prefixLengthsPsr4;
    91             $loader->prefixDirsPsr4 = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$prefixDirsPsr4;
    92             $loader->prefixesPsr0 = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$prefixesPsr0;
    93             $loader->classMap = ComposerStaticInit09347b3d36fb9de9d34777796c5e08ee::$classMap;
     90            $loader->prefixLengthsPsr4 = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$prefixLengthsPsr4;
     91            $loader->prefixDirsPsr4 = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$prefixDirsPsr4;
     92            $loader->prefixesPsr0 = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$prefixesPsr0;
     93            $loader->classMap = ComposerStaticInit3836c91313ab2ce66e7903b7eb493963::$classMap;
    9494
    9595        }, null, ClassLoader::class);
  • stop-user-enumeration/trunk/includes/vendor/composer/installed.json

    r3724500 r3761091  
    33        {
    44            "name": "alanef/free_plugin_lib",
    5             "version": "1.3.0",
    6             "version_normalized": "1.3.0.0",
     5            "version": "1.3.2",
     6            "version_normalized": "1.3.2.0",
    77            "source": {
    88                "type": "git",
    99                "url": "https://github.com/alanef/free_plugin_lib.git",
    10                 "reference": "6dfce2995dde31a1d12e6f8113c4b420ebd83fe9"
     10                "reference": "9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a"
    1111            },
    1212            "dist": {
    1313                "type": "zip",
    14                 "url": "https://api.github.com/repos/alanef/free_plugin_lib/zipball/6dfce2995dde31a1d12e6f8113c4b420ebd83fe9",
    15                 "reference": "6dfce2995dde31a1d12e6f8113c4b420ebd83fe9",
     14                "url": "https://api.github.com/repos/alanef/free_plugin_lib/zipball/9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a",
     15                "reference": "9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a",
    1616                "shasum": ""
    1717            },
    … …  
    2525                "yoast/wp-test-utils": "^1.2"
    2626            },
    27             "time": "2026-10-02T09:11:20+00:00",
     27            "time": "2026-10-23T06:44:46+00:00",
    2828            "type": "library",
    2929            "installation-source": "dist",
    … …  
    4646            "support": {
    4747                "issues": "https://github.com/alanef/free_plugin_lib/issues",
    48                 "source": "https://github.com/alanef/free_plugin_lib/tree/v1.3.0"
     48                "source": "https://github.com/alanef/free_plugin_lib/tree/v1.3.2"
    4949            },
    5050            "install-path": "../alanef/free_plugin_lib"
  • stop-user-enumeration/trunk/includes/vendor/composer/installed.php

    r3724500 r3761091  
    22    'root' => array(
    33        'name' => 'fullworks/stop-user-enumeration',
    4         'pretty_version' => 'v1.7.9',
    5         'version' => '1.7.9.0',
    6         'reference' => 'a87eb0b6cac8051b239c5ad7294599b74bdbe768',
     4        'pretty_version' => 'v1.7.10',
     5        'version' => '1.7.10.0',
     6        'reference' => 'eb7d4253c3576c49fdff00384dbac79813ec744a',
    77        'type' => 'wordpress-plugin',
    88        'install_path' => __DIR__ . '/../../../',
    … …  
    1212    'versions' => array(
    1313        'alanef/free_plugin_lib' => array(
    14             'pretty_version' => '1.3.0',
    15             'version' => '1.3.0.0',
    16             'reference' => '6dfce2995dde31a1d12e6f8113c4b420ebd83fe9',
     14            'pretty_version' => '1.3.2',
     15            'version' => '1.3.2.0',
     16            'reference' => '9e94ffc83b7ec4cb82ce341cfc7771fc3af0146a',
    1717            'type' => 'library',
    1818            'install_path' => __DIR__ . '/../alanef/free_plugin_lib',
    … …  
    3030        ),
    3131        'fullworks/stop-user-enumeration' => array(
    32             'pretty_version' => 'v1.7.9',
    33             'version' => '1.7.9.0',
    34             'reference' => 'a87eb0b6cac8051b239c5ad7294599b74bdbe768',
     32            'pretty_version' => 'v1.7.10',
     33            'version' => '1.7.10.0',
     34            'reference' => 'eb7d4253c3576c49fdff00384dbac79813ec744a',
    3535            'type' => 'wordpress-plugin',
    3636            'install_path' => __DIR__ . '/../../../',
  • stop-user-enumeration/trunk/languages/stop-user-enumeration.pot

    r3724500 r3761091  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Stop User Enumeration 1.7.9\n"
     5"Project-Id-Version: Stop User Enumeration 1.7.10\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/stop-user-enumeration\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
  • stop-user-enumeration/trunk/readme.txt

    r3724500 r3761091  
    66Tested up to: 7.1
    77Requires PHP: 7.4
    8 Stable tag: 1.7.9
     8Stable tag: 1.7.10
     9Type: free
    910License: GPLv2 or later
    1011License URI: http://www.gnu.org/licenses/gpl-2.0.html
  • stop-user-enumeration/trunk/stop-user-enumeration.php

    r3724500 r3761091  
    44Plugin URI: https://fullworksplugins.com/products/stop-user-enumeration/
    55Description: Helps secure your site against hacking attacks through detecting  User Enumeration
    6 Version: 1.7.9
     6Version: 1.7.10
    77Author: Fullworks
    88Requires at least: 6.3
    … …  
    4343
    4444// Define the plugin version constant.
    45 define( 'STOP_USER_ENUMERATION_PLUGIN_VERSION', '1.7.9' );
     45define( 'STOP_USER_ENUMERATION_PLUGIN_VERSION', '1.7.10' );
    4646
    4747// Include the autoloader to dynamically include the classes.
Note: See TracChangeset for help on using the changeset viewer.