robotstxt-manager/admin/views/page-catalog.php
2026-08-17 16:03:10 +00:00

333 lines
14 KiB
PHP

<?php
/**
* Admin page view: ROBOTSTXT Plugins catalog.
*
* Catalog table (Plugin · Version · Requires WP · Requires PHP · Price ·
* Action · Status) with an always-open detail row under each plugin
* (website link + description), an intro paragraph, and Support /
* Payments sections below the table.
*
* @package Robotstxt_Manager
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Variables provided by Robotstxt_Manager_Admin::render_page().
*
* @var list<array<string,mixed>> $catalog
* @var array<string, array{installed:bool, active:bool, version:string}> $local
*/
$client_configured = Robotstxt_Manager_Core_Client::from_options()->is_configured();
$refresh_url = add_query_arg(
array(
'action' => 'robotstxt_manager_refresh_catalog',
),
admin_url( 'admin-post.php' )
);
$refresh_url = wp_nonce_url( $refresh_url, 'robotstxt_manager_refresh_catalog' );
$refreshed = isset( $_GET['refreshed'] ) && '1' === $_GET['refreshed']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
// Action notice from an install/activate/update redirect.
$notice_result = '';
$notice_message = '';
if ( isset( $_GET['robotstxt_manager_result'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only, set by this plugin's own redirects.
$raw_result = wp_unslash( $_GET['robotstxt_manager_result'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized on the next line.
if ( is_string( $raw_result ) ) {
$notice_result = sanitize_key( $raw_result );
}
}
if ( isset( $_GET['robotstxt_manager_message'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only, set by this plugin's own redirects.
$raw_message = wp_unslash( $_GET['robotstxt_manager_message'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized on the next line after decoding.
if ( is_string( $raw_message ) ) {
$notice_message = sanitize_text_field( rawurldecode( $raw_message ) );
}
}
$local_wp_version = (string) get_bloginfo( 'version' );
$local_php_version = (string) PHP_VERSION;
$compat_warnings = 0;
?>
<div class="wrap">
<h1><?php esc_html_e( 'Manager (by ROBOTSTXT) — Plugins', 'robotstxt-manager' ); ?></h1>
<p class="description">
<?php esc_html_e( 'This is the ROBOTSTXT plugin store: the catalog of plugins published by ROBOTSTXT, installable and updatable straight from your own wp-admin. Free plugins install with one click; premium plugins require an annual subscription that you purchase on our website.', 'robotstxt-manager' ); ?>
</p>
<?php if ( $refreshed ) : ?>
<div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Catalog refreshed.', 'robotstxt-manager' ); ?></p></div>
<?php endif; ?>
<?php if ( '' !== $notice_message && in_array( $notice_result, array( 'success', 'error' ), true ) ) : ?>
<div class="notice notice-<?php echo esc_attr( $notice_result ); ?> is-dismissible"><p><?php echo esc_html( $notice_message ); ?></p></div>
<?php endif; ?>
<?php if ( ! $client_configured ) : ?>
<div class="notice notice-error">
<p>
<?php
echo wp_kses_post(
sprintf(
/* translators: %s: settings URL. */
__( 'ROBOTSTXT Manager is not configured yet. <a href="%s">Set the Store URL and API key</a> to see your plugin catalog.', 'robotstxt-manager' ),
esc_url( admin_url( 'admin.php?page=' . Robotstxt_Manager_Settings::PAGE_SLUG ) )
)
);
?>
</p>
</div>
<?php elseif ( empty( $catalog ) ) : ?>
<div class="notice notice-warning">
<p>
<?php esc_html_e( 'No plugins were returned by the ROBOTSTXT store. Check the Store URL and API key on the Settings page, or click Refresh to try again.', 'robotstxt-manager' ); ?>
</p>
</div>
<p><a class="button" href="<?php echo esc_url( $refresh_url ); ?>"><?php esc_html_e( 'Refresh catalog', 'robotstxt-manager' ); ?></a></p>
<?php else : ?>
<p>
<a class="button" href="<?php echo esc_url( $refresh_url ); ?>"><?php esc_html_e( 'Refresh catalog', 'robotstxt-manager' ); ?></a>
</p>
<table class="wp-list-table widefat striped robotstxt-manager-catalog">
<thead>
<tr>
<th scope="col"><?php esc_html_e( 'Plugin', 'robotstxt-manager' ); ?></th>
<th scope="col"><?php esc_html_e( 'Version', 'robotstxt-manager' ); ?></th>
<th scope="col"><?php esc_html_e( 'Requires WP', 'robotstxt-manager' ); ?></th>
<th scope="col"><?php esc_html_e( 'Requires PHP', 'robotstxt-manager' ); ?></th>
<th scope="col"><?php esc_html_e( 'Price', 'robotstxt-manager' ); ?></th>
<th scope="col"><?php esc_html_e( 'Action', 'robotstxt-manager' ); ?></th>
<th scope="col"><?php esc_html_e( 'Status', 'robotstxt-manager' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $catalog as $entry ) : ?>
<?php
$raw_slug = $entry['slug'] ?? '';
$raw_name = $entry['name'] ?? '';
$raw_kind = $entry['type'] ?? 'free';
$raw_cost = $entry['price_annual'] ?? 0;
$raw_ver = $entry['current_version'] ?? '';
$raw_url = $entry['page_url'] ?? '';
$raw_home = $entry['homepage'] ?? '';
$raw_desc = $entry['description'] ?? '';
$raw_rwp = $entry['requires_wp'] ?? '';
$raw_rphp = $entry['requires_php'] ?? '';
$slug = is_string( $raw_slug ) ? $raw_slug : '';
$name = is_string( $raw_name ) ? $raw_name : $slug;
$kind = is_string( $raw_kind ) ? $raw_kind : 'free';
$cost = is_numeric( $raw_cost ) ? (float) ( $raw_cost + 0.0 ) : 0.0;
$remote_v = is_string( $raw_ver ) ? $raw_ver : '';
$page_url = is_string( $raw_url ) ? $raw_url : '';
$homepage = is_string( $raw_home ) ? $raw_home : '';
$desc = is_string( $raw_desc ) ? trim( $raw_desc ) : '';
$req_wp = is_string( $raw_rwp ) ? $raw_rwp : '';
$req_php = is_string( $raw_rphp ) ? $raw_rphp : '';
$website_url = '' !== $page_url ? $page_url : $homepage;
$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.
$wp_ok = '' === $req_wp || version_compare( $local_wp_version, $req_wp, '>=' );
$php_ok = '' === $req_php || version_compare( $local_php_version, $req_php, '>=' );
if ( ! $wp_ok || ! $php_ok ) {
++$compat_warnings;
}
$raw_state = $local[ $slug ] ?? array(
'installed' => false,
'active' => false,
'version' => '',
);
$l_installed = (bool) ( $raw_state['installed'] ?? false );
$l_active = (bool) ( $raw_state['active'] ?? false );
$raw_lv = $raw_state['version'] ?? '';
$l_version = is_string( $raw_lv ) ? $raw_lv : '';
$update_available = $l_installed && '' !== $remote_v && '' !== $l_version
&& version_compare( $l_version, $remote_v, '<' );
$row_class = ( ! $wp_ok || ! $php_ok ) ? ' robotstxt-manager-row--incompatible' : '';
?>
<tr class="<?php echo esc_attr( ltrim( $row_class ) ); ?>">
<td class="robotstxt-manager-name-cell">
<strong><?php echo esc_html( $name ); ?></strong>
</td>
<td><?php echo '' !== $remote_v ? esc_html( $remote_v ) : '&mdash;'; ?></td>
<td>
<?php if ( '' !== $req_wp ) : ?>
<span class="<?php echo $wp_ok ? 'robotstxt-manager-compat--ok' : 'robotstxt-manager-compat--fail'; ?>">
<?php echo esc_html( $req_wp ); ?>
<?php if ( ! $wp_ok ) : ?>
<span class="robotstxt-manager-compat-warning" title="<?php esc_attr_e( 'Your site runs WordPress', 'robotstxt-manager' ); ?> <?php echo esc_attr( $local_wp_version ); ?>"> &#9888;</span>
<?php endif; ?>
</span>
<?php else : ?>
&mdash;
<?php endif; ?>
</td>
<td>
<?php if ( '' !== $req_php ) : ?>
<span class="<?php echo $php_ok ? 'robotstxt-manager-compat--ok' : 'robotstxt-manager-compat--fail'; ?>">
<?php echo esc_html( $req_php ); ?>
<?php if ( ! $php_ok ) : ?>
<span class="robotstxt-manager-compat-warning" title="<?php esc_attr_e( 'Your server runs PHP', 'robotstxt-manager' ); ?> <?php echo esc_attr( $local_php_version ); ?>"> &#9888;</span>
<?php endif; ?>
</span>
<?php else : ?>
&mdash;
<?php endif; ?>
</td>
<td>
<?php
if ( $cost > 0.0 || 'premium' === $kind ) {
echo esc_html(
sprintf(
/* translators: %s: formatted price number. */
__( '€%s / year', 'robotstxt-manager' ),
number_format_i18n( $cost, 0 )
)
);
} else {
echo esc_html__( 'Free', 'robotstxt-manager' );
}
?>
</td>
<td>
<?php if ( ! $l_installed ) : ?>
<?php if ( ! $wp_ok || ! $php_ok ) : ?>
<span class="description"><?php esc_html_e( 'Incompatible', 'robotstxt-manager' ); ?></span>
<?php else : ?>
<?php if ( $cost > 0.0 && '' !== $page_url ) : ?>
<a class="button button-secondary" href="<?php echo esc_url( $page_url ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Buy', 'robotstxt-manager' ); ?></a>
<?php endif; ?>
<?php
$install_class = ( $cost > 0.0 && '' !== $page_url ) ? 'button-secondary' : 'button-primary';
?>
<a class="button <?php echo esc_attr( $install_class ); ?>" href="<?php echo esc_url( Robotstxt_Manager_Admin::action_url( 'install', $slug ) ); ?>"><?php esc_html_e( 'Install', 'robotstxt-manager' ); ?></a>
<?php endif; ?>
<?php elseif ( ! $l_active ) : ?>
<a class="button button-primary" href="<?php echo esc_url( Robotstxt_Manager_Admin::action_url( 'activate', $slug ) ); ?>"><?php esc_html_e( 'Activate', 'robotstxt-manager' ); ?></a>
<?php elseif ( $update_available ) : ?>
<a class="button button-secondary" href="<?php echo esc_url( Robotstxt_Manager_Admin::action_url( 'update', $slug ) ); ?>"><?php esc_html_e( 'Update', 'robotstxt-manager' ); ?></a>
<?php endif; ?>
</td>
<td>
<?php
if ( ! $l_installed ) {
echo '<span class="robotstxt-manager-state robotstxt-manager-state--not-installed">' . esc_html__( 'Not installed', 'robotstxt-manager' ) . '</span>';
} elseif ( ! $l_active ) {
echo '<span class="robotstxt-manager-state robotstxt-manager-state--inactive">' . esc_html__( 'Installed (inactive)', 'robotstxt-manager' ) . '</span>';
} elseif ( $update_available ) {
echo '<span class="robotstxt-manager-state robotstxt-manager-state--update">' . esc_html(
sprintf(
/* translators: 1: installed version, 2: available version. */
__( 'Update available (v%1$s → v%2$s)', 'robotstxt-manager' ),
$l_version,
$remote_v
)
) . '</span>';
} else {
echo '<span class="robotstxt-manager-state robotstxt-manager-state--ok">' . esc_html__( 'Up to date', 'robotstxt-manager' ) . '</span>';
}
?>
</td>
</tr>
<?php if ( $has_detail ) : ?>
<tr class="robotstxt-manager-detail-row">
<td colspan="7">
<?php if ( '' !== $website_url ) : ?>
<a href="<?php echo esc_url( $website_url ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Visit website', 'robotstxt-manager' ); ?> <span class="screen-reader-text"><?php echo esc_html( sprintf( /* translators: %s: plugin name. */ __( '(about %s)', 'robotstxt-manager' ), $name ) ); ?></span></a>
<?php endif; ?>
<?php if ( '' !== $desc ) : ?>
<span class="robotstxt-manager-detail-description"><?php echo esc_html( $desc ); ?></span>
<?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>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
<h2><?php esc_html_e( 'Support', 'robotstxt-manager' ); ?></h2>
<p class="description">
<?php esc_html_e( 'Need help with a ROBOTSTXT plugin? Visit the plugin\'s website (the link in its row above) for documentation and guides, or contact us through our website — we are happy to help.', 'robotstxt-manager' ); ?>
</p>
<h2><?php esc_html_e( 'Payments, and how it works', 'robotstxt-manager' ); ?></h2>
<p class="description">
<?php esc_html_e( '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.', 'robotstxt-manager' ); ?>
</p>
<?php if ( $compat_warnings > 0 ) : ?>
<div class="notice notice-warning inline" style="margin-top:1em">
<p>
<?php
echo esc_html(
sprintf(
/* translators: %d: number of incompatible plugins. */
_n( '%d plugin in the catalog is not compatible with this site\'s WordPress or PHP version.', '%d plugins in the catalog are not compatible with this site\'s WordPress or PHP version.', $compat_warnings, 'robotstxt-manager' ),
$compat_warnings
)
);
?>
<?php
echo esc_html(
sprintf(
/* translators: 1: local WP version, 2: local PHP version. */
__( 'This site runs WordPress %1$s on PHP %2$s.', 'robotstxt-manager' ),
$local_wp_version,
$local_php_version
)
);
?>
</p>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<style>
.robotstxt-manager-compat--ok { color: #00a32a; }
.robotstxt-manager-compat--fail { color: #d63638; font-weight: 600; }
.robotstxt-manager-compat-warning { cursor: help; }
.robotstxt-manager-row--incompatible { background-color: #fef7f0 !important; }
/* Detail rows (always open): muted, attached to the row above. */
.robotstxt-manager-detail-row td { border-top: none !important; padding-top: 0; }
.robotstxt-manager-detail-description { color: #50575e; }
</style>