diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9814f0fb7..35cfb260a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -71,6 +71,9 @@ jobs: - name: Check PHPCS standard run: ./vendor/bin/phpcs -i + - name: Check Compatibility of main plugin file. + run: ./vendor/bin/phpcs ./distributor.php --runtime-set testVersion 5.6- --standard=PHPCompatibilityWP + - name: PHPCS check uses: chekalsky/phpcs-action@v1 with: diff --git a/Dockunit.json b/Dockunit.json index 2ecfa7c25..2f8d8533c 100644 --- a/Dockunit.json +++ b/Dockunit.json @@ -1,8 +1,8 @@ { "containers": [ { - "prettyName": "PHP 5.6", - "image": "php:5.6", + "prettyName": "PHP 7.4", + "image": "php:7.4", "beforeScripts": [ "php -r \"copy('https://getcomposer.org/installer', 'composer-setup.php');\"", "php composer-setup.php --install-dir=/usr/local/bin --filename=composer", @@ -14,8 +14,8 @@ "testCommand": "phpunit" }, { - "prettyName": "PHP 7.0", - "image": "php:7.0", + "prettyName": "PHP 8.1", + "image": "php:8.1", "beforeScripts": [ "php -r \"copy('https://getcomposer.org/installer', 'composer-setup.php');\"", "php composer-setup.php --install-dir=/usr/local/bin --filename=composer", @@ -27,4 +27,4 @@ "testCommand": "phpunit" } ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index 267bda9c9..5c518a7d0 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,9 @@ Distributor is built with the same extensible approach as WordPress itself, with ## Requirements -* PHP 5.6+ -* [WordPress](http://wordpress.org) 4.7+ -* External connections require HTTP Basic Authentication or [WordPress.com OAuth2](https://developer.wordpress.com/docs/oauth2/) (must be on [WordPress VIP](https://wpvip.com/)) be set up on the remote website. For Basic Auth, we recommend the [Application Passwords](https://wordpress.org/plugins/application-passwords/) plugin (note that other plugins like JWT Auth will likely cause issues for external connections) though if you're running WordPress 5.6 or above then Application Passwords is already installed. +* PHP 7.4+ +* [WordPress](http://wordpress.org) 5.7+ +* External connections require HTTP Basic Authentication or [WordPress.com OAuth2](https://developer.wordpress.com/docs/oauth2/) (must be on [WordPress VIP](https://wpvip.com/)) be set up on the remote website. For Basic Auth, we recommend using [Application Passwords](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/#Getting-Credentials) built in to WordPress. * For external connections, Distributor needs to be installed on BOTH sides of the connection. ## Installation @@ -169,9 +169,15 @@ Enabling this will also provide more debugging information in your error log for ### Application Passwords and WordPress 5.6 -In WordPress 5.6, Application Passwords was merged into WordPress core with some limitations. From 5.6, Application Passwords is enabled by default only for live sites with HTTPS. To enable Application Passwords for development sites, you will need the following snippet: +Application passwords are only available for live sites running over an HTTPS connection. + +For your local development environment, you will need these snippets to enable application passwords without the need for an HTTPS connection. A local development environment is one that "can reach the internet but **is not reachable from the internet**". ```php +// In your local environment's wp-config.php file. +define( 'WP_ENVIRONMENT_TYPE', 'local' ); + +// In a custom plugin on your local environment. add_filter( 'wp_is_application_passwords_available', '__return_true' ); add_action( 'wp_authorize_application_password_request_errors', function( $error ) { diff --git a/assets/js/gutenberg-syndicated-post.js b/assets/js/gutenberg-syndicated-post.js index 55d9b0347..c307368ca 100644 --- a/assets/js/gutenberg-syndicated-post.js +++ b/assets/js/gutenberg-syndicated-post.js @@ -1,10 +1,8 @@ import { dispatch } from '@wordpress/data'; -import { __, setLocaleData, sprintf } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; const { dtGutenberg } = window; -setLocaleData( dtGutenberg.i18n, 'distributor' ); - if ( '0' !== dtGutenberg.originalSourceId || '0' !== dtGutenberg.originalBlogId diff --git a/composer.json b/composer.json index b7564a321..5b59beb1c 100644 --- a/composer.json +++ b/composer.json @@ -10,24 +10,9 @@ "email": "opensource@10up.com" } ], - "repositories": [ - { - "type":"package", - "package": { - "name": "georgestephanis/application-passwords", - "version":"0.1.3", - "source": { - "url": "https://github.com/WordPress/application-passwords.git", - "type": "git", - "reference":"0eef095b4dc984c26ee8149c01a323be83da715a" - } - } - } - ], "require": { - "php": ">=5.6", + "php": ">=7.4", "yahnis-elsts/plugin-update-checker": "4.9", - "georgestephanis/application-passwords": "0.1.3", "ext-json": "*" }, "autoload": { @@ -43,7 +28,7 @@ "automattic/vipwpcs": "^2.3" }, "scripts": { - "lint": "phpcs . --runtime-set testVersion 5.6-", + "lint": "phpcs . --runtime-set testVersion 7.4-", "lint-fix": "phpcbf .", "test": "@php phpunit" }, diff --git a/composer.lock b/composer.lock index 59d752e75..3404d815b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,18 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a9a94024c09ec4b273a1af6556928861", + "content-hash": "37702a48014cfd9fd912b833973a3064", "packages": [ - { - "name": "georgestephanis/application-passwords", - "version": "0.1.3", - "source": { - "type": "git", - "url": "https://github.com/WordPress/application-passwords.git", - "reference": "0eef095b4dc984c26ee8149c01a323be83da715a" - }, - "type": "library" - }, { "name": "yahnis-elsts/plugin-update-checker", "version": "v4.9", @@ -95,10 +85,6 @@ "email": "ephraim.gregor@10up.com" } ], - "support": { - "issues": "https://github.com/10up/phpcs-composer/issues", - "source": "https://github.com/10up/phpcs-composer/tree/master" - }, "time": "2021-01-08T03:03:06+00:00" }, { @@ -140,10 +126,6 @@ "GPL-2.0-or-later" ], "description": "A mocking library to take the pain out of unit testing for WordPress", - "support": { - "issues": "https://github.com/10up/wp_mock/issues", - "source": "https://github.com/10up/wp_mock/tree/master" - }, "time": "2019-03-16T03:44:39+00:00" }, { @@ -188,6 +170,10 @@ "runkit", "testing" ], + "support": { + "issues": "https://github.com/antecedent/patchwork/issues", + "source": "https://github.com/antecedent/patchwork/tree/2.1.21" + }, "time": "2022-02-07T07:28:34+00:00" }, { @@ -311,6 +297,10 @@ "stylecheck", "tests" ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, "time": "2022-02-04T12:51:07+00:00" }, { @@ -363,6 +353,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -424,6 +418,10 @@ "keywords": [ "test" ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, "time": "2020-07-09T08:09:16+00:00" }, { @@ -492,6 +490,10 @@ "test double", "testing" ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.5.0" + }, "time": "2022-01-20T13:18:17+00:00" }, { @@ -541,6 +543,10 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", @@ -603,6 +609,10 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, "time": "2021-07-20T11:28:43+00:00" }, { @@ -650,6 +660,10 @@ } ], "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, "time": "2022-02-21T01:04:05+00:00" }, { @@ -708,10 +722,6 @@ "phpcs", "standards" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" - }, "time": "2019-12-27T09:44:58+00:00" }, { @@ -764,6 +774,10 @@ "polyfill", "standards" ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, "time": "2021-02-15T10:24:51+00:00" }, { @@ -814,6 +828,10 @@ "standards", "wordpress" ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, "time": "2021-12-30T16:37:40+00:00" }, { @@ -863,6 +881,10 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -916,6 +938,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, "time": "2021-10-19T17:43:47+00:00" }, { @@ -962,6 +988,10 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, "time": "2022-03-15T21:29:03+00:00" }, { @@ -1025,6 +1055,10 @@ "spy", "stub" ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + }, "time": "2021-12-08T12:19:24+00:00" }, { @@ -1088,6 +1122,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1144,6 +1182,10 @@ "filesystem", "iterator" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1191,10 +1233,6 @@ "keywords": [ "template" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, "time": "2015-06-21T13:50:34+00:00" }, { @@ -1244,6 +1282,10 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1299,6 +1341,10 @@ "keywords": [ "tokenizer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1389,6 +1435,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.26" + }, "funding": [ { "url": "https://phpunit.de/sponsors.html", @@ -1444,6 +1494,10 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1514,6 +1568,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1576,6 +1634,10 @@ "unidiff", "unified diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1635,6 +1697,10 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1708,6 +1774,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1768,6 +1838,10 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1821,6 +1895,10 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1872,6 +1950,10 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1931,6 +2013,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1979,6 +2065,10 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2031,6 +2121,10 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -2080,10 +2174,6 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, "time": "2016-10-03T07:35:21+00:00" }, { @@ -2188,6 +2278,11 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, "time": "2022-06-13T06:31:38+00:00" }, { @@ -2228,6 +2323,10 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, "funding": [ { "url": "https://github.com/theseer", @@ -2288,6 +2387,10 @@ "check", "validate" ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, "time": "2022-06-03T18:03:27+00:00" }, { @@ -2334,11 +2437,6 @@ "standards", "wordpress" ], - "support": { - "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", - "source": "https://github.com/WordPress/WordPress-Coding-Standards", - "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" - }, "time": "2020-05-13T23:57:56+00:00" }, { @@ -2396,6 +2494,10 @@ "polyfill", "testing" ], + "support": { + "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "source": "https://github.com/Yoast/PHPUnit-Polyfills" + }, "time": "2021-11-23T01:37:03+00:00" } ], diff --git a/distributor.php b/distributor.php index bd030597b..f6d5eea40 100644 --- a/distributor.php +++ b/distributor.php @@ -5,6 +5,8 @@ * Update URI: https://distributorplugin.com * Description: Makes it easy to distribute and reuse content across your websites, whether inside of a multisite or across the web. * Version: 1.7.1 + * Requires at least: 5.7 + * Requires PHP: 7.4 * Author: 10up Inc. * Author URI: https://distributorplugin.com * License: GPLv2 or later @@ -12,8 +14,16 @@ * Domain Path: /lang/ * * @package distributor + * + * Developer note: This file is used to test whether the user's server supports + * the minimum software requirements for this plugin prior to bootstrapping. + * + * Unlike the rest of the plugin, this file needs to be compatible with PHP 5.6 + * and WordPress 4.7.0. */ +namespace Distributor; + if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } @@ -21,6 +31,7 @@ define( 'DT_VERSION', '1.7.1' ); define( 'DT_PLUGIN_FILE', preg_replace( '#^.*plugins/(.*)$#i', '$1', __FILE__ ) ); define( 'DT_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); +define( 'DT_PLUGIN_FULL_FILE', __FILE__ ); // Define a constant if we're network activated to allow plugin to respond accordingly. $active_plugins = get_site_option( 'active_sitewide_plugins' ); @@ -32,230 +43,164 @@ } /** - * PSR-4 autoloading + * Get the minimum version of WordPress required by this plugin. + * + * @since x.x.x + * + * @return string Minimum version required. */ -spl_autoload_register( - function( $class ) { - // Project-specific namespace prefix. - $prefix = 'Distributor\\'; - // Base directory for the namespace prefix. - $base_dir = __DIR__ . '/includes/classes/'; - // Does the class use the namespace prefix? - $len = strlen( $prefix ); - if ( strncmp( $prefix, $class, $len ) !== 0 ) { - return; - } - $relative_class = substr( $class, $len ); - $file = $base_dir . str_replace( '\\', '/', $relative_class ) . '.php'; - // If the file exists, require it. - if ( file_exists( $file ) ) { - require $file; - } - } -); +function minimum_wp_requirement() { + return '5.7'; +} /** - * Require PHP version 5.6 - throw an error if the plugin is activated on an older version. + * Get the minimum version of PHP required by this plugin. + * + * @since x.x.x + * + * @return string Minimum version required. */ -register_activation_hook( - __FILE__, - function() { - if ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) { - wp_die( - esc_html__( 'Distributor requires PHP version 5.6.', 'distributor' ), - esc_html__( 'Error Activating', 'distributor' ) - ); - } - } -); +function minimum_php_requirement() { + return '7.4'; +} /** - * Tell the world this site supports Distributor. We need this for external connections. + * Whether WP installation meets the minimum requirements + * + * @since x.x.x + * + * @return bool True if meets minimum requirements, false otherwise. */ -add_action( - 'send_headers', - function() { - if ( ! headers_sent() ) { - header( 'X-Distributor: yes' ); - } - } -); +function site_meets_wp_requirements() { + global $wp_version; + return version_compare( $wp_version, minimum_wp_requirement(), '>=' ); +} /** - * Set Distributor header in all API responses. + * Whether PHP installation meets the minimum requirements + * + * @since x.x.x + * + * @return bool True if meets minimum requirements, false otherwise. */ -add_filter( - 'rest_post_dispatch', - function( $response ) { - $response->header( 'X-Distributor', 'yes' ); - - return $response; - } -); - -\Distributor\Connections::factory(); - -// Include in case we have composer issues. -require_once __DIR__ . '/vendor/yahnis-elsts/plugin-update-checker/plugin-update-checker.php'; - -require_once __DIR__ . '/includes/utils.php'; -require_once __DIR__ . '/includes/global-functions.php'; -require_once __DIR__ . '/includes/external-connection-cpt.php'; -require_once __DIR__ . '/includes/push-ui.php'; -require_once __DIR__ . '/includes/pull-ui.php'; -require_once __DIR__ . '/includes/rest-api.php'; -require_once __DIR__ . '/includes/subscriptions.php'; -require_once __DIR__ . '/includes/syndicated-post-ui.php'; -require_once __DIR__ . '/includes/distributed-post-ui.php'; -require_once __DIR__ . '/includes/settings.php'; -require_once __DIR__ . '/includes/template-tags.php'; -require_once __DIR__ . '/includes/debug-info.php'; +function site_meets_php_requirements() { + return version_compare( phpversion(), minimum_php_requirement(), '>=' ); +} -// Include application passwords. -add_action( - 'plugins_loaded', +/** + * Require PHP 7.4+, WP 5.7+ - throw an error if the plugin is activated on an older version. + */ +register_activation_hook( + __FILE__, function() { - if ( function_exists( 'wp_is_application_passwords_available' ) ) { - if ( ! wp_is_application_passwords_available() ) { - add_action( - 'admin_notices', - function() { - if ( get_current_screen()->id !== 'toplevel_page_distributor' ) { - return; - } - ?> -
- guide to enable Application Passwords.', 'distributor' ), - 'https://github.com/10up/distributor#application-passwords-and-wordpress-56' - ) - ); - ?> -
-+ +
++ +
++ +
++ guide to enable Application Passwords.', 'distributor' ), + 'https://github.com/10up/distributor#application-passwords-and-wordpress-56' + ) + ); + ?> +
+