v1.1.0
This commit is contained in:
parent
e7a4a45a74
commit
e80820f416
10 changed files with 389 additions and 9 deletions
|
|
@ -54,19 +54,297 @@ class Robotstxt_Manager_Installer {
|
||||||
$name = $this->entry_name( $entry, $slug );
|
$name = $this->entry_name( $entry, $slug );
|
||||||
$this->ensure_plugin_functions();
|
$this->ensure_plugin_functions();
|
||||||
|
|
||||||
|
// Install missing dependencies first (ecosystem catalog plugins via
|
||||||
|
// the store, WordPress.org plugins via their repository ZIPs).
|
||||||
|
$deps_installed = $this->install_dependencies( $slug );
|
||||||
|
|
||||||
|
if ( is_wp_error( $deps_installed ) ) {
|
||||||
|
$this->redirect_error( $deps_installed->get_error_message() );
|
||||||
|
}
|
||||||
|
|
||||||
$result = $this->download_and_install( $slug );
|
$result = $this->download_and_install( $slug );
|
||||||
|
|
||||||
if ( is_wp_error( $result ) ) {
|
if ( is_wp_error( $result ) ) {
|
||||||
$this->redirect_error( $result->get_error_message() );
|
$this->redirect_error( $result->get_error_message() );
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->redirect_success(
|
$message = sprintf(
|
||||||
sprintf(
|
|
||||||
/* translators: %s: plugin name. */
|
/* translators: %s: plugin name. */
|
||||||
__( '%s installed. Activate it from the list below.', 'robotstxt-manager' ),
|
__( '%s installed. Activate it from the list below.', 'robotstxt-manager' ),
|
||||||
$name
|
$name
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_string( $deps_installed ) && '' !== $deps_installed ) {
|
||||||
|
$message .= ' ' . $deps_installed;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect_success( $message );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs the plugin's missing dependencies, dependencies first.
|
||||||
|
*
|
||||||
|
* Each dependency slug is resolved either against the store catalog
|
||||||
|
* (installed through the store's download endpoint, like any catalog
|
||||||
|
* plugin) or, when not in the catalog, against WordPress.org (installed
|
||||||
|
* from the repository's plugin ZIP).
|
||||||
|
*
|
||||||
|
* @param string $slug Plugin slug being installed.
|
||||||
|
*
|
||||||
|
* @return string|WP_Error Installed-dependency names for the notice, '' when none were needed.
|
||||||
|
*/
|
||||||
|
private function install_dependencies( string $slug ) {
|
||||||
|
$deps = $this->dependencies_for( $slug );
|
||||||
|
|
||||||
|
if ( array() === $deps ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed_names = array();
|
||||||
|
|
||||||
|
foreach ( $deps as $dep_slug ) {
|
||||||
|
if ( '' !== $this->find_plugin_file( $dep_slug ) ) {
|
||||||
|
continue; // Already installed.
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->install_one_dependency( $dep_slug );
|
||||||
|
|
||||||
|
if ( is_wp_error( $result ) ) {
|
||||||
|
/* translators: %s: dependency slug. */
|
||||||
|
$detail = sprintf( __( 'Could not install the required plugin %s.', 'robotstxt-manager' ), $dep_slug );
|
||||||
|
|
||||||
|
return new WP_Error(
|
||||||
|
'robotstxt_manager_dependency',
|
||||||
|
$detail . ' ' . $result->get_error_message()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed_names[] = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( array() === $installed_names ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* translators: %s: list of installed dependency names. */
|
||||||
|
return sprintf( __( 'Installed required plugins: %s.', 'robotstxt-manager' ), implode( ', ', $installed_names ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a single dependency: catalog plugin via the store, else wp.org.
|
||||||
|
*
|
||||||
|
* @param string $dep_slug Dependency slug.
|
||||||
|
*
|
||||||
|
* @return string|WP_Error The dependency's display name on success.
|
||||||
|
*/
|
||||||
|
private function install_one_dependency( string $dep_slug ) {
|
||||||
|
if ( null !== $this->find_catalog_entry( $dep_slug ) ) {
|
||||||
|
return $this->install_via_store( $dep_slug );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in the catalog — treat as a WordPress.org plugin.
|
||||||
|
return $this->install_via_wordpress_org( $dep_slug );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a dependency that exists in the store catalog.
|
||||||
|
*
|
||||||
|
* @param string $dep_slug Dependency slug.
|
||||||
|
*
|
||||||
|
* @return string|WP_Error Dependency name on success.
|
||||||
|
*/
|
||||||
|
protected function install_via_store( string $dep_slug ) {
|
||||||
|
$result = $this->download_and_install( $dep_slug );
|
||||||
|
|
||||||
|
if ( is_wp_error( $result ) ) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $this->find_catalog_entry( $dep_slug );
|
||||||
|
|
||||||
|
return $this->entry_name( is_array( $entry ) ? $entry : null, $dep_slug );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a dependency that is not in the catalog from WordPress.org.
|
||||||
|
*
|
||||||
|
* @param string $dep_slug wp.org plugin slug.
|
||||||
|
*
|
||||||
|
* @return string|WP_Error Plugin name on success.
|
||||||
|
*/
|
||||||
|
protected function install_via_wordpress_org( string $dep_slug ) {
|
||||||
|
return $this->install_wporg_zip( $dep_slug );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a plugin's dependency slugs (parsed, deduplicated, deps-first
|
||||||
|
* order, self-references dropped).
|
||||||
|
*
|
||||||
|
* @param string $slug Plugin slug.
|
||||||
|
*
|
||||||
|
* @return list<string> Dependency slugs.
|
||||||
|
*/
|
||||||
|
private function dependencies_for( string $slug ): array {
|
||||||
|
$all = array( $slug => true );
|
||||||
|
$queue = array( $slug );
|
||||||
|
$ordered = array();
|
||||||
|
|
||||||
|
while ( ! empty( $queue ) ) {
|
||||||
|
$current = array_shift( $queue );
|
||||||
|
|
||||||
|
foreach ( $this->raw_dependencies_of( $current ) as $dep ) {
|
||||||
|
if ( isset( $all[ $dep ] ) ) {
|
||||||
|
continue; // Already seen: self, duplicate, or cycle.
|
||||||
|
}
|
||||||
|
|
||||||
|
$all[ $dep ] = true;
|
||||||
|
$ordered[] = $dep;
|
||||||
|
$queue[] = $dep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependencies discovered later are deeper — reverse so dependencies
|
||||||
|
// of dependencies install first.
|
||||||
|
return array_reverse( $ordered );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the raw requires-plugins list of a catalog entry.
|
||||||
|
*
|
||||||
|
* @param string $slug Plugin slug.
|
||||||
|
*
|
||||||
|
* @return list<string> Dependency slugs (unresolved).
|
||||||
|
*/
|
||||||
|
private function raw_dependencies_of( string $slug ): array {
|
||||||
|
$entry = $this->find_catalog_entry( $slug );
|
||||||
|
|
||||||
|
if ( null === $entry ) {
|
||||||
|
return array(); // wp.org plugin: dependencies come from its own headers on install.
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw = $entry['requires_plugins'] ?? '';
|
||||||
|
$raw = is_string( $raw ) ? $raw : '';
|
||||||
|
|
||||||
|
if ( '' === $raw ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$slugs = array();
|
||||||
|
foreach ( explode( ',', $raw ) as $part ) {
|
||||||
|
$dep = sanitize_key( trim( $part ) );
|
||||||
|
if ( '' !== $dep ) {
|
||||||
|
$slugs[ $dep ] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys( $slugs );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a WordPress.org plugin by slug via plugins_api + Plugin_Upgrader.
|
||||||
|
*
|
||||||
|
* @param string $slug wp.org plugin slug.
|
||||||
|
*
|
||||||
|
* @return string|WP_Error Plugin name on success.
|
||||||
|
*/
|
||||||
|
private function install_wporg_zip( string $slug ) {
|
||||||
|
$this->ensure_plugin_functions();
|
||||||
|
|
||||||
|
if ( ! function_exists( 'plugins_api' ) ) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = plugins_api(
|
||||||
|
'plugin_information',
|
||||||
|
array(
|
||||||
|
'slug' => $slug,
|
||||||
|
'fields' => array(
|
||||||
|
'sections' => false,
|
||||||
|
'versions' => false,
|
||||||
|
'downloaded' => false,
|
||||||
|
'rating' => false,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( is_wp_error( $api ) ) {
|
||||||
|
/* translators: %s: error message from WordPress.org. */
|
||||||
|
return new WP_Error( 'robotstxt_manager_wporg', sprintf( __( 'WordPress.org lookup failed: %s', 'robotstxt-manager' ), $api->get_error_message() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$download_link = is_object( $api ) && isset( $api->download_link ) && is_string( $api->download_link )
|
||||||
|
? $api->download_link
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if ( '' === $download_link ) {
|
||||||
|
/* translators: %s: plugin slug. */
|
||||||
|
return new WP_Error( 'robotstxt_manager_wporg', sprintf( __( 'No download found on WordPress.org for %s.', 'robotstxt-manager' ), $slug ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp_file = wp_tempnam( $slug . '.zip' );
|
||||||
|
|
||||||
|
if ( ! $tmp_file ) {
|
||||||
|
return new WP_Error( 'robotstxt_manager_temp', __( 'Could not create a temporary file for download.', 'robotstxt-manager' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = wp_remote_get(
|
||||||
|
$download_link,
|
||||||
|
array(
|
||||||
|
'timeout' => 300,
|
||||||
|
'stream' => true,
|
||||||
|
'filename' => $tmp_file,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( is_wp_error( $response ) ) {
|
||||||
|
wp_delete_file( $tmp_file );
|
||||||
|
|
||||||
|
/* translators: %s: HTTP transport error message. */
|
||||||
|
return new WP_Error( 'robotstxt_manager_download', sprintf( __( 'Download failed: %s', 'robotstxt-manager' ), $response->get_error_message() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
|
||||||
|
wp_delete_file( $tmp_file );
|
||||||
|
|
||||||
|
/* translators: %d: HTTP status code. */
|
||||||
|
return new WP_Error( 'robotstxt_manager_http', sprintf( __( 'Download failed (HTTP %d).', 'robotstxt-manager' ), (int) wp_remote_retrieve_response_code( $response ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $this->is_valid_zip( $tmp_file ) ) {
|
||||||
|
wp_delete_file( $tmp_file );
|
||||||
|
|
||||||
|
return new WP_Error( 'robotstxt_manager_zip', __( 'The store returned an invalid file.', 'robotstxt-manager' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
|
||||||
|
$result = $upgrader->install(
|
||||||
|
$tmp_file,
|
||||||
|
array(
|
||||||
|
'overwrite' => false,
|
||||||
|
'overwrite_package' => false,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_delete_file( $tmp_file );
|
||||||
|
|
||||||
|
if ( true !== $result ) {
|
||||||
|
$detail = ( $result instanceof WP_Error ) ? $result->get_error_message() : '';
|
||||||
|
|
||||||
|
/* translators: %s: upgrader error message. */
|
||||||
|
return new WP_Error( 'robotstxt_manager_install', '' !== $detail ? sprintf( __( 'Installation failed: %s', 'robotstxt-manager' ), $detail ) : __( 'Installation failed.', 'robotstxt-manager' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->find_plugin_file( $slug );
|
||||||
|
|
||||||
|
if ( '' === $file ) {
|
||||||
|
/* translators: %s: plugin slug. */
|
||||||
|
return new WP_Error( 'robotstxt_manager_wporg', sprintf( __( 'The downloaded plugin for %s does not have the expected folder structure.', 'robotstxt-manager' ), $slug ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = get_plugins();
|
||||||
|
$raw_name = isset( $data[ $file ]['Name'] ) && is_string( $data[ $file ]['Name'] ) ? $data[ $file ]['Name'] : '';
|
||||||
|
|
||||||
|
return '' !== $raw_name ? $raw_name : $slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,18 @@ $compat_warnings = 0;
|
||||||
$website_url = '' !== $page_url ? $page_url : $homepage;
|
$website_url = '' !== $page_url ? $page_url : $homepage;
|
||||||
$has_detail = ( '' !== $website_url || '' !== $desc );
|
$has_detail = ( '' !== $website_url || '' !== $desc );
|
||||||
|
|
||||||
|
$raw_reqs = $entry['requires_plugins'] ?? '';
|
||||||
|
$req_slugs = array();
|
||||||
|
if ( is_string( $raw_reqs ) && '' !== trim( $raw_reqs ) ) {
|
||||||
|
foreach ( explode( ',', $raw_reqs ) as $part ) {
|
||||||
|
$dep = sanitize_key( trim( $part ) );
|
||||||
|
if ( '' !== $dep ) {
|
||||||
|
$req_slugs[] = $dep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$has_detail = $has_detail || array() !== $req_slugs;
|
||||||
|
|
||||||
// Compatibility checks.
|
// Compatibility checks.
|
||||||
$wp_ok = '' === $req_wp || version_compare( $local_wp_version, $req_wp, '>=' );
|
$wp_ok = '' === $req_wp || version_compare( $local_wp_version, $req_wp, '>=' );
|
||||||
$php_ok = '' === $req_php || version_compare( $local_php_version, $req_php, '>=' );
|
$php_ok = '' === $req_php || version_compare( $local_php_version, $req_php, '>=' );
|
||||||
|
|
@ -251,6 +263,19 @@ $compat_warnings = 0;
|
||||||
<?php if ( '' !== $desc ) : ?>
|
<?php if ( '' !== $desc ) : ?>
|
||||||
<span class="robotstxt-manager-detail-description"><?php echo esc_html( $desc ); ?></span>
|
<span class="robotstxt-manager-detail-description"><?php echo esc_html( $desc ); ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php if ( array() !== $req_slugs ) : ?>
|
||||||
|
<span class="robotstxt-manager-detail-requires">
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: list of plugin slugs. */
|
||||||
|
__( 'Requires: %s', 'robotstxt-manager' ),
|
||||||
|
implode( ', ', $req_slugs )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,22 @@
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.1.0 =
|
||||||
|
|
||||||
|
_Release date: 2026-08-15_
|
||||||
|
|
||||||
|
**Added**
|
||||||
|
|
||||||
|
* Cascade dependency install: when installing a plugin whose catalog entry declares `requires_plugins` (Core 1.7.0+), Manager first installs every missing dependency — ecosystem plugins through the store flow, WordPress.org plugins (e.g. Action Scheduler) via `plugins_api` and their repository ZIPs — dependencies of dependencies first, cycle-safe. The success notice lists what was installed; failures name the dependency and abort before the main install. Catalog rows show a "Requires: …" hint in the detail line.
|
||||||
|
|
||||||
|
**Changed**
|
||||||
|
|
||||||
|
* Plugin version 1.0.0 → 1.1.0. No database schema changes (no custom tables).
|
||||||
|
|
||||||
|
**Compatibility**
|
||||||
|
|
||||||
|
* WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4)
|
||||||
|
* PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit)
|
||||||
|
|
||||||
= 1.0.0 =
|
= 1.0.0 =
|
||||||
|
|
||||||
_Release date: 2026-08-15_
|
_Release date: 2026-08-15_
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -249,3 +249,21 @@ msgstr "Pagaments, i com funciona"
|
||||||
|
|
||||||
msgid "Free plugins install instantly at no cost. Premium plugins are annual subscriptions: you pay once on our website and the subscription renews automatically every year until you cancel. You can cancel at any time from your ROBOTSTXT account page — access keeps working until the end of the paid period. All payments are processed securely by Mollie; we never see or store your card details."
|
msgid "Free plugins install instantly at no cost. Premium plugins are annual subscriptions: you pay once on our website and the subscription renews automatically every year until you cancel. You can cancel at any time from your ROBOTSTXT account page — access keeps working until the end of the paid period. All payments are processed securely by Mollie; we never see or store your card details."
|
||||||
msgstr "Els plugins gratuïts s'instal·len a l'instant i sense cost. Els plugins premium són subscripcions anuals: pagues una vegada al nostre lloc web i la subscripció es renova automàticament cada any fins que la cancellis. Pots cancel·lar-la en qualsevol moment des de la teva pàgina de compte de ROBOTSTXT; l'accés segueix funcionant fins al final del període pagat. Tots els pagaments els processa de manera segura Mollie; nosaltres mai no veiem ni desem les dades de la teva targeta."
|
msgstr "Els plugins gratuïts s'instal·len a l'instant i sense cost. Els plugins premium són subscripcions anuals: pagues una vegada al nostre lloc web i la subscripció es renova automàticament cada any fins que la cancellis. Pots cancel·lar-la en qualsevol moment des de la teva pàgina de compte de ROBOTSTXT; l'accés segueix funcionant fins al final del període pagat. Tots els pagaments els processa de manera segura Mollie; nosaltres mai no veiem ni desem les dades de la teva targeta."
|
||||||
|
|
||||||
|
msgid "Installed required plugins: %s."
|
||||||
|
msgstr "Plugins requerits instal·lats: %s."
|
||||||
|
|
||||||
|
msgid "Could not install the required plugin %s."
|
||||||
|
msgstr "No s'ha pogut instal·lar el plugin requerit %s."
|
||||||
|
|
||||||
|
msgid "WordPress.org lookup failed: %s"
|
||||||
|
msgstr "La cerca a WordPress.org ha fallat: %s"
|
||||||
|
|
||||||
|
msgid "No download found on WordPress.org for %s."
|
||||||
|
msgstr "No s'ha trobat cap baixada a WordPress.org per a %s."
|
||||||
|
|
||||||
|
msgid "The downloaded plugin for %s does not have the expected folder structure."
|
||||||
|
msgstr "El plugin baixat per a %s no té lestructura de carpetes esperada."
|
||||||
|
|
||||||
|
msgid "Requires: %s"
|
||||||
|
msgstr "Requereix: %s"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -249,3 +249,21 @@ msgstr "Pagos, y cómo funciona"
|
||||||
|
|
||||||
msgid "Free plugins install instantly at no cost. Premium plugins are annual subscriptions: you pay once on our website and the subscription renews automatically every year until you cancel. You can cancel at any time from your ROBOTSTXT account page — access keeps working until the end of the paid period. All payments are processed securely by Mollie; we never see or store your card details."
|
msgid "Free plugins install instantly at no cost. Premium plugins are annual subscriptions: you pay once on our website and the subscription renews automatically every year until you cancel. You can cancel at any time from your ROBOTSTXT account page — access keeps working until the end of the paid period. All payments are processed securely by Mollie; we never see or store your card details."
|
||||||
msgstr "Los plugins gratis se instalan al instante y sin coste. Los plugins premium son suscripciones anuales: pagas una vez en nuestro sitio web y la suscripción se renueva automáticamente cada año hasta que la canceles. Puedes cancelarla en cualquier momento desde tu página de cuenta de ROBOTSTXT; el acceso sigue funcionando hasta el final del periodo pagado. Todos los pagos los procesa de forma segura Mollie; nosotros nunca vemos ni guardamos los datos de tu tarjeta."
|
msgstr "Los plugins gratis se instalan al instante y sin coste. Los plugins premium son suscripciones anuales: pagas una vez en nuestro sitio web y la suscripción se renueva automáticamente cada año hasta que la canceles. Puedes cancelarla en cualquier momento desde tu página de cuenta de ROBOTSTXT; el acceso sigue funcionando hasta el final del periodo pagado. Todos los pagos los procesa de forma segura Mollie; nosotros nunca vemos ni guardamos los datos de tu tarjeta."
|
||||||
|
|
||||||
|
msgid "Installed required plugins: %s."
|
||||||
|
msgstr "Plugins requeridos instalados: %s."
|
||||||
|
|
||||||
|
msgid "Could not install the required plugin %s."
|
||||||
|
msgstr "No se pudo instalar el plugin requerido %s."
|
||||||
|
|
||||||
|
msgid "WordPress.org lookup failed: %s"
|
||||||
|
msgstr "La búsqueda en WordPress.org falló: %s"
|
||||||
|
|
||||||
|
msgid "No download found on WordPress.org for %s."
|
||||||
|
msgstr "No se encontró ninguna descarga en WordPress.org para %s."
|
||||||
|
|
||||||
|
msgid "The downloaded plugin for %s does not have the expected folder structure."
|
||||||
|
msgstr "El plugin descargado para %s no tiene la estructura de carpetas esperada."
|
||||||
|
|
||||||
|
msgid "Requires: %s"
|
||||||
|
msgstr "Requiere: %s"
|
||||||
|
|
|
||||||
|
|
@ -248,3 +248,21 @@ msgstr ""
|
||||||
|
|
||||||
msgid "Free plugins install instantly at no cost. Premium plugins are annual subscriptions: you pay once on our website and the subscription renews automatically every year until you cancel. You can cancel at any time from your ROBOTSTXT account page — access keeps working until the end of the paid period. All payments are processed securely by Mollie; we never see or store your card details."
|
msgid "Free plugins install instantly at no cost. Premium plugins are annual subscriptions: you pay once on our website and the subscription renews automatically every year until you cancel. You can cancel at any time from your ROBOTSTXT account page — access keeps working until the end of the paid period. All payments are processed securely by Mollie; we never see or store your card details."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Installed required plugins: %s."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Could not install the required plugin %s."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "WordPress.org lookup failed: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No download found on WordPress.org for %s."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The downloaded plugin for %s does not have the expected folder structure."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Requires: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
|
||||||
10
readme.txt
10
readme.txt
|
|
@ -3,9 +3,9 @@ Contributors: javiercasares, robotstxt
|
||||||
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.0.0
|
Stable tag: 1.1.0
|
||||||
Requires PHP: 8.0
|
Requires PHP: 8.0
|
||||||
Version: 1.0.0
|
Version: 1.1.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
|
||||||
|
|
||||||
|
|
@ -92,6 +92,12 @@ Encrypted at rest using AES-256-CBC with a key derived from your site's WordPres
|
||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.1.0 =
|
||||||
|
|
||||||
|
_Release date: 2026-08-15_
|
||||||
|
|
||||||
|
* Cascade dependency install: required plugins (ecosystem or WordPress.org, e.g. Action Scheduler) are installed automatically before the plugin; "Requires:" hints in the catalog.
|
||||||
|
|
||||||
= 1.0.0 =
|
= 1.0.0 =
|
||||||
|
|
||||||
_Release date: 2026-08-15_
|
_Release date: 2026-08-15_
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Plugin Name: Manager (by ROBOTSTXT)
|
* Plugin Name: Manager (by ROBOTSTXT)
|
||||||
* Plugin URI: https://git.robotstxt.es/ROBOTSTXT/robotstxt-manager
|
* Plugin URI: https://git.robotstxt.es/ROBOTSTXT/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.0.0
|
* Version: 1.1.0
|
||||||
* Requires at least: 4.4
|
* Requires at least: 4.4
|
||||||
* Requires PHP: 8.0
|
* Requires PHP: 8.0
|
||||||
* Author: ROBOTSTXT
|
* Author: ROBOTSTXT
|
||||||
|
|
@ -21,7 +21,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Plugin version. */
|
/** Plugin version. */
|
||||||
define( 'ROBOTSTXT_MANAGER_VERSION', '1.0.0' );
|
define( 'ROBOTSTXT_MANAGER_VERSION', '1.1.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