diff --git a/1984-hosting-support-ui.php b/1984-hosting-support-ui.php
index b878347..24ff79d 100644
--- a/1984-hosting-support-ui.php
+++ b/1984-hosting-support-ui.php
@@ -15,26 +15,51 @@
* Feel free to remove this file if you don't intend to use the 1984/HostingSupport1984
* user interface additions.
*/
-class HostingSupport1984UI {
+class HostingSupport1984UI
+{
const VIEWS_DIR = './views/';
/**
- * Construct a new HostingSupport1984UI object
+ * Build a new HostingSupport1984UI object
*/
- public function __construct() {
+ public function __construct()
+ {
// Add the 1984 Support Information dashboard widget.
add_action(
'wp_dashboard_setup',
- array( $this, 'add_dashboard_widget' )
+ array($this,
+ 'add_dashboard_widget')
);
// Enqueue the admin JS and CSS.
add_action(
'admin_enqueue_scripts',
- array( $this, 'enqueue_admin_scripts' )
+ array($this,
+ 'enqueue_admin_scripts')
);
}
+ /**
+ * Add the 1984 Hosting dashboard widget.
+ */
+ public function add_dashboard_widget()
+ {
+ wp_add_dashboard_widget(
+ '1984_hosting_support_widget',
+ __('1984 Hosting Support', 'hostingsupport1984'),
+ array($this,
+ 'render_dashboard_widget')
+ );
+ }
+
+ /**
+ * Render the 1984 Hosting dashboard widget.
+ */
+ public function render_dashboard_widget()
+ {
+ $this->render_view('support-dashboard-widget.php');
+ }
+
/**
* Render a view
*
@@ -43,12 +68,13 @@ class HostingSupport1984UI {
*
* @return Boolean True if the user has access to the view. False if not.
*/
- public function render_view( string $view_file, bool $admin_only = true ) {
+ public function render_view(string $view_file, bool $admin_only = true): bool
+ {
if (
- true === $admin_only && current_user_can( 'manage_options' ) ||
+ true === $admin_only && current_user_can('manage_options') ||
false === $admin_only
) {
- require self::view_path( $view_file );
+ require self::view_path($view_file);
return true;
}
return false;
@@ -57,44 +83,20 @@ class HostingSupport1984UI {
/**
* Get full file path for a view
*
- * @param String $view_file The view file name.
+ * @param String $view_file The view file name.
+ *
* @return String The path.
*/
- private function view_path( $view_file ) {
- return plugin_dir_path( __FILE__ ) . self::VIEWS_DIR . $view_file;
- }
-
- /**
- * Check if the user can see the 1984 admin panel
- *
- * @return [type] [description]
- */
- private function current_user_can_view_admin_panel() {
- return ( true === current_user_can( 'manage_options' ) );
- }
-
- /**
- * Add the 1984 Hosting dashboard widget.
- */
- public function add_dashboard_widget() {
- wp_add_dashboard_widget(
- '1984_hosting_support_widget',
- __( '1984 Hosting Support', 'hostingsupport1984' ),
- array( $this, 'render_dashboard_widget' )
- );
- }
-
- /**
- * Render the 1984 Hosting dashboard widget.
- */
- public function render_dashboard_widget() {
- $this->render_view( 'support-dashboard-widget.php' );
+ private function view_path(string $view_file)
+ {
+ return plugin_dir_path(__FILE__) . self::VIEWS_DIR . $view_file;
}
/**
* Enqueue the 1984 wp-admin scripts
*/
- public function enqueue_admin_scripts() {
+ public function enqueue_admin_scripts()
+ {
wp_enqueue_script(
'1984-hosting-support-admin',
plugins_url(
@@ -116,6 +118,16 @@ class HostingSupport1984UI {
false
);
}
+
+ /**
+ * Check if the user can see the 1984 admin panel
+ *
+ * @return bool [type] [description]
+ */
+ private function current_user_can_view_admin_panel(): bool
+ {
+ return (true === current_user_can('manage_options'));
+ }
}
$hosting_support_1984_ui = new HostingSupport1984UI();
diff --git a/1984-hosting-support.php b/1984-hosting-support.php
index 3cc08bf..16b43d6 100644
--- a/1984-hosting-support.php
+++ b/1984-hosting-support.php
@@ -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 =
+ '';
+
+ $links[] =
+ '' . $warning_icon . esc_html($warning_text) . '' .
+ ''
+ .
+ ''
+ .
+ ''
+ .
+ ''
+ .
+ ''
+ .
+ '
'
+ .
+ ''
+ .
+ '';
+
+ 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';
diff --git a/README.md b/README.md
index 9d00019..8ac17d6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# 1984 Hosting Support Plugin
-This is a plugin provided by [1984 Hosting Company](https://1984.hosting/) to improve users' support experience while using WordPress. It is primarily intended for customers of 1984 Hosting Company, though anyone can use it. If you are a 1984 customer, feel free to disable or remove the plugin on your site if you do not find it useful.
+This is a plugin provided by [1984 Hosting Company](https://1984.hosting/) to improve users' support experience while
+using WordPress. It is primarily intended for customers of 1984 Hosting Company, though anyone can use it. If you are a
+1984 customer, feel free to disable or remove the plugin on your site if you do not find it useful.
The main function is provide a widget to users, as shown below:
diff --git a/icons/contact-support.svg b/icons/contact-support.svg
index c419875..f4016c5 100644
--- a/icons/contact-support.svg
+++ b/icons/contact-support.svg
@@ -1 +1,10 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/icons/knowledge-base.svg b/icons/knowledge-base.svg
index 4e2d78b..98e6fb5 100644
--- a/icons/knowledge-base.svg
+++ b/icons/knowledge-base.svg
@@ -1 +1,8 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/icons/patchstack-logo.svg b/icons/patchstack-logo.svg
new file mode 100644
index 0000000..39579b1
--- /dev/null
+++ b/icons/patchstack-logo.svg
@@ -0,0 +1,115 @@
+
\ No newline at end of file
diff --git a/icons/profile.svg b/icons/profile.svg
index 8fef49a..3b78de1 100644
--- a/icons/profile.svg
+++ b/icons/profile.svg
@@ -1 +1,7 @@
-
+
diff --git a/icons/sites.svg b/icons/sites.svg
index aa02e02..88030c8 100644
--- a/icons/sites.svg
+++ b/icons/sites.svg
@@ -1 +1,8 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/js/1984-hosting-support.js b/js/1984-hosting-support.js
index 310ba84..c70d2fe 100644
--- a/js/1984-hosting-support.js
+++ b/js/1984-hosting-support.js
@@ -4,16 +4,19 @@
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html GNU General Public License v2.0
*/
-function HostingSupport1984CopyEmailAddress( e ) {
- e.preventDefault();
- var emailAddressTag = document.querySelector( '#hosting-support-1984-address' );
- emailAddressTag.select();
- document.execCommand( 'copy' );
+function HostingSupport1984CopyEmailAddress(e) {
+ e.preventDefault();
+ const emailAddressTag = document.querySelector('#hosting-support-1984-address');
+ navigator.clipboard.writeText(emailAddressTag.value)
+ .catch(err => console.error("Clipboard write failed:", err));
}
-if (document.querySelector( '#hosting-support-1984-link' )) {
- document.querySelector( '#hosting-support-1984-link' ).addEventListener(
- 'click',
- HostingSupport1984CopyEmailAddress
- );
+document.querySelector('#hosting-support-1984-link')
+ ?.addEventListener('click', HostingSupport1984CopyEmailAddress);
+
+if (document.querySelector('#hosting-support-1984-link')) {
+ document.querySelector('#hosting-support-1984-link').addEventListener(
+ 'click',
+ HostingSupport1984CopyEmailAddress
+ );
}
diff --git a/style/1984-hosting-support.css b/style/1984-hosting-support.css
index 49ca127..538528b 100644
--- a/style/1984-hosting-support.css
+++ b/style/1984-hosting-support.css
@@ -7,51 +7,34 @@
* https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
**/
-#hostingsupport1984_support_widget {
- background: #fffbee;
-}
-
-#adminmenu a.toplevel_page_hostingsupport1984_manage {
- background-color: #b48701;
- font-weight: bold;
-}
-
-#adminmenu a.toplevel_page_hostingsupport1984_manage:hover {
- color: #fff;
-}
-
-#adminmenu a.toplevel_page_hostingsupport1984_manage div.wp-menu-image::before {
- color: #fff;
-}
-
#hosting-support-1984-main-links {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 0.2rem;
- list-style-type: none;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.2rem;
+ list-style-type: none;
}
.hosting-support-1984-li {
}
.hosting-support-1984-icon {
- width: 5.3em;
- height: auto;
- display: block;
- margin: auto;
+ width: 5.3em;
+ height: auto;
+ display: block;
+ margin: auto;
}
.hosting-support-1984-text {
- text-align: center;
+ text-align: center;
}
#hosting-support-1984-logo {
- display: flex;
- justify-content: flex-end;
+ display: flex;
+ justify-content: flex-end;
}
#hosting-support-1984-logo img {
- width: auto;
- height: 1.5em;
+ width: auto;
+ height: 1.5em;
}
diff --git a/views/support-dashboard-widget.php b/views/support-dashboard-widget.php
index 2fcaacc..aad2937 100644
--- a/views/support-dashboard-widget.php
+++ b/views/support-dashboard-widget.php
@@ -10,58 +10,67 @@
?>