Compare commits

..

4 Commits

Author SHA1 Message Date
db662096e4 Add prominent notification next to WordPress error messages (v1.3.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-10 00:26:32 +01:00
d6b89887fc Add instructional notification for users (v1.3.1)
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:15:16 +01:00
cd593f68d3 Complete redesign to use WordPress plugins list directly (v1.3.0)
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:06:07 +01:00
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
4 changed files with 276 additions and 176 deletions

View File

@ -2,6 +2,43 @@
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.3.2] - 2023-10-05
### Added
- Prominent notification that appears directly below WordPress error messages
- Visual styling to help users connect error message with solution
- Direction arrows and highlighted text to improve user guidance
## [1.3.1] - 2023-10-05
### Added
- Instructional notification explaining how to use the plugin
- Step-by-step guidance for removing plugin references
- Clear visual indicators for missing plugins
## [1.3.0] - 2023-10-05
### Changed
- Complete redesign for maximum compatibility with all WordPress themes
- Now uses the plugins list table for missing plugin references
- Uses standard WordPress admin UI patterns and hooks
### Added
- Missing plugins now appear directly in the plugins list
- "Remove Reference" action link in the plugins list
- Success/error notices after removing references
### Fixed
- Compatibility issues with various WordPress admin themes
- Reliability issues with notification detection
## [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 ## [1.2.3] - 2023-10-05
### Fixed ### Fixed
- Button not appearing in some WordPress admin themes - Button not appearing in some WordPress admin themes

View File

@ -1,7 +1,7 @@
# Plugin Reference Cleaner # Plugin Reference Cleaner
Author: Marcus Quinn Author: Marcus Quinn
Author URI: https://www.wpallstars.com Author URI: https://www.wpallstars.com
Version: 1.2.3 Version: 1.3.2
License: GPL-2.0+ License: GPL-2.0+
## Description ## Description
@ -47,6 +47,29 @@ If you don't have this notification perpetually showing on your /wp-admin/plugin
## Changelog ## Changelog
### 1.3.2
* Added prominent notification directly below WordPress error messages
* Improved user guidance with visual cues to connect error and solution
* Added eye-catching styling to help users understand how to fix errors
### 1.3.1
* Added instructional notification explaining how to use the plugin
* Improved user guidance with step-by-step instructions
* Enhanced visual identification of missing plugins
### 1.3.0
* Complete redesign for maximum compatibility with all WordPress themes
* Now adds missing plugins directly to the plugins list table
* Uses standard WordPress admin UI patterns instead of DOM manipulation
* Added "Remove Reference" action link in the plugins list
* Significantly improved reliability across all WordPress configurations
### 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 ### 1.2.3
* Fixed button not appearing in some WordPress admin themes * Fixed button not appearing in some WordPress admin themes
* Improved error message detection for greater compatibility * Improved error message detection for greater compatibility

View File

@ -2,7 +2,7 @@
/* /*
* 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.3 * Version: 1.3.2
* Author: Marcus Quinn * Author: Marcus Quinn
* Author URI: https://www.wpallstars.com * Author URI: https://www.wpallstars.com
* License: GPL-2.0+ * License: GPL-2.0+
@ -15,184 +15,108 @@ 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 our plugin to the plugins list
add_action('current_screen', function($screen) { add_filter('all_plugins', array($this, 'add_missing_plugins_references'));
if (isset($screen->id) && ($screen->id === 'plugins' || $screen->id === 'plugins-network')) {
// Hook into admin notices to modify plugin error messages
add_action('admin_notices', array($this, 'inject_remove_button'), 100);
add_action('network_admin_notices', array($this, 'inject_remove_button'), 100);
}
});
// Handle the AJAX request to remove the plugin reference // Add our action link to the plugins list
add_action('wp_ajax_remove_plugin_reference', array($this, 'remove_plugin_reference')); add_filter('plugin_action_links', array($this, 'add_remove_reference_action'), 20, 4);
// Handle the remove reference action
add_action('admin_init', array($this, 'handle_remove_reference'));
// Add admin notices for operation feedback
add_action('admin_notices', array($this, 'admin_notices'));
} }
// Inject "Remove Reference" button only if a relevant notice exists /**
public function inject_remove_button() { * Find and add invalid plugin references to the plugins list
// Check if a "Plugin file does not exist" notice exists */
$notices = $this->get_admin_notices(); public function add_missing_plugins_references($plugins) {
$has_error_notice = false; // Only run on the plugins page
$plugin_files = array(); if (!$this->is_plugins_page()) {
return $plugins;
}
if (!empty($notices)) { // Get active plugins that don't exist
foreach ($notices as $notice) { $invalid_plugins = $this->get_invalid_plugins();
// Look for both variations of the error message
if (strpos($notice, 'has been deactivated due to an error: Plugin file does not exist') !== false || // Add each invalid plugin to the plugin list
strpos($notice, 'deactivated due to an error: Plugin file does not exist') !== false) { foreach ($invalid_plugins as $plugin_path) {
// Extract plugin file from notice using various patterns if (!isset($plugins[$plugin_path])) {
if (preg_match('/The plugin ([^ ]+)/', $notice, $match)) { $plugin_name = basename($plugin_path);
$plugin_files[] = $match[1]; $plugins[$plugin_path] = array(
$has_error_notice = true; 'Name' => $plugin_name . ' <span class="error">(File Missing)</span>',
} 'Description' => 'This plugin file does not exist. You can safely remove this reference.',
} 'Version' => 'N/A',
'Author' => '',
'PluginURI' => '',
'AuthorURI' => '',
'Title' => $plugin_name . ' (Missing)',
'AuthorName' => ''
);
} }
} }
// Only proceed if a relevant notice was found return $plugins;
if (!$has_error_notice || empty($plugin_files)) { }
/**
* Add the Remove Reference action link to invalid plugins
*/
public function add_remove_reference_action($actions, $plugin_file, $plugin_data, $context) {
// Only run on the plugins page
if (!$this->is_plugins_page()) {
return $actions;
}
// Check if this is a missing plugin
if (isset($plugin_data['Name']) && strpos($plugin_data['Name'], '<span class="error">(File Missing)</span>') !== false) {
// Clear existing actions
$actions = array();
// Add our action
$nonce = wp_create_nonce('remove_plugin_reference_' . $plugin_file);
$remove_url = admin_url('plugins.php?action=remove_reference&plugin=' . urlencode($plugin_file) . '&_wpnonce=' . $nonce);
$actions['remove_reference'] = '<a href="' . esc_url($remove_url) . '" class="delete" aria-label="' . esc_attr__('Remove Reference', 'plugin-reference-cleaner') . '">Remove Reference</a>';
}
return $actions;
}
/**
* Handle the remove reference action
*/
public function handle_remove_reference() {
// Check if we're removing a reference
if (!isset($_GET['action']) || $_GET['action'] !== 'remove_reference' || !isset($_GET['plugin'])) {
return; return;
} }
// Inject JavaScript with the specific plugin files // Verify permissions
?>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
var pluginFiles = <?php echo wp_json_encode($plugin_files); ?>;
// Try multiple selectors to find error notices
var notices = document.querySelectorAll('.notice-error p, .error p, .notice p, .updated p, .update-nag p');
if (notices.length === 0) {
// If no p elements found, try the parent elements
notices = document.querySelectorAll('.notice-error, .error, .notice, .updated, .update-nag');
}
if (notices.length === 0) {
// Try a more generic approach - find any element containing the error text
var allElements = document.querySelectorAll('*');
var errorNotices = [];
for (var i = 0; i < allElements.length; i++) {
var el = allElements[i];
if (el.childNodes.length === 1 && el.childNodes[0].nodeType === 3) { // Text node
if (el.textContent.includes('has been deactivated due to an error: Plugin file does not exist') ||
el.textContent.includes('deactivated due to an error: Plugin file does not exist')) {
errorNotices.push(el);
}
}
}
if (errorNotices.length > 0) {
notices = errorNotices;
}
}
if (notices.length === 0) {
console.log('Plugin Reference Cleaner: No notice elements found');
return;
}
notices.forEach(function(notice) {
pluginFiles.forEach(function(pluginFile) {
if (notice.textContent.includes(pluginFile) ||
notice.textContent.includes('Plugin file does not exist')) {
var button = document.createElement('button');
button.textContent = 'Remove Reference';
button.className = 'button button-secondary remove-plugin-ref';
button.dataset.plugin = pluginFile;
button.style.marginLeft = '10px';
notice.appendChild(button);
console.log('Plugin Reference Cleaner: Added button for ' + pluginFile);
}
});
});
document.querySelectorAll('.remove-plugin-ref').forEach(function(button) {
button.addEventListener('click', function(e) {
e.preventDefault();
var pluginFile = this.dataset.plugin;
if (confirm('Are you sure you want to remove the reference to ' + pluginFile + '?')) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '<?php echo esc_url(admin_url('admin-ajax.php')); ?>', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
if (xhr.status === 200) {
try {
var response = JSON.parse(xhr.responseText);
if (response.success) {
alert('Plugin reference removed successfully.');
location.reload();
} else {
alert('Failed to remove plugin reference: ' + (response.data || 'Unknown error'));
}
} catch (e) {
alert('Failed to parse server response.');
console.error(e);
}
} else {
alert('Failed to remove plugin reference. Server returned status ' + xhr.status);
}
};
xhr.onerror = function() {
alert('Network error occurred while trying to remove plugin reference.');
};
xhr.send('action=remove_plugin_reference&plugin=' + encodeURIComponent(pluginFile) + '&nonce=<?php echo wp_create_nonce('remove_plugin_reference'); ?>');
}
});
});
});
</script>
<?php
}
// Helper function to capture 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();
do_action('admin_notices');
do_action('network_admin_notices');
$output = ob_get_clean();
$is_capturing = false;
if (empty($output)) {
return array();
}
return array_filter(explode("\n", $output));
}
// Handle the AJAX request to remove the plugin reference
public function remove_plugin_reference() {
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'remove_plugin_reference')) {
wp_send_json_error('Invalid security token. Please refresh the page and try again.');
wp_die();
}
// Check user permissions
if (!current_user_can('activate_plugins')) { if (!current_user_can('activate_plugins')) {
wp_send_json_error('You do not have sufficient permissions to perform this action.'); wp_die(__('You do not have sufficient permissions to perform this action.', 'plugin-reference-cleaner'));
wp_die();
} }
// Get and validate plugin file parameter // Get the plugin file
$plugin_file = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : ''; $plugin_file = isset($_GET['plugin']) ? $_GET['plugin'] : '';
if (empty($plugin_file)) {
wp_send_json_error('No plugin specified.'); // Verify nonce
wp_die(); check_admin_referer('remove_plugin_reference_' . $plugin_file);
// Remove the plugin reference
$success = $this->remove_plugin_reference($plugin_file);
// Redirect back to plugins page with a message
$redirect = admin_url('plugins.php');
$redirect = add_query_arg($success ? 'reference_removed' : 'reference_removal_failed', '1', $redirect);
wp_redirect($redirect);
exit;
} }
/**
* Remove a plugin reference from the active plugins
*/
public function remove_plugin_reference($plugin_file) {
$success = false; $success = false;
// Handle multisite network admin // Handle multisite network admin
@ -214,13 +138,106 @@ class Plugin_Reference_Cleaner {
} }
} }
if ($success) { return $success;
wp_send_json_success('Plugin reference removed successfully.');
} else {
wp_send_json_error('Plugin reference not found or could not be removed.');
} }
wp_die(); /**
* Display admin notices
*/
public function admin_notices() {
// Only run on the plugins page
if (!$this->is_plugins_page()) {
return;
}
// Get invalid plugins
$invalid_plugins = $this->get_invalid_plugins();
// Create a highlighted notice immediately after WordPress error messages
if (!empty($invalid_plugins)) {
// Add a notice specifically targeting the WordPress error notification
add_action('admin_print_footer_scripts', function() use ($invalid_plugins) {
?>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
// Find all error notifications about missing plugins
var errorNotices = document.querySelectorAll('.notice-error, .error, .updated.error');
errorNotices.forEach(function(notice) {
if (notice.textContent.includes('Plugin file does not exist')) {
// Create our custom notice
var ourNotice = document.createElement('div');
ourNotice.className = 'notice notice-warning';
ourNotice.style.borderLeft = '4px solid #ffba00';
ourNotice.style.backgroundColor = '#fff8e5';
ourNotice.style.padding = '10px 12px';
ourNotice.style.margin = '5px 0 15px';
ourNotice.style.fontSize = '14px';
// Add content
ourNotice.innerHTML = '<h3 style="margin-top:0;color:#826200;">👉 Plugin Reference Cleaner Can Fix This</h3>' +
'<p>To remove the above error notification, scroll down to find the plugin marked with "<strong style="color:red">(File Missing)</strong>" and click its "<strong>Remove Reference</strong>" link.</p>' +
'<p>This will permanently remove the missing plugin reference from your database.</p>';
// Insert our notice right after the error
notice.parentNode.insertBefore(ourNotice, notice.nextSibling);
}
});
});
</script>
<?php
});
// Also display our standard info notice with more details
echo '<div class="notice notice-info is-dismissible">';
echo '<h3>Plugin Reference Cleaner</h3>';
echo '<p><strong>Missing plugin files detected:</strong> The plugins listed below with <span style="color:red;">(File Missing)</span> tag no longer exist but are still referenced in your database.</p>';
echo '<p><strong>How to fix:</strong> Click the "Remove Reference" link next to each missing plugin to safely remove it from your active plugins list.</p>';
echo '<p>This will clean up your database and remove the error notifications.</p>';
echo '</div>';
}
// Show success message
if (isset($_GET['reference_removed']) && $_GET['reference_removed'] === '1') {
echo '<div class="notice notice-success is-dismissible"><p>Plugin reference removed successfully.</p></div>';
}
// Show error message
if (isset($_GET['reference_removal_failed']) && $_GET['reference_removal_failed'] === '1') {
echo '<div class="notice notice-error is-dismissible"><p>Failed to remove plugin reference. The plugin may already have been removed.</p></div>';
}
}
/**
* Check if we're on the plugins page
*/
private function is_plugins_page() {
global $pagenow;
return is_admin() && $pagenow === 'plugins.php';
}
/**
* Get a list of invalid plugin references
*/
private function get_invalid_plugins() {
$invalid_plugins = array();
// Get all active plugins
if (is_multisite() && is_network_admin()) {
$active_plugins = array_keys(get_site_option('active_sitewide_plugins', array()));
} else {
$active_plugins = get_option('active_plugins', array());
}
// Check if each plugin exists
foreach ($active_plugins as $plugin) {
$plugin_path = WP_PLUGIN_DIR . '/' . $plugin;
if (!file_exists($plugin_path)) {
$invalid_plugins[] = $plugin;
}
}
return $invalid_plugins;
} }
} }

View File

@ -1,7 +1,7 @@
=== Plugin Reference Cleaner === === Plugin Reference Cleaner ===
Author: Marcus Quinn Author: Marcus Quinn
Author URI: https://www.wpallstars.com Author URI: https://www.wpallstars.com
Version: 1.2.3 Version: 1.3.2
License: GPL-2.0+ License: GPL-2.0+
== Description == == Description ==
@ -47,6 +47,29 @@ If you don't have this notification perpetually showing on your /wp-admin/plugin
== Changelog == == Changelog ==
= 1.3.2 =
* Added prominent notification directly below WordPress error messages
* Improved user guidance with visual cues to connect error and solution
* Added eye-catching styling to help users understand how to fix errors
= 1.3.1 =
* Added instructional notification explaining how to use the plugin
* Improved user guidance with step-by-step instructions
* Enhanced visual identification of missing plugins
= 1.3.0 =
* Complete redesign for maximum compatibility with all WordPress themes
* Now adds missing plugins directly to the plugins list table
* Uses standard WordPress admin UI patterns instead of DOM manipulation
* Added "Remove Reference" action link in the plugins list
* Significantly improved reliability across all WordPress configurations
= 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 = = 1.2.3 =
* Fixed button not appearing in some WordPress admin themes * Fixed button not appearing in some WordPress admin themes
* Improved error message detection for greater compatibility * Improved error message detection for greater compatibility