page_hook = add_management_page(
__('Reprint Server', 'reprint'),
__('Reprint Server', 'reprint'),
'manage_options',
'reprint-server',
[$this, 'render_admin_page']
);
}
/** Network credentials are configured only by network administrators. */
public function add_network_admin_menu(): void {
$this->page_hook = add_submenu_page(
'settings.php',
__('Reprint Server', 'reprint'),
__('Reprint Server', 'reprint'),
'manage_network_options',
'reprint-server',
[$this, 'render_network_admin_page']
);
}
/** Render a network-option form without the site Settings API. */
public function render_network_admin_page(): void {
if (!is_multisite() || !current_user_can('manage_network_options')) {
return;
}
$this->render_page();
}
/** Validate network capability and nonce before updating the network token. */
public function handle_network_token_save(): void {
if (!is_multisite() || !current_user_can('manage_network_options')) {
wp_die(esc_html__('You are not allowed to manage this network.', 'reprint'));
}
check_admin_referer('reprint_server_save_network_token');
$connection_token = isset($_POST[CONNECTION_TOKEN_OPTION]) && is_string($_POST[CONNECTION_TOKEN_OPTION])
? sanitize_text_field(wp_unslash($_POST[CONNECTION_TOKEN_OPTION]))
: '';
$result = change_connection_token($connection_token);
if ($result === 'storage_failure') {
wp_die(esc_html__('The network connection token could not be saved.', 'reprint'));
}
$this->redirect_to_page(['reprint_server_notice' => 'network_token_' . $result]);
}
/** Add the Settings link to the plugin row. */
public function add_settings_link(array $links): array {
$url = is_multisite()
? network_admin_url('settings.php?page=reprint-server')
: admin_url('tools.php?page=reprint-server');
array_unshift(
$links,
'' . esc_html__('Settings', 'reprint') . ''
);
return $links;
}
/** Enqueue browser-only behavior on the bundled page. */
public function enqueue_assets(string $hook_suffix): void {
if ($this->page_hook === false || $hook_suffix !== $this->page_hook) {
return;
}
wp_enqueue_script(
'reprint-server-admin',
plugins_url('wordpress/reprint-server.js', PLUGIN_FILE),
['wp-a11y'],
VERSION,
true
);
wp_enqueue_style(
'reprint-server-admin',
plugins_url('wordpress/reprint-server.css', PLUGIN_FILE),
[],
VERSION
);
}
/** Apply one push-access change and redirect back to the bundled page. */
public function handle_push_access_save(): void {
if (!current_user_can('manage_options')) {
wp_die(esc_html__('You are not allowed to manage Reprint Server.', 'reprint'));
}
check_admin_referer('reprint_server_save_push_access');
$enabled = isset($_POST['reprint_server_push_enabled']);
$result = change_push_access($enabled);
$redirect_url = add_query_arg(
'reprint_server_notice',
$result,
admin_url('tools.php?page=reprint-server')
);
wp_safe_redirect($redirect_url);
exit;
}
/** Validate capability and nonce, then enroll one pasted public key. */
public function handle_public_key_enroll(): void {
$this->require_manage_capability();
check_admin_referer('reprint_server_enroll_public_key');
// assert_valid_public_key() is the real check. The sanitizer only cleans the pasted text.
$pasted_key = isset($_POST['reprint_server_public_key']) && is_string($_POST['reprint_server_public_key'])
? sanitize_textarea_field(wp_unslash($_POST['reprint_server_public_key']))
: '';
$result = enroll_public_key($pasted_key);
$query = ['reprint_server_notice' => $result === 'saved' ? 'enrolled' : 'enroll_' . $result];
if ($result === 'saved') {
$query['reprint_server_key_id'] = (string) get_last_enrolled_key_id();
}
$this->redirect_to_page($query);
}
/** Validate capability and nonce, then remove one enrolled key. */
public function handle_public_key_remove(): void {
$this->require_manage_capability();
check_admin_referer('reprint_server_remove_public_key');
$key_id = isset($_POST['reprint_server_key_id']) && is_string($_POST['reprint_server_key_id'])
? sanitize_key(wp_unslash($_POST['reprint_server_key_id']))
: '';
$result = remove_public_key($key_id);
$this->redirect_to_page(['reprint_server_notice' => $result === 'saved' ? 'key_removed' : 'remove_' . $result]);
}
/** Validate capability and nonce, then grant or revoke push for one key. */
public function handle_key_push_access_save(): void {
$this->require_manage_capability();
check_admin_referer('reprint_server_save_key_push_access');
$key_id = isset($_POST['reprint_server_key_id']) && is_string($_POST['reprint_server_key_id'])
? sanitize_key(wp_unslash($_POST['reprint_server_key_id']))
: '';
$enabled = isset($_POST['reprint_server_key_push_enabled']);
$result = change_key_push_access($key_id, $enabled);
$this->redirect_to_page(['reprint_server_notice' => $result === 'saved' ? 'key_push_saved' : 'key_push_' . $result]);
}
/**
* Validate capability and nonce, then clear the option-backed connection token.
*
* Offered on a key host, where a stored token is never accepted; the plugin
* does not delete a credential the administrator set without being asked.
*/
public function handle_connection_token_remove(): void {
$this->require_manage_capability();
check_admin_referer('reprint_server_remove_connection_token');
$result = change_connection_token('');
$this->redirect_to_page([
'reprint_server_notice' => $result === 'storage_failure' ? 'token_remove_storage_failure' : 'token_removed',
]);
}
/** Stop with wp_die() unless the current user may manage this site's, or on multisite the network's, options. */
private function require_manage_capability(): void {
$capability = is_multisite() ? 'manage_network_options' : 'manage_options';
if (!current_user_can($capability)) {
wp_die(esc_html__('You are not allowed to manage Reprint Server.', 'reprint'));
}
}
/** @param array
render_public_keys_section($configuration); ?>
render_stored_connection_token_section($configuration); ?>
render_connection_token_form(); ?>
public-keys.php '
. esc_html__('override is active.', 'reprint') . ' '
. esc_html__('Edit that file to authorize a tool or remove a Migration Token. This page cannot change its Migration Tokens.', 'reprint') . '
|
0 ? gmdate('Y-m-d', $entry['added_at']) : '—'); ?> |
['error', __('That is not a usable Migration Token. Copy the Migration Token from your tool and paste it here.', 'reprint')], 'enroll_no_openssl' => ['error', __('This host cannot read Migration Tokens because the OpenSSL extension is missing. Clients authenticate with the connection token here.', 'reprint')], 'enroll_duplicate' => ['info', __('That Migration Token is already enrolled.', 'reprint')], 'enroll_file_override' => ['error', __('public-keys.php is active. Edit that file to change enrolled Migration Tokens.', 'reprint')], 'enroll_storage_failure' => ['error', __('Failed to save the Migration Token.', 'reprint')], 'enroll_runtime_missing' => ['error', __('The Reprint Server runtime is missing. Run composer install in the plugin directory or reinstall the release package.', 'reprint')], 'key_removed' => ['success', __('Migration Token removed.', 'reprint')], 'remove_unknown' => ['error', __('That Migration Token is not enrolled.', 'reprint')], 'remove_file_override' => ['error', __('public-keys.php is active. Edit that file to change enrolled Migration Tokens.', 'reprint')], 'remove_storage_failure' => ['error', __('Failed to remove the Migration Token.', 'reprint')], 'remove_runtime_missing' => ['error', __('The Reprint Server runtime is missing. Run composer install in the plugin directory or reinstall the release package.', 'reprint')], 'network_token_saved' => ['success', __('Connection token saved.', 'reprint')], 'network_token_unchanged' => ['success', __('Connection token was already up to date.', 'reprint')], 'token_removed' => ['success', __('Connection token removed.', 'reprint')], 'token_remove_storage_failure' => ['error', __('Failed to remove the connection token.', 'reprint')], ]; // A saved push-result URL can be reloaded after changing distributions. if (!defined('REPRINT_PUSH_AVAILABLE') || REPRINT_PUSH_AVAILABLE) { $notices += [ 'saved' => ['success', __('Push access updated.', 'reprint')], 'unchanged' => ['success', __('Push access was already up to date.', 'reprint')], 'unsupported' => ['error', __('Push access requires PHP 7.2 or newer. Downloads remain available.', 'reprint')], 'managed' => ['info', __('Push access is managed by your hosting provider.', 'reprint')], 'not_configured' => ['error', __('Configure a connection token before enabling push access.', 'reprint')], 'storage_failure' => ['error', __('Failed to save push access.', 'reprint')], 'key_push_saved' => ['success', __('Push access for the Migration Token updated.', 'reprint')], 'key_push_unchanged' => ['success', __('Push access for the Migration Token was already up to date.', 'reprint')], 'key_push_unknown' => ['error', __('That Migration Token is not enrolled.', 'reprint')], 'key_push_multisite' => ['info', __('Push is not supported on multisite networks.', 'reprint')], 'key_push_unsupported' => ['error', __('Push access requires PHP 7.2 or newer.', 'reprint')], 'key_push_managed' => ['info', __('Push access is managed by your hosting provider.', 'reprint')], 'key_push_file_override' => ['error', __('public-keys.php is active. Push grants cannot be stored for file-provided Migration Tokens.', 'reprint')], 'key_push_storage_failure' => ['error', __('Failed to save push access for the Migration Token.', 'reprint')], 'key_push_runtime_missing' => ['error', __('The Reprint Server runtime is missing. Run composer install in the plugin directory or reinstall the release package.', 'reprint')], ]; } if (!isset($notices[$result])) { return; } $message = esc_html($notices[$result][1]); $this->render_notice( $notices[$result][0], $message, true ); } /** Render Settings API results with stable native notice markup. */ private function render_settings_notices(): void { foreach (get_settings_errors() as $notice) { $type = $notice['type'] === 'updated' ? 'success' : $notice['type']; $this->render_notice( $type, $notice['message'], true, 'setting-error-' . $notice['code'], true ); } } /** Render one native inline administrator notice. */ private function render_notice( string $type, string $message, bool $dismissible = false, string $id = '', bool $settings_error = false ): void { $allowed_types = ['error', 'success', 'warning', 'info']; if (!in_array($type, $allowed_types, true)) { $type = 'error'; } $classes = 'notice notice-' . $type; if ($settings_error) { $classes .= ' settings-error'; } if ($dismissible) { $classes .= ' is-dismissible'; } $classes .= ' inline'; ?>