v1.5.0
This commit is contained in:
parent
8e6a52d8ce
commit
b2849880ec
8 changed files with 91 additions and 16 deletions
|
|
@ -263,7 +263,16 @@ class Robotstxt_Manager_Settings {
|
||||||
}
|
}
|
||||||
echo '</p>';
|
echo '</p>';
|
||||||
} else {
|
} else {
|
||||||
echo '<p class="description">' . esc_html__( 'Account-level API key from the ROBOTSTXT store (create your free account there to get one). Optional — the free catalog works without it — but required to link your subscriptions, install premium plugins, and receive their updates. Encrypted before storage.', 'robotstxt-manager' ) . '</p>';
|
printf(
|
||||||
|
'<p class="description">%s</p>',
|
||||||
|
wp_kses_post(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Registration URL. */
|
||||||
|
__( 'Account-level API key from the ROBOTSTXT store (<a href="%s" target="_blank" rel="noopener">create your free account there to get one</a>). Optional — the free catalog works without it — but required to link your subscriptions, install premium plugins, and receive their updates. Encrypted before storage.', 'robotstxt-manager' ),
|
||||||
|
esc_url( 'https://www.robotstxt.software/wp-login.php?action=register' )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action buttons.
|
// Action buttons.
|
||||||
|
|
@ -333,6 +342,14 @@ class Robotstxt_Manager_Settings {
|
||||||
return is_string( $raw ) ? $raw : '';
|
return is_string( $raw ) ? $raw : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the input is already encrypted (v2: prefix), it means the browser
|
||||||
|
// auto-filled the password field with the stored encrypted value.
|
||||||
|
// Return it as-is (already encrypted) rather than re-encrypting or
|
||||||
|
// trying to read the option (which may not be saved yet in the WP flow).
|
||||||
|
if ( str_starts_with( $plain, 'v2:' ) ) {
|
||||||
|
return $plain;
|
||||||
|
}
|
||||||
|
|
||||||
// Account keys are UUIDs issued by the store; reject anything that
|
// Account keys are UUIDs issued by the store; reject anything that
|
||||||
// cannot be one rather than storing a mangled key that only fails
|
// cannot be one rather than storing a mangled key that only fails
|
||||||
// later at connection time.
|
// later at connection time.
|
||||||
|
|
@ -379,7 +396,25 @@ class Robotstxt_Manager_Settings {
|
||||||
wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'robotstxt-manager' ) ) );
|
wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'robotstxt-manager' ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow testing a key from the form field (not yet saved) by passing it in the request.
|
||||||
|
$input_key = '';
|
||||||
|
if ( isset( $_POST['robotstxt_manager_api_key'] ) ) {
|
||||||
|
$unslashed = wp_unslash( $_POST['robotstxt_manager_api_key'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below.
|
||||||
|
if ( is_string( $unslashed ) ) {
|
||||||
|
$input_key = sanitize_text_field( $unslashed );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$store_url = get_option( 'robotstxt_manager_store_url', 'https://www.robotstxt.software' );
|
||||||
|
$store_url = is_string( $store_url ) ? $store_url : 'https://www.robotstxt.software';
|
||||||
|
|
||||||
|
// Use input key if provided, otherwise fall back to saved (decrypted) key.
|
||||||
|
if ( '' !== $input_key ) {
|
||||||
|
$client = new Robotstxt_Manager_Core_Client( $store_url, $input_key );
|
||||||
|
} else {
|
||||||
$client = Robotstxt_Manager_Core_Client::from_options();
|
$client = Robotstxt_Manager_Core_Client::from_options();
|
||||||
|
}
|
||||||
|
|
||||||
$result = $client->test_connection();
|
$result = $client->test_connection();
|
||||||
|
|
||||||
if ( $result['ok'] ) {
|
if ( $result['ok'] ) {
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,14 @@
|
||||||
$btn.prop( 'disabled', true ).text( RobotstxtManagerSettings.i18n.testing );
|
$btn.prop( 'disabled', true ).text( RobotstxtManagerSettings.i18n.testing );
|
||||||
$result.text( '' ).css( 'color', '' );
|
$result.text( '' ).css( 'color', '' );
|
||||||
|
|
||||||
|
var apiKey = $( '#robotstxt_manager_api_key' ).val();
|
||||||
|
|
||||||
$.post(
|
$.post(
|
||||||
RobotstxtManagerSettings.ajaxUrl,
|
RobotstxtManagerSettings.ajaxUrl,
|
||||||
{
|
{
|
||||||
action: 'robotstxt_manager_test_connection',
|
action: 'robotstxt_manager_test_connection',
|
||||||
nonce: RobotstxtManagerSettings.nonce,
|
nonce: RobotstxtManagerSettings.nonce,
|
||||||
|
robotstxt_manager_api_key: apiKey,
|
||||||
},
|
},
|
||||||
function ( response ) {
|
function ( response ) {
|
||||||
if ( response.success ) {
|
if ( response.success ) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1><?php esc_html_e( 'Manager (by ROBOTSTXT) — Settings', 'robotstxt-manager' ); ?></h1>
|
<h1><?php esc_html_e( 'Manager (by ROBOTSTXT) — Settings', 'robotstxt-manager' ); ?></h1>
|
||||||
|
<?php settings_errors(); ?>
|
||||||
<form method="post" action="<?php echo esc_url( admin_url( 'options.php' ) ); ?>">
|
<form method="post" action="<?php echo esc_url( admin_url( 'options.php' ) ); ?>">
|
||||||
<?php
|
<?php
|
||||||
settings_fields( Robotstxt_Manager_Settings::OPTION_GROUP );
|
settings_fields( Robotstxt_Manager_Settings::OPTION_GROUP );
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,26 @@
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.5.0 =
|
||||||
|
|
||||||
|
_Release date: 2026-08-18_
|
||||||
|
|
||||||
|
**Fixed**
|
||||||
|
|
||||||
|
* API key not saving when the browser auto-fills the password field with the stored encrypted value — the sanitizer now detects already-encrypted input (`v2:` prefix) and returns it as-is instead of failing UUID validation.
|
||||||
|
* "Test connection" validated against the public catalog endpoint (`/plugins`), so invalid API keys reported "Connected". Now uses the authenticated `/me/subscriptions` endpoint; a 401 response produces "Invalid API key".
|
||||||
|
* "Test connection" read the key from saved options only — entering a new key and testing before saving always tested the old (or empty) key. The AJAX handler now accepts the key from the form field.
|
||||||
|
|
||||||
|
**Added**
|
||||||
|
|
||||||
|
* `settings_errors()` call on the settings page so validation messages (e.g. "invalid API key format") are actually visible to the user.
|
||||||
|
* Registration link (`https://www.robotstxt.software/wp-login.php?action=register`) in the API key field description.
|
||||||
|
|
||||||
|
**Tests**
|
||||||
|
|
||||||
|
* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): pass
|
||||||
|
* PHPStan level 9 + wp-compat: pass
|
||||||
|
* PHPUnit: 146 tests, 385 assertions
|
||||||
|
|
||||||
= 1.4.1 =
|
= 1.4.1 =
|
||||||
|
|
||||||
_Release date: 2026-08-17_
|
_Release date: 2026-08-17_
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ class Robotstxt_Manager_Core_Client {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->get( '/plugins' );
|
$response = $this->get( '/me/subscriptions' );
|
||||||
|
|
||||||
if ( is_wp_error( $response ) ) {
|
if ( is_wp_error( $response ) ) {
|
||||||
return array(
|
return array(
|
||||||
|
|
@ -161,6 +161,13 @@ class Robotstxt_Manager_Core_Client {
|
||||||
|
|
||||||
$code = (int) wp_remote_retrieve_response_code( $response );
|
$code = (int) wp_remote_retrieve_response_code( $response );
|
||||||
|
|
||||||
|
if ( 401 === $code ) {
|
||||||
|
return array(
|
||||||
|
'ok' => false,
|
||||||
|
'message' => __( 'Invalid API key. Please check your key and try again.', 'robotstxt-manager' ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ( 200 !== $code ) {
|
if ( 200 !== $code ) {
|
||||||
return array(
|
return array(
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
|
|
@ -175,7 +182,7 @@ class Robotstxt_Manager_Core_Client {
|
||||||
return array(
|
return array(
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
'message' => __( 'Connected.', 'robotstxt-manager' ),
|
'message' => __( 'Connected.', 'robotstxt-manager' ),
|
||||||
'catalog_count' => $count,
|
'subscriptions' => $count,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# This file is distributed under the GPL-3.0-or-later.
|
# This file is distributed under the GPL-3.0-or-later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Manager (by ROBOTSTXT) 1.4.1\n"
|
"Project-Id-Version: Manager (by ROBOTSTXT) 1.5.0\n"
|
||||||
"Report-Msgid-Bugs-To: https://www.robotstxt.software/plugins/robotstxt-manager/\n"
|
"Report-Msgid-Bugs-To: https://www.robotstxt.software/plugins/robotstxt-manager/\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -259,7 +259,7 @@ msgid "A key is stored but could not be decoded. You can replace it by entering
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: admin/class-robotstxt-manager-settings.php:266
|
#: admin/class-robotstxt-manager-settings.php:266
|
||||||
msgid "Account-level API key from the ROBOTSTXT store (create your free account there to get one). Optional — the free catalog works without it — but required to link your subscriptions, install premium plugins, and receive their updates. Encrypted before storage."
|
msgid "Account-level API key from the ROBOTSTXT store (<a href=\"%s\" target=\"_blank\" rel=\"noopener\">create your free account there to get one</a>). Optional — the free catalog works without it — but required to link your subscriptions, install premium plugins, and receive their updates. Encrypted before storage."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: admin/class-robotstxt-manager-settings.php:298
|
#: admin/class-robotstxt-manager-settings.php:298
|
||||||
|
|
@ -475,6 +475,10 @@ msgstr ""
|
||||||
msgid "The store responded with HTTP %d. Check the Store URL and API key."
|
msgid "The store responded with HTTP %d. Check the Store URL and API key."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: includes/class-robotstxt-manager-core-client.php:164
|
||||||
|
msgid "Invalid API key. Please check your key and try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: includes/class-robotstxt-manager-core-client.php:177
|
#: includes/class-robotstxt-manager-core-client.php:177
|
||||||
msgid "Connected."
|
msgid "Connected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
20
readme.txt
20
readme.txt
|
|
@ -3,9 +3,9 @@ Contributors: robotstxt, javiercasares
|
||||||
Tags: dashboard, catalog, updates, subscriptions, management
|
Tags: dashboard, catalog, updates, subscriptions, management
|
||||||
Requires at least: 4.4
|
Requires at least: 4.4
|
||||||
Tested up to: 7.1
|
Tested up to: 7.1
|
||||||
Stable tag: 1.4.1
|
Stable tag: 1.5.0
|
||||||
Requires PHP: 8.0
|
Requires PHP: 8.0
|
||||||
Version: 1.4.1
|
Version: 1.5.0
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
License URI: https://www.gnu.org/licenses/gpl-3.0.txt
|
License URI: https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
|
||||||
|
|
@ -94,6 +94,16 @@ Encrypted at rest using AES-256-CBC with a key derived from your site's WordPres
|
||||||
|
|
||||||
Only the 3 last versions. The full changelog will be at changelog.txt
|
Only the 3 last versions. The full changelog will be at changelog.txt
|
||||||
|
|
||||||
|
= 1.5.0 =
|
||||||
|
|
||||||
|
_Release date: 2026-08-18_
|
||||||
|
|
||||||
|
* Fixed: API key not saving when the browser auto-fills the password field with the stored encrypted value.
|
||||||
|
* Fixed: "Test connection" now validates the API key against an authenticated Core endpoint (`/me/subscriptions`) instead of the public catalog — invalid keys are correctly rejected.
|
||||||
|
* Fixed: "Test connection" reads the key from the form field, so a key can be tested before saving.
|
||||||
|
* Added: Validation errors now display on the settings page (missing `settings_errors()` call).
|
||||||
|
* Added: Registration link in the API key field description.
|
||||||
|
|
||||||
= 1.4.1 =
|
= 1.4.1 =
|
||||||
|
|
||||||
_Release date: 2026-08-17_
|
_Release date: 2026-08-17_
|
||||||
|
|
@ -107,12 +117,6 @@ _Release date: 2026-08-17_
|
||||||
|
|
||||||
* Premium update URLs carry a short-lived download token (Core 1.9.0+) instead of the API key; automatic fallback on older Core.
|
* Premium update URLs carry a short-lived download token (Core 1.9.0+) instead of the API key; automatic fallback on older Core.
|
||||||
|
|
||||||
= 1.3.1 =
|
|
||||||
|
|
||||||
_Release date: 2026-08-17_
|
|
||||||
|
|
||||||
* Descriptions render in the admin's language (Core 1.8.0+ `description_translations`), English fallback.
|
|
||||||
|
|
||||||
= Previous versions =
|
= Previous versions =
|
||||||
|
|
||||||
If you want to see the full changelog, visit the [plugin page](https://www.robotstxt.software/plugins/robotstxt-manager/).
|
If you want to see the full changelog, visit the [plugin page](https://www.robotstxt.software/plugins/robotstxt-manager/).
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Plugin Name: Manager (by ROBOTSTXT)
|
* Plugin Name: Manager (by ROBOTSTXT)
|
||||||
* Plugin URI: https://www.robotstxt.software/plugins/robotstxt-manager/
|
* Plugin URI: https://www.robotstxt.software/plugins/robotstxt-manager/
|
||||||
* Description: Client-side dashboard for the ROBOTSTXT plugin ecosystem. Lists the catalog from a remote Plugins Core install, resolves local install/update state, and installs, activates, and updates plugins directly from the store.
|
* Description: Client-side dashboard for the ROBOTSTXT plugin ecosystem. Lists the catalog from a remote Plugins Core install, resolves local install/update state, and installs, activates, and updates plugins directly from the store.
|
||||||
* Version: 1.4.1
|
* Version: 1.5.0
|
||||||
* Requires at least: 4.4
|
* Requires at least: 4.4
|
||||||
* Requires PHP: 8.0
|
* Requires PHP: 8.0
|
||||||
* Update URI: https://www.robotstxt.software/plugins/robotstxt-manager/
|
* Update URI: https://www.robotstxt.software/plugins/robotstxt-manager/
|
||||||
|
|
@ -22,7 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Plugin version. */
|
/** Plugin version. */
|
||||||
define( 'ROBOTSTXT_MANAGER_VERSION', '1.4.1' );
|
define( 'ROBOTSTXT_MANAGER_VERSION', '1.5.0' );
|
||||||
|
|
||||||
/** Absolute path to the plugin directory, with trailing slash. */
|
/** Absolute path to the plugin directory, with trailing slash. */
|
||||||
define( 'ROBOTSTXT_MANAGER_DIR', plugin_dir_path( __FILE__ ) );
|
define( 'ROBOTSTXT_MANAGER_DIR', plugin_dir_path( __FILE__ ) );
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue