334 lines
9.1 KiB
PHP
334 lines
9.1 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Plugin Name: 1984 Hosting Support
|
|
* Version: 1.1.0
|
|
* Author: 1984 ehf.
|
|
* Author URI: https://1984.hosting
|
|
* Description: Support plugin for 1984 Hosting customers.
|
|
* Update URI: https://git.1984.is/1984/1984-hosting-support/
|
|
* Text Domain: hostingsupport1984
|
|
* Domain Path: /languages
|
|
*/
|
|
|
|
/**
|
|
* The main HostingSupport1984 class
|
|
*
|
|
* @package HostingSupport1984
|
|
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html GNU General Public License v2.0
|
|
*
|
|
* This is where basic constants such as the version number are pulled from.
|
|
* 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
|
|
{
|
|
const HOSTING_SUPPORT_VERSION = '0.4.0';
|
|
|
|
/**
|
|
* Construct a new HostingSupport1984 object
|
|
*/
|
|
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')
|
|
);
|
|
|
|
// Register the activation hook.
|
|
register_activation_hook(
|
|
__FILE__,
|
|
array($this,
|
|
'activation_hook')
|
|
);
|
|
|
|
// Run on activation of any plugin.
|
|
add_action(
|
|
'activated_plugin',
|
|
array($this,
|
|
'plugin_activation_hook')
|
|
);
|
|
|
|
// Run on activation of theme.
|
|
add_action(
|
|
'switch_theme',
|
|
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'));
|
|
}
|
|
|
|
/**
|
|
* Load the 'hosting_support_1984' text domain
|
|
*
|
|
* @return Boolean
|
|
*/
|
|
public function load_textdomain(): bool
|
|
{
|
|
return load_plugin_textdomain(
|
|
'hostingsupport1984',
|
|
false,
|
|
'1984-hosting-support/languages'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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()) {
|
|
// In case Patchstack is active, do not continue.
|
|
return;
|
|
}
|
|
|
|
// Auto-update WordPress core.
|
|
update_site_option('auto_update_core_major', 'enabled');
|
|
|
|
// Auto-update plugins.
|
|
$all_plugins = apply_filters('all_plugins', get_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));
|
|
}
|
|
|
|
/**
|
|
* Check if Patchstack is enabled.
|
|
*
|
|
* @return Boolean
|
|
*/
|
|
public function is_patchstack_active()
|
|
{
|
|
return is_plugin_active('patchstack/patchstack.php');
|
|
}
|
|
|
|
/**
|
|
* On plugin activation, add to auto-updated list of plugins.
|
|
*
|
|
* This is skipped if Patchstack is active on the site.
|
|
*/
|
|
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[] = $plugin;
|
|
|
|
update_site_option('auto_update_plugins', array_unique($auto_updated_plugins));
|
|
}
|
|
|
|
/**
|
|
* On theme switch, enable auto-update for all themes.
|
|
*
|
|
* This is skipped if Patchstack is active on the site.
|
|
*/
|
|
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));
|
|
}
|
|
|
|
/**
|
|
* 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 Hosting: Please do not deactivate or delete this plugin - part of your WordPress Service Pack.', 'hostingsupport1984');
|
|
|
|
$logo_1984_url = plugins_url('icons/1984-hosting-logo.webp', __FILE__);
|
|
$logo_patchstack = plugins_url('icons/patchstack-logo.svg', __FILE__);
|
|
|
|
$title_1984 = __('Visit 1984 Hosting Homepage', 'hostingsupport1984');
|
|
$title_patchstack = __('Visit Patchstack Homepage', 'hostingsupport1984');
|
|
|
|
$container_open =
|
|
'<span class="description" style="color:#b32d2e;font-weight:700;display:inline-grid;grid-template-columns:auto 1fr;column-gap:6px;row-gap:4px;align-items:center;line-height:1.5;vertical-align:middle;">';
|
|
|
|
$warning_icon =
|
|
'<span class="dashicons dashicons-warning" aria-hidden="true" style="color:#b32d2e;grid-column:1;"></span>';
|
|
|
|
$text_html = '<span style="grid-column:2;">' . esc_html($warning_text) . '</span>';
|
|
|
|
|
|
$logos_html =
|
|
'<span style="grid-column:2;display:inline-flex;align-items:center;gap:4px;">'
|
|
.
|
|
'<a href="https://1984.hosting" target="_blank" rel="noopener noreferrer" title="' . esc_attr($title_1984) . '" aria-label="' . esc_attr($title_1984) . '">'
|
|
.
|
|
'<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="' . esc_attr($title_patchstack) . '" aria-label="' . esc_attr($title_patchstack) . '">'
|
|
.
|
|
'<img src="' . esc_url($logo_patchstack) . '" alt="Patchstack" style="height:16px;width:auto;display:inline-block;" />'
|
|
.
|
|
'</a>'
|
|
.
|
|
'</span>';
|
|
|
|
$links[] = $container_open . $warning_icon . $text_html . $logos_html . '</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();
|
|
|
|
const A1984_HOSTING_SUPPORT_BASE_FILE = __FILE__;
|
|
|
|
require_once __DIR__ . '/1984-hosting-support-ui.php';
|