├── vendor ├── composer │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── platform_check.php │ ├── LICENSE │ ├── autoload_real.php │ ├── installed.php │ ├── autoload_classmap.php │ ├── autoload_static.php │ ├── InstalledVersions.php │ ├── ClassLoader.php │ └── installed.json └── autoload.php ├── uninstall.php ├── wp-beta-tester.php ├── README.md ├── composer.json ├── old-changelog.txt ├── src └── WPBT │ ├── WPBT_Help.php │ ├── WPBT_Bootstrap.php │ ├── WPBT_Extras.php │ ├── WPBT_Settings.php │ ├── WP_Beta_Tester.php │ └── WPBT_Core.php ├── languages └── wordpress-beta-tester.pot ├── CHANGES.md └── readme.txt /vendor/composer/autoload_namespaces.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | = 50600)) { 8 | $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.'; 9 | } 10 | 11 | if ($issues) { 12 | if (!headers_sent()) { 13 | header('HTTP/1.1 500 Internal Server Error'); 14 | } 15 | if (!ini_get('display_errors')) { 16 | if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { 17 | fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); 18 | } elseif (!headers_sent()) { 19 | echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; 20 | } 21 | } 22 | throw new \RuntimeException( 23 | 'Composer detected issues in your platform: ' . implode(' ', $issues) 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | run(); 39 | } 40 | ); 41 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | register(true); 35 | 36 | return $loader; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress Beta Tester 2 | * Tags: beta, advanced, testing 3 | * Contributors: [afragen](https://github.com/afragen), [costdev](https:/github.com/costdev), [pbiron](https://github.com/pbiron), [westi](https://github.com/westi), [mlteal](https://github.com/mlteal) 4 | * License: GPLv2 5 | * License URI: https://www.opensource.org/licenses/GPL-2.0 6 | * Requires at least: 4.9 7 | * Requires PHP: 5.6 8 | * Stable Tag: master 9 | 10 | Allows you to easily upgrade for testing the next versions of WordPress. 11 | 12 | ## Description 13 | This plugin provides an easy way to get involved with beta testing WordPress. 14 | 15 | Once installed it will enable you to upgrade your website to the latest Nightly, Beta, or Release Candidate at the click of a button using the built in upgrader. 16 | 17 | By default once enabled it switches your website onto the point release development track. 18 | 19 | For the more adventurous there is the option to switch to the bleeding edge (trunk) of development. 20 | 21 | Don't forget to backup before you start! 22 | 23 | Please enable auto-updates for this plugin to ensure future changes are properly handled with core updates. 24 | 25 | ### Extra Settings 26 | 27 | There is a setting to **Skip successful autoupdate emails**. It functions to disable sending emails to the admin user for successful autoupdates. Only emails indicating failures of the autoupdate process are sent. 28 | 29 | There is a setting to **Skip bundled plugins and themes**. It functions to disable the installation of any plugins or themes that are bundled with the Core update. 30 | 31 | PRs are welcome on [GitHub](https://github.com/afragen/wordpress-beta-tester). 32 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "afragen/wordpress-beta-tester", 3 | "description": "Allows you to easily upgrade to Beta releases.", 4 | "type": "wordpress-plugin", 5 | "keywords": [ 6 | "wordpress", 7 | "beta", 8 | "test" 9 | ], 10 | "license": "GPL-2.0-or-later", 11 | "authors": [ 12 | { 13 | "name": "Andy Fragen", 14 | "email": "andy@thefragens.com", 15 | "homepage": "https://thefragens.com", 16 | "role": "Developer" 17 | }, 18 | { 19 | "name": "Peter Westwood", 20 | "homepage": "https://blog.ftwr.co.uk/", 21 | "role": "Developer" 22 | }, 23 | { 24 | "name": "Maura Teal", 25 | "homepage": "https://github.com/mlteal", 26 | "role": "Developer" 27 | } 28 | ], 29 | "repositories": [ 30 | { 31 | "type": "vcs", 32 | "url": "https://github.com/afragen/wordpress-beta-tester" 33 | } 34 | ], 35 | "support": { 36 | "issues": "https://github.com/afragen/wordpress-beta-tester/issues", 37 | "source": "https://github.com/afragen/wordpress-beta-tester" 38 | }, 39 | "prefer-stable": true, 40 | "require": { 41 | "php": ">=5.6" 42 | }, 43 | "require-dev": { 44 | "wp-coding-standards/wpcs": "^3.0.0" 45 | }, 46 | "config": { 47 | "allow-plugins": { 48 | "dealerdirect/phpcodesniffer-composer-installer": true 49 | } 50 | }, 51 | "autoload": { 52 | "classmap": [ 53 | "src/" 54 | ] 55 | }, 56 | "scripts": { 57 | "wpcs": [ 58 | "vendor/bin/phpcbf .; vendor/bin/phpcs ." 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /old-changelog.txt: -------------------------------------------------------------------------------- 1 | 2 | = 0.99 = 3 | * Add support for HTTPS urls on WP.org as well as non-HTTPS ones. 4 | 5 | = 0.98 = 6 | * Unforce HTTPS for all api.wordpress.org requests. 7 | 8 | = 0.97 = 9 | * Force HTTPS for all api.wordpress.org requests. 10 | 11 | = 0.96 = 12 | * WordPress 3.4.x compatibility fixes 13 | 14 | = 0.95 = 15 | * Further WordPress 3.2 compatibility fixes from nacin 16 | * Addition of activate and deactivate hooks to clear down the update core transient. 17 | 18 | = 0.94 = 19 | * WordPress 3.2 compatibility fixes from dd32 20 | 21 | = 0.93 = 22 | * Fixed a bug in the point release nightlies stream whereby we displayed the downgrade message erroneously 23 | 24 | = 0.92 = 25 | * Add support for converting WordPress mu installs over to WordPress 3.0 RC 1 dev track 26 | 27 | = 0.91 = 28 | * Fix bug which causes the message to always display 29 | 30 | = 0.90 = 31 | * Added a big error message to warn people that there configuration is going to downgrade the version of WordPress they have installed 32 | 33 | = 0.81 = 34 | * Fixed an issue in the version mangling for the bleeding edge develpment track which didn't handle the x.9 => y.0 transition 35 | * Added translation files for Albanian and French. 36 | 37 | = 0.8 = 38 | * Fixed noticed on dashboard after upgrade and before the update api data was refreshed 39 | * Added translation files for German, Bosnian, Italian, Polish and Romanian. 40 | 41 | = 0.7 = 42 | * Completed support for translations 43 | * Added translation files for Japanese 44 | * Fixed issue with calls to get_preferred_from_update_core() when running on a cron hook. 45 | 46 | = 0.6 = 47 | * Update the code to validate the returned upgrade url so as to ensure that we only offer to upgrade to builds that exist. 48 | 49 | = 0.5 = 50 | * Initial Release containing support for switching your blog to point release nightlies or bleeding edge nightlies 51 | 52 | ## Upgrade Notice 53 | 54 | = 0.97 = 55 | * This update forces https for all WordPress.org API requests please report issues here: http://core.trac.wordpress.org/ticket/18577 56 | 57 | = 0.95 = 58 | * Further WordPress 3.2 compatibility fixes from nacin 59 | 60 | = 0.94 = 61 | * WordPress 3.2 compatibility fixes from dd32 62 | 63 | = 0.92 = 64 | Added support for upgrading WordPress MU to WordPress 3.0 dev builds using built-in upgrader. 65 | Updated Russian language pack. 66 | -------------------------------------------------------------------------------- /vendor/composer/installed.php: -------------------------------------------------------------------------------- 1 | array( 3 | 'name' => 'afragen/wordpress-beta-tester', 4 | 'pretty_version' => 'dev-develop', 5 | 'version' => 'dev-develop', 6 | 'reference' => 'a17684361cdcf1217914b568513e4fddc894ecad', 7 | 'type' => 'wordpress-plugin', 8 | 'install_path' => __DIR__ . '/../../', 9 | 'aliases' => array(), 10 | 'dev' => true, 11 | ), 12 | 'versions' => array( 13 | 'afragen/wordpress-beta-tester' => array( 14 | 'pretty_version' => 'dev-develop', 15 | 'version' => 'dev-develop', 16 | 'reference' => 'a17684361cdcf1217914b568513e4fddc894ecad', 17 | 'type' => 'wordpress-plugin', 18 | 'install_path' => __DIR__ . '/../../', 19 | 'aliases' => array(), 20 | 'dev_requirement' => false, 21 | ), 22 | 'dealerdirect/phpcodesniffer-composer-installer' => array( 23 | 'pretty_version' => 'v1.1.2', 24 | 'version' => '1.1.2.0', 25 | 'reference' => 'e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1', 26 | 'type' => 'composer-plugin', 27 | 'install_path' => __DIR__ . '/../dealerdirect/phpcodesniffer-composer-installer', 28 | 'aliases' => array(), 29 | 'dev_requirement' => true, 30 | ), 31 | 'phpcsstandards/phpcsextra' => array( 32 | 'pretty_version' => '1.4.2', 33 | 'version' => '1.4.2.0', 34 | 'reference' => '8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e', 35 | 'type' => 'phpcodesniffer-standard', 36 | 'install_path' => __DIR__ . '/../phpcsstandards/phpcsextra', 37 | 'aliases' => array(), 38 | 'dev_requirement' => true, 39 | ), 40 | 'phpcsstandards/phpcsutils' => array( 41 | 'pretty_version' => '1.1.3', 42 | 'version' => '1.1.3.0', 43 | 'reference' => '8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9', 44 | 'type' => 'phpcodesniffer-standard', 45 | 'install_path' => __DIR__ . '/../phpcsstandards/phpcsutils', 46 | 'aliases' => array(), 47 | 'dev_requirement' => true, 48 | ), 49 | 'squizlabs/php_codesniffer' => array( 50 | 'pretty_version' => '3.13.5', 51 | 'version' => '3.13.5.0', 52 | 'reference' => '0ca86845ce43291e8f5692c7356fccf3bcf02bf4', 53 | 'type' => 'library', 54 | 'install_path' => __DIR__ . '/../squizlabs/php_codesniffer', 55 | 'aliases' => array(), 56 | 'dev_requirement' => true, 57 | ), 58 | 'wp-coding-standards/wpcs' => array( 59 | 'pretty_version' => '3.2.0', 60 | 'version' => '3.2.0.0', 61 | 'reference' => 'd2421de7cec3274ae622c22c744de9a62c7925af', 62 | 'type' => 'phpcodesniffer-standard', 63 | 'install_path' => __DIR__ . '/../wp-coding-standards/wpcs', 64 | 'aliases' => array(), 65 | 'dev_requirement' => true, 66 | ), 67 | ), 68 | ); 69 | -------------------------------------------------------------------------------- /src/WPBT/WPBT_Help.php: -------------------------------------------------------------------------------- 1 | id, 'wp-beta-tester' ) ) { 32 | return false; 33 | } 34 | 35 | get_current_screen()->set_help_sidebar( 36 | '

' . __( 'For more information:', 'wordpress-beta-tester' ) . '

' 37 | . '

' . __( 'Beta Testing', 'wordpress-beta-tester' ) . '

' 38 | ); 39 | 40 | get_current_screen()->add_help_tab( 41 | array( 42 | 'id' => 'overview', 43 | 'title' => __( 'Overview', 'wordpress-beta-tester' ), 44 | 'content' => '

' 45 | . sprintf( 46 | /* translators: 1: link to backing up database, 2: link to make.wp.org/core, 3: link to beta support forum */ 47 | __( 'By their nature, these releases are unstable and should not be used any place where your data is important. So please back up your database before upgrading to a test release. In order to hear about the latest beta releases, your best bet is to watch the development blog and the beta forum.', 'wordpress-beta-tester' ), 48 | _x( 'https://wordpress.org/support/article/wordpress-backups/', 'URL to database backup instructions on HelpHub', 'wordpress-beta-tester' ), 49 | 'https://make.wordpress.org/core/', 50 | _x( 'https://wordpress.org/support/forum/alphabeta', 'URL to beta support forum', 'wordpress-beta-tester' ) 51 | ) . '

', 52 | ) 53 | ); 54 | 55 | get_current_screen()->add_help_tab( 56 | array( 57 | 'id' => 'beta/RC', 58 | 'title' => __( 'Beta/RC', 'wordpress-beta-tester' ), 59 | 'content' => '

' 60 | . __( 'You must select either the Point release or Bleeding edge channel. Then select the Beta/RC Only or Release Candidates Only stream. Once saved you will only see an update notice when the next release, RC, or beta is available.', 'wordpress-beta-tester' ) . '

' 61 | . __( 'Point release channel only has the Nightlies stream available at this time.', 'wordpress-beta-tester' ) . '

', 62 | ) 63 | ); 64 | 65 | get_current_screen()->add_help_tab( 66 | array( 67 | 'id' => 'dashboard', 68 | 'title' => __( 'Dashboard Widget', 'wordpress-beta-tester' ), 69 | 'content' => '

' . __( 'A dashboard widget is displayed when the plugin is active. It will contain links to milestone commits and filing a bug report. It may contain links to Dev Notes, the Field Guide, and beta/RC release posts.', 'wordpress-beta-tester' ) . '

', 70 | ) 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/WPBT/WPBT_Bootstrap.php: -------------------------------------------------------------------------------- 1 | file = $file; 37 | } 38 | 39 | /** 40 | * Let's get started. 41 | * 42 | * @return void 43 | */ 44 | public function run() { 45 | $this->deactivate_die_wordpress_develop(); 46 | $this->load_hooks(); 47 | self::$options = get_site_option( 48 | 'wp_beta_tester', 49 | array( 50 | 'channel' => 'branch-development', 51 | 'stream-option' => '', 52 | ) 53 | ); 54 | $this->v2_v3_settings_migrator(); 55 | 56 | ( new WP_Beta_Tester( $this->file, self::$options ) )->run(); 57 | } 58 | 59 | /** 60 | * Gracefully transfer v2 settings to v4. 61 | * 62 | * @since 3.0.0 63 | * @return void 64 | */ 65 | private function v2_v3_settings_migrator() { 66 | if ( isset( self::$options['stream'] ) && ! isset( self::$options['channel'] ) ) { 67 | switch ( self::$options['stream'] ) { 68 | case 'point': 69 | self::$options['channel'] = 'branch-development'; 70 | self::$options['stream-option'] = ''; 71 | break; 72 | case 'beta-rc-point': 73 | self::$options['channel'] = 'branch-development'; 74 | self::$options['stream-option'] = 'beta'; 75 | break; 76 | case 'unstable': 77 | self::$options['channel'] = 'development'; 78 | self::$options['stream-option'] = ''; 79 | break; 80 | case 'beta-rc-unstable': 81 | self::$options['channel'] = 'development'; 82 | self::$options['stream-option'] = 'beta'; 83 | break; 84 | } 85 | update_site_option( 'wp_beta_tester', (array) self::$options ); 86 | } 87 | } 88 | 89 | /** 90 | * Deactivate and die if trying to use with `wordpress-develop`. 91 | * 92 | * @return void 93 | */ 94 | private function deactivate_die_wordpress_develop() { 95 | $wp_version = get_bloginfo( 'version' ); 96 | $version_regex = '@(\d+\.\d+(\.\d+)?)-(alpha|beta|RC)(\d+)?-(\d+-src|\d{8}\.\d{6})@'; 97 | $is_wp_develop = preg_match( $version_regex, $wp_version ); 98 | 99 | if ( $is_wp_develop ) { 100 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; 101 | deactivate_plugins( $this->file ); 102 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 103 | wp_die( new \WP_Error( 'deactivate', esc_html__( 'Cannot run WordPress Beta Tester plugin in `wordpress-develop`', 'wordpress-beta-tester' ) ) ); 104 | } 105 | } 106 | 107 | /** 108 | * Load hooks. 109 | * 110 | * @return void 111 | */ 112 | public function load_hooks() { 113 | add_action( 'init', array( $this, 'load_textdomain' ) ); 114 | register_activation_hook( $this->file, array( $this, 'de_activate' ) ); 115 | register_deactivation_hook( $this->file, array( $this, 'de_activate' ) ); 116 | } 117 | 118 | /** 119 | * Load textdomain. 120 | * 121 | * @return void 122 | */ 123 | public function load_textdomain() { 124 | load_plugin_textdomain( 'wordpress-beta-tester' ); 125 | } 126 | 127 | /** 128 | * Run on plugin activation/deactivation. 129 | * 130 | * Delete 'update_core' transient. 131 | * 132 | * @return void 133 | */ 134 | public function de_activate() { 135 | delete_site_transient( 'update_core' ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/WPBT/WPBT_Extras.php: -------------------------------------------------------------------------------- 1 | wp_beta_tester = $wp_beta_tester; 38 | self::$options = $options; 39 | } 40 | 41 | /** 42 | * Initialize Extras. 43 | * 44 | * @return void 45 | */ 46 | public function init() { 47 | $this->load_hooks(); 48 | $this->skip_autoupdate_email(); 49 | $this->skip_bundled_files_on_upgrade(); 50 | } 51 | 52 | /** 53 | * Load hooks. 54 | * 55 | * @return void 56 | */ 57 | public function load_hooks() { 58 | add_filter( 'wp_beta_tester_add_settings_tabs', array( $this, 'add_settings_tab' ) ); 59 | add_action( 'wp_beta_tester_add_settings', array( $this, 'add_settings' ) ); 60 | add_action( 'wp_beta_tester_add_admin_page', array( $this, 'add_admin_page' ), 10, 2 ); 61 | add_action( 'wp_beta_tester_update_settings', array( $this, 'save_settings' ) ); 62 | } 63 | 64 | /** 65 | * Add class settings tab. 66 | * 67 | * @param array $tabs Settings tabs. 68 | * @return array 69 | */ 70 | public function add_settings_tab( $tabs ) { 71 | return array_merge( $tabs, array( 'wp_beta_tester_extras' => esc_html__( 'Extra Settings', 'wordpress-beta-tester' ) ) ); 72 | } 73 | 74 | /** 75 | * Setup Settings API. 76 | * 77 | * @return void 78 | */ 79 | public function add_settings() { 80 | register_setting( 81 | 'wp_beta_tester', 82 | 'wp_beta_tester_extras', 83 | array( 'WPBT_Settings', 'sanitize' ) 84 | ); 85 | 86 | add_settings_section( 87 | 'wp_beta_tester_email', 88 | null, 89 | null, 90 | 'wp_beta_tester_extras' 91 | ); 92 | 93 | add_settings_field( 94 | 'skip_autoupdate_email', 95 | null, 96 | array( 'WPBT_Settings', 'checkbox_setting' ), 97 | 'wp_beta_tester_extras', 98 | 'wp_beta_tester_email', 99 | array( 100 | 'id' => 'skip_autoupdate_email', 101 | 'title' => esc_html__( 'Skip successful autoupdate emails.', 'wordpress-beta-tester' ), 102 | 'description' => esc_html__( 'Disable sending emails to the admin user for successful autoupdates. Only emails indicating failures of the autoupdate process are sent.', 'wordpress-beta-tester' ), 103 | ) 104 | ); 105 | 106 | add_settings_section( 107 | 'wp_beta_tester_bundled', 108 | null, 109 | null, 110 | 'wp_beta_tester_extras' 111 | ); 112 | 113 | add_settings_field( 114 | 'skip_bundled_files_on_upgrade', 115 | null, 116 | array( 'WPBT_Settings', 'checkbox_setting' ), 117 | 'wp_beta_tester_extras', 118 | 'wp_beta_tester_bundled', 119 | array( 120 | 'id' => 'skip_bundled_files_on_upgrade', 121 | 'title' => esc_html__( 'Skip bundled plugins and themes on upgrade.', 'wordpress-beta-tester' ), 122 | 'description' => esc_html__( 'Bundled plugins and themes are skipped on Core Upgrade.', 'wordpress-beta-tester' ), 123 | ) 124 | ); 125 | } 126 | 127 | /** 128 | * Save settings. 129 | * 130 | * @param mixed $post_data $_POST data. 131 | * @return void 132 | */ 133 | public function save_settings( $post_data ) { 134 | if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ), 'wp_beta_tester_extras-options' ) 135 | ) { 136 | return; 137 | } 138 | 139 | if ( isset( $post_data['option_page'] ) 140 | && 'wp_beta_tester_extras' === $post_data['option_page'] 141 | ) { 142 | $options = isset( $post_data['wp-beta-tester'] ) 143 | ? $post_data['wp-beta-tester'] 144 | : array(); 145 | $options = WPBT_Settings::sanitize( $options ); 146 | $filtered_options = array_filter( self::$options, array( $this, 'get_unchecked_options' ) ); 147 | $options = array_merge( $filtered_options, $options ); 148 | update_site_option( 'wp_beta_tester', (array) $options ); 149 | add_filter( 'wp_beta_tester_save_redirect', array( $this, 'save_redirect_page' ) ); 150 | } 151 | } 152 | 153 | /** 154 | * Filter saved setting to remove unchecked checkboxes. 155 | * 156 | * @param array $checked Options. 157 | * @return bool 158 | */ 159 | private function get_unchecked_options( $checked ) { 160 | return '1' !== $checked; 161 | } 162 | 163 | /** 164 | * Redirect page/tab after saving options. 165 | * 166 | * @param mixed $option_page Settings page. 167 | * @return array 168 | */ 169 | public function save_redirect_page( $option_page ) { 170 | return array_merge( $option_page, array( 'wp_beta_tester_extras' ) ); 171 | } 172 | 173 | /** 174 | * Create core settings page. 175 | * 176 | * @param array $tab Settings tab. 177 | * @param string $action Form action. 178 | * @return void 179 | */ 180 | public function add_admin_page( $tab, $action ) { 181 | ?> 182 |
183 | 184 |
185 | 186 | 187 | 188 |
189 | 190 |
191 | $vendorDir . '/composer/InstalledVersions.php', 10 | 'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php', 11 | 'PHPCSUtils\\BackCompat\\BCFile' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php', 12 | 'PHPCSUtils\\BackCompat\\BCTokens' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php', 13 | 'PHPCSUtils\\BackCompat\\Helper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/Helper.php', 14 | 'PHPCSUtils\\Exceptions\\InvalidTokenArray' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/InvalidTokenArray.php', 15 | 'PHPCSUtils\\Exceptions\\LogicException' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/LogicException.php', 16 | 'PHPCSUtils\\Exceptions\\MissingArgumentError' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/MissingArgumentError.php', 17 | 'PHPCSUtils\\Exceptions\\OutOfBoundsStackPtr' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/OutOfBoundsStackPtr.php', 18 | 'PHPCSUtils\\Exceptions\\RuntimeException' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/RuntimeException.php', 19 | 'PHPCSUtils\\Exceptions\\TestFileNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestFileNotFound.php', 20 | 'PHPCSUtils\\Exceptions\\TestMarkerNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestMarkerNotFound.php', 21 | 'PHPCSUtils\\Exceptions\\TestTargetNotFound' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestTargetNotFound.php', 22 | 'PHPCSUtils\\Exceptions\\TypeError' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TypeError.php', 23 | 'PHPCSUtils\\Exceptions\\UnexpectedTokenType' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/UnexpectedTokenType.php', 24 | 'PHPCSUtils\\Exceptions\\ValueError' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/ValueError.php', 25 | 'PHPCSUtils\\Fixers\\SpacesFixer' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Fixers/SpacesFixer.php', 26 | 'PHPCSUtils\\Internal\\Cache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/Cache.php', 27 | 'PHPCSUtils\\Internal\\IsShortArrayOrList' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrList.php', 28 | 'PHPCSUtils\\Internal\\IsShortArrayOrListWithCache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrListWithCache.php', 29 | 'PHPCSUtils\\Internal\\NoFileCache' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/NoFileCache.php', 30 | 'PHPCSUtils\\Internal\\StableCollections' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/StableCollections.php', 31 | 'PHPCSUtils\\TestUtils\\ConfigDouble' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/ConfigDouble.php', 32 | 'PHPCSUtils\\TestUtils\\RulesetDouble' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/RulesetDouble.php', 33 | 'PHPCSUtils\\TestUtils\\UtilityMethodTestCase' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/UtilityMethodTestCase.php', 34 | 'PHPCSUtils\\Tokens\\Collections' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/Collections.php', 35 | 'PHPCSUtils\\Tokens\\TokenHelper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/TokenHelper.php', 36 | 'PHPCSUtils\\Utils\\Arrays' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Arrays.php', 37 | 'PHPCSUtils\\Utils\\Conditions' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Conditions.php', 38 | 'PHPCSUtils\\Utils\\Constants' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Constants.php', 39 | 'PHPCSUtils\\Utils\\Context' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php', 40 | 'PHPCSUtils\\Utils\\ControlStructures' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php', 41 | 'PHPCSUtils\\Utils\\FileInfo' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FileInfo.php', 42 | 'PHPCSUtils\\Utils\\FilePath' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FilePath.php', 43 | 'PHPCSUtils\\Utils\\FunctionDeclarations' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php', 44 | 'PHPCSUtils\\Utils\\GetTokensAsString' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/GetTokensAsString.php', 45 | 'PHPCSUtils\\Utils\\Lists' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Lists.php', 46 | 'PHPCSUtils\\Utils\\MessageHelper' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/MessageHelper.php', 47 | 'PHPCSUtils\\Utils\\Namespaces' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Namespaces.php', 48 | 'PHPCSUtils\\Utils\\NamingConventions' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/NamingConventions.php', 49 | 'PHPCSUtils\\Utils\\Numbers' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Numbers.php', 50 | 'PHPCSUtils\\Utils\\ObjectDeclarations' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ObjectDeclarations.php', 51 | 'PHPCSUtils\\Utils\\Operators' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Operators.php', 52 | 'PHPCSUtils\\Utils\\Orthography' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Orthography.php', 53 | 'PHPCSUtils\\Utils\\Parentheses' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Parentheses.php', 54 | 'PHPCSUtils\\Utils\\PassedParameters' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/PassedParameters.php', 55 | 'PHPCSUtils\\Utils\\Scopes' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Scopes.php', 56 | 'PHPCSUtils\\Utils\\TextStrings' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TextStrings.php', 57 | 'PHPCSUtils\\Utils\\TypeString' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TypeString.php', 58 | 'PHPCSUtils\\Utils\\UseStatements' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/UseStatements.php', 59 | 'PHPCSUtils\\Utils\\Variables' => $vendorDir . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Variables.php', 60 | 'WPBT_Bootstrap' => $baseDir . '/src/WPBT/WPBT_Bootstrap.php', 61 | 'WPBT_Core' => $baseDir . '/src/WPBT/WPBT_Core.php', 62 | 'WPBT_Extras' => $baseDir . '/src/WPBT/WPBT_Extras.php', 63 | 'WPBT_Help' => $baseDir . '/src/WPBT/WPBT_Help.php', 64 | 'WPBT_Settings' => $baseDir . '/src/WPBT/WPBT_Settings.php', 65 | 'WP_Beta_Tester' => $baseDir . '/src/WPBT/WP_Beta_Tester.php', 66 | ); 67 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 57, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 18 | array ( 19 | 0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src', 20 | ), 21 | ); 22 | 23 | public static $classMap = array ( 24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 25 | 'PHPCSUtils\\AbstractSniffs\\AbstractArrayDeclarationSniff' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/AbstractSniffs/AbstractArrayDeclarationSniff.php', 26 | 'PHPCSUtils\\BackCompat\\BCFile' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCFile.php', 27 | 'PHPCSUtils\\BackCompat\\BCTokens' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/BCTokens.php', 28 | 'PHPCSUtils\\BackCompat\\Helper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/BackCompat/Helper.php', 29 | 'PHPCSUtils\\Exceptions\\InvalidTokenArray' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/InvalidTokenArray.php', 30 | 'PHPCSUtils\\Exceptions\\LogicException' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/LogicException.php', 31 | 'PHPCSUtils\\Exceptions\\MissingArgumentError' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/MissingArgumentError.php', 32 | 'PHPCSUtils\\Exceptions\\OutOfBoundsStackPtr' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/OutOfBoundsStackPtr.php', 33 | 'PHPCSUtils\\Exceptions\\RuntimeException' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/RuntimeException.php', 34 | 'PHPCSUtils\\Exceptions\\TestFileNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestFileNotFound.php', 35 | 'PHPCSUtils\\Exceptions\\TestMarkerNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestMarkerNotFound.php', 36 | 'PHPCSUtils\\Exceptions\\TestTargetNotFound' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TestTargetNotFound.php', 37 | 'PHPCSUtils\\Exceptions\\TypeError' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/TypeError.php', 38 | 'PHPCSUtils\\Exceptions\\UnexpectedTokenType' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/UnexpectedTokenType.php', 39 | 'PHPCSUtils\\Exceptions\\ValueError' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Exceptions/ValueError.php', 40 | 'PHPCSUtils\\Fixers\\SpacesFixer' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Fixers/SpacesFixer.php', 41 | 'PHPCSUtils\\Internal\\Cache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/Cache.php', 42 | 'PHPCSUtils\\Internal\\IsShortArrayOrList' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrList.php', 43 | 'PHPCSUtils\\Internal\\IsShortArrayOrListWithCache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/IsShortArrayOrListWithCache.php', 44 | 'PHPCSUtils\\Internal\\NoFileCache' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/NoFileCache.php', 45 | 'PHPCSUtils\\Internal\\StableCollections' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Internal/StableCollections.php', 46 | 'PHPCSUtils\\TestUtils\\ConfigDouble' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/ConfigDouble.php', 47 | 'PHPCSUtils\\TestUtils\\RulesetDouble' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/RulesetDouble.php', 48 | 'PHPCSUtils\\TestUtils\\UtilityMethodTestCase' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/TestUtils/UtilityMethodTestCase.php', 49 | 'PHPCSUtils\\Tokens\\Collections' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/Collections.php', 50 | 'PHPCSUtils\\Tokens\\TokenHelper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Tokens/TokenHelper.php', 51 | 'PHPCSUtils\\Utils\\Arrays' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Arrays.php', 52 | 'PHPCSUtils\\Utils\\Conditions' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Conditions.php', 53 | 'PHPCSUtils\\Utils\\Constants' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Constants.php', 54 | 'PHPCSUtils\\Utils\\Context' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Context.php', 55 | 'PHPCSUtils\\Utils\\ControlStructures' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ControlStructures.php', 56 | 'PHPCSUtils\\Utils\\FileInfo' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FileInfo.php', 57 | 'PHPCSUtils\\Utils\\FilePath' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FilePath.php', 58 | 'PHPCSUtils\\Utils\\FunctionDeclarations' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/FunctionDeclarations.php', 59 | 'PHPCSUtils\\Utils\\GetTokensAsString' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/GetTokensAsString.php', 60 | 'PHPCSUtils\\Utils\\Lists' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Lists.php', 61 | 'PHPCSUtils\\Utils\\MessageHelper' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/MessageHelper.php', 62 | 'PHPCSUtils\\Utils\\Namespaces' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Namespaces.php', 63 | 'PHPCSUtils\\Utils\\NamingConventions' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/NamingConventions.php', 64 | 'PHPCSUtils\\Utils\\Numbers' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Numbers.php', 65 | 'PHPCSUtils\\Utils\\ObjectDeclarations' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/ObjectDeclarations.php', 66 | 'PHPCSUtils\\Utils\\Operators' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Operators.php', 67 | 'PHPCSUtils\\Utils\\Orthography' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Orthography.php', 68 | 'PHPCSUtils\\Utils\\Parentheses' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Parentheses.php', 69 | 'PHPCSUtils\\Utils\\PassedParameters' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/PassedParameters.php', 70 | 'PHPCSUtils\\Utils\\Scopes' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Scopes.php', 71 | 'PHPCSUtils\\Utils\\TextStrings' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TextStrings.php', 72 | 'PHPCSUtils\\Utils\\TypeString' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/TypeString.php', 73 | 'PHPCSUtils\\Utils\\UseStatements' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/UseStatements.php', 74 | 'PHPCSUtils\\Utils\\Variables' => __DIR__ . '/..' . '/phpcsstandards/phpcsutils/PHPCSUtils/Utils/Variables.php', 75 | 'WPBT_Bootstrap' => __DIR__ . '/../..' . '/src/WPBT/WPBT_Bootstrap.php', 76 | 'WPBT_Core' => __DIR__ . '/../..' . '/src/WPBT/WPBT_Core.php', 77 | 'WPBT_Extras' => __DIR__ . '/../..' . '/src/WPBT/WPBT_Extras.php', 78 | 'WPBT_Help' => __DIR__ . '/../..' . '/src/WPBT/WPBT_Help.php', 79 | 'WPBT_Settings' => __DIR__ . '/../..' . '/src/WPBT/WPBT_Settings.php', 80 | 'WP_Beta_Tester' => __DIR__ . '/../..' . '/src/WPBT/WP_Beta_Tester.php', 81 | ); 82 | 83 | public static function getInitializer(ClassLoader $loader) 84 | { 85 | return \Closure::bind(function () use ($loader) { 86 | $loader->prefixLengthsPsr4 = ComposerStaticInit248763277659ec81f8c4ff690d89804d::$prefixLengthsPsr4; 87 | $loader->prefixDirsPsr4 = ComposerStaticInit248763277659ec81f8c4ff690d89804d::$prefixDirsPsr4; 88 | $loader->classMap = ComposerStaticInit248763277659ec81f8c4ff690d89804d::$classMap; 89 | 90 | }, null, ClassLoader::class); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /languages/wordpress-beta-tester.pot: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2025 WordPress Upgrade/Install Team 2 | # This file is distributed under the GPL v2 or later. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: WordPress Beta Tester 4.0.0\n" 6 | "Report-Msgid-Bugs-To: https://github.com/afragen/wordpress-beta-tester/issues\n" 7 | "Last-Translator: FULL NAME \n" 8 | "Language-Team: LANGUAGE \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2025-11-07T01:38:42+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.12.0\n" 15 | "X-Domain: wordpress-beta-tester\n" 16 | 17 | #. Plugin Name of the plugin 18 | #: wp-beta-tester.php 19 | msgid "WordPress Beta Tester" 20 | msgstr "" 21 | 22 | #. Plugin URI of the plugin 23 | #: wp-beta-tester.php 24 | msgid "https://wordpress.org/plugins/wordpress-beta-tester/" 25 | msgstr "" 26 | 27 | #. Description of the plugin 28 | #: wp-beta-tester.php 29 | msgid "Allows you to easily upgrade to Beta releases." 30 | msgstr "" 31 | 32 | #. Author of the plugin 33 | #: wp-beta-tester.php 34 | msgid "WordPress Upgrade/Install Team" 35 | msgstr "" 36 | 37 | #. Author URI of the plugin 38 | #: wp-beta-tester.php 39 | msgid "https://make.wordpress.org/core/components/upgrade-install/" 40 | msgstr "" 41 | 42 | #: src/WPBT/WPBT_Bootstrap.php:103 43 | msgid "Cannot run WordPress Beta Tester plugin in `wordpress-develop`" 44 | msgstr "" 45 | 46 | #: src/WPBT/WPBT_Core.php:60 47 | msgid "WP Beta Tester Settings" 48 | msgstr "" 49 | 50 | #: src/WPBT/WPBT_Core.php:77 51 | msgid "Core Settings" 52 | msgstr "" 53 | 54 | #: src/WPBT/WPBT_Core.php:84 55 | msgid "Select the update channel you would like this website to use:" 56 | msgstr "" 57 | 58 | #: src/WPBT/WPBT_Core.php:93 59 | msgid "Select one of the stream options below:" 60 | msgstr "" 61 | 62 | #: src/WPBT/WPBT_Core.php:150 63 | msgid "Warning: The `WP_AUTO_UPDATE_CORE` constant is set. WordPress Beta Tester settings will be overridden." 64 | msgstr "" 65 | 66 | #. translators: 1: link to backing up database, 2: link to make.wp.org/core, 3: link to beta support forum 67 | #: src/WPBT/WPBT_Core.php:159 68 | #, php-format 69 | msgid "By their nature, these releases are unstable and should not be used anyplace where your data is important. So please back up your database before upgrading to a test release. In order to hear about the latest beta releases, your best bet is to watch the development blog and the beta forum." 70 | msgstr "" 71 | 72 | #: src/WPBT/WPBT_Core.php:160 73 | #: src/WPBT/WPBT_Help.php:48 74 | msgctxt "URL to database backup instructions on HelpHub" 75 | msgid "https://wordpress.org/support/article/wordpress-backups/" 76 | msgstr "" 77 | 78 | #: src/WPBT/WPBT_Core.php:162 79 | #: src/WPBT/WPBT_Help.php:50 80 | msgctxt "URL to beta support forum" 81 | msgid "https://wordpress.org/support/forum/alphabeta" 82 | msgstr "" 83 | 84 | #. translators: %s: link to new trac ticket 85 | #: src/WPBT/WPBT_Core.php:167 86 | #, php-format 87 | msgid "Thank you for helping test WordPress. Please report any bugs you find." 88 | msgstr "" 89 | 90 | #: src/WPBT/WPBT_Core.php:171 91 | msgid "By default, your WordPress installation uses the stable update channel. To return to this, please deactivate this plugin and re-install from the WordPress Updates page." 92 | msgstr "" 93 | 94 | #. translators: %s: update version 95 | #: src/WPBT/WPBT_Core.php:175 96 | #, php-format 97 | msgid "Currently your site is set to update to %s." 98 | msgstr "" 99 | 100 | #: src/WPBT/WPBT_Core.php:191 101 | msgid "Save after switching" 102 | msgstr "" 103 | 104 | #: src/WPBT/WPBT_Core.php:198 105 | msgid "Point release" 106 | msgstr "" 107 | 108 | #. translators: %s: Current WordPress version base, eg 5.5 109 | #: src/WPBT/WPBT_Core.php:204 110 | #, php-format 111 | msgid "This contains the work that is occurring on a branch in preparation for a %s point release. This should also be fairly stable but will be available before the branch is ready for release." 112 | msgstr "" 113 | 114 | #: src/WPBT/WPBT_Core.php:212 115 | msgid "Bleeding edge" 116 | msgstr "" 117 | 118 | #: src/WPBT/WPBT_Core.php:214 119 | msgid "This is the bleeding edge development code from `trunk` which may be unstable at times. Only use this if you really know what you are doing." 120 | msgstr "" 121 | 122 | #: src/WPBT/WPBT_Core.php:230 123 | msgid "Nightlies" 124 | msgstr "" 125 | 126 | #: src/WPBT/WPBT_Core.php:232 127 | msgid "Latest daily updates." 128 | msgstr "" 129 | 130 | #: src/WPBT/WPBT_Core.php:237 131 | #: src/WPBT/WPBT_Core.php:251 132 | msgid "Beta/RC Only" 133 | msgstr "" 134 | 135 | #: src/WPBT/WPBT_Core.php:239 136 | #: src/WPBT/WPBT_Core.php:253 137 | msgid "This is for the Beta/RC releases only of the selected channel." 138 | msgstr "" 139 | 140 | #: src/WPBT/WPBT_Core.php:243 141 | #: src/WPBT/WPBT_Core.php:257 142 | msgid "Release Candidates Only" 143 | msgstr "" 144 | 145 | #: src/WPBT/WPBT_Core.php:245 146 | #: src/WPBT/WPBT_Core.php:259 147 | msgid "This is for the Release Candidate releases only of the selected channel." 148 | msgstr "" 149 | 150 | #. translators: %s: version number 151 | #: src/WPBT/WPBT_Core.php:329 152 | #, php-format 153 | msgid "version %s" 154 | msgstr "" 155 | 156 | #: src/WPBT/WPBT_Core.php:335 157 | msgid "next development version" 158 | msgstr "" 159 | 160 | #. translators: %l: next version numbers 161 | #: src/WPBT/WPBT_Core.php:340 162 | msgid "version %l, whichever is released first" 163 | msgstr "" 164 | 165 | #. translators: Used to join items in a list with more than 2 items. 166 | #: src/WPBT/WPBT_Core.php:432 167 | #, php-format 168 | msgid "%1$s, %2$s" 169 | msgstr "" 170 | 171 | #. translators: Used to join last two items in a list with more than 2 times. 172 | #: src/WPBT/WPBT_Core.php:434 173 | #, php-format 174 | msgid "%1$s, or %2$s" 175 | msgstr "" 176 | 177 | #. translators: Used to join items in a list with only 2 items. 178 | #: src/WPBT/WPBT_Core.php:436 179 | #, php-format 180 | msgid "%1$s or %2$s" 181 | msgstr "" 182 | 183 | #: src/WPBT/WPBT_Extras.php:71 184 | msgid "Extra Settings" 185 | msgstr "" 186 | 187 | #: src/WPBT/WPBT_Extras.php:101 188 | msgid "Skip successful autoupdate emails." 189 | msgstr "" 190 | 191 | #: src/WPBT/WPBT_Extras.php:102 192 | msgid "Disable sending emails to the admin user for successful autoupdates. Only emails indicating failures of the autoupdate process are sent." 193 | msgstr "" 194 | 195 | #: src/WPBT/WPBT_Extras.php:121 196 | msgid "Skip bundled plugins and themes on upgrade." 197 | msgstr "" 198 | 199 | #: src/WPBT/WPBT_Extras.php:122 200 | msgid "Bundled plugins and themes are skipped on Core Upgrade." 201 | msgstr "" 202 | 203 | #: src/WPBT/WPBT_Help.php:36 204 | msgid "For more information:" 205 | msgstr "" 206 | 207 | #: src/WPBT/WPBT_Help.php:37 208 | msgid "Beta Testing" 209 | msgstr "" 210 | 211 | #: src/WPBT/WPBT_Help.php:43 212 | msgid "Overview" 213 | msgstr "" 214 | 215 | #. translators: 1: link to backing up database, 2: link to make.wp.org/core, 3: link to beta support forum 216 | #: src/WPBT/WPBT_Help.php:47 217 | #, php-format 218 | msgid "By their nature, these releases are unstable and should not be used any place where your data is important. So please back up your database before upgrading to a test release. In order to hear about the latest beta releases, your best bet is to watch the development blog and the beta forum." 219 | msgstr "" 220 | 221 | #: src/WPBT/WPBT_Help.php:58 222 | msgid "Beta/RC" 223 | msgstr "" 224 | 225 | #: src/WPBT/WPBT_Help.php:60 226 | msgid "You must select either the Point release or Bleeding edge channel. Then select the Beta/RC Only or Release Candidates Only stream. Once saved you will only see an update notice when the next release, RC, or beta is available." 227 | msgstr "" 228 | 229 | #: src/WPBT/WPBT_Help.php:61 230 | msgid "Point release channel only has the Nightlies stream available at this time." 231 | msgstr "" 232 | 233 | #: src/WPBT/WPBT_Help.php:68 234 | msgid "Dashboard Widget" 235 | msgstr "" 236 | 237 | #: src/WPBT/WPBT_Help.php:69 238 | msgid "A dashboard widget is displayed when the plugin is active. It will contain links to milestone commits and filing a bug report. It may contain links to Dev Notes, the Field Guide, and beta/RC release posts." 239 | msgstr "" 240 | 241 | #: src/WPBT/WPBT_Settings.php:89 242 | #: src/WPBT/WPBT_Settings.php:285 243 | msgid "Beta Testing WordPress" 244 | msgstr "" 245 | 246 | #: src/WPBT/WPBT_Settings.php:90 247 | msgctxt "Menu item" 248 | msgid "Beta Testing" 249 | msgstr "" 250 | 251 | #: src/WPBT/WPBT_Settings.php:109 252 | msgid "Settings" 253 | msgstr "" 254 | 255 | #: src/WPBT/WPBT_Settings.php:253 256 | msgid "Saved." 257 | msgstr "" 258 | 259 | #: src/WPBT/WPBT_Settings.php:254 260 | msgid "Perhaps you should head on over and upgrade now." 261 | msgstr "" 262 | 263 | #: src/WPBT/WPBT_Settings.php:288 264 | msgid "Please note: Once you have switched your website to one of these beta versions of software, it will not always be possible to downgrade as the database structure may be updated during the development of a major release." 265 | msgstr "" 266 | 267 | #. translators: %s: WordPress Beta Tester Settings page URL 268 | #: src/WPBT/WP_Beta_Tester.php:125 269 | #, php-format 270 | msgid "Warning: Your current WordPress Beta Tester plugin configuration will downgrade your installation to a previous version - please reconfigure it." 271 | msgstr "" 272 | 273 | #: src/WPBT/WP_Beta_Tester.php:249 274 | msgid "WordPress Beta Testing" 275 | msgstr "" 276 | 277 | #. translators: %s: WordPress version 278 | #: src/WPBT/WP_Beta_Tester.php:269 279 | #, php-format 280 | msgid "Please help test WordPress %s." 281 | msgstr "" 282 | 283 | #. translators: %1: link to closed and reopened trac tickets on current milestone 284 | #: src/WPBT/WP_Beta_Tester.php:275 285 | #, php-format 286 | msgid "Here are the commits for the milestone." 287 | msgstr "" 288 | 289 | #. translators: %s: update version 290 | #: src/WPBT/WP_Beta_Tester.php:284 291 | #, php-format 292 | msgid "Currently your site is set to update to %s." 293 | msgstr "" 294 | 295 | #. translators: %s: WP Beta Tester settings URL 296 | #: src/WPBT/WP_Beta_Tester.php:290 297 | #, php-format 298 | msgid "Head over to your WordPress Beta Tester Settings and make sure the beta/RC stream is selected." 299 | msgstr "" 300 | 301 | #. translators: %s: Milestone version 302 | #: src/WPBT/WP_Beta_Tester.php:350 303 | #, php-format 304 | msgid "WordPress %s Dev Notes" 305 | msgstr "" 306 | 307 | #. translators: %s: Milestone version 308 | #: src/WPBT/WP_Beta_Tester.php:360 309 | #, php-format 310 | msgid "WordPress %s Field Guide" 311 | msgstr "" 312 | -------------------------------------------------------------------------------- /src/WPBT/WPBT_Settings.php: -------------------------------------------------------------------------------- 1 | wp_beta_tester = $wp_beta_tester; 46 | } 47 | 48 | /** 49 | * Load up the Settings. 50 | * 51 | * @return void 52 | */ 53 | public function run() { 54 | $this->load_hooks(); 55 | ( new WPBT_Core( $this->wp_beta_tester, self::$options ) )->load_hooks(); 56 | ( new WPBT_Extras( $this->wp_beta_tester, self::$options ) )->init(); 57 | ( new WPBT_Help() )->load_hooks(); 58 | } 59 | 60 | /** 61 | * Load hooks. 62 | * 63 | * @return void 64 | */ 65 | public function load_hooks() { 66 | add_action( 'admin_init', array( $this, 'add_settings' ) ); 67 | add_action( is_multisite() ? 'network_admin_menu' : 'admin_menu', array( $this, 'add_plugin_menu' ) ); 68 | add_action( 'network_admin_edit_wp_beta_tester', array( $this, 'update_settings' ) ); 69 | add_action( 'admin_init', array( $this, 'update_settings' ) ); 70 | 71 | $al_hook = 'plugin_action_links_wordpress-beta-tester/wp-beta-tester.php'; 72 | add_action( is_multisite() ? 'network_admin_' . $al_hook : $al_hook, array( $this, 'add_plugin_action_links' ) ); 73 | 74 | add_action( 'admin_head-plugins.php', array( $this->wp_beta_tester, 'action_admin_head_plugins_php' ) ); 75 | add_action( 'admin_head-update-core.php', array( $this->wp_beta_tester, 'action_admin_head_plugins_php' ) ); 76 | } 77 | 78 | /** 79 | * Add plugin menu to Tools or Settings. 80 | * 81 | * @return void 82 | */ 83 | public function add_plugin_menu() { 84 | $parent = is_multisite() ? 'settings.php' : 'tools.php'; 85 | $capability = is_multisite() ? 'manage_network_options' : 'manage_options'; 86 | 87 | add_submenu_page( 88 | $parent, 89 | esc_html__( 'Beta Testing WordPress', 'wordpress-beta-tester' ), 90 | esc_html_x( 'Beta Testing', 'Menu item', 'wordpress-beta-tester' ), 91 | $capability, 92 | self::$page_slug, 93 | array( $this, 'create_settings_page' ) 94 | ); 95 | } 96 | 97 | /** 98 | * Add Plugin Action Links to Plugin List 99 | * 100 | * @param array $actions Links for the Plugin List Table in the WordPress Admin. 101 | * 102 | * @return array 103 | */ 104 | public function add_plugin_action_links( array $actions ) { 105 | $url = add_query_arg( array( 'page' => self::$page_slug ), network_admin_url( is_multisite() ? 'settings.php' : 'tools.php' ) ); 106 | 107 | return array_merge( 108 | array( 109 | 'settings' => wp_kses_post( '' . esc_html__( 'Settings', 'wordpress-beta-tester' ) . '' ), 110 | ), 111 | $actions 112 | ); 113 | } 114 | 115 | /** 116 | * Calls individuals settings class save methods. 117 | * 118 | * @return void 119 | */ 120 | public function update_settings() { 121 | /** 122 | * Save $options in add-on classes. 123 | * 124 | * @since 2.0.0 125 | */ 126 | // phpcs:ignore WordPress.Security.NonceVerification.Missing 127 | do_action( 'wp_beta_tester_update_settings', $_POST ); 128 | 129 | $this->redirect_on_save(); 130 | } 131 | 132 | /** 133 | * Redirect to correct Settings/Tools tab on Save. 134 | */ 135 | protected function redirect_on_save() { 136 | if ( ! isset( $_POST['_wpnonce'] ) 137 | || ! ( ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ), 'wp_beta_tester_core-options' ) 138 | || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ), 'wp_beta_tester_extras-options' ) ) 139 | ) { 140 | return; 141 | } 142 | 143 | /** 144 | * Filter to add to $option_page array. 145 | * 146 | * @since 2.0.0 147 | * @param array Default array. 148 | */ 149 | $option_page = apply_filters( 'wp_beta_tester_save_redirect', array( 'wp-beta-tester' ) ); 150 | $update = false; 151 | 152 | if ( ( isset( $_POST['action'] ) && 'update' === $_POST['action'] ) 153 | && ( isset( $_POST['option_page'] ) && in_array( $_POST['option_page'], $option_page, true ) ) 154 | ) { 155 | $update = true; 156 | } 157 | 158 | $redirect_url = is_multisite() ? network_admin_url( 'settings.php' ) : admin_url( 'tools.php' ); 159 | 160 | if ( $update ) { 161 | // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url 162 | $query = isset( $_POST['_wp_http_referer'] ) ? parse_url( sanitize_url( wp_unslash( $_POST['_wp_http_referer'] ) ), PHP_URL_QUERY ) : null; 163 | parse_str( (string) $query, $arr ); 164 | $arr['tab'] = ! empty( $arr['tab'] ) ? $arr['tab'] : 'wp_beta_tester_core'; 165 | 166 | $location = add_query_arg( 167 | array( 168 | 'page' => 'wp-beta-tester', 169 | 'tab' => $arr['tab'], 170 | 'updated' => $update, 171 | ), 172 | $redirect_url 173 | ); 174 | $location = add_query_arg( '_wpnonce', wp_create_nonce( 'wpbt_redirect' ), $location ); 175 | wp_safe_redirect( $location ); 176 | exit; 177 | } 178 | } 179 | 180 | /** 181 | * Define tabs for Settings page. 182 | * 183 | * @return array 184 | */ 185 | private function settings_tabs() { 186 | /** 187 | * Filter settings tabs. 188 | * 189 | * @since 2.0.0 190 | * 191 | * @param array $tabs Array of default tabs. 192 | */ 193 | return apply_filters( 'wp_beta_tester_add_settings_tabs', array() ); 194 | } 195 | 196 | /** 197 | * Defines the menu for the admin bar. 198 | * 199 | * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar object. 200 | * @return void 201 | */ 202 | public function admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) { 203 | // Exit if user doesn't have correct capabilities. 204 | $capability = is_multisite() ? 'manage_network_options' : 'manage_options'; 205 | if ( ! current_user_can( $capability ) ) { 206 | return; 207 | } 208 | 209 | /** 210 | * Action hook to add adminbar menu. 211 | * 212 | * @since 3.3.0 213 | * 214 | * @param WP_Admin_Bar $wpadminbar The WP_Admin_Bar object. 215 | */ 216 | do_action( 'wp_beta_tester_add_admin_bar_menu', $wp_admin_bar ); 217 | } 218 | 219 | /** 220 | * Renders setting tabs. 221 | * 222 | * Walks through the object's tabs array and prints them one by one. 223 | * Provides the heading for the settings page. 224 | * 225 | * @access private 226 | * @return void 227 | */ 228 | private function options_tabs() { 229 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended 230 | $current_tab = isset( $_GET['tab'] ) ? sanitize_title_with_dashes( wp_unslash( $_GET['tab'] ) ) : 'wp_beta_tester_core'; 231 | echo ''; 237 | } 238 | 239 | /** 240 | * Create 'Saved' notice for saved settings. 241 | * 242 | * @return void 243 | */ 244 | private function saved_settings_notice() { 245 | if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_GET['_wpnonce'] ) ), 'wpbt_redirect' ) ) { 246 | return; 247 | } 248 | 249 | if ( ( isset( $_GET['updated'] ) && '1' === $_GET['updated'] ) || 250 | ( isset( $_GET['settings-updated'] ) && '1' === $_GET['settings-updated'] ) 251 | ) { 252 | echo '

'; 253 | esc_html_e( 'Saved.', 'wordpress-beta-tester' ); 254 | echo '' . wp_kses_post( __( 'Perhaps you should head on over and upgrade now.', 'wordpress-beta-tester' ) ) . ''; 255 | echo '

'; 256 | } 257 | } 258 | 259 | /** 260 | * Creates a placeholder for added classes settings. 261 | * 262 | * @return void 263 | */ 264 | public function add_settings() { 265 | /** 266 | * Action hook to add settings. 267 | * 268 | * @since 2.0.0 269 | */ 270 | do_action( 'wp_beta_tester_add_settings' ); 271 | } 272 | 273 | /** 274 | * Create the template for all settings pages. 275 | * 276 | * @return void 277 | */ 278 | public function create_settings_page() { 279 | $this->saved_settings_notice(); 280 | $action = is_multisite() ? 'edit.php?action=wp-beta-tester' : 'options.php'; 281 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended 282 | $tab = isset( $_GET['tab'] ) ? sanitize_title_with_dashes( wp_unslash( $_GET['tab'] ) ) : 'wp_beta_tester_core'; 283 | ?> 284 |
285 |

286 | options_tabs(); ?> 287 |
288 |

Please note: Once you have switched your website to one of these beta versions of software, it will not always be possible to downgrade as the database structure may be updated during the development of a major release.', 'wordpress-beta-tester' ) ); ?>

289 |
290 | '; 303 | } 304 | 305 | /** 306 | * Sanitize each setting field as needed. 307 | * 308 | * @param array $input Contains all settings fields as array keys. 309 | * 310 | * @return array 311 | */ 312 | public static function sanitize( $input ) { 313 | $new_input = array(); 314 | if ( ! is_array( $input ) ) { 315 | $new_input = sanitize_text_field( $input ); 316 | } else { 317 | foreach ( array_keys( (array) $input ) as $id ) { 318 | $new_input[ sanitize_text_field( $id ) ] = sanitize_text_field( $input[ $id ] ); 319 | } 320 | } 321 | 322 | return $new_input; 323 | } 324 | 325 | /** 326 | * Get the settings option array and print one of its values. 327 | * 328 | * @param array $args 'id' and 'title'. 329 | */ 330 | public static function checkbox_setting( $args ) { 331 | $checked = isset( self::$options[ $args['id'] ] ) ? self::$options[ $args['id'] ] : null; 332 | ?> 333 | 334 | 343 | 5.6 205 | * allows for overrides when using the `WP_AUTO_UPDATE_CORE` constant 206 | * update on-screen help 207 | 208 | #### 2.2.13 / 2020-09-05 209 | * enclose `WPConfigTransformer` in try/catch 210 | 211 | #### 2.2.12 / 2020-08-10 212 | * fix intermittent PHP warning [#21](https://github.com/afragen/wordpress-beta-tester/issues/21) 213 | * deactivate and die if user attempting to run with `wordpress-develop` 214 | 215 | #### 2.2.11 / 2020-08-01 216 | * minor cleanup 217 | 218 | #### 2.2.10 / 2020-05-01 219 | * sanitize, escape & ignore 220 | * move multiline boolean operator to front of line, new guidelines in WPCS 221 | * fix `correct_versions_for_downgrade()` for being on current release version 222 | 223 | #### 2.2.9 / 2020-03-24 224 | * delete development RSS feed transient after core upgrade 225 | 226 | #### 2.2.8 / 2020-03-17 🍀 227 | * add Dev Notes and Field Guide links to dashboard 228 | * add text/link for bug reporting to trac 229 | * add help tabs to screen 230 | * arbitrarily changed settings page id from `wp_beta_tester` to `wp-beta-tester` 😏 231 | 232 | #### 2.2.7 / 2020-03-02 233 | * update trac link in callout for _closed_ or _reopened_ tickets on the milestone 234 | * only show Beta Tester Settings page link in callout with appropriate privileges, using `manage_network_options` and `manage_options` 235 | * menu to Settings page also checks privileges as above 236 | 237 | #### 2.2.6 / 2020-02-25 238 | * removed extra `` in dashboard callout, 4th time's the charm 😭 239 | 240 | #### 2.2.5 / 2020-02-25 241 | * less greedy regex for matching release posts in RSS for dashboard callout 242 | 243 | #### 2.2.4 / 2020-02-25 🤦‍♂️ 244 | * added dashboard widget for network dashboard 245 | 246 | #### 2.2.3 / 2020-02-25 247 | * add dashboard widget callout for testing 248 | 249 | #### 2.2.2 / 2020-02-22 250 | * fix for strange Core API response where preferred version response contained the word 'version'. We now grab the last word of that response 251 | 252 | #### 2.2.1 / 2020-02-20 253 | * fix some i18n strings, thanks @pedro-mendonca 254 | 255 | #### 2.2.0 / 2020-02-19 256 | * added support for updating to the _beta/RC offer_. Based on and with tons of help from @pbrion, thanks Paul 👏🏻 257 | * fixed so a downgrade from 'unstable' to 'point' serves the correct download 258 | * test and exit from **Extra Settings** if `wp-config.php` is not writeable 259 | 260 | #### 2.1.0 / 2019-09-17 261 | * add extra setting to skip successful autoupdate emails 262 | * add description to checkbox settings 263 | * composer update 264 | 265 | #### 2.0.4 266 | * add update version information to settings page text 267 | 268 | #### 2.0.3 269 | * a11y fixes for settings tabs 270 | * update `wp-cli/wp-config-transformer` 271 | 272 | #### 2.0.2 273 | * a11y fixes for checkbox, thanks @audrasjb 274 | 275 | #### 2.0.1 276 | * fix for incorrect last updated message 277 | 278 | #### 2.0.0 279 | * near complete re-write to use more OOPy practices 280 | * put distinct process into separate classes 281 | * allows for multiple settings tabs for addtional settings 282 | 283 | #### 1.2.6 284 | * remove extraneous code 285 | * add GitHub Plugin URI header 286 | 287 | #### 1.2.5 288 | * fixed error message for downgrading version, thanks @andreas-andersson 289 | 290 | #### 1.2.4 291 | * don't use $GLOBALS 292 | 293 | #### 1.2.3 294 | * updated a few strings and correct typos 295 | * run through WPCS linter 296 | * fixed translation strings to include HTML in context and properly escape with `wp_kses_post()` 297 | * fixed link to settings page under Multisite 298 | 299 | #### 1.2.2 300 | * change wording from blog to website 301 | 302 | #### 1.2.0 303 | * Escape output 304 | * Indicate that _Bleeding edge nightlies_ are _trunk_ 305 | * new screenshot 306 | * code improvements from linter 307 | 308 | #### 1.1.2 309 | * Remove anonymous function for PHP 5.2 compatibility. 310 | 311 | #### 1.1.1 312 | * fixed PHP notice for PHP 7.1 313 | * made URL scheme agnostic 314 | 315 | #### 1.1.0 316 | * Fixed to work properly under Multisite. 317 | 318 | #### 1.0.2 319 | * Update tested up to version to 4.7. 320 | * Fix the location of the settings screen in Multisite (moved under Settings in Network Admin). 321 | * Minor text fixes. 322 | 323 | #### 1.0.1 324 | * Update tested up to version to 4.5. 325 | * Fix PHP7 deprecated constructor notice. 326 | * Change text domain to match the plugin slug. 327 | * Update WordPress.org links to use HTTPS. 328 | * Remove outdated bundled translations in favor of language packs. 329 | 330 | #### 1.0 331 | * Update tested up to version to 4.2. 332 | * Update screenshot. 333 | * Fix a couple typos. 334 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | # WordPress Beta Tester 2 | 3 | Tags: beta, advanced, testing 4 | Contributors: afragen, costdev, pbiron, mlteal, westi 5 | License: GPLv2 6 | License URI: https://www.opensource.org/licenses/GPL-2.0 7 | Requires at least: 4.9 8 | Requires PHP: 5.6 9 | Tested up to: 6.9 10 | Stable Tag: 4.0.0 11 | 12 | Allows you to easily upgrade for testing the next versions of WordPress. 13 | 14 | ## Description 15 | This plugin provides an easy way to get involved with beta testing WordPress. 16 | 17 | Once installed it will enable you to upgrade your website to the latest Nightly, Beta, or Release Candidate at the click of a button using the built in upgrader. 18 | 19 | By default once enabled it switches your website onto the point release update channel. 20 | 21 | For the more adventurous there is the option to switch to the bleeding edge (trunk) of development. 22 | 23 | Don't forget to backup before you start! 24 | 25 | Please enable auto-updates for this plugin to ensure future changes are properly handled with core updates. 26 | 27 | ### Extra Settings 28 | 29 | There is a setting to **Skip successful autoupdate emails**. It functions to disable sending emails to the admin user for successful autoupdates. Only emails indicating failures of the autoupdate process are sent. 30 | 31 | There is a setting to **Skip bundled plugins and themes**. It functions to disable the installation of any plugins or themes that are bundled with the Core update. 32 | 33 | 34 | PRs are welcome on [GitHub](https://github.com/afragen/wordpress-beta-tester). 35 | 36 | ## Changelog 37 | 38 | #### 4.0.0 / 2025-11-07 39 | * refactor to use `core_version_check_query_args` filter for simplicity and compatibility 40 | * remove checks on `WP_AUTO_UPDATE_CORE` 41 | * update to require WordPress 4.9 42 | 43 | #### 3.6.4 / 2025-07-10 44 | * update readme 45 | * fixed incorrect path to `delete_plugins()` 46 | * check for `require_filesystem_credentials()` as missing during update. 47 | 48 | #### 3.6.3 / 2025-03-25 49 | * update workflow 50 | * add extra setting to remove auto-installed plugin(s) 51 | 52 | #### 3.6.2 / 2024-12-02 53 | * Plugin Check and i18n updates 54 | 55 | #### 3.6.1 / 2024-10-23 56 | * fix if `get_preferred_from_update_core()` continues to return less than a complete response 57 | 58 | #### 3.6.0 / 2024-10-09 59 | * remove Report a Bug in favor of using the standalone Test Reports plugin 60 | 61 | #### 3.5.6 / 2024-07-06 62 | * string update 63 | * no need to skip debug email 64 | 65 | #### 3.5.5 / 2023-10-19 66 | * add `Settings` to action links, thanks @0aveRyan 67 | 68 | #### 3.5.4 / 2023-09-09 69 | * use `automatic_updates_send_debug_email` filter to turn off sending debug email 70 | * `mysql_get_client_info()` no longer in PHP 7.0, switch to `mysqli_get_client_info()` 71 | * make anonymous functions static 72 | 73 | #### 3.5.3 / 2023-08-10 74 | * update for changed standalone afragen/test-reports plugin 75 | * set actual next beta/RC in messaging 76 | 77 | #### 3.5.2 / 2023-07-12 78 | * fix dev-notes URL 79 | 80 | #### 3.5.1 / 2023-07-06 81 | * add setting in `Extra Settings` to hide `Report a Bug` 82 | * add filter `wpbt_hide_report_a_bug` 83 | * update if `Report a Bug` plugin active 84 | * link to settings if `Report a Bug` is hidden 85 | * update for latest WP API responses, thanks @dd32 86 | 87 | #### 3.5.0 / 2023-06-30 88 | * update dashboard widget for MarComm publishing of posts 89 | * update to correctly identify Opera browser in `Report a Bug` 90 | * update API query when channel set to beta or RC and version is non-current 91 | * fix `WP_Beta_Tester::switch_update_offer()` to correctly display 'Update' or 'Re-install' buttons on update-core.php 92 | * remove unused item from **Extra Settings** tab 93 | 94 | #### 3.4.1 / 2023-05-26 95 | * **Report a Bug** only for logged in users 96 | 97 | #### 3.4.0 / 2023-05-21 98 | * update to point release if set for development beta/rc and new point release occurs 99 | 100 | #### 3.3.8 / 2023-05-18 101 | * update composer.json 102 | * update GitHub Actions 103 | * update to correctly return 'upgrade' or 'latest' offer when set to 'beta' or 'rc' stream 104 | 105 | #### 3.3.7 / 2023-03-28 106 | * better fix for spacing of bug report copy 107 | * `Report a Bug`: update database data for SQLite 108 | 109 | #### 3.3.6 / 2023-02-25 110 | * fix spacing of bug report copy 111 | 112 | #### 3.3.5 / 2023-02-22 113 | * updated dashboard widget with some better dynamic information 114 | * `Report a Bug`: introduce search button 115 | * updated strings 116 | * `Report a Bug`: Truncate the value of mysqli::$client_info 117 | 118 | #### 3.3.4 / 2023-03-20 119 | * PHP 5.6 and `EOD`, why we can't have nice looking code in the editor 120 | 121 | #### 3.3.3 / 2023-03-20 122 | * add an icon 🐞 123 | * improved environment data and display 124 | * improve clipboard text for insertion 125 | * lots of other stuff for Colin to do 126 | 127 | #### 3.3.2 / 2023-03-17 🇮🇪☘️ 128 | * more fixes for 'Report a Bug' 129 | * updated/added strings 130 | * some developery stuff 131 | 132 | #### 3.3.1 / 2023-03-17 ☘️ 133 | * update readme 134 | * sort listed plugins in 'Report a Bug' 135 | * add mu-plugins in 'Report a Bug' 136 | * fix for multisite 137 | * initiate plugin in `plugins_loaded` 138 | 139 | #### 3.3.0 / 2023-03-16 140 | * added `Report a Bug` feature, thanks @costdev, @ironprogrammer 141 | 142 | #### 3.2.9 / 2023-02-27 143 | * mitigate some issues/possible issues with PHP 8.1/8.2 144 | 145 | #### 3.2.8 / 2023-02-07 146 | * Composer 2.5.2 is fixed. 147 | 148 | #### 3.2.7 / 2023-02-07 149 | * revert to Composer v2.2.x locally for autoloader compatibility 150 | 151 | #### 3.2.6 / 2023-01-30 152 | * revert to Composer v2.5.0 as v2.5.1 has bug causing fatal, fixed in next version of Composer 153 | 154 | #### 3.2.5 / 2023-01-29 155 | * added auto display relative fields immediately bleeding edge option is selected, thanks @Preciousomonze 156 | * fixes for PHP8.1 157 | 158 | #### 3.2.4 / 2022-11-07 159 | * return empty array for 8.1 compatibility 160 | 161 | #### 3.2.3 / 2022-09-29 162 | * update for PHP 8.1 compatibility 163 | 164 | #### 3.2.2 / 2022-06-23 165 | * correctly use `sanitize_url()` and `esc_url()` 166 | * fix `WP_Config_Transformer` to get anchor if wp-config.php has been modified 167 | 168 | #### 3.2.1 / 2022-04-13 169 | * update composer to work with PHP 5.6 170 | 171 | #### 3.2.0 / 2022-04-12 172 | * use `sanitize_key()` for nonces 173 | * fix for transition from WP x.9 to WP x.0 to display correct next versions 174 | 175 | #### 3.1.5 / 2022-01-28 176 | * use `sanitize_title_with_dashes()` as `sanitize_file_name()` maybe have attached filter that changes output 177 | * fix variable docblocks 178 | * update nonce checks 179 | 180 | #### 3.1.4 / 2021-09-24 **Hotfix** 181 | * don't load `pluggable.php` for `wp_create_nonce()`, load in `plugins_loaded` hook 182 | 183 | #### 3.1.3 / 2021-09-23 184 | * nonce, escape, and sanitize all the things 185 | 186 | #### 3.1.2 / 2021-09-04 187 | * only use `esc_attr_e` for translating strings 188 | 189 | #### 3.1.1 / 2021-07-11 190 | * add @10up GitHub Actions WordPress SVN integration 191 | * update Codex links for HelpHub links @audrasjb 192 | 193 | #### 3.1.0 / 2021-02-08 194 | * update for working correctly if new `WP_AUTO_UPDATE_CORE` constant is used. 195 | * update `WP_Beta_Tester::channel_switching_modification()` to update past current release if appropriate 196 | * tweak next versions when coming from point release to bleeding edge 197 | 198 | #### 3.0.10 / 2021-01-11 199 | * re-write `WP_Beta_Tester::get_current_wp_release()` to check https://api.wordpress.org/core/stable-check/1.0/ 200 | * fix `WPBT_Core::get_next_versions()` if user on current release 201 | * tweak `WP_Beta_Tester::channel_switching_modification()` to work correctly with $wp_version <= $current_release and if on current release 202 | 203 | #### 3.0.9 / 2020-12-01 204 | * add conditional for filter to fix `core_update_footer()`, fixed in [r49708](https://core.trac.wordpress.org/changeset/49708) 205 | * simplify some `preg_match()` calls 206 | * fix PHP warning 207 | 208 | #### 3.0.8 / 2020-11-28 209 | * fix some PHP errors when using older versions of WP, for testing updates directly from these older versions like when using Core Rollback plugin 210 | 211 | #### 3.0.7 / 2020-11-24 212 | * tweak to `channel_switching_modification()` 213 | 214 | #### 3.0.6 / 2020-11-21 215 | * improved flow between _Bleeding edge_ and _Point release_ 216 | 217 | #### 3.0.5 / 2020-11-18 218 | * don't show beta as a next version when on RC 219 | 220 | #### 3.0.4 / 2020-11-17 221 | * fix to correctly downgrade from _Bleeding edge_ to _Point release nightlies_. 222 | * hide stream options other than _Nightlies_ for _Point release_ channel until [new Updates API changes](https://meta.trac.wordpress.org/ticket/5511) 223 | * add settings for future Updates API above 224 | * added `channel_settings_migrator()` for switching between `Bleeding edge` and `Point release` channels 225 | 226 | #### 3.0.1 - 3.0.3 / 2020-10-27 227 | * fixed regex to get next versions 228 | * really didn't need to use `ReflectionClass` 🤦‍♂️, thanks @pbiron 229 | * use `ReflectionClass` to get static variable `$core_update_constant` from `class WP_Beta_Tester` into `class WPBT_Core` 230 | 231 | #### 3.0.0 / 2020-10-23 232 | * major refactor for new core update API, thanks @dd32! 233 | * now requires PHP >5.6 234 | * allows for overrides when using the `WP_AUTO_UPDATE_CORE` constant 235 | * update on-screen help 236 | 237 | #### 2.2.13 / 2020-09-05 238 | * enclose `WPConfigTransformer` in try/catch 239 | 240 | #### 2.2.12 / 2020-08-10 241 | * fix intermittent PHP warning [#21](https://github.com/afragen/wordpress-beta-tester/issues/21) 242 | * deactivate and die if user attempting to run with `wordpress-develop` 243 | 244 | #### 2.2.11 / 2020-08-01 245 | * minor cleanup 246 | 247 | #### 2.2.10 / 2020-05-01 248 | * sanitize, escape & ignore 249 | * move multiline boolean operator to front of line, new guidelines in WPCS 250 | * fix `correct_versions_for_downgrade()` for being on current release version 251 | 252 | #### 2.2.9 / 2020-03-24 253 | * delete development RSS feed transient after core upgrade 254 | 255 | #### 2.2.8 / 2020-03-17 🍀 256 | * add Dev Notes and Field Guide links to dashboard 257 | * add text/link for bug reporting to trac 258 | * add help tabs to screen 259 | * arbitrarily changed settings page id from `wp_beta_tester` to `wp-beta-tester` 😏 260 | 261 | #### 2.2.7 / 2020-03-02 262 | * update trac link in callout for _closed_ or _reopened_ tickets on the milestone 263 | * only show Beta Tester Settings page link in callout with appropriate privileges, using `manage_network_options` and `manage_options` 264 | * menu to Settings page also checks privileges as above 265 | 266 | #### 2.2.6 / 2020-02-25 267 | * removed extra `` in dashboard callout, 4th time's the charm 😭 268 | 269 | #### 2.2.5 / 2020-02-25 270 | * less greedy regex for matching release posts in RSS for dashboard callout 271 | 272 | #### 2.2.4 / 2020-02-25 🤦‍♂️ 273 | * added dashboard widget for network dashboard 274 | 275 | #### 2.2.3 / 2020-02-25 276 | * add dashboard widget callout for testing 277 | 278 | #### 2.2.2 / 2020-02-22 279 | * fix for strange Core API response where preferred version response contained the word 'version'. We now grab the last word of that response 280 | 281 | #### 2.2.1 / 2020-02-20 282 | * fix some i18n strings, thanks @pedro-mendonca 283 | 284 | #### 2.2.0 / 2020-02-19 285 | * added support for updating to the _beta/RC offer_. Based on and with tons of help from @pbrion, thanks Paul 👏🏻 286 | * fixed so a downgrade from 'unstable' to 'point' serves the correct download 287 | * test and exit from **Extra Settings** if `wp-config.php` is not writeable 288 | 289 | #### 2.1.0 / 2019-09-17 290 | * add extra setting to skip successful autoupdate emails 291 | * add description to checkbox settings 292 | * composer update 293 | 294 | #### 2.0.4 295 | * add update version information to settings page text 296 | 297 | #### 2.0.3 298 | * a11y fixes for settings tabs 299 | * update `wp-cli/wp-config-transformer` 300 | 301 | #### 2.0.2 302 | * a11y fixes for checkbox, thanks @audrasjb 303 | 304 | #### 2.0.1 305 | * fix for incorrect last updated message 306 | 307 | #### 2.0.0 308 | * near complete re-write to use more OOPy practices 309 | * put distinct process into separate classes 310 | * allows for multiple settings tabs for addtional settings 311 | 312 | #### 1.2.6 313 | * remove extraneous code 314 | * add GitHub Plugin URI header 315 | 316 | #### 1.2.5 317 | * fixed error message for downgrading version, thanks @andreas-andersson 318 | 319 | #### 1.2.4 320 | * don't use $GLOBALS 321 | 322 | #### 1.2.3 323 | * updated a few strings and correct typos 324 | * run through WPCS linter 325 | * fixed translation strings to include HTML in context and properly escape with `wp_kses_post()` 326 | * fixed link to settings page under Multisite 327 | 328 | #### 1.2.2 329 | * change wording from blog to website 330 | 331 | #### 1.2.0 332 | * Escape output 333 | * Indicate that _Bleeding edge nightlies_ are _trunk_ 334 | * new screenshot 335 | * code improvements from linter 336 | 337 | #### 1.1.2 338 | * Remove anonymous function for PHP 5.2 compatibility. 339 | 340 | #### 1.1.1 341 | * fixed PHP notice for PHP 7.1 342 | * made URL scheme agnostic 343 | 344 | #### 1.1.0 345 | * Fixed to work properly under Multisite. 346 | 347 | #### 1.0.2 348 | * Update tested up to version to 4.7. 349 | * Fix the location of the settings screen in Multisite (moved under Settings in Network Admin). 350 | * Minor text fixes. 351 | 352 | #### 1.0.1 353 | * Update tested up to version to 4.5. 354 | * Fix PHP7 deprecated constructor notice. 355 | * Change text domain to match the plugin slug. 356 | * Update WordPress.org links to use HTTPS. 357 | * Remove outdated bundled translations in favor of language packs. 358 | 359 | #### 1.0 360 | * Update tested up to version to 4.2. 361 | * Update screenshot. 362 | * Fix a couple typos. 363 | 364 | #### See old-changelog.txt for previous changelog items 365 | 366 | ## Installation 367 | 368 | 1. Upload to your plugins folder, usually `wp-content/plugins/` 369 | 2. Activate the plugin on the plugin screen. 370 | 3. Navigate to Tools ... Beta Testing to configure the plugin. 371 | 4. Under Mulitsite, navigate to Settings ... Beta Testing to configure the plugin. 372 | 5. Visit Dashboard ... Upgrade (Or Tools ... Upgrade in versions before 3.0) and update to the latest Beta Release. 373 | 374 | ## Screenshots 375 | 376 | 1. Main Settings page 377 | 2. Extra Settings page 378 | 3. Dashboard widget 379 | -------------------------------------------------------------------------------- /src/WPBT/WP_Beta_Tester.php: -------------------------------------------------------------------------------- 1 | file = $file; 38 | self::$options = $options; 39 | } 40 | 41 | /** 42 | * Rev up the engines. 43 | * 44 | * @return void 45 | */ 46 | public function run() { 47 | $this->load_hooks(); 48 | ( new WPBT_Settings( $this, self::$options ) )->run(); 49 | } 50 | 51 | /** 52 | * Load hooks. 53 | * 54 | * @return void 55 | */ 56 | protected function load_hooks() { 57 | add_action( 58 | 'update_option_wp_beta_tester_stream', 59 | array( 60 | $this, 61 | 'action_update_option_wp_beta_tester_stream', 62 | ) 63 | ); 64 | 65 | // Set channel query arg for core version check. 66 | add_filter( 'core_version_check_query_args', array( $this, 'set_core_update_channel_constant' ), 10, 1 ); 67 | 68 | /* // phpcs:ignore Squiz.PHP.CommentedOutCode.Found 69 | * For testing pretend we're on another release. 70 | * $url = add_query_arg( 'pretend_releases', array( '5.6-beta2' ), $url ); 71 | add_filter( 72 | 'core_version_check_query_args', 73 | function ( $query_args ) { 74 | return array_merge( $query_args, array( 'pretend_releases' => array( '5.6-beta2' ) ) ); 75 | }, 76 | 10, 77 | 1 78 | ); 79 | */ 80 | 81 | // Fixed in https://core.trac.wordpress.org/changeset/49708. 82 | if ( version_compare( get_bloginfo( 'version' ), '5.6-RC1-49708', '<=' ) 83 | && preg_match( '/alpha|beta|RC/', get_bloginfo( 'version' ) ) 84 | ) { 85 | // set priority to 11 so that we fire after the function core hooks into this filter. 86 | add_filter( 'update_footer', array( $this, 'update_footer' ), 11 ); 87 | } 88 | 89 | // Add dashboard widget. 90 | add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widget' ) ); 91 | add_action( 'wp_network_dashboard_setup', array( $this, 'add_dashboard_widget' ) ); 92 | 93 | // Delete development RSS feed transient for dashboard widget on core upgrade. 94 | add_action( 'upgrader_process_complete', array( $this, 'delete_feed_transient_on_upgrade' ), 10, 2 ); 95 | } 96 | 97 | /** 98 | * Check and display notice if 'update' really downgrade. 99 | * 100 | * @return void 101 | */ 102 | public function action_admin_head_plugins_php() { 103 | // Workaround the check throttling in wp_version_check(). 104 | $st = get_site_transient( 'update_core' ); 105 | if ( is_object( $st ) ) { 106 | $st->last_checked = 0; 107 | set_site_transient( 'update_core', $st ); 108 | } 109 | wp_version_check(); 110 | 111 | // Can output an error here if current config drives version backwards. 112 | if ( $this->check_if_settings_downgrade() ) { 113 | echo '

'; 114 | $admin_page = is_multisite() ? network_admin_url( 'settings.php' ) : admin_url( 'tools.php' ); 115 | $admin_page = add_query_arg( 116 | array( 117 | 'page' => 'wp-beta-tester', 118 | 'tab' => 'wp_beta_tester_core', 119 | ), 120 | $admin_page 121 | ); 122 | /* translators: %s: link to setting page */ 123 | printf( 124 | /* translators: %s: WordPress Beta Tester Settings page URL */ 125 | wp_kses_post( __( 'Warning: Your current WordPress Beta Tester plugin configuration will downgrade your installation to a previous version - please reconfigure it.', 'wordpress-beta-tester' ) ), 126 | esc_url( $admin_page ) 127 | ); 128 | echo '

'; 129 | } 130 | } 131 | 132 | /** 133 | * Get channel query arg. 134 | * 135 | * @return string 136 | */ 137 | public static function get_channel() { 138 | return empty( self::$options['stream-option'] ) 139 | ? self::$options['channel'] 140 | : self::$options['stream-option']; 141 | } 142 | 143 | /** 144 | * Set channel query arg in wp_version_check(). 145 | * 146 | * @param array $query_args Array of query args. 147 | * 148 | * @return array 149 | */ 150 | public function set_core_update_channel_constant( $query_args ) { 151 | return array_merge( $query_args, array( 'channel' => self::get_channel() ) ); 152 | } 153 | 154 | /** 155 | * Our option has changed so update the cached information pronto. 156 | * 157 | * @return void 158 | */ 159 | public function action_update_option_wp_beta_tester_stream() { 160 | do_action( 'wp_version_check' ); 161 | } 162 | 163 | /** 164 | * Get preferred update version from core. 165 | * 166 | * @return /stdClass 167 | */ 168 | public function get_preferred_from_update_core() { 169 | if ( ! function_exists( 'get_preferred_from_update_core' ) ) { 170 | require_once ABSPATH . 'wp-admin/includes/update.php'; 171 | } 172 | 173 | // Validate that we have api data and if not get the normal data so we always have it. 174 | $preferred = get_preferred_from_update_core(); 175 | if ( false === $preferred ) { 176 | wp_version_check(); 177 | $preferred = get_preferred_from_update_core(); 178 | } 179 | 180 | // get_preferred_from_update_core() can return false. 181 | if ( false === $preferred ) { 182 | $preferred = array(); 183 | $preferred['response'] = 'latest'; 184 | $preferred['version'] = '0'; 185 | $preferred = (object) $preferred; 186 | } 187 | 188 | $preferred->version = property_exists( $preferred, 'version' ) ? $preferred->version : '0'; 189 | 190 | return $preferred; 191 | } 192 | 193 | /** 194 | * Get current WP release version. 195 | * 196 | * @since 3.1.0 197 | * @return string|array $wp_version 198 | */ 199 | public function get_current_wp_release() { 200 | $response = get_site_transient( 'current_wp_release' ); 201 | 202 | if ( ! $response ) { 203 | $response = wp_remote_get( 'https://api.wordpress.org/core/stable-check/1.0/' ); 204 | $response = wp_remote_retrieve_body( $response ); 205 | 206 | if ( is_wp_error( $response ) ) { 207 | return array(); 208 | } 209 | 210 | $response = (array) json_decode( $response ); 211 | $response = array_keys( $response, 'latest', true ); 212 | $response = array_pop( $response ); 213 | $response = null === $response ? '' : $response; 214 | set_site_transient( 'current_wp_release', $response, DAY_IN_SECONDS ); 215 | } 216 | 217 | return $response; 218 | } 219 | 220 | /** 221 | * Returns whether 'update' is really downgrade. 222 | * 223 | * @return bool 224 | */ 225 | public function check_if_settings_downgrade() { 226 | $wp_version = get_bloginfo( 'version' ); 227 | $wp_real_version = explode( '-', $wp_version ); 228 | $wpbt_core = new WPBT_Core( $this, self::$options ); 229 | $next_versions = $wpbt_core->calculate_next_versions(); 230 | if ( empty( $next_versions ) ) { 231 | return false; 232 | } 233 | 234 | return version_compare( $next_versions['release'], $wp_real_version[0], 'lt' ); 235 | } 236 | 237 | /** 238 | * Add dashboard widget for beta testing information. 239 | * 240 | * @since 2.2.3 241 | * 242 | * @return void 243 | */ 244 | public function add_dashboard_widget() { 245 | $wp_version = get_bloginfo( 'version' ); 246 | $beta_rc = preg_match( '/alpha|beta|RC/', $wp_version ); 247 | 248 | if ( $beta_rc ) { 249 | wp_add_dashboard_widget( 'beta_tester_dashboard_widget', __( 'WordPress Beta Testing', 'wordpress-beta-tester' ), array( $this, 'beta_tester_dashboard' ) ); 250 | } 251 | } 252 | 253 | /** 254 | * Setup dashboard widget. 255 | * 256 | * @since 2.2.3 257 | * 258 | * @return void 259 | */ 260 | public function beta_tester_dashboard() { 261 | $wp_version = get_bloginfo( 'version' ); 262 | $next_version = explode( '-', $wp_version ); 263 | $milestone = array_shift( $next_version ); 264 | 265 | $preferred = $this->get_preferred_from_update_core(); 266 | $update_version = ( new WPBT_Core( $this, self::$options ) )->get_next_version( $preferred->version ); 267 | 268 | /* translators: %s: WordPress version */ 269 | printf( wp_kses_post( '

' . __( 'Please help test WordPress %s.', 'wordpress-beta-tester' ) . '

' ), esc_attr( $milestone ) ); 270 | 271 | echo wp_kses_post( $this->add_dev_notes_field_guide_links( $milestone ) ); 272 | echo wp_kses_post( $this->parse_development_feed( $milestone ) ); 273 | 274 | /* translators: %1: link to closed and reopened trac tickets on current milestone */ 275 | printf( wp_kses_post( '

' . __( 'Here are the commits for the milestone.', 'wordpress-beta-tester' ) . '

' ), esc_url( "https://core.trac.wordpress.org/query?status=closed&status=reopened&milestone=$milestone" ) ); 276 | 277 | $capability = is_multisite() ? 'manage_network_options' : 'manage_options'; 278 | if ( current_user_can( $capability ) ) { 279 | $parent = is_multisite() ? 'settings.php' : 'tools.php'; 280 | $wpbt_settings_page = add_query_arg( 'page', 'wp-beta-tester', network_admin_url( $parent ) ); 281 | 282 | printf( 283 | /* translators: %s: update version */ 284 | wp_kses_post( '

' . __( 'Currently your site is set to update to %s.', 'wordpress-beta-tester' ) . '

' ), 285 | esc_html( $update_version ) 286 | ); 287 | 288 | if ( 'beta' !== self::$options['stream-option'] ) { 289 | /* translators: %s: WP Beta Tester settings URL */ 290 | printf( wp_kses_post( '

' . __( 'Head over to your WordPress Beta Tester Settings and make sure the beta/RC stream is selected.', 'wordpress-beta-tester' ) . '

' ), esc_url( $wpbt_settings_page ) ); 291 | } 292 | } 293 | } 294 | 295 | /** 296 | * Parse development RSS feed for list of milestoned items. 297 | * 298 | * @since 2.2.3 299 | * @param string $milestone Milestone version. 300 | * 301 | * @return string HTML unordered list. 302 | */ 303 | private function parse_development_feed( $milestone ) { 304 | $rss_args = array( 305 | 'show_summary' => 0, 306 | 'items' => 10, 307 | ); 308 | 309 | // For testing, set cache to 10 seconds. 310 | // phpcs:ignore Squiz.Commenting.InlineComment.InvalidEndChar 311 | // add_filter( 'wp_feed_cache_transient_lifetime', function () { return 10; } ); 312 | 313 | ob_start(); 314 | wp_widget_rss_output( "https://wordpress.org/news/tag/development/feed/?s=$milestone", $rss_args ); 315 | wp_widget_rss_output( "https://make.wordpress.org/core/tag/development/feed/?s=$milestone", $rss_args ); 316 | $feed = ob_get_contents(); 317 | ob_end_clean(); 318 | 319 | $milestone = preg_quote( $milestone, '.' ); 320 | $li_regex = "#
  • .*$milestone.*<\/li>#"; 321 | preg_match( $li_regex, $feed, $matches ); 322 | $match = array_pop( $matches ); 323 | $list = empty( $match ) ? '' : "
      $match
    "; 324 | 325 | return $list; 326 | } 327 | 328 | /** 329 | * Add milestone dev notes and field guide when on RC version. 330 | * 331 | * @since 2.2.3 332 | * @param string $milestone Milestone version. 333 | * 334 | * @return string HTML unordered list. 335 | */ 336 | private function add_dev_notes_field_guide_links( $milestone ) { 337 | $wp_version = get_bloginfo( 'version' ); 338 | $beta_rc = preg_match( '/beta|RC/', $wp_version ); 339 | $rc = preg_match( '/RC/', $wp_version ); 340 | $milestone_dash = str_replace( '.', '-', $milestone ); 341 | $dev_note_link = ''; 342 | $field_guide_link = ''; 343 | 344 | if ( $beta_rc ) { 345 | $dev_note_link = sprintf( 346 | /* translators: %1$s Link to dev notes, %2$s: Link title */ 347 | '%2$s', 348 | "https://make.wordpress.org/core/tag/dev-notes+$milestone_dash/", 349 | /* translators: %s: Milestone version */ 350 | sprintf( __( 'WordPress %s Dev Notes', 'wordpress-beta-tester' ), $milestone ) 351 | ); 352 | $dev_note_link = "
  • $dev_note_link
  • "; 353 | } 354 | if ( $rc ) { 355 | $field_guide_link = sprintf( 356 | /* translators: %1$s Link to field guide, %2$s: Link title */ 357 | '%2$s', 358 | "https://make.wordpress.org/core/wordpress-$milestone_dash-field-guide/", 359 | /* translators: %s: Milestone version */ 360 | sprintf( __( 'WordPress %s Field Guide', 'wordpress-beta-tester' ), $milestone ) 361 | ); 362 | $field_guide_link = "
  • $field_guide_link
  • "; 363 | } 364 | $links = $beta_rc || $rc ? "
      $dev_note_link $field_guide_link
    " : ''; 365 | 366 | return $links; 367 | } 368 | 369 | /** 370 | * Delete development RSS feed transient on core upgrade. 371 | * 372 | * @uses filter 'upgrader_process_complete'. 373 | * 374 | * @param \Core_Upgrader $obj \Core_Upgrader object. 375 | * @param array $hook_extra $hook_extra array from filter. 376 | * 377 | * @return void 378 | */ 379 | public function delete_feed_transient_on_upgrade( $obj, $hook_extra ) { 380 | if ( $obj instanceof \Core_Upgrader && 'core' === $hook_extra['type'] ) { 381 | $transient = md5( 'https://wordpress.org/news/category/development/feed/' ); 382 | delete_transient( "feed_{$transient}" ); 383 | delete_transient( "feed_mod_{$transient}" ); 384 | } 385 | } 386 | 387 | /** 388 | * Ensure core still displays "You are using a development verison..." in the admin 389 | * footer, even if we've removed the `development` update response because the next 390 | * beta/RC package is not available. 391 | * 392 | * @since 2.2.0 393 | * 394 | * @return string 395 | * 396 | * @filter update_footer 397 | */ 398 | public function update_footer() { 399 | add_filter( 'pre_site_transient_update_core', array( $this, 'add_minimal_development_response' ), 10, 0 ); 400 | 401 | $content = core_update_footer(); 402 | 403 | remove_filter( 'pre_site_transient_update_core', array( $this, 'add_minimal_development_response' ) ); 404 | 405 | return $content; 406 | } 407 | 408 | /** 409 | * Add a minimal development response as the preferred update. 410 | * 411 | * @since 2.2.0 412 | * 413 | * @return object 414 | * 415 | * @filter pre_site_transient_update_core 416 | */ 417 | public function add_minimal_development_response() { 418 | $from_api = new stdClass(); 419 | $update = new stdClass(); 420 | // a "minimal" response is one with the `response`, `current` and `locale` properties. 421 | $update->response = 'development'; 422 | $update->current = get_bloginfo( 'version' ); 423 | $update->version = $update->current; 424 | $update->locale = get_locale(); 425 | 426 | $from_api->updates = array( 427 | $update, 428 | ); 429 | 430 | return $from_api; 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /vendor/composer/InstalledVersions.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer; 14 | 15 | use Composer\Autoload\ClassLoader; 16 | use Composer\Semver\VersionParser; 17 | 18 | /** 19 | * This class is copied in every Composer installed project and available to all 20 | * 21 | * See also https://getcomposer.org/doc/07-runtime.md#installed-versions 22 | * 23 | * To require its presence, you can require `composer-runtime-api ^2.0` 24 | * 25 | * @final 26 | */ 27 | class InstalledVersions 28 | { 29 | /** 30 | * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to 31 | * @internal 32 | */ 33 | private static $selfDir = null; 34 | 35 | /** 36 | * @var mixed[]|null 37 | * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null 38 | */ 39 | private static $installed; 40 | 41 | /** 42 | * @var bool 43 | */ 44 | private static $installedIsLocalDir; 45 | 46 | /** 47 | * @var bool|null 48 | */ 49 | private static $canGetVendors; 50 | 51 | /** 52 | * @var array[] 53 | * @psalm-var array}> 54 | */ 55 | private static $installedByVendor = array(); 56 | 57 | /** 58 | * Returns a list of all package names which are present, either by being installed, replaced or provided 59 | * 60 | * @return string[] 61 | * @psalm-return list 62 | */ 63 | public static function getInstalledPackages() 64 | { 65 | $packages = array(); 66 | foreach (self::getInstalled() as $installed) { 67 | $packages[] = array_keys($installed['versions']); 68 | } 69 | 70 | if (1 === \count($packages)) { 71 | return $packages[0]; 72 | } 73 | 74 | return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); 75 | } 76 | 77 | /** 78 | * Returns a list of all package names with a specific type e.g. 'library' 79 | * 80 | * @param string $type 81 | * @return string[] 82 | * @psalm-return list 83 | */ 84 | public static function getInstalledPackagesByType($type) 85 | { 86 | $packagesByType = array(); 87 | 88 | foreach (self::getInstalled() as $installed) { 89 | foreach ($installed['versions'] as $name => $package) { 90 | if (isset($package['type']) && $package['type'] === $type) { 91 | $packagesByType[] = $name; 92 | } 93 | } 94 | } 95 | 96 | return $packagesByType; 97 | } 98 | 99 | /** 100 | * Checks whether the given package is installed 101 | * 102 | * This also returns true if the package name is provided or replaced by another package 103 | * 104 | * @param string $packageName 105 | * @param bool $includeDevRequirements 106 | * @return bool 107 | */ 108 | public static function isInstalled($packageName, $includeDevRequirements = true) 109 | { 110 | foreach (self::getInstalled() as $installed) { 111 | if (isset($installed['versions'][$packageName])) { 112 | return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; 113 | } 114 | } 115 | 116 | return false; 117 | } 118 | 119 | /** 120 | * Checks whether the given package satisfies a version constraint 121 | * 122 | * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: 123 | * 124 | * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') 125 | * 126 | * @param VersionParser $parser Install composer/semver to have access to this class and functionality 127 | * @param string $packageName 128 | * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package 129 | * @return bool 130 | */ 131 | public static function satisfies(VersionParser $parser, $packageName, $constraint) 132 | { 133 | $constraint = $parser->parseConstraints((string) $constraint); 134 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); 135 | 136 | return $provided->matches($constraint); 137 | } 138 | 139 | /** 140 | * Returns a version constraint representing all the range(s) which are installed for a given package 141 | * 142 | * It is easier to use this via isInstalled() with the $constraint argument if you need to check 143 | * whether a given version of a package is installed, and not just whether it exists 144 | * 145 | * @param string $packageName 146 | * @return string Version constraint usable with composer/semver 147 | */ 148 | public static function getVersionRanges($packageName) 149 | { 150 | foreach (self::getInstalled() as $installed) { 151 | if (!isset($installed['versions'][$packageName])) { 152 | continue; 153 | } 154 | 155 | $ranges = array(); 156 | if (isset($installed['versions'][$packageName]['pretty_version'])) { 157 | $ranges[] = $installed['versions'][$packageName]['pretty_version']; 158 | } 159 | if (array_key_exists('aliases', $installed['versions'][$packageName])) { 160 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); 161 | } 162 | if (array_key_exists('replaced', $installed['versions'][$packageName])) { 163 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); 164 | } 165 | if (array_key_exists('provided', $installed['versions'][$packageName])) { 166 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); 167 | } 168 | 169 | return implode(' || ', $ranges); 170 | } 171 | 172 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 173 | } 174 | 175 | /** 176 | * @param string $packageName 177 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present 178 | */ 179 | public static function getVersion($packageName) 180 | { 181 | foreach (self::getInstalled() as $installed) { 182 | if (!isset($installed['versions'][$packageName])) { 183 | continue; 184 | } 185 | 186 | if (!isset($installed['versions'][$packageName]['version'])) { 187 | return null; 188 | } 189 | 190 | return $installed['versions'][$packageName]['version']; 191 | } 192 | 193 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 194 | } 195 | 196 | /** 197 | * @param string $packageName 198 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present 199 | */ 200 | public static function getPrettyVersion($packageName) 201 | { 202 | foreach (self::getInstalled() as $installed) { 203 | if (!isset($installed['versions'][$packageName])) { 204 | continue; 205 | } 206 | 207 | if (!isset($installed['versions'][$packageName]['pretty_version'])) { 208 | return null; 209 | } 210 | 211 | return $installed['versions'][$packageName]['pretty_version']; 212 | } 213 | 214 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 215 | } 216 | 217 | /** 218 | * @param string $packageName 219 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference 220 | */ 221 | public static function getReference($packageName) 222 | { 223 | foreach (self::getInstalled() as $installed) { 224 | if (!isset($installed['versions'][$packageName])) { 225 | continue; 226 | } 227 | 228 | if (!isset($installed['versions'][$packageName]['reference'])) { 229 | return null; 230 | } 231 | 232 | return $installed['versions'][$packageName]['reference']; 233 | } 234 | 235 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 236 | } 237 | 238 | /** 239 | * @param string $packageName 240 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. 241 | */ 242 | public static function getInstallPath($packageName) 243 | { 244 | foreach (self::getInstalled() as $installed) { 245 | if (!isset($installed['versions'][$packageName])) { 246 | continue; 247 | } 248 | 249 | return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; 250 | } 251 | 252 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 253 | } 254 | 255 | /** 256 | * @return array 257 | * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} 258 | */ 259 | public static function getRootPackage() 260 | { 261 | $installed = self::getInstalled(); 262 | 263 | return $installed[0]['root']; 264 | } 265 | 266 | /** 267 | * Returns the raw installed.php data for custom implementations 268 | * 269 | * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. 270 | * @return array[] 271 | * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} 272 | */ 273 | public static function getRawData() 274 | { 275 | @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); 276 | 277 | if (null === self::$installed) { 278 | // only require the installed.php file if this file is loaded from its dumped location, 279 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 280 | if (substr(__DIR__, -8, 1) !== 'C') { 281 | self::$installed = include __DIR__ . '/installed.php'; 282 | } else { 283 | self::$installed = array(); 284 | } 285 | } 286 | 287 | return self::$installed; 288 | } 289 | 290 | /** 291 | * Returns the raw data of all installed.php which are currently loaded for custom implementations 292 | * 293 | * @return array[] 294 | * @psalm-return list}> 295 | */ 296 | public static function getAllRawData() 297 | { 298 | return self::getInstalled(); 299 | } 300 | 301 | /** 302 | * Lets you reload the static array from another file 303 | * 304 | * This is only useful for complex integrations in which a project needs to use 305 | * this class but then also needs to execute another project's autoloader in process, 306 | * and wants to ensure both projects have access to their version of installed.php. 307 | * 308 | * A typical case would be PHPUnit, where it would need to make sure it reads all 309 | * the data it needs from this class, then call reload() with 310 | * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure 311 | * the project in which it runs can then also use this class safely, without 312 | * interference between PHPUnit's dependencies and the project's dependencies. 313 | * 314 | * @param array[] $data A vendor/composer/installed.php data set 315 | * @return void 316 | * 317 | * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data 318 | */ 319 | public static function reload($data) 320 | { 321 | self::$installed = $data; 322 | self::$installedByVendor = array(); 323 | 324 | // when using reload, we disable the duplicate protection to ensure that self::$installed data is 325 | // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, 326 | // so we have to assume it does not, and that may result in duplicate data being returned when listing 327 | // all installed packages for example 328 | self::$installedIsLocalDir = false; 329 | } 330 | 331 | /** 332 | * @return string 333 | */ 334 | private static function getSelfDir() 335 | { 336 | if (self::$selfDir === null) { 337 | self::$selfDir = strtr(__DIR__, '\\', '/'); 338 | } 339 | 340 | return self::$selfDir; 341 | } 342 | 343 | /** 344 | * @return array[] 345 | * @psalm-return list}> 346 | */ 347 | private static function getInstalled() 348 | { 349 | if (null === self::$canGetVendors) { 350 | self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); 351 | } 352 | 353 | $installed = array(); 354 | $copiedLocalDir = false; 355 | 356 | if (self::$canGetVendors) { 357 | $selfDir = self::getSelfDir(); 358 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { 359 | $vendorDir = strtr($vendorDir, '\\', '/'); 360 | if (isset(self::$installedByVendor[$vendorDir])) { 361 | $installed[] = self::$installedByVendor[$vendorDir]; 362 | } elseif (is_file($vendorDir.'/composer/installed.php')) { 363 | /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ 364 | $required = require $vendorDir.'/composer/installed.php'; 365 | self::$installedByVendor[$vendorDir] = $required; 366 | $installed[] = $required; 367 | if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { 368 | self::$installed = $required; 369 | self::$installedIsLocalDir = true; 370 | } 371 | } 372 | if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { 373 | $copiedLocalDir = true; 374 | } 375 | } 376 | } 377 | 378 | if (null === self::$installed) { 379 | // only require the installed.php file if this file is loaded from its dumped location, 380 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 381 | if (substr(__DIR__, -8, 1) !== 'C') { 382 | /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ 383 | $required = require __DIR__ . '/installed.php'; 384 | self::$installed = $required; 385 | } else { 386 | self::$installed = array(); 387 | } 388 | } 389 | 390 | if (self::$installed !== array() && !$copiedLocalDir) { 391 | $installed[] = self::$installed; 392 | } 393 | 394 | return $installed; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see https://www.php-fig.org/psr/psr-0/ 41 | * @see https://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | /** @var \Closure(string):void */ 46 | private static $includeFile; 47 | 48 | /** @var string|null */ 49 | private $vendorDir; 50 | 51 | // PSR-4 52 | /** 53 | * @var array> 54 | */ 55 | private $prefixLengthsPsr4 = array(); 56 | /** 57 | * @var array> 58 | */ 59 | private $prefixDirsPsr4 = array(); 60 | /** 61 | * @var list 62 | */ 63 | private $fallbackDirsPsr4 = array(); 64 | 65 | // PSR-0 66 | /** 67 | * List of PSR-0 prefixes 68 | * 69 | * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) 70 | * 71 | * @var array>> 72 | */ 73 | private $prefixesPsr0 = array(); 74 | /** 75 | * @var list 76 | */ 77 | private $fallbackDirsPsr0 = array(); 78 | 79 | /** @var bool */ 80 | private $useIncludePath = false; 81 | 82 | /** 83 | * @var array 84 | */ 85 | private $classMap = array(); 86 | 87 | /** @var bool */ 88 | private $classMapAuthoritative = false; 89 | 90 | /** 91 | * @var array 92 | */ 93 | private $missingClasses = array(); 94 | 95 | /** @var string|null */ 96 | private $apcuPrefix; 97 | 98 | /** 99 | * @var array 100 | */ 101 | private static $registeredLoaders = array(); 102 | 103 | /** 104 | * @param string|null $vendorDir 105 | */ 106 | public function __construct($vendorDir = null) 107 | { 108 | $this->vendorDir = $vendorDir; 109 | self::initializeIncludeClosure(); 110 | } 111 | 112 | /** 113 | * @return array> 114 | */ 115 | public function getPrefixes() 116 | { 117 | if (!empty($this->prefixesPsr0)) { 118 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 119 | } 120 | 121 | return array(); 122 | } 123 | 124 | /** 125 | * @return array> 126 | */ 127 | public function getPrefixesPsr4() 128 | { 129 | return $this->prefixDirsPsr4; 130 | } 131 | 132 | /** 133 | * @return list 134 | */ 135 | public function getFallbackDirs() 136 | { 137 | return $this->fallbackDirsPsr0; 138 | } 139 | 140 | /** 141 | * @return list 142 | */ 143 | public function getFallbackDirsPsr4() 144 | { 145 | return $this->fallbackDirsPsr4; 146 | } 147 | 148 | /** 149 | * @return array Array of classname => path 150 | */ 151 | public function getClassMap() 152 | { 153 | return $this->classMap; 154 | } 155 | 156 | /** 157 | * @param array $classMap Class to filename map 158 | * 159 | * @return void 160 | */ 161 | public function addClassMap(array $classMap) 162 | { 163 | if ($this->classMap) { 164 | $this->classMap = array_merge($this->classMap, $classMap); 165 | } else { 166 | $this->classMap = $classMap; 167 | } 168 | } 169 | 170 | /** 171 | * Registers a set of PSR-0 directories for a given prefix, either 172 | * appending or prepending to the ones previously set for this prefix. 173 | * 174 | * @param string $prefix The prefix 175 | * @param list|string $paths The PSR-0 root directories 176 | * @param bool $prepend Whether to prepend the directories 177 | * 178 | * @return void 179 | */ 180 | public function add($prefix, $paths, $prepend = false) 181 | { 182 | $paths = (array) $paths; 183 | if (!$prefix) { 184 | if ($prepend) { 185 | $this->fallbackDirsPsr0 = array_merge( 186 | $paths, 187 | $this->fallbackDirsPsr0 188 | ); 189 | } else { 190 | $this->fallbackDirsPsr0 = array_merge( 191 | $this->fallbackDirsPsr0, 192 | $paths 193 | ); 194 | } 195 | 196 | return; 197 | } 198 | 199 | $first = $prefix[0]; 200 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 201 | $this->prefixesPsr0[$first][$prefix] = $paths; 202 | 203 | return; 204 | } 205 | if ($prepend) { 206 | $this->prefixesPsr0[$first][$prefix] = array_merge( 207 | $paths, 208 | $this->prefixesPsr0[$first][$prefix] 209 | ); 210 | } else { 211 | $this->prefixesPsr0[$first][$prefix] = array_merge( 212 | $this->prefixesPsr0[$first][$prefix], 213 | $paths 214 | ); 215 | } 216 | } 217 | 218 | /** 219 | * Registers a set of PSR-4 directories for a given namespace, either 220 | * appending or prepending to the ones previously set for this namespace. 221 | * 222 | * @param string $prefix The prefix/namespace, with trailing '\\' 223 | * @param list|string $paths The PSR-4 base directories 224 | * @param bool $prepend Whether to prepend the directories 225 | * 226 | * @throws \InvalidArgumentException 227 | * 228 | * @return void 229 | */ 230 | public function addPsr4($prefix, $paths, $prepend = false) 231 | { 232 | $paths = (array) $paths; 233 | if (!$prefix) { 234 | // Register directories for the root namespace. 235 | if ($prepend) { 236 | $this->fallbackDirsPsr4 = array_merge( 237 | $paths, 238 | $this->fallbackDirsPsr4 239 | ); 240 | } else { 241 | $this->fallbackDirsPsr4 = array_merge( 242 | $this->fallbackDirsPsr4, 243 | $paths 244 | ); 245 | } 246 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 247 | // Register directories for a new namespace. 248 | $length = strlen($prefix); 249 | if ('\\' !== $prefix[$length - 1]) { 250 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 251 | } 252 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 253 | $this->prefixDirsPsr4[$prefix] = $paths; 254 | } elseif ($prepend) { 255 | // Prepend directories for an already registered namespace. 256 | $this->prefixDirsPsr4[$prefix] = array_merge( 257 | $paths, 258 | $this->prefixDirsPsr4[$prefix] 259 | ); 260 | } else { 261 | // Append directories for an already registered namespace. 262 | $this->prefixDirsPsr4[$prefix] = array_merge( 263 | $this->prefixDirsPsr4[$prefix], 264 | $paths 265 | ); 266 | } 267 | } 268 | 269 | /** 270 | * Registers a set of PSR-0 directories for a given prefix, 271 | * replacing any others previously set for this prefix. 272 | * 273 | * @param string $prefix The prefix 274 | * @param list|string $paths The PSR-0 base directories 275 | * 276 | * @return void 277 | */ 278 | public function set($prefix, $paths) 279 | { 280 | if (!$prefix) { 281 | $this->fallbackDirsPsr0 = (array) $paths; 282 | } else { 283 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 284 | } 285 | } 286 | 287 | /** 288 | * Registers a set of PSR-4 directories for a given namespace, 289 | * replacing any others previously set for this namespace. 290 | * 291 | * @param string $prefix The prefix/namespace, with trailing '\\' 292 | * @param list|string $paths The PSR-4 base directories 293 | * 294 | * @throws \InvalidArgumentException 295 | * 296 | * @return void 297 | */ 298 | public function setPsr4($prefix, $paths) 299 | { 300 | if (!$prefix) { 301 | $this->fallbackDirsPsr4 = (array) $paths; 302 | } else { 303 | $length = strlen($prefix); 304 | if ('\\' !== $prefix[$length - 1]) { 305 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 306 | } 307 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 308 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 309 | } 310 | } 311 | 312 | /** 313 | * Turns on searching the include path for class files. 314 | * 315 | * @param bool $useIncludePath 316 | * 317 | * @return void 318 | */ 319 | public function setUseIncludePath($useIncludePath) 320 | { 321 | $this->useIncludePath = $useIncludePath; 322 | } 323 | 324 | /** 325 | * Can be used to check if the autoloader uses the include path to check 326 | * for classes. 327 | * 328 | * @return bool 329 | */ 330 | public function getUseIncludePath() 331 | { 332 | return $this->useIncludePath; 333 | } 334 | 335 | /** 336 | * Turns off searching the prefix and fallback directories for classes 337 | * that have not been registered with the class map. 338 | * 339 | * @param bool $classMapAuthoritative 340 | * 341 | * @return void 342 | */ 343 | public function setClassMapAuthoritative($classMapAuthoritative) 344 | { 345 | $this->classMapAuthoritative = $classMapAuthoritative; 346 | } 347 | 348 | /** 349 | * Should class lookup fail if not found in the current class map? 350 | * 351 | * @return bool 352 | */ 353 | public function isClassMapAuthoritative() 354 | { 355 | return $this->classMapAuthoritative; 356 | } 357 | 358 | /** 359 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 360 | * 361 | * @param string|null $apcuPrefix 362 | * 363 | * @return void 364 | */ 365 | public function setApcuPrefix($apcuPrefix) 366 | { 367 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 368 | } 369 | 370 | /** 371 | * The APCu prefix in use, or null if APCu caching is not enabled. 372 | * 373 | * @return string|null 374 | */ 375 | public function getApcuPrefix() 376 | { 377 | return $this->apcuPrefix; 378 | } 379 | 380 | /** 381 | * Registers this instance as an autoloader. 382 | * 383 | * @param bool $prepend Whether to prepend the autoloader or not 384 | * 385 | * @return void 386 | */ 387 | public function register($prepend = false) 388 | { 389 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 390 | 391 | if (null === $this->vendorDir) { 392 | return; 393 | } 394 | 395 | if ($prepend) { 396 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 397 | } else { 398 | unset(self::$registeredLoaders[$this->vendorDir]); 399 | self::$registeredLoaders[$this->vendorDir] = $this; 400 | } 401 | } 402 | 403 | /** 404 | * Unregisters this instance as an autoloader. 405 | * 406 | * @return void 407 | */ 408 | public function unregister() 409 | { 410 | spl_autoload_unregister(array($this, 'loadClass')); 411 | 412 | if (null !== $this->vendorDir) { 413 | unset(self::$registeredLoaders[$this->vendorDir]); 414 | } 415 | } 416 | 417 | /** 418 | * Loads the given class or interface. 419 | * 420 | * @param string $class The name of the class 421 | * @return true|null True if loaded, null otherwise 422 | */ 423 | public function loadClass($class) 424 | { 425 | if ($file = $this->findFile($class)) { 426 | $includeFile = self::$includeFile; 427 | $includeFile($file); 428 | 429 | return true; 430 | } 431 | 432 | return null; 433 | } 434 | 435 | /** 436 | * Finds the path to the file where the class is defined. 437 | * 438 | * @param string $class The name of the class 439 | * 440 | * @return string|false The path if found, false otherwise 441 | */ 442 | public function findFile($class) 443 | { 444 | // class map lookup 445 | if (isset($this->classMap[$class])) { 446 | return $this->classMap[$class]; 447 | } 448 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 449 | return false; 450 | } 451 | if (null !== $this->apcuPrefix) { 452 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 453 | if ($hit) { 454 | return $file; 455 | } 456 | } 457 | 458 | $file = $this->findFileWithExtension($class, '.php'); 459 | 460 | // Search for Hack files if we are running on HHVM 461 | if (false === $file && defined('HHVM_VERSION')) { 462 | $file = $this->findFileWithExtension($class, '.hh'); 463 | } 464 | 465 | if (null !== $this->apcuPrefix) { 466 | apcu_add($this->apcuPrefix.$class, $file); 467 | } 468 | 469 | if (false === $file) { 470 | // Remember that this class does not exist. 471 | $this->missingClasses[$class] = true; 472 | } 473 | 474 | return $file; 475 | } 476 | 477 | /** 478 | * Returns the currently registered loaders keyed by their corresponding vendor directories. 479 | * 480 | * @return array 481 | */ 482 | public static function getRegisteredLoaders() 483 | { 484 | return self::$registeredLoaders; 485 | } 486 | 487 | /** 488 | * @param string $class 489 | * @param string $ext 490 | * @return string|false 491 | */ 492 | private function findFileWithExtension($class, $ext) 493 | { 494 | // PSR-4 lookup 495 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 496 | 497 | $first = $class[0]; 498 | if (isset($this->prefixLengthsPsr4[$first])) { 499 | $subPath = $class; 500 | while (false !== $lastPos = strrpos($subPath, '\\')) { 501 | $subPath = substr($subPath, 0, $lastPos); 502 | $search = $subPath . '\\'; 503 | if (isset($this->prefixDirsPsr4[$search])) { 504 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 505 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 506 | if (file_exists($file = $dir . $pathEnd)) { 507 | return $file; 508 | } 509 | } 510 | } 511 | } 512 | } 513 | 514 | // PSR-4 fallback dirs 515 | foreach ($this->fallbackDirsPsr4 as $dir) { 516 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 517 | return $file; 518 | } 519 | } 520 | 521 | // PSR-0 lookup 522 | if (false !== $pos = strrpos($class, '\\')) { 523 | // namespaced class name 524 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 525 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 526 | } else { 527 | // PEAR-like class name 528 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 529 | } 530 | 531 | if (isset($this->prefixesPsr0[$first])) { 532 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 533 | if (0 === strpos($class, $prefix)) { 534 | foreach ($dirs as $dir) { 535 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 536 | return $file; 537 | } 538 | } 539 | } 540 | } 541 | } 542 | 543 | // PSR-0 fallback dirs 544 | foreach ($this->fallbackDirsPsr0 as $dir) { 545 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 546 | return $file; 547 | } 548 | } 549 | 550 | // PSR-0 include paths. 551 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 552 | return $file; 553 | } 554 | 555 | return false; 556 | } 557 | 558 | /** 559 | * @return void 560 | */ 561 | private static function initializeIncludeClosure() 562 | { 563 | if (self::$includeFile !== null) { 564 | return; 565 | } 566 | 567 | /** 568 | * Scope isolated include. 569 | * 570 | * Prevents access to $this/self from included files. 571 | * 572 | * @param string $file 573 | * @return void 574 | */ 575 | self::$includeFile = \Closure::bind(static function($file) { 576 | include $file; 577 | }, null, null); 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | { 4 | "name": "dealerdirect/phpcodesniffer-composer-installer", 5 | "version": "v1.1.2", 6 | "version_normalized": "1.1.2.0", 7 | "source": { 8 | "type": "git", 9 | "url": "https://github.com/PHPCSStandards/composer-installer.git", 10 | "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1" 11 | }, 12 | "dist": { 13 | "type": "zip", 14 | "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", 15 | "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1", 16 | "shasum": "" 17 | }, 18 | "require": { 19 | "composer-plugin-api": "^2.2", 20 | "php": ">=5.4", 21 | "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" 22 | }, 23 | "require-dev": { 24 | "composer/composer": "^2.2", 25 | "ext-json": "*", 26 | "ext-zip": "*", 27 | "php-parallel-lint/php-parallel-lint": "^1.4.0", 28 | "phpcompatibility/php-compatibility": "^9.0", 29 | "yoast/phpunit-polyfills": "^1.0" 30 | }, 31 | "time": "2025-07-17T20:45:56+00:00", 32 | "type": "composer-plugin", 33 | "extra": { 34 | "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" 35 | }, 36 | "installation-source": "dist", 37 | "autoload": { 38 | "psr-4": { 39 | "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" 40 | } 41 | }, 42 | "notification-url": "https://packagist.org/downloads/", 43 | "license": [ 44 | "MIT" 45 | ], 46 | "authors": [ 47 | { 48 | "name": "Franck Nijhof", 49 | "email": "opensource@frenck.dev", 50 | "homepage": "https://frenck.dev", 51 | "role": "Open source developer" 52 | }, 53 | { 54 | "name": "Contributors", 55 | "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" 56 | } 57 | ], 58 | "description": "PHP_CodeSniffer Standards Composer Installer Plugin", 59 | "keywords": [ 60 | "PHPCodeSniffer", 61 | "PHP_CodeSniffer", 62 | "code quality", 63 | "codesniffer", 64 | "composer", 65 | "installer", 66 | "phpcbf", 67 | "phpcs", 68 | "plugin", 69 | "qa", 70 | "quality", 71 | "standard", 72 | "standards", 73 | "style guide", 74 | "stylecheck", 75 | "tests" 76 | ], 77 | "support": { 78 | "issues": "https://github.com/PHPCSStandards/composer-installer/issues", 79 | "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", 80 | "source": "https://github.com/PHPCSStandards/composer-installer" 81 | }, 82 | "funding": [ 83 | { 84 | "url": "https://github.com/PHPCSStandards", 85 | "type": "github" 86 | }, 87 | { 88 | "url": "https://github.com/jrfnl", 89 | "type": "github" 90 | }, 91 | { 92 | "url": "https://opencollective.com/php_codesniffer", 93 | "type": "open_collective" 94 | }, 95 | { 96 | "url": "https://thanks.dev/u/gh/phpcsstandards", 97 | "type": "thanks_dev" 98 | } 99 | ], 100 | "install-path": "../dealerdirect/phpcodesniffer-composer-installer" 101 | }, 102 | { 103 | "name": "phpcsstandards/phpcsextra", 104 | "version": "1.4.2", 105 | "version_normalized": "1.4.2.0", 106 | "source": { 107 | "type": "git", 108 | "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", 109 | "reference": "8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e" 110 | }, 111 | "dist": { 112 | "type": "zip", 113 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e", 114 | "reference": "8e89a01c7b8fed84a12a2a7f5a23a44cdbe4f62e", 115 | "shasum": "" 116 | }, 117 | "require": { 118 | "php": ">=5.4", 119 | "phpcsstandards/phpcsutils": "^1.1.2", 120 | "squizlabs/php_codesniffer": "^3.13.4 || ^4.0" 121 | }, 122 | "require-dev": { 123 | "php-parallel-lint/php-console-highlighter": "^1.0", 124 | "php-parallel-lint/php-parallel-lint": "^1.4.0", 125 | "phpcsstandards/phpcsdevcs": "^1.1.6", 126 | "phpcsstandards/phpcsdevtools": "^1.2.1", 127 | "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" 128 | }, 129 | "time": "2025-10-28T17:00:02+00:00", 130 | "type": "phpcodesniffer-standard", 131 | "extra": { 132 | "branch-alias": { 133 | "dev-stable": "1.x-dev", 134 | "dev-develop": "1.x-dev" 135 | } 136 | }, 137 | "installation-source": "dist", 138 | "notification-url": "https://packagist.org/downloads/", 139 | "license": [ 140 | "LGPL-3.0-or-later" 141 | ], 142 | "authors": [ 143 | { 144 | "name": "Juliette Reinders Folmer", 145 | "homepage": "https://github.com/jrfnl", 146 | "role": "lead" 147 | }, 148 | { 149 | "name": "Contributors", 150 | "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" 151 | } 152 | ], 153 | "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", 154 | "keywords": [ 155 | "PHP_CodeSniffer", 156 | "phpcbf", 157 | "phpcodesniffer-standard", 158 | "phpcs", 159 | "standards", 160 | "static analysis" 161 | ], 162 | "support": { 163 | "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", 164 | "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", 165 | "source": "https://github.com/PHPCSStandards/PHPCSExtra" 166 | }, 167 | "funding": [ 168 | { 169 | "url": "https://github.com/PHPCSStandards", 170 | "type": "github" 171 | }, 172 | { 173 | "url": "https://github.com/jrfnl", 174 | "type": "github" 175 | }, 176 | { 177 | "url": "https://opencollective.com/php_codesniffer", 178 | "type": "open_collective" 179 | }, 180 | { 181 | "url": "https://thanks.dev/u/gh/phpcsstandards", 182 | "type": "thanks_dev" 183 | } 184 | ], 185 | "install-path": "../phpcsstandards/phpcsextra" 186 | }, 187 | { 188 | "name": "phpcsstandards/phpcsutils", 189 | "version": "1.1.3", 190 | "version_normalized": "1.1.3.0", 191 | "source": { 192 | "type": "git", 193 | "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", 194 | "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9" 195 | }, 196 | "dist": { 197 | "type": "zip", 198 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", 199 | "reference": "8b8e17615d04f2fc2cd46fc1d2fd888fa21b3cf9", 200 | "shasum": "" 201 | }, 202 | "require": { 203 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", 204 | "php": ">=5.4", 205 | "squizlabs/php_codesniffer": "^3.13.3 || ^4.0" 206 | }, 207 | "require-dev": { 208 | "ext-filter": "*", 209 | "php-parallel-lint/php-console-highlighter": "^1.0", 210 | "php-parallel-lint/php-parallel-lint": "^1.4.0", 211 | "phpcsstandards/phpcsdevcs": "^1.1.6", 212 | "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" 213 | }, 214 | "time": "2025-10-16T16:39:32+00:00", 215 | "type": "phpcodesniffer-standard", 216 | "extra": { 217 | "branch-alias": { 218 | "dev-stable": "1.x-dev", 219 | "dev-develop": "1.x-dev" 220 | } 221 | }, 222 | "installation-source": "dist", 223 | "autoload": { 224 | "classmap": [ 225 | "PHPCSUtils/" 226 | ] 227 | }, 228 | "notification-url": "https://packagist.org/downloads/", 229 | "license": [ 230 | "LGPL-3.0-or-later" 231 | ], 232 | "authors": [ 233 | { 234 | "name": "Juliette Reinders Folmer", 235 | "homepage": "https://github.com/jrfnl", 236 | "role": "lead" 237 | }, 238 | { 239 | "name": "Contributors", 240 | "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" 241 | } 242 | ], 243 | "description": "A suite of utility functions for use with PHP_CodeSniffer", 244 | "homepage": "https://phpcsutils.com/", 245 | "keywords": [ 246 | "PHP_CodeSniffer", 247 | "phpcbf", 248 | "phpcodesniffer-standard", 249 | "phpcs", 250 | "phpcs3", 251 | "phpcs4", 252 | "standards", 253 | "static analysis", 254 | "tokens", 255 | "utility" 256 | ], 257 | "support": { 258 | "docs": "https://phpcsutils.com/", 259 | "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", 260 | "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", 261 | "source": "https://github.com/PHPCSStandards/PHPCSUtils" 262 | }, 263 | "funding": [ 264 | { 265 | "url": "https://github.com/PHPCSStandards", 266 | "type": "github" 267 | }, 268 | { 269 | "url": "https://github.com/jrfnl", 270 | "type": "github" 271 | }, 272 | { 273 | "url": "https://opencollective.com/php_codesniffer", 274 | "type": "open_collective" 275 | }, 276 | { 277 | "url": "https://thanks.dev/u/gh/phpcsstandards", 278 | "type": "thanks_dev" 279 | } 280 | ], 281 | "install-path": "../phpcsstandards/phpcsutils" 282 | }, 283 | { 284 | "name": "squizlabs/php_codesniffer", 285 | "version": "3.13.5", 286 | "version_normalized": "3.13.5.0", 287 | "source": { 288 | "type": "git", 289 | "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", 290 | "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" 291 | }, 292 | "dist": { 293 | "type": "zip", 294 | "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", 295 | "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", 296 | "shasum": "" 297 | }, 298 | "require": { 299 | "ext-simplexml": "*", 300 | "ext-tokenizer": "*", 301 | "ext-xmlwriter": "*", 302 | "php": ">=5.4.0" 303 | }, 304 | "require-dev": { 305 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" 306 | }, 307 | "time": "2025-11-04T16:30:35+00:00", 308 | "bin": [ 309 | "bin/phpcbf", 310 | "bin/phpcs" 311 | ], 312 | "type": "library", 313 | "installation-source": "dist", 314 | "notification-url": "https://packagist.org/downloads/", 315 | "license": [ 316 | "BSD-3-Clause" 317 | ], 318 | "authors": [ 319 | { 320 | "name": "Greg Sherwood", 321 | "role": "Former lead" 322 | }, 323 | { 324 | "name": "Juliette Reinders Folmer", 325 | "role": "Current lead" 326 | }, 327 | { 328 | "name": "Contributors", 329 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" 330 | } 331 | ], 332 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 333 | "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", 334 | "keywords": [ 335 | "phpcs", 336 | "standards", 337 | "static analysis" 338 | ], 339 | "support": { 340 | "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", 341 | "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", 342 | "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", 343 | "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" 344 | }, 345 | "funding": [ 346 | { 347 | "url": "https://github.com/PHPCSStandards", 348 | "type": "github" 349 | }, 350 | { 351 | "url": "https://github.com/jrfnl", 352 | "type": "github" 353 | }, 354 | { 355 | "url": "https://opencollective.com/php_codesniffer", 356 | "type": "open_collective" 357 | }, 358 | { 359 | "url": "https://thanks.dev/u/gh/phpcsstandards", 360 | "type": "thanks_dev" 361 | } 362 | ], 363 | "install-path": "../squizlabs/php_codesniffer" 364 | }, 365 | { 366 | "name": "wp-coding-standards/wpcs", 367 | "version": "3.2.0", 368 | "version_normalized": "3.2.0.0", 369 | "source": { 370 | "type": "git", 371 | "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", 372 | "reference": "d2421de7cec3274ae622c22c744de9a62c7925af" 373 | }, 374 | "dist": { 375 | "type": "zip", 376 | "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/d2421de7cec3274ae622c22c744de9a62c7925af", 377 | "reference": "d2421de7cec3274ae622c22c744de9a62c7925af", 378 | "shasum": "" 379 | }, 380 | "require": { 381 | "ext-filter": "*", 382 | "ext-libxml": "*", 383 | "ext-tokenizer": "*", 384 | "ext-xmlreader": "*", 385 | "php": ">=5.4", 386 | "phpcsstandards/phpcsextra": "^1.4.0", 387 | "phpcsstandards/phpcsutils": "^1.1.0", 388 | "squizlabs/php_codesniffer": "^3.13.0" 389 | }, 390 | "require-dev": { 391 | "php-parallel-lint/php-console-highlighter": "^1.0.0", 392 | "php-parallel-lint/php-parallel-lint": "^1.4.0", 393 | "phpcompatibility/php-compatibility": "^9.0", 394 | "phpcsstandards/phpcsdevtools": "^1.2.0", 395 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" 396 | }, 397 | "suggest": { 398 | "ext-iconv": "For improved results", 399 | "ext-mbstring": "For improved results" 400 | }, 401 | "time": "2025-07-24T20:08:31+00:00", 402 | "type": "phpcodesniffer-standard", 403 | "installation-source": "dist", 404 | "notification-url": "https://packagist.org/downloads/", 405 | "license": [ 406 | "MIT" 407 | ], 408 | "authors": [ 409 | { 410 | "name": "Contributors", 411 | "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" 412 | } 413 | ], 414 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", 415 | "keywords": [ 416 | "phpcs", 417 | "standards", 418 | "static analysis", 419 | "wordpress" 420 | ], 421 | "support": { 422 | "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", 423 | "source": "https://github.com/WordPress/WordPress-Coding-Standards", 424 | "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" 425 | }, 426 | "funding": [ 427 | { 428 | "url": "https://opencollective.com/php_codesniffer", 429 | "type": "custom" 430 | } 431 | ], 432 | "install-path": "../wp-coding-standards/wpcs" 433 | } 434 | ], 435 | "dev": true, 436 | "dev-package-names": [ 437 | "dealerdirect/phpcodesniffer-composer-installer", 438 | "phpcsstandards/phpcsextra", 439 | "phpcsstandards/phpcsutils", 440 | "squizlabs/php_codesniffer", 441 | "wp-coding-standards/wpcs" 442 | ] 443 | } 444 | -------------------------------------------------------------------------------- /src/WPBT/WPBT_Core.php: -------------------------------------------------------------------------------- 1 | wp_beta_tester = $wp_beta_tester; 39 | } 40 | 41 | /** 42 | * Load hooks. 43 | * 44 | * @return void 45 | */ 46 | public function load_hooks() { 47 | add_filter( 'wp_beta_tester_add_settings_tabs', array( $this, 'add_settings_tab' ) ); 48 | add_action( 'wp_beta_tester_add_settings', array( $this, 'add_settings' ) ); 49 | add_action( 'wp_beta_tester_add_admin_page', array( $this, 'add_admin_page' ), 10, 2 ); 50 | add_action( 'wp_beta_tester_update_settings', array( $this, 'save_settings' ) ); 51 | } 52 | 53 | /** 54 | * Add settings tab for class. 55 | * 56 | * @param array $tabs Settings tabs. 57 | * @return array 58 | */ 59 | public function add_settings_tab( $tabs ) { 60 | return array_merge( (array) $tabs, array( 'wp_beta_tester_core' => esc_html__( 'WP Beta Tester Settings', 'wordpress-beta-tester' ) ) ); 61 | } 62 | 63 | /** 64 | * Setup Settings API. 65 | * 66 | * @return void 67 | */ 68 | public function add_settings() { 69 | register_setting( 70 | 'wp_beta_tester', 71 | 'wp_beta_tester_core', 72 | array( 'WPBT_Setting', 'sanitize' ) 73 | ); 74 | 75 | add_settings_section( 76 | 'wp_beta_tester_core', 77 | esc_html__( 'Core Settings', 'wordpress-beta-tester' ), 78 | array( $this, 'print_core_settings_top' ), 79 | 'wp_beta_tester_core' 80 | ); 81 | 82 | add_settings_field( 83 | 'channel_settings', 84 | __( 'Select the update channel you would like this website to use:', 'wordpress-beta-tester' ), 85 | array( $this, 'channel_radio_group' ), 86 | 'wp_beta_tester_core', 87 | 'wp_beta_tester_core', 88 | array( 'class' => 'wpbt-settings-title' ) 89 | ); 90 | 91 | add_settings_field( 92 | 'stream_settings', 93 | __( 'Select one of the stream options below:', 'wordpress-beta-tester' ), 94 | array( $this, 'stream_radio_group' ), 95 | 'wp_beta_tester_core', 96 | 'wp_beta_tester_core', 97 | array( 'class' => 'wpbt-settings-title' ) 98 | ); 99 | } 100 | 101 | /** 102 | * Save settings. 103 | * 104 | * @param mixed $post_data $_POST data. 105 | * @return void 106 | */ 107 | public function save_settings( $post_data ) { 108 | if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ), 'wp_beta_tester_core-options' ) 109 | ) { 110 | return; 111 | } 112 | 113 | if ( isset( $post_data['option_page'] ) 114 | && 'wp_beta_tester_core' === $post_data['option_page'] 115 | ) { 116 | $option_channel = isset( $post_data['wp-beta-tester'] ) ? $post_data['wp-beta-tester'] : 'branch-development'; 117 | $options['channel'] = WPBT_Settings::sanitize( $option_channel ); 118 | 119 | $option_beta_rc = isset( $post_data['wp-beta-tester-beta-rc'] ) ? $post_data['wp-beta-tester-beta-rc'] : ''; 120 | $options['stream-option'] = WPBT_Settings::sanitize( $option_beta_rc ); 121 | 122 | $options = $this->channel_settings_migrator( $options ); 123 | $options = array_merge( self::$options, (array) $options ); 124 | 125 | update_site_option( 'wp_beta_tester', (array) $options ); 126 | add_filter( 'wp_beta_tester_save_redirect', array( $this, 'save_redirect_page' ) ); 127 | } 128 | } 129 | 130 | /** 131 | * Redirect page/tab after saving options. 132 | * 133 | * @param array $option_page Settings tabs. 134 | * @return array 135 | */ 136 | public function save_redirect_page( $option_page ) { 137 | return array_merge( $option_page, array( 'wp_beta_tester_core' ) ); 138 | } 139 | 140 | /** 141 | * Print settings section information. 142 | * 143 | * @return void 144 | */ 145 | public function print_core_settings_top() { 146 | $this->wp_beta_tester->action_admin_head_plugins_php(); // Check configuration. 147 | $preferred = $this->wp_beta_tester->get_preferred_from_update_core(); 148 | if ( defined( 'WP_AUTO_UPDATE_CORE' ) && WP_AUTO_UPDATE_CORE ) { 149 | echo '

    '; 150 | echo wp_kses_post( __( 'Warning: The `WP_AUTO_UPDATE_CORE` constant is set. WordPress Beta Tester settings will be overridden.', 'wordpress-beta-tester' ) ); 151 | echo '

    '; 152 | } 153 | 154 | $version = $this->get_next_version( $preferred->version ); 155 | 156 | echo '

    '; 157 | printf( 158 | /* translators: 1: link to backing up database, 2: link to make.wp.org/core, 3: link to beta support forum */ 159 | wp_kses_post( __( 'By their nature, these releases are unstable and should not be used anyplace where your data is important. So please back up your database before upgrading to a test release. In order to hear about the latest beta releases, your best bet is to watch the development blog and the beta forum.', 'wordpress-beta-tester' ) ), 160 | esc_url( _x( 'https://wordpress.org/support/article/wordpress-backups/', 'URL to database backup instructions on HelpHub', 'wordpress-beta-tester' ) ), 161 | 'https://make.wordpress.org/core/', 162 | esc_url( _x( 'https://wordpress.org/support/forum/alphabeta', 'URL to beta support forum', 'wordpress-beta-tester' ) ) 163 | ); 164 | echo '

    '; 165 | printf( 166 | /* translators: %s: link to new trac ticket */ 167 | wp_kses_post( __( 'Thank you for helping test WordPress. Please report any bugs you find.', 'wordpress-beta-tester' ) ), 168 | 'https://core.trac.wordpress.org/newticket' 169 | ); 170 | echo '

    '; 171 | echo wp_kses_post( __( 'By default, your WordPress installation uses the stable update channel. To return to this, please deactivate this plugin and re-install from the WordPress Updates page.', 'wordpress-beta-tester' ) ); 172 | echo '

    '; 173 | printf( 174 | /* translators: %s: update version */ 175 | wp_kses_post( __( 'Currently your site is set to update to %s.', 'wordpress-beta-tester' ) ), 176 | '' . esc_attr( $version ) . '' 177 | ); 178 | echo '

    '; 179 | } 180 | 181 | /** 182 | * Create channel settings radio button options. 183 | * 184 | * @return void 185 | */ 186 | public function channel_radio_group() { 187 | $next_versions = $this->calculate_next_versions(); 188 | ?> 189 |
    190 | 191 | 192 | 193 | 194 |
    195 |
    196 | 197 | 200 | 201 | 208 | 209 | 210 | 211 | 214 | Only use this if you really know what you are doing.', 'wordpress-beta-tester' ) ); ?> 215 | 216 |
    217 | 227 |
    228 | 229 | 232 | 233 | 234 | 235 | 236 | 239 | 240 | 241 | 242 | 245 | 246 | 247 | 248 | 249 | 250 | 253 | 254 | 255 | 256 | 259 | 260 | 261 | 262 |
    263 | 275 |
    276 | 277 |
    278 | 279 | 280 | 281 |
    282 | 283 |
    284 | 302 | calculate_next_versions(); 316 | 317 | // Try to set actual next beta/RC. 318 | if ( ( isset( $next_version['beta'] ) && version_compare( $next_version['preferred'], $next_version['beta'], '<' ) ) 319 | || ( isset( $next_version['rc'] ) && version_compare( $next_version['preferred'], $next_version['rc'], '<' ) ) 320 | ) { 321 | unset( $next_version['preferred'] ); 322 | } else { 323 | unset( $next_version['beta'], $next_version['rc'] ); 324 | } 325 | 326 | // Site is not on a beta/RC stream so use the preferred version. 327 | if ( ! $beta_rc && ! empty( $next_version ) ) { 328 | /* translators: %s: version number */ 329 | return sprintf( __( 'version %s', 'wordpress-beta-tester' ), $preferred_version ); 330 | } 331 | 332 | if ( 1 === count( $next_version ) ) { 333 | $next_version = array_shift( $next_version ); 334 | } elseif ( empty( $next_version ) ) { 335 | $next_version = __( 'next development version', 'wordpress-beta-tester' ); 336 | } else { 337 | // show all versions that may come next. 338 | add_filter( 'wp_sprintf_l', array( $this, 'wpbt_sprintf_or' ) ); 339 | /* translators: %l: next version numbers */ 340 | $next_version = wp_sprintf( __( 'version %l, whichever is released first', 'wordpress-beta-tester' ), $next_version ); 341 | remove_filter( 'wp_sprintf_l', array( $this, 'wpbt_sprintf_or' ) ); 342 | } 343 | 344 | return $next_version; 345 | } 346 | 347 | /** 348 | * Calculate next versions. 349 | * 350 | * @since 3.0.0 351 | * @return array $next_versions 352 | */ 353 | public function calculate_next_versions() { 354 | $wp_version = get_bloginfo( 'version' ); 355 | $exploded_version = explode( '-', $wp_version ); 356 | $current_release = $this->wp_beta_tester->get_current_wp_release(); 357 | $next_release = array_map( 'intval', explode( '.', $current_release ) ); 358 | $is_development_version = preg_match( '/alpha|beta|RC/', $wp_version ); 359 | $preferred = $this->wp_beta_tester->get_preferred_from_update_core(); 360 | 361 | // User on a current release. 362 | if ( ! $is_development_version ) { 363 | unset( $next_release[2] ); 364 | $next_release[1] = $next_release[1] + 1; 365 | 366 | // x.10 moves to (x+1).0 as core doesn't follow semver. 367 | if ( 10 === $next_release[1] ) { 368 | $next_release[0] = $next_release[0] + 1; 369 | $next_release[1] = 0; 370 | } 371 | $next_release = implode( '.', $next_release ); 372 | $exploded_version[0] = $next_release; 373 | $exploded_version[1] = ''; 374 | } 375 | 376 | // Set base version for development channel if necessary. 377 | $current_exploded = array_map( 'intval', explode( '.', $exploded_version[0] ) ); 378 | 379 | if ( 'development' === self::$options['channel'] && isset( $current_exploded[2] ) ) { 380 | $current_exploded[1] = ++$current_exploded[1]; 381 | unset( $current_exploded[2] ); 382 | $exploded_version[0] = implode( '.', $current_exploded ); 383 | $exploded_version[1] = 'alpha'; 384 | } 385 | 386 | preg_match( '/beta(\d+)/', $exploded_version[1], $current_beta ); 387 | preg_match( '/RC(\d+)/', $exploded_version[1], $current_rc ); 388 | $next_beta = ! empty( $current_beta ) ? $current_beta[1] + 1 : 1; 389 | $next_rc = ! empty( $current_rc ) ? $current_rc[1] + 1 : 1; 390 | 391 | // Make next point release. 392 | $next_point = array_map( 'intval', explode( '.', $current_release ) ); 393 | $next_point[2] = isset( $next_point[2] ) ? ++$next_point[2] : 1; 394 | $next_point = implode( '.', $next_point ); 395 | $next_point = 'development' === self::$options['channel'] && version_compare( $current_release, $wp_version, '>' ) ? $current_release : $next_point; 396 | 397 | // Set base version for branch-development channel. 398 | if ( 'branch-development' === self::$options['channel'] ) { 399 | $exploded_version = (array) $next_point; 400 | } 401 | 402 | $next_versions = array( 403 | 'point' => $next_point, 404 | 'beta' => $exploded_version[0] . '-beta' . $next_beta, 405 | 'rc' => $exploded_version[0] . '-RC' . $next_rc, 406 | 'preferred' => isset( $preferred->version ) ? $preferred->version : 0, 407 | 'release' => $exploded_version[0], 408 | ); 409 | 410 | if ( ! $next_versions['beta'] || 'rc' === self::$options['stream-option'] || 1 < $next_rc ) { 411 | unset( $next_versions['beta'] ); 412 | } 413 | 414 | return $next_versions; 415 | } 416 | 417 | /** 418 | * Change the delimiters used by wp_sprintf_l(). 419 | * 420 | * Placeholders (%s) are included to assist translators and then 421 | * removed before the array of strings reaches the filter. 422 | * 423 | * Please note: Ampersands and entities should be avoided here. 424 | * 425 | * @since 2.2.1 426 | * 427 | * @param array $delimiters An array of translated delimiters. 428 | */ 429 | public function wpbt_sprintf_or( $delimiters ) { 430 | $delimiters = array( 431 | /* translators: Used to join items in a list with more than 2 items. */ 432 | 'between' => sprintf( __( '%1$s, %2$s', 'wordpress-beta-tester' ), '', '' ), 433 | /* translators: Used to join last two items in a list with more than 2 times. */ 434 | 'between_last_two' => sprintf( __( '%1$s, or %2$s', 'wordpress-beta-tester' ), '', '' ), 435 | /* translators: Used to join items in a list with only 2 items. */ 436 | 'between_only_two' => sprintf( __( '%1$s or %2$s', 'wordpress-beta-tester' ), '', '' ), 437 | ); 438 | 439 | return $delimiters; 440 | } 441 | 442 | /** 443 | * Migrate stream during channel switch. 444 | * 445 | * @param array $options Array of channel and stream options. 446 | * 447 | * @return array 448 | */ 449 | private function channel_settings_migrator( $options ) { 450 | if ( isset( $options['channel'], $options['stream-option'] ) ) { 451 | switch ( $options['channel'] ) { 452 | case 'development': 453 | if ( 'branch-beta' === $options['stream-option'] ) { 454 | $options['stream-option'] = 'beta'; 455 | } elseif ( 'branch-rc' === $options['stream-option'] ) { 456 | $options['stream-option'] = 'rc'; 457 | } 458 | break; 459 | case 'branch-development': 460 | if ( 'beta' === $options['stream-option'] ) { 461 | $options['stream-option'] = 'branch-beta'; 462 | } elseif ( 'rc' === $options['stream-option'] ) { 463 | $options['stream-option'] = 'branch-rc'; 464 | } 465 | $options['stream-option'] = ''; // Hard set to Nightlies until API updated. 466 | break; 467 | } 468 | } 469 | 470 | return $options; 471 | } 472 | } 473 | --------------------------------------------------------------------------------