First draft

This commit is contained in:
2025-12-02 10:06:37 +00:00
parent 6d76533d1f
commit 801c24885b
11 changed files with 498 additions and 165 deletions

View File

@@ -19,38 +19,77 @@
* There are other classes that may depend on this file, so only remove it if
* you do not intend to use the plugin.
*/
class HostingSupport1984 {
class HostingSupport1984
{
const HOSTING_SUPPORT_VERSION = '0.4.0';
/**
* Construct a new HostingSupport1984 object
*/
public function __construct() {
public function __construct()
{
// Loading the text domain for a mu-plugin is tricky.
// We need to use admin_init instead of plugins_loaded as we are only
// using the plugin in the admin interface.
add_action(
'admin_init',
array( $this, 'load_textdomain' )
array($this,
'load_textdomain')
);
// Register the activation hook.
register_activation_hook(
__FILE__,
array( $this, 'activation_hook' )
array($this,
'activation_hook')
);
// Run on activation of any plugin.
add_action(
'activated_plugin',
array( $this, 'plugin_activation_hook' )
array($this,
'plugin_activation_hook')
);
// Run on activation of theme.
add_action(
'switch_theme',
array( $this, 'theme_switch_hook' )
array($this,
'theme_switch_hook')
);
// Add a clear warning on the Patchstack plugin row to discourage removal.
add_filter(
'plugin_row_meta',
array($this,
'add_patchstack_warning_row_meta'),
10,
2
);
// Prevent deactivation/deletion of this plugin and Patchstack via UI links.
add_filter(
'plugin_action_links',
array($this,
'filter_plugin_action_links'),
10,
4
);
add_filter(
'network_admin_plugin_action_links',
array($this,
'filter_plugin_action_links'),
10,
4
);
// Guard against direct requests (single and bulk) on Plugins screens and updater endpoints.
add_action('load-plugins.php', array($this,
'protect_plugins_admin_actions'));
add_action('load-plugins-network.php', array($this,
'protect_plugins_admin_actions'));
add_action('load-update.php', array($this,
'protect_plugins_admin_actions'));
}
/**
@@ -58,7 +97,8 @@ class HostingSupport1984 {
*
* @return Boolean
*/
public function load_textdomain() {
public function load_textdomain()
{
return load_plugin_textdomain(
'hostingsupport1984',
false,
@@ -66,38 +106,40 @@ class HostingSupport1984 {
);
}
/**
* Check if Patchstack is enabled.
*
* @return Boolean
*/
public function is_patchstack_active() {
return is_plugin_active( 'patchstack/patchstack.php' );
}
/**
* On activation of this plugin, set auto-update option for core,
* plus enable auto-updates for installed plugins and themes.
*
* This is skipped if Patchstack is active on the site.
*/
public function activation_hook() {
if ( $this->is_patchstack_active() ) {
public function activation_hook()
{
if ($this->is_patchstack_active()) {
// In case Patchstack is active, do not continue.
return;
}
// Auto-update WordPress core.
update_site_option( 'auto_update_core_major', 'enabled' );
update_site_option('auto_update_core_major', 'enabled');
// Auto-update plugins.
$all_plugins = apply_filters( 'all_plugins', get_plugins() );
$all_plugins = apply_filters('all_plugins', get_plugins());
update_site_option( 'auto_update_plugins', array_keys( $all_plugins ) );
update_site_option('auto_update_plugins', array_keys($all_plugins));
// Auto-update themes.
$all_themes = wp_get_themes();
update_site_option( 'auto_update_themes', array_keys( $all_themes ) );
update_site_option('auto_update_themes', array_keys($all_themes));
}
/**
* Check if Patchstack is enabled.
*
* @return Boolean
*/
public function is_patchstack_active()
{
return is_plugin_active('patchstack/patchstack.php');
}
/**
@@ -105,16 +147,17 @@ class HostingSupport1984 {
*
* This is skipped if Patchstack is active on the site.
*/
public function plugin_activation_hook( $plugin ) {
if ( $this->is_patchstack_active() ) {
public function plugin_activation_hook($plugin)
{
if ($this->is_patchstack_active()) {
// In case Patchstack is active, do not continue.
return;
}
$auto_updated_plugins = (array) get_site_option( 'auto_update_plugins', array() );
$auto_updated_plugins = (array)get_site_option('auto_update_plugins', array());
$auto_updated_plugins[] = $plugin;
update_site_option( 'auto_update_plugins', array_unique( $auto_updated_plugins ) );
update_site_option('auto_update_plugins', array_unique($auto_updated_plugins));
}
/**
@@ -122,19 +165,156 @@ class HostingSupport1984 {
*
* This is skipped if Patchstack is active on the site.
*/
public function theme_switch_hook( $theme ) {
if ( $this->is_patchstack_active() ) {
public function theme_switch_hook($theme)
{
if ($this->is_patchstack_active()) {
// In case Patchstack is active, do not continue.
return;
}
$all_themes = wp_get_themes();
update_site_option( 'auto_update_themes', array_keys( $all_themes ) );
update_site_option('auto_update_themes', array_keys($all_themes));
}
/**
* Append a prominent warning to the Patchstack plugin row in the Plugins screen.
*
* @param array $links Existing row meta links.
* @param string $file Plugin file path about plugins directory.
*
* @return array Modified row meta-links.
*/
public function add_patchstack_warning_row_meta(array $links, string $file): array
{
// Only show the warning on the Patchstack row AND only if Patchstack is active.
if ('patchstack/patchstack.php' !== $file || !$this->is_patchstack_active()) {
return $links;
}
$warning_text = '1984: Please do not deactivate or delete this plugin - part of your WordPress Service Pack.';
$logo_1984_url = plugins_url('icons/1984-hosting-logo.webp', __FILE__);
$logo_patchstack = plugins_url('icons/patchstack-logo.svg', __FILE__);
$warning_icon =
'<span class="dashicons dashicons-warning" aria-hidden="true" style="color:#b32d2e;vertical-align:text-bottom;margin-right:6px;"></span>';
$links[] =
'<span class="description" style="color:#b32d2e;font-weight:700;display:inline-flex;align-items:center;line-height:1;">' . $warning_icon . esc_html($warning_text) . '</span>' .
'<span class="description" style="margin-left:6px;display:inline-flex;align-items:center;gap:4px;">'
.
'<a href="https://1984.hosting" target="_blank" rel="noopener noreferrer" title="1984 Hosting" aria-label="1984 Hosting (opens in a new tab)">'
.
'<img src="' . esc_url($logo_1984_url) . '" alt="1984 Hosting" style="height:16px;width:auto;display:inline-block;" />'
.
'</a>'
.
'<a href="https://patchstack.com" target="_blank" rel="noopener noreferrer" title="Patchstack" aria-label="Patchstack (opens in a new tab)">'
.
'<img src="' . esc_url($logo_patchstack) . '" alt="Patchstack" style="height:16px;width:auto;display:inline-block;" />'
.
'</a>'
.
'</span>';
return $links;
}
/**
* Remove Deactivate/Delete action links for protected plugins (single and network admin).
*
* @param array $actions
* @param string $plugin_file
* @param array $plugin_data
* @param string $context
*
* @return array
*/
public function filter_plugin_action_links(array $actions, string $plugin_file, array $plugin_data,
string $context): array
{
if ($this->is_protected_plugin($plugin_file)) {
unset($actions['deactivate']);
unset($actions['delete']);
}
return $actions;
}
/**
* Determine if a plugin file is in the protected list.
*
* @param string $plugin_file
*
* @return bool
*/
private function is_protected_plugin(string $plugin_file): bool
{
return in_array($plugin_file, $this->get_protected_plugins(), true);
}
/**
* Return plugin basenames that must be protected from deactivation/deletion.
*
* @return string[]
*/
private function get_protected_plugins(): array
{
$own = plugin_basename(__FILE__);
return array(
$own,
'patchstack/patchstack.php',
);
}
/**
* Intercept admin actions attempting to deactivate or delete protected plugins and block them.
*/
public function protect_plugins_admin_actions()
{
// Collect requested action(s).
$action = isset($_REQUEST['action']) ? sanitize_key((string)$_REQUEST['action']) : '';
$action2 = isset($_REQUEST['action2']) ? sanitize_key((string)$_REQUEST['action2']) : '';
$targets = array();
if (!empty($_REQUEST['plugin'])) {
$targets[] = sanitize_text_field((string)$_REQUEST['plugin']);
}
if (!empty($_REQUEST['checked']) && is_array($_REQUEST['checked'])) {
foreach ($_REQUEST['checked'] as $pf) {
$targets[] = sanitize_text_field((string)$pf);
}
}
if (empty($targets)) {
return;
}
$blocked_actions = array(
'deactivate',
'deactivate-selected',
'delete',
'delete-selected',
'delete-plugin',
);
if (!in_array($action, $blocked_actions, true) && !in_array($action2, $blocked_actions, true)) {
return;
}
$protected = $this->get_protected_plugins();
$intersect = array_intersect($targets, $protected);
if (!empty($intersect)) {
wp_die(
__('For security, this action is blocked: 1984 Hosting Support and Patchstack cannot be deactivated or deleted while this policy is active.', 'hostingsupport1984'),
403
);
}
}
}
$hosting_support_1984 = new HostingSupport1984();
define( 'A1984_HOSTING_SUPPORT_BASE_FILE', __FILE__ );
const A1984_HOSTING_SUPPORT_BASE_FILE = __FILE__;
require_once __DIR__ . '/1984-hosting-support-ui.php';