| 1 |
<?php |
| 2 |
defined('ABSPATH') or die('Unauthorized Access'); |
| 3 |
|
| 4 |
/** |
| 5 |
* Renders a retention modal when the user clicks "Deactivate" on the |
| 6 |
* phpinfo() WP row in the WordPress Plugins screen. Shows them which |
| 7 |
* features they'd lose so they don't blindly deactivate a plugin that |
| 8 |
* was protecting them (PHP EOL warnings, Config Grader, SSL monitor, etc.). |
| 9 |
* |
| 10 |
* The modal HTML + JS are injected only on /wp-admin/plugins.php to keep |
| 11 |
* the rest of the admin clean. The "Deactivate anyway" button just lets |
| 12 |
* the original deactivate link follow through. |
| 13 |
* |
| 14 |
* Free users see the free feature list; Pro users see both. |
| 15 |
*/ |
| 16 |
class Phpinfo_WP_Deactivate_Modal { |
| 17 |
|
| 18 |
public static function register(): void { |
| 19 |
add_action('admin_footer-plugins.php', [__CLASS__, 'render']); |
| 20 |
} |
| 21 |
|
| 22 |
public static function render(): void { |
| 23 |
$is_pro = Phpinfo_WP_License::is_valid(); |
| 24 |
$plugin_basename = plugin_basename(PHPINFOWP_DIR . 'phpinfo-wp.php'); |
| 25 |
|
| 26 |
$free_features = [ |
| 27 |
'phpinfo() viewer + .htaccess editor', |
| 28 |
'PHP EOL Timeline + admin-bar PHP version warning', |
| 29 |
'Config Grader summary (A–F grade)', |
| 30 |
'Troubleshooting Mode (per-user safe-mode with one-click undo)', |
| 31 |
'PHP Compatibility Scanner (catches plugin breakages before PHP upgrades)', |
| 32 |
'Pre-update PHP-version warnings on the Plugins screen', |
| 33 |
'Activity Log of every config change', |
| 34 |
]; |
| 35 |
$pro_features = [ |
| 36 |
'White-label PDF Audit Report (your branding, hand to clients)', |
| 37 |
'One-click Config Auto-Fix with rollback', |
| 38 |
'Security Headers + SSL Certificate monitors', |
| 39 |
'OPcache Dashboard + PHP Error Log viewer', |
| 40 |
'Database Health + Autoload bloat detection', |
| 41 |
'WP-Cron Monitor + orphan-hook purge', |
| 42 |
'Email Alerts, Weekly Digest, Slack/Discord webhooks', |
| 43 |
'Multi-site (Network) support', |
| 44 |
]; |
| 45 |
?> |
| 46 |
<div id="phpinfowp-deactivate-modal" class="phpinfowp-dm" aria-hidden="true" role="dialog" aria-labelledby="phpinfowp-dm-title"> |
| 47 |
<div class="phpinfowp-dm-backdrop"></div> |
| 48 |
<div class="phpinfowp-dm-dialog" role="document"> |
| 49 |
<button type="button" class="phpinfowp-dm-close" aria-label="Close">×</button> |
| 50 |
|
| 51 |
<div class="phpinfowp-dm-header"> |
| 52 |
<img src="<?php echo esc_url(PHPINFOWP_URL . 'assets/icon-128x128.png'); ?>" |
| 53 |
alt="" width="48" height="48" class="phpinfowp-dm-logo" |
| 54 |
onerror="this.style.display='none'"> |
| 55 |
<div> |
| 56 |
<h2 id="phpinfowp-dm-title" class="phpinfowp-dm-title">Before you deactivate phpinfo() WP…</h2> |
| 57 |
<p class="phpinfowp-dm-sub"><?php _e('Here\'s what stops working the moment this plugin goes off:', 'phpinfo-wp'); ?></p> |
| 58 |
</div> |
| 59 |
</div> |
| 60 |
|
| 61 |
<div class="phpinfowp-dm-body"> |
| 62 |
<div class="phpinfowp-dm-col"> |
| 63 |
<div class="phpinfowp-dm-col-label"><?php _e('You\'ll lose (Free)', 'phpinfo-wp'); ?></div> |
| 64 |
<ul class="phpinfowp-dm-list"> |
| 65 |
<?php foreach ($free_features as $f): ?> |
| 66 |
<li><span class="dashicons dashicons-no-alt phpinfowp-dm-x"></span><?php echo esc_html($f); ?></li> |
| 67 |
<?php endforeach; ?> |
| 68 |
</ul> |
| 69 |
</div> |
| 70 |
<?php if ($is_pro): ?> |
| 71 |
<div class="phpinfowp-dm-col phpinfowp-dm-col-pro"> |
| 72 |
<div class="phpinfowp-dm-col-label"><?php _e('You\'ll also lose (Pro)', 'phpinfo-wp'); ?></div> |
| 73 |
<ul class="phpinfowp-dm-list"> |
| 74 |
<?php foreach ($pro_features as $f): ?> |
| 75 |
<li><span class="dashicons dashicons-no-alt phpinfowp-dm-x"></span><?php echo esc_html($f); ?></li> |
| 76 |
<?php endforeach; ?> |
| 77 |
</ul> |
| 78 |
</div> |
| 79 |
<?php endif; ?> |
| 80 |
</div> |
| 81 |
|
| 82 |
<div class="phpinfowp-dm-warn"> |
| 83 |
<span class="dashicons dashicons-warning"></span> |
| 84 |
<div> |
| 85 |
Deactivating <strong><?php _e('doesn\'t delete', 'phpinfo-wp'); ?></strong> your settings or logs — you can re-activate any time. |
| 86 |
<?php if ($is_pro): ?> |
| 87 |
Your license stays on this site; Pro features simply pause while the plugin is off. |
| 88 |
<?php else: ?> |
| 89 |
But your site will lose the safety net the moment it's off. |
| 90 |
<?php endif; ?> |
| 91 |
</div> |
| 92 |
</div> |
| 93 |
|
| 94 |
<div class="phpinfowp-dm-actions"> |
| 95 |
<button type="button" class="button button-primary button-large phpinfowp-dm-keep"><?php _e('Keep it active', 'phpinfo-wp'); ?></button> |
| 96 |
<a href="#" class="phpinfowp-dm-confirm" data-href=""><?php _e('Deactivate anyway →', 'phpinfo-wp'); ?></a> |
| 97 |
</div> |
| 98 |
</div> |
| 99 |
</div> |
| 100 |
|
| 101 |
<script> |
| 102 |
(function () { |
| 103 |
var slug = <?php echo wp_json_encode($plugin_basename); ?>; |
| 104 |
var modal = document.getElementById('phpinfowp-deactivate-modal'); |
| 105 |
if (!modal) return; |
| 106 |
var confirmLink = modal.querySelector('.phpinfowp-dm-confirm'); |
| 107 |
var closeBtns = modal.querySelectorAll('.phpinfowp-dm-close, .phpinfowp-dm-keep, .phpinfowp-dm-backdrop'); |
| 108 |
|
| 109 |
function open(url) { |
| 110 |
confirmLink.setAttribute('href', url); |
| 111 |
modal.classList.add('is-open'); |
| 112 |
modal.setAttribute('aria-hidden', 'false'); |
| 113 |
document.body.style.overflow = 'hidden'; |
| 114 |
} |
| 115 |
function close() { |
| 116 |
modal.classList.remove('is-open'); |
| 117 |
modal.setAttribute('aria-hidden', 'true'); |
| 118 |
document.body.style.overflow = ''; |
| 119 |
} |
| 120 |
closeBtns.forEach(function (b) { b.addEventListener('click', close); }); |
| 121 |
document.addEventListener('keydown', function (e) { |
| 122 |
if (e.key === 'Escape' && modal.classList.contains('is-open')) close(); |
| 123 |
}); |
| 124 |
|
| 125 |
// Match WP's deactivate link: tr[data-plugin="…/phpinfo-wp.php"] .deactivate a |
| 126 |
// The slug differs between standard and must-use installs, so we |
| 127 |
// also match the action=deactivate URL fragment as a safety net. |
| 128 |
document.addEventListener('click', function (e) { |
| 129 |
var a = e.target.closest && e.target.closest('a'); |
| 130 |
if (!a) return; |
| 131 |
var href = a.getAttribute('href') || ''; |
| 132 |
if (!/action=deactivate/.test(href)) return; |
| 133 |
var row = a.closest('tr[data-plugin]'); |
| 134 |
var matchesRow = row && row.getAttribute('data-plugin') === slug; |
| 135 |
var matchesUrl = href.indexOf(encodeURIComponent(slug)) !== -1; |
| 136 |
if (!matchesRow && !matchesUrl) return; |
| 137 |
if (a.closest('.phpinfowp-dm')) return; |
| 138 |
e.preventDefault(); |
| 139 |
open(href); |
| 140 |
}, true); |
| 141 |
}()); |
| 142 |
</script> |
| 143 |
<?php |
| 144 |
} |
| 145 |
} |
| 146 |
|