Compare commits

...

3 Commits

Author SHA1 Message Date
7ac72fd3c0 Fix compatibility with more WordPress admin themes (v1.2.4)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline is pending
Build Release / Build and Create Release (push) Has been cancelled
ci/woodpecker/tag/woodpecker Pipeline failed
2025-04-10 00:01:20 +01:00
0a394fa671 Fix button not appearing in some WP admin themes (v1.2.3)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline is pending
Build Release / Build and Create Release (push) Has been cancelled
ci/woodpecker/tag/woodpecker Pipeline failed
2025-04-09 23:56:47 +01:00
f200ff6f96 Fix timeout issue during plugin activation (v1.2.2)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline is pending
Build Release / Build and Create Release (push) Has been cancelled
ci/woodpecker/tag/woodpecker Pipeline failed
2025-04-09 23:46:06 +01:00
4 changed files with 200 additions and 29 deletions

View File

@ -2,6 +2,34 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [1.2.4] - 2023-10-05
### Fixed
- Compatibility with more WordPress admin UI variations
- Specific targeting for admin notices in various themes
### Added
- Advanced DOM traversal using TreeWalker API
- Multiple fallback approaches to ensure button appears
- Enhanced console logging for troubleshooting
## [1.2.3] - 2023-10-05
### Fixed
- Button not appearing in some WordPress admin themes
- Error message detection for greater theme compatibility
### Improved
- DOM traversal to find notification elements in various themes
- Added console logging for troubleshooting
## [1.2.2] - 2023-10-05
### Fixed
- Timeout issue during plugin activation
- Potential infinite recursion in admin notices handling
### Improved
- Hook management to prevent performance issues
- Optimized by only loading on plugins page
## [1.2.1] - 2025-04-07 ## [1.2.1] - 2025-04-07
### Improved ### Improved
- Fixed typos in documentation - Fixed typos in documentation

View File

@ -1,7 +1,7 @@
# Plugin Reference Cleaner # Plugin Reference Cleaner
Author: Marcus Quinn Author: Marcus Quinn
Author URI: https://wpallstars.com Author URI: https://www.wpallstars.com
Version: 1.2.1 Version: 1.2.4
License: GPL-2.0+ License: GPL-2.0+
## Description ## Description
@ -47,6 +47,22 @@ If you don't have this notification perpetually showing on your /wp-admin/plugin
## Changelog ## Changelog
### 1.2.4
* Fixed compatibility with more WordPress admin themes
* Added advanced DOM traversal to find error messages
* Implemented fallback mechanisms to ensure button appears
* Added detailed console logging for troubleshooting
### 1.2.3
* Fixed button not appearing in some WordPress admin themes
* Improved error message detection for greater compatibility
* Enhanced DOM traversal to find notification elements
### 1.2.2
* Fixed timeout issue during plugin activation
* Improved hook management to prevent potential infinite recursion
* Optimized performance by only loading on plugins page
### 1.2.1 ### 1.2.1
* Fixed typos in documentation * Fixed typos in documentation
* Improved text clarity * Improved text clarity

View File

@ -2,9 +2,9 @@
/* /*
* Plugin Name: Plugin Reference Cleaner * Plugin Name: Plugin Reference Cleaner
* Description: Adds a "Remove Reference" button to plugin deactivation error notices, allowing users to clean up invalid plugin entries. * Description: Adds a "Remove Reference" button to plugin deactivation error notices, allowing users to clean up invalid plugin entries.
* Version: 1.2.1 * Version: 1.2.4
* Author: Marcus Quinn * Author: Marcus Quinn
* Author URI: https://wpallstars.com * Author URI: https://www.wpallstars.com
* License: GPL-2.0+ * License: GPL-2.0+
*/ */
@ -15,22 +15,21 @@ if (!defined('ABSPATH')) {
class Plugin_Reference_Cleaner { class Plugin_Reference_Cleaner {
public function __construct() { public function __construct() {
// Only hook into admin actions when on the plugins page
add_action('current_screen', function($screen) {
if (isset($screen->id) && ($screen->id === 'plugins' || $screen->id === 'plugins-network')) {
// Hook into admin notices to modify plugin error messages // Hook into admin notices to modify plugin error messages
add_action('admin_notices', array($this, 'inject_remove_button'), 100); add_action('admin_notices', array($this, 'inject_remove_button'), 100);
add_action('network_admin_notices', array($this, 'inject_remove_button'), 100); // Ensure notices in network admin add_action('network_admin_notices', array($this, 'inject_remove_button'), 100);
}
});
// Handle the AJAX request to remove the plugin reference // Handle the AJAX request to remove the plugin reference
add_action('wp_ajax_remove_plugin_reference', array($this, 'remove_plugin_reference')); add_action('wp_ajax_remove_plugin_reference', array($this, 'remove_plugin_reference'));
} }
// Inject "Remove Reference" button only if a relevant notice exists // Inject "Remove Reference" button only if a relevant notice exists
public function inject_remove_button() { public function inject_remove_button() {
global $pagenow;
// Only run on plugins.php or network admin plugins page
if (!in_array($pagenow, array('plugins.php', 'plugins.php'))) {
return;
}
// Check if a "Plugin file does not exist" notice exists // Check if a "Plugin file does not exist" notice exists
$notices = $this->get_admin_notices(); $notices = $this->get_admin_notices();
$has_error_notice = false; $has_error_notice = false;
@ -38,8 +37,10 @@ class Plugin_Reference_Cleaner {
if (!empty($notices)) { if (!empty($notices)) {
foreach ($notices as $notice) { foreach ($notices as $notice) {
if (strpos($notice, 'has been deactivated due to an error: Plugin file does not exist') !== false) { // Look for both variations of the error message
// Extract plugin file from notice if (strpos($notice, 'has been deactivated due to an error: Plugin file does not exist') !== false ||
strpos($notice, 'deactivated due to an error: Plugin file does not exist') !== false) {
// Extract plugin file from notice using various patterns
if (preg_match('/The plugin ([^ ]+)/', $notice, $match)) { if (preg_match('/The plugin ([^ ]+)/', $notice, $match)) {
$plugin_files[] = $match[1]; $plugin_files[] = $match[1];
$has_error_notice = true; $has_error_notice = true;
@ -58,25 +59,123 @@ class Plugin_Reference_Cleaner {
<script type="text/javascript"> <script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var pluginFiles = <?php echo wp_json_encode($plugin_files); ?>; var pluginFiles = <?php echo wp_json_encode($plugin_files); ?>;
var notices = document.querySelectorAll('.notice-error p'); console.log('Plugin Reference Cleaner: Detected plugin files:', pluginFiles);
if (notices.length === 0) { // Get all notifications directly in the plugins page
var pluginsPage = document.querySelector('.wrap');
if (!pluginsPage) {
console.log('Plugin Reference Cleaner: Could not find the plugins page wrapper');
return; return;
} }
notices.forEach(function(notice) { // Look specifically for elements containing the error message
// Use direct DOM traversal to find all text nodes
function findTextNodesWithText(element, searchText) {
var result = [];
var walk = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
var node;
while (node = walk.nextNode()) {
if (node.nodeValue.indexOf(searchText) > -1) {
result.push(node);
}
}
return result;
}
// Find paragraphs containing error messages
var errorTexts = findTextNodesWithText(pluginsPage, 'Plugin file does not exist');
console.log('Plugin Reference Cleaner: Found error nodes:', errorTexts.length);
if (errorTexts.length === 0) {
// Try another approach - find direct error messages
var allElements = pluginsPage.querySelectorAll('*');
for (var i = 0; i < allElements.length; i++) {
var el = allElements[i];
var text = el.textContent;
if (text && text.includes('has been deactivated due to an error: Plugin file does not exist')) {
errorTexts.push(el);
}
}
}
// Handle each error message
errorTexts.forEach(function(textNode) {
console.log('Plugin Reference Cleaner: Processing error node:', textNode);
// Get the parent element of the text node
var errorElement = textNode.parentNode || textNode;
// Check if a button already exists to avoid duplicates
if (errorElement.querySelector('.remove-plugin-ref')) {
console.log('Plugin Reference Cleaner: Button already exists for this element');
return;
}
// Find which plugin file this error refers to
var matchingPluginFile = null;
pluginFiles.forEach(function(pluginFile) { pluginFiles.forEach(function(pluginFile) {
if (notice.textContent.includes('The plugin ' + pluginFile)) { if (textNode.nodeValue && textNode.nodeValue.includes(pluginFile) ||
(textNode.textContent && textNode.textContent.includes(pluginFile))) {
matchingPluginFile = pluginFile;
}
});
// If we couldn't match a specific plugin, use the first one as fallback
if (!matchingPluginFile && pluginFiles.length > 0) {
matchingPluginFile = pluginFiles[0];
console.log('Plugin Reference Cleaner: Using fallback plugin file:', matchingPluginFile);
}
if (matchingPluginFile) {
// Create button
var button = document.createElement('button');
button.textContent = 'Remove Reference';
button.className = 'button button-secondary remove-plugin-ref';
button.dataset.plugin = matchingPluginFile;
button.style.marginLeft = '10px';
// Append the button to the error message
errorElement.appendChild(button);
console.log('Plugin Reference Cleaner: Added button for ' + matchingPluginFile + ' to', errorElement);
} else {
console.log('Plugin Reference Cleaner: Could not determine plugin file for this error');
}
});
// Inject a more direct approach if the above fails
if (errorTexts.length === 0) {
// Target the specific format in the screenshot
var errorMessages = document.querySelectorAll('.wrap > .notice, .wrap > div > .notice');
console.log('Plugin Reference Cleaner: Trying direct approach, found notices:', errorMessages.length);
errorMessages.forEach(function(notice) {
var text = notice.textContent;
if (text && text.includes('Plugin file does not exist')) {
// Extract the plugin path
var pluginMatch = /The plugin ([^ ]+) has been deactivated/.exec(text);
var pluginFile = pluginMatch ? pluginMatch[1] : (pluginFiles.length > 0 ? pluginFiles[0] : null);
if (pluginFile) {
// Get container to add button
var container = notice.querySelector('p') || notice;
if (!container.querySelector('.remove-plugin-ref')) {
var button = document.createElement('button'); var button = document.createElement('button');
button.textContent = 'Remove Reference'; button.textContent = 'Remove Reference';
button.className = 'button button-secondary remove-plugin-ref'; button.className = 'button button-secondary remove-plugin-ref';
button.dataset.plugin = pluginFile; button.dataset.plugin = pluginFile;
button.style.marginLeft = '10px'; button.style.marginLeft = '10px';
notice.appendChild(button); container.appendChild(button);
console.log('Plugin Reference Cleaner: Direct approach - added button for ' + pluginFile);
}
}
} }
}); });
}); }
// Add click event listeners to all created buttons
document.querySelectorAll('.remove-plugin-ref').forEach(function(button) { document.querySelectorAll('.remove-plugin-ref').forEach(function(button) {
button.addEventListener('click', function(e) { button.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault();
@ -117,11 +216,23 @@ class Plugin_Reference_Cleaner {
// Helper function to capture admin notices // Helper function to capture admin notices
private function get_admin_notices() { private function get_admin_notices() {
// Static flag to prevent infinite recursion
static $is_capturing = false;
// If already capturing, return empty to break potential loops
if ($is_capturing) {
return array();
}
$is_capturing = true;
ob_start(); ob_start();
do_action('admin_notices'); do_action('admin_notices');
do_action('network_admin_notices'); do_action('network_admin_notices');
$output = ob_get_clean(); $output = ob_get_clean();
$is_capturing = false;
if (empty($output)) { if (empty($output)) {
return array(); return array();
} }

View File

@ -1,7 +1,7 @@
=== Plugin Reference Cleaner === === Plugin Reference Cleaner ===
Author: Marcus Quinn Author: Marcus Quinn
Author URI: https://wpallstars.com Author URI: https://www.wpallstars.com
Version: 1.2.1 Version: 1.2.4
License: GPL-2.0+ License: GPL-2.0+
== Description == == Description ==
@ -47,6 +47,22 @@ If you don't have this notification perpetually showing on your /wp-admin/plugin
== Changelog == == Changelog ==
= 1.2.4 =
* Fixed compatibility with more WordPress admin themes
* Added advanced DOM traversal to find error messages
* Implemented fallback mechanisms to ensure button appears
* Added detailed console logging for troubleshooting
= 1.2.3 =
* Fixed button not appearing in some WordPress admin themes
* Improved error message detection for greater compatibility
* Enhanced DOM traversal to find notification elements
= 1.2.2 =
* Fixed timeout issue during plugin activation
* Improved hook management to prevent potential infinite recursion
* Optimized performance by only loading on plugins page
= 1.2.1 = = 1.2.1 =
* Fixed typos in documentation * Fixed typos in documentation
* Improved text clarity * Improved text clarity