PluginProbe
phpinfo() WP – Site Health, PHP Compatibility & Server Audit / 7.2.5
phpinfo() WP – Site Health, PHP Compatibility & Server Audit v7.2.5
7.2.7 7.2.6 7.2.5 7.2.4 7.2.3 7.2.0 7.2.1 7.2.2 7.1.0 7.0.3 7.0.4 7.0.5 trunk 6.0 7.0.0 7.0.1 7.0.2
phpinfo-wp / views / pro / report.php

report.php in phpinfo() WP – Site Health, PHP Compatibility & Server Audit 7.2.5, at views/pro/report.php

711 lines 47.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined('ABSPATH') or die('Unauthorized Access');
3
4 $is_pro = Phpinfo_WP_License::is_valid();
5 $is_unlimited = Phpinfo_WP_License::is_unlimited();
6 $is_free_preview = !$is_pro;
7
8 // Handle branding form save before building report
9 if (isset($_POST['phpinfowp_report_branding']) && check_admin_referer('phpinfowp_report_branding')) {
10 Phpinfo_WP_Report::save_branding([
11 'enabled' => isset($_POST['branding_enabled']),
12 'company' => $_POST['branding_company'] ?? '',
13 'tagline' => $_POST['branding_tagline'] ?? '',
14 'footer_note' => $_POST['branding_footer_note'] ?? '',
15 'accent' => $_POST['branding_accent'] ?? '#777BB3',
16 'logo_id' => (int) ($_POST['branding_logo_id'] ?? 0),
17 'logo_url' => $_POST['branding_logo_url'] ?? '',
18 ]);
19 echo '<div class="notice notice-success inline" style="margin:0 0 20px"><p>Report branding saved.</p></div>';
20 }
21
22 // Media library is needed for the logo picker. Only loaded on this view.
23 wp_enqueue_media();
24
25 $r = Phpinfo_WP_Report::build();
26
27 $b = $r['branding'];
28 $is_unlimited = Phpinfo_WP_License::is_unlimited();
29 $accent = $is_unlimited && $b['enabled'] && $b['accent'] ? esc_attr($b['accent']) : '#777BB3';
30 $brand_name = $is_unlimited && $b['enabled'] && $b['company'] ? esc_html($b['company']) : 'phpinfo() WP';
31 $brand_tag = $is_unlimited && $b['enabled'] && $b['tagline'] ? esc_html($b['tagline']) : 'Server Health Audit';
32 ?>
33
34 <style>.phpinfowp-report .phpinfowp-report-brand,.phpinfowp-report .phpinfowp-report-h{color:<?php echo $accent; ?> !important}.phpinfowp-report .phpinfowp-report-cover{border-bottom-color:<?php echo $accent; ?> !important}</style>
35
36 <div class="phpinfowp-pro-page">
37
38 <?php if (isset($_GET['trial_activated']) && $_GET['trial_activated'] == 1): ?>
39 <div class="notice notice-success inline is-dismissible" style="margin:0 0 20px">
40 <p><strong><?php _e('Success!', 'phpinfo-wp'); ?></strong> <?php _e('Your 7-day Pro Trial is now active. All Pro features are unlocked.', 'phpinfo-wp'); ?></p>
41 </div>
42 <?php endif; ?>
43
44 <?php if ($is_free_preview): ?>
45 <div class="phpinfowp-price-alert-banner" style="position:relative; background:#fff3cd; border-left:4px solid #ffc107; padding:12px 40px 12px 16px; margin: 0 0 24px; border-radius: 0 4px 4px 0; font-size:13.5px; color:#664d03; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px; box-shadow:0 1px 2px rgba(0,0,0,0.02);">
46 <button type="button" onclick="try{localStorage.setItem('phpinfowp_price_alert_dismissed','1')}catch(e){} this.closest('.phpinfowp-price-alert-banner').style.display='none';" aria-label="<?php esc_attr_e('Dismiss', 'phpinfo-wp'); ?>" style="position:absolute; top:6px; right:8px; background:none; border:none; cursor:pointer; color:#664d03; opacity:0.5; font-size:20px; line-height:1; padding:2px 4px;" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.5'">&times;</button>
47 <span>
48 <strong> <?php _e('Price Increase Alert:', 'phpinfo-wp'); ?></strong>
49 <?php _e('On August 31st, the Single Site Pro license increases from $29 to $39/year. Upgrade today to secure the current $29/year rate before the price goes up.', 'phpinfo-wp'); ?>
50 </span>
51 <a href="https://exeebit.com/phpinfo-wp#pricing" target="_blank" rel="noopener" style="background:#777BB3; color:#fff; padding:6px 14px; border-radius:4px; font-size:12.5px; font-weight:600; text-decoration:none; display:inline-block;">
52 <?php _e('Lock in $29 Now →', 'phpinfo-wp'); ?>
53 </a>
54 </div>
55 <script>if(localStorage.getItem('phpinfowp_price_alert_dismissed')==='1'){document.querySelectorAll('.phpinfowp-price-alert-banner').forEach(function(e){e.style.display='none';});}</script>
56 <?php endif; ?>
57
58 <div class="phpinfowp-page-header no-print">
59 <div>
60 <h1>Audit Report <span class="phpinfowp-pro-badge"><?php _e('PRO', 'phpinfo-wp'); ?></span></h1>
61 <p class="phpinfowp-page-subtitle"><?php _e('A single-page server health report — print to PDF or share with clients', 'phpinfo-wp'); ?></p>
62 </div>
63 <div>
64 <?php if ($is_free_preview): ?>
65 <button type="button" class="button" disabled style="opacity: 0.6; cursor: not-allowed;">
66 <span class="dashicons dashicons-lock" style="vertical-align:middle"></span> <?php _e('White-label', 'phpinfo-wp'); ?>
67 </button>
68 <button type="button" class="button button-primary" disabled style="opacity: 0.6; cursor: not-allowed;">
69 <span class="dashicons dashicons-lock" style="vertical-align:middle"></span> <?php _e('Print / Save as PDF', 'phpinfo-wp'); ?>
70 </button>
71 <?php else: ?>
72 <button type="button" class="button" onclick="document.getElementById('phpinfowp-report-branding').style.display='block';return false;">
73 <span class="dashicons dashicons-admin-customizer" style="vertical-align:middle"></span> <?php _e('White-label', 'phpinfo-wp'); ?>
74 </button>
75 <button type="button" class="button button-primary" onclick="window.print()">
76 <span class="dashicons dashicons-printer" style="vertical-align:middle"></span> <?php _e('Print / Save as PDF', 'phpinfo-wp'); ?>
77 </button>
78 <?php endif; ?>
79 </div>
80 </div>
81
82 <!-- White-label branding form. Always hidden on initial render; the
83 "White-label" button in the page header toggles it open. The form
84 stays hidden after save so it doesn't crowd the report — the
85 success notice above confirms the change was applied. -->
86 <div id="phpinfowp-report-branding" class="phpinfowp-report-branding-form no-print" style="display:none">
87 <?php if (!Phpinfo_WP_License::is_unlimited()): ?>
88 <div class="phpinfowp-upgrade-banner" style="background:#fcfaff;border:1px solid #e2d9f3;border-radius:8px;padding:24px;text-align:center;margin-bottom:12px">
89 <span class="dashicons dashicons-lock" style="font-size:36px;width:36px;height:36px;color:#7c3aed;margin-bottom:12px"></span>
90 <h3 style="margin:0 0 8px;font-size:18px;color:#2c3338"><?php _e('White-Label Branding is an Unlimited Tier Feature', 'phpinfo-wp'); ?></h3>
91 <p style="margin:0 0 16px;color:#646970;font-size:14px"><?php _e('Upload your own company logo, set a custom accent color, and hide the "phpinfo() WP" branding from client PDF reports.', 'phpinfo-wp'); ?></p>
92 <a href="https://exeebit.com/phpinfo-wp#pricing" target="_blank" class="button button-primary button-large" style="background:#7c3aed;border-color:#7c3aed"><?php _e('Upgrade License ', 'phpinfo-wp'); ?></a>
93 </div>
94 <?php else: ?>
95 <form method="post">
96 <?php wp_nonce_field('phpinfowp_report_branding'); ?>
97 <input type="hidden" name="phpinfowp_report_branding" value="1">
98 <h3><?php _e('Report Branding', 'phpinfo-wp'); ?></h3>
99 <p class="description"><?php _e('Customize the report cover and footer with your own brand. Toggle off to restore the default look.', 'phpinfo-wp'); ?></p>
100 <table class="form-table">
101 <tr>
102 <th><label for="branding_enabled"><?php _e('Enable white-label', 'phpinfo-wp'); ?></label></th>
103 <td><label><input type="checkbox" id="branding_enabled" name="branding_enabled" value="1" <?php checked($b['enabled']); ?>> Use my branding instead of "phpinfo() WP"</label></td>
104 </tr>
105 <tr>
106 <th><label for="branding_company"><?php _e('Company name', 'phpinfo-wp'); ?></label></th>
107 <td><input type="text" id="branding_company" name="branding_company" class="regular-text" value="<?php echo esc_attr($b['company']); ?>" placeholder="<?php echo esc_attr__('Acme Digital Studio', 'phpinfo-wp'); ?>"></td>
108 </tr>
109 <tr>
110 <th><label for="branding_tagline"><?php _e('Report title', 'phpinfo-wp'); ?></label></th>
111 <td><input type="text" id="branding_tagline" name="branding_tagline" class="regular-text" value="<?php echo esc_attr($b['tagline']); ?>" placeholder="<?php echo esc_attr__('Quarterly Site Health Audit', 'phpinfo-wp'); ?>"></td>
112 </tr>
113 <tr>
114 <th><label for="branding_footer_note"><?php _e('Footer note', 'phpinfo-wp'); ?></label></th>
115 <td><textarea id="branding_footer_note" name="branding_footer_note" class="regular-text" rows="2" placeholder="<?php echo esc_attr__('Prepared by Acme Digital Studio · support@acme.com', 'phpinfo-wp'); ?>"><?php echo esc_textarea($b['footer_note']); ?></textarea></td>
116 </tr>
117 <tr>
118 <th><label><?php _e('Company logo', 'phpinfo-wp'); ?></label></th>
119 <td>
120 <div class="phpinfowp-logo-picker" id="phpinfowp-logo-picker">
121 <div class="phpinfowp-logo-preview" id="phpinfowp-logo-preview">
122 <?php if (!empty($b['logo_url'])): ?>
123 <img src="<?php echo esc_url($b['logo_url']); ?>" alt="Company logo">
124 <?php else: ?>
125 <span class="phpinfowp-logo-empty"><?php _e('No logo set', 'phpinfo-wp'); ?></span>
126 <?php endif; ?>
127 </div>
128 <div class="phpinfowp-logo-actions">
129 <button type="button" class="button" id="phpinfowp-logo-choose">
130 <?php echo !empty($b['logo_url']) ? 'Change logo' : 'Upload logo'; ?>
131 </button>
132 <button type="button" class="button button-link-delete" id="phpinfowp-logo-remove" <?php echo empty($b['logo_url']) ? 'style="display:none"' : ''; ?>>
133 Remove
134 </button>
135 <p class="description" style="margin-top:6px">
136 PNG or SVG with a transparent background works best. Renders on the report cover at about 60×60.
137 </p>
138 </div>
139 </div>
140 <input type="hidden" id="branding_logo_id" name="branding_logo_id" value="<?php echo (int) ($b['logo_id'] ?? 0); ?>">
141 <input type="hidden" id="branding_logo_url" name="branding_logo_url" value="<?php echo esc_attr($b['logo_url']); ?>">
142 </td>
143 </tr>
144 <tr>
145 <th><label for="branding_accent"><?php _e('Accent color', 'phpinfo-wp'); ?></label></th>
146 <td><input type="color" id="branding_accent" name="branding_accent" value="<?php echo esc_attr($b['accent']); ?>"></td>
147 </tr>
148 </table>
149 <p><button type="submit" class="button button-primary"><?php _e('Save branding', 'phpinfo-wp'); ?></button></p>
150 </form>
151 <?php endif; ?>
152 </div>
153
154 <script>
155 /* Logo picker wiring.
156 Previous version bailed out at script-parse time if `wp.media` wasn't
157 defined yet but wp_enqueue_media() prints its JS in the admin footer,
158 so wp.media is undefined while this script is being parsed and the
159 guard silently disabled the entire feature. Fix: always attach the
160 click handler; check wp.media at click time (footer has loaded by then). */
161 (function () {
162 var idField = document.getElementById('branding_logo_id');
163 var urlField = document.getElementById('branding_logo_url');
164 var preview = document.getElementById('phpinfowp-logo-preview');
165 var chooseBtn = document.getElementById('phpinfowp-logo-choose');
166 var removeBtn = document.getElementById('phpinfowp-logo-remove');
167 if (!chooseBtn || !idField || !urlField || !preview) return;
168 var frame;
169
170 function setLogo(id, url) {
171 idField.value = id;
172 urlField.value = url;
173 preview.innerHTML = url
174 ? '<img src="' + url + '" alt="Company logo">'
175 : '<span class="phpinfowp-logo-empty"><?php _e('No logo set', 'phpinfo-wp'); ?></span>';
176 chooseBtn.textContent = url ? 'Change logo' : 'Upload logo';
177 if (removeBtn) removeBtn.style.display = url ? '' : 'none';
178 }
179
180 chooseBtn.addEventListener('click', function (e) {
181 e.preventDefault();
182 if (!window.wp || !wp.media) {
183 window.alert('The WordPress media library is still loading. Wait one second and try again.');
184 return;
185 }
186 if (frame) { frame.open(); return; }
187 frame = wp.media({
188 title: 'Choose company logo',
189 button: { text: 'Use this logo' },
190 library: { type: ['image'] },
191 multiple: false
192 });
193 frame.on('select', function () {
194 var att = frame.state().get('selection').first().toJSON();
195 var url = (att.sizes && att.sizes.medium && att.sizes.medium.url) || att.url;
196 setLogo(att.id, url);
197 });
198 frame.open();
199 });
200
201 if (removeBtn) {
202 removeBtn.addEventListener('click', function (e) {
203 e.preventDefault();
204 setLogo(0, '');
205 });
206 }
207 }());
208 </script>
209
210 <div class="phpinfowp-report">
211
212 <?php
213 $overall = $r['overall'];
214 $crit_n = count($r['criticals']);
215 $warn_n = count($r['warnings']);
216
217 // Helper to render a verdict pill inline anywhere.
218 $verdict_pill = static function (string $v, string $text = '') {
219 $svg_ok = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width:12px;height:12px;display:inline-block;vertical-align:-1px"><path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd"/></svg>';
220 $svg_warn = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width:12px;height:12px;display:inline-block;vertical-align:-1px"><path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a.75.75 0 100-1.5.75.75 0 000 1.5z" clip-rule="evenodd"/></svg>';
221 $svg_critical = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width:12px;height:12px;display:inline-block;vertical-align:-1px"><path fill-rule="evenodd" d="M4.22 4.22a.75.75 0 011.06 0L10 8.94l4.72-4.72a.75.75 0 111.06 1.06L11.06 10l4.72 4.72a.75.75 0 11-1.06 1.06L10 11.06l-4.72 4.72a.75.75 0 01-1.06-1.06L8.94 10 4.22 5.28a.75.75 0 010-1.06z" clip-rule="evenodd"/></svg>';
222
223 $map = [
224 'ok' => ['ok', '#0a7d2e', '#e8f6ec', '#a7d8b3', $svg_ok],
225 'warn' => ['warn', '#8a6500', '#fff5d6', '#e9c977', $svg_warn],
226 'critical' => ['critical', '#8e1414', '#fbe7e8', '#ecb4b5', $svg_critical],
227 ];
228 [$cls, $fg, $bg, $br, $ic] = $map[$v] ?? $map['ok'];
229 $label = $text ?: ($v === 'ok' ? 'OK' : ($v === 'warn' ? 'Warning' : 'Critical'));
230 return sprintf(
231 '<span class="phpinfowp-rv phpinfowp-rv-%s" style="color:%s;background:%s;border-color:%s"><span class="phpinfowp-rv-ic">%s</span>%s</span>',
232 $cls, $fg, $bg, $br, $ic, esc_html($label)
233 );
234 };
235
236 // Small helpers to classify a value into a verdict.
237 $verdict_eol = static function (array $eol) {
238 if (($eol['status'] ?? '') === 'eol') return 'critical';
239 if (($eol['status'] ?? '') === 'warning' && (int) ($eol['days'] ?? 0) < 90) return 'critical';
240 if (($eol['status'] ?? '') === 'warning') return 'warn';
241 return 'ok';
242 };
243 $verdict_score = static function (?int $s) {
244 if ($s === null) return 'ok';
245 if ($s < 50) return 'critical';
246 if ($s < 80) return 'warn';
247 return 'ok';
248 };
249 $verdict_hit = static function ($rate) {
250 $rate = (float) $rate;
251 if ($rate < 50) return 'critical';
252 if ($rate < 90) return 'warn';
253 return 'ok';
254 };
255 ?>
256
257 <!-- Cover -->
258 <div class="phpinfowp-report-cover">
259 <?php if ($is_unlimited && $b['enabled'] && !empty($b['logo_url'])): ?>
260 <img src="<?php echo esc_url($b['logo_url']); ?>" alt="<?php echo esc_attr($brand_name); ?> logo"
261 class="phpinfowp-report-logo">
262 <?php endif; ?>
263 <div class="phpinfowp-report-brand"><?php echo $brand_name; ?></div>
264 <h2 class="phpinfowp-report-title"><?php echo $brand_tag; ?></h2>
265 <div class="phpinfowp-report-site"><?php echo esc_html($r['site']); ?></div>
266 <div class="phpinfowp-report-url"><?php echo esc_html($r['url']); ?></div>
267 <div class="phpinfowp-report-date">Generated <?php echo esc_html(wp_date('F j, Y · H:i', $r['generated_at'])); ?></div>
268 </div>
269
270 <!-- Overall site health (one number, page-1 hero) -->
271 <div class="phpinfowp-report-overall">
272 <div class="phpinfowp-report-overall-donut">
273 <?php echo Phpinfo_WP_Report::render_donut(
274 $overall['score'], 180, $overall['verdict_color'], '#eef0f4', 14
275 ); ?>
276 <div class="phpinfowp-report-overall-num">
277 <div class="phpinfowp-report-overall-num-big" style="color:<?php echo esc_attr($overall['verdict_color']); ?>"><?php echo (int) $overall['score']; ?></div>
278 <div class="phpinfowp-report-overall-num-sm">/ 100</div>
279 </div>
280 </div>
281 <div class="phpinfowp-report-overall-info">
282 <div class="phpinfowp-report-overall-label"><?php _e('Overall Site Health', 'phpinfo-wp'); ?></div>
283 <div class="phpinfowp-report-overall-verdict" style="color:<?php echo esc_attr($overall['verdict_color']); ?>"><?php echo esc_html($overall['verdict_label']); ?></div>
284 <div class="phpinfowp-report-overall-grade">Grade <strong><?php echo esc_html($overall['grade']); ?></strong></div>
285 <?php if ($crit_n || $warn_n): ?>
286 <div class="phpinfowp-report-overall-counts">
287 <?php if ($crit_n): ?><span class="phpinfowp-rc phpinfowp-rc-crit"><?php echo $crit_n; ?> critical</span><?php endif; ?>
288 <?php if ($warn_n): ?><span class="phpinfowp-rc phpinfowp-rc-warn"><?php echo $warn_n; ?> warning<?php echo $warn_n === 1 ? '' : 's'; ?></span><?php endif; ?>
289 </div>
290 <?php else: ?>
291 <div class="phpinfowp-report-overall-counts"><span class="phpinfowp-rc phpinfowp-rc-ok"><?php _e('No critical issues', 'phpinfo-wp'); ?></span></div>
292 <?php endif; ?>
293 <p class="phpinfowp-report-overall-note"><?php _e('A weighted score across PHP config, security headers, OPcache, SSL, PHP support window, and database health. <strong>Aim for 85+.</strong>', 'phpinfo-wp'); ?></p>
294 </div>
295 </div>
296
297 <!-- Category bar chart -->
298 <?php if (!empty($r['bars'])): ?>
299 <div class="phpinfowp-report-bars">
300 <h3 class="phpinfowp-report-h"><?php _e('Subscores', 'phpinfo-wp'); ?></h3>
301 <div class="phpinfowp-report-bars-grid">
302 <?php foreach ($r['bars'] as $bar):
303 [$bv, $bvl, $bvc] = Phpinfo_WP_Report::score_to_verdict((int) $bar['score']); ?>
304 <div class="phpinfowp-report-bar">
305 <div class="phpinfowp-report-bar-top">
306 <span class="phpinfowp-report-bar-lbl"><?php echo esc_html($bar['label']); ?></span>
307 <span class="phpinfowp-report-bar-num" style="color:<?php echo esc_attr($bvc); ?>"><?php echo (int) $bar['score']; ?></span>
308 </div>
309 <div class="phpinfowp-report-bar-track">
310 <div class="phpinfowp-report-bar-fill" style="width:<?php echo (int) max(2, $bar['score']); ?>%;background:<?php echo esc_attr($bvc); ?>"></div>
311 </div>
312 </div>
313 <?php endforeach; ?>
314 </div>
315 </div>
316 <?php endif; ?>
317
318 <?php if ($is_free_preview): ?>
319 <!-- Free preview: skeleton rows + gradient overlay no real data sent to browser -->
320 <div style="position:relative; margin-top:24px; overflow:hidden; border-radius:10px;">
321
322 <!-- Skeleton rows mimicking actual report content -->
323 <div style="pointer-events:none; user-select:none;">
324
325 <!-- Skeleton: critical issues bar -->
326 <div style="background:#fff5f5; border:1px solid #fecaca; border-radius:8px; padding:16px 20px; margin-bottom:16px; display:flex; align-items:center; gap:14px;">
327 <div style="width:32px; height:32px; border-radius:50%; background:#fee2e2; flex-shrink:0;"></div>
328 <div style="flex:1;">
329 <div style="height:13px; width:55%; background:#fecaca; border-radius:4px; margin-bottom:8px;"></div>
330 <div style="height:11px; width:80%; background:#fee2e2; border-radius:4px;"></div>
331 </div>
332 </div>
333
334 <!-- Skeleton: section label -->
335 <div style="height:11px; width:160px; background:#e2e8f0; border-radius:4px; margin:20px 0 12px; text-transform:uppercase;"></div>
336
337 <!-- Skeleton: priority card 1 -->
338 <div style="background:#fff; border:1px solid #e2e8f0; border-radius:8px; padding:18px 20px; margin-bottom:10px; display:flex; gap:16px; align-items:flex-start;">
339 <div style="width:28px; height:28px; border-radius:50%; background:#f1f5f9; flex-shrink:0;"></div>
340 <div style="flex:1;">
341 <div style="display:flex; gap:8px; margin-bottom:10px; align-items:center;">
342 <div style="height:20px; width:68px; background:#fecaca; border-radius:20px;"></div>
343 <div style="height:12px; width:90px; background:#e2e8f0; border-radius:4px;"></div>
344 </div>
345 <div style="height:13px; width:70%; background:#cbd5e1; border-radius:4px; margin-bottom:8px;"></div>
346 <div style="height:11px; width:95%; background:#e2e8f0; border-radius:4px; margin-bottom:6px;"></div>
347 <div style="height:11px; width:85%; background:#e2e8f0; border-radius:4px;"></div>
348 </div>
349 </div>
350
351 <!-- Skeleton: priority card 2 -->
352 <div style="background:#fff; border:1px solid #e2e8f0; border-radius:8px; padding:18px 20px; margin-bottom:10px; display:flex; gap:16px; align-items:flex-start;">
353 <div style="width:28px; height:28px; border-radius:50%; background:#f1f5f9; flex-shrink:0;"></div>
354 <div style="flex:1;">
355 <div style="display:flex; gap:8px; margin-bottom:10px; align-items:center;">
356 <div style="height:20px; width:60px; background:#fef3c7; border-radius:20px;"></div>
357 <div style="height:12px; width:110px; background:#e2e8f0; border-radius:4px;"></div>
358 </div>
359 <div style="height:13px; width:60%; background:#cbd5e1; border-radius:4px; margin-bottom:8px;"></div>
360 <div style="height:11px; width:90%; background:#e2e8f0; border-radius:4px;"></div>
361 </div>
362 </div>
363
364 <!-- Skeleton: priority card 3 -->
365 <div style="background:#fff; border:1px solid #e2e8f0; border-radius:8px; padding:18px 20px; margin-bottom:10px; display:flex; gap:16px; align-items:flex-start;">
366 <div style="width:28px; height:28px; border-radius:50%; background:#f1f5f9; flex-shrink:0;"></div>
367 <div style="flex:1;">
368 <div style="display:flex; gap:8px; margin-bottom:10px; align-items:center;">
369 <div style="height:20px; width:60px; background:#fef3c7; border-radius:20px;"></div>
370 <div style="height:12px; width:80px; background:#e2e8f0; border-radius:4px;"></div>
371 </div>
372 <div style="height:13px; width:75%; background:#cbd5e1; border-radius:4px; margin-bottom:8px;"></div>
373 <div style="height:11px; width:88%; background:#e2e8f0; border-radius:4px;"></div>
374 </div>
375 </div>
376
377 <!-- Skeleton: section divider rows -->
378 <div style="margin-top:20px; padding-top:16px; border-top:1px solid #e2e8f0;">
379 <div style="height:11px; width:140px; background:#e2e8f0; border-radius:4px; margin-bottom:14px;"></div>
380 <div style="display:flex; gap:12px; margin-bottom:10px;">
381 <div style="height:40px; flex:1; background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px;"></div>
382 <div style="height:40px; flex:1; background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px;"></div>
383 <div style="height:40px; flex:1; background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px;"></div>
384 </div>
385 <div style="height:11px; width:120px; background:#e2e8f0; border-radius:4px; margin-top:16px; margin-bottom:14px;"></div>
386 <div style="height:38px; background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px;"></div>
387 </div>
388 </div>
389
390 <!-- Gradient fade-out overlay -->
391 <div style="position:absolute; bottom:0; left:0; right:0; height:75%; background:linear-gradient(to bottom, rgba(246,249,252,0) 0%, rgba(246,249,252,0.92) 40%, #f6f9fc 100%); pointer-events:none;"></div>
392
393 <!-- Upgrade card floating over skeleton -->
394 <div style="position:absolute; bottom:24px; left:50%; transform:translateX(-50%); width:100%; max-width:520px; background:#fff; border:1px solid #e2e8f0; border-radius:10px; padding:32px 28px; box-shadow:0 8px 24px -6px rgba(0,0,0,0.08); text-align:center;">
395 <span class="dashicons dashicons-lock" style="font-size:36px; width:36px; height:36px; color:#777BB3; display:inline-block; margin-bottom:12px;"></span>
396 <h3 style="margin:0 0 8px; font-size:20px; font-weight:600; color:#1d2327;">Full diagnostics locked</h3>
397 <p style="margin:0 0 6px; font-size:13.5px; color:#475569; line-height:1.6; max-width:400px; margin-left:auto; margin-right:auto;">Unlock critical issues, security headers, SSL, OPcache, database health, and 1-click auto-fixes.</p>
398 <p style="margin:0 0 22px; font-size:12.5px; color:#94a3b8;">Your overall score above is real - the details explain exactly what to fix.</p>
399 <a href="https://exeebit.com/phpinfo-wp#pricing" target="_blank" rel="noopener" class="button button-primary button-large" style="background:#777BB3; border-color:#777BB3; height:42px; line-height:40px; font-size:14px; font-weight:600; padding:0 26px; border-radius:6px; text-decoration:none; display:inline-block;">Upgrade to Pro &rarr;</a>
400 </div>
401
402 <!-- Spacer so the floating card has room -->
403 <div style="height:220px;"></div>
404 </div>
405 <?php else: // Pro users get the full detail HTML below ?>
406
407 <!-- Critical issues banner -->
408 <?php if ($crit_n): ?>
409 <div class="phpinfowp-report-critbar">
410 <div class="phpinfowp-report-critbar-head">
411 <span class="phpinfowp-report-critbar-ic"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" style="width:16px;height:16px;display:block"><path fill-rule="evenodd" d="M4.22 4.22a.75.75 0 011.06 0L10 8.94l4.72-4.72a.75.75 0 111.06 1.06L11.06 10l4.72 4.72a.75.75 0 11-1.06 1.06L10 11.06l-4.72 4.72a.75.75 0 01-1.06-1.06L8.94 10 4.22 5.28a.75.75 0 010-1.06z" clip-rule="evenodd"/></svg></span>
412 <div>
413 <div class="phpinfowp-report-critbar-title"><?php echo $crit_n; ?> critical issue<?php echo $crit_n === 1 ? '' : 's'; ?> need immediate attention</div>
414 <div class="phpinfowp-report-critbar-sub"><?php _e('Anything here is impacting security, performance, or breaks within the next 90 days.', 'phpinfo-wp'); ?></div>
415 </div>
416 </div>
417 <ul class="phpinfowp-report-critbar-list">
418 <?php foreach ($r['criticals'] as $c): ?>
419 <li>
420 <strong><?php echo esc_html($c['title']); ?></strong>
421 <span class="phpinfowp-report-critbar-cat"><?php echo esc_html($c['category']); ?></span>
422 <div class="phpinfowp-report-critbar-reason"><?php echo esc_html($c['reason']); ?></div>
423 </li>
424 <?php endforeach; ?>
425 </ul>
426 </div>
427 <?php endif; ?>
428
429 <!-- Top 3 actions this week -->
430 <?php if (!empty($r['priorities'])): ?>
431 <h3 class="phpinfowp-report-h phpinfowp-report-h-page2">Top <?php echo count($r['priorities']); ?> action<?php echo count($r['priorities']) === 1 ? '' : 's'; ?> this week</h3>
432 <p class="phpinfowp-report-h-sub"><?php _e('Tackle these in order. Each card explains why it matters and how to fix it.', 'phpinfo-wp'); ?></p>
433 <ol class="phpinfowp-report-priorities">
434 <?php foreach ($r['priorities'] as $i => $p): ?>
435 <li class="phpinfowp-report-priority phpinfowp-report-priority-<?php echo esc_attr($p['urgency']); ?>">
436 <div class="phpinfowp-report-priority-num"><?php echo $i + 1; ?></div>
437 <div class="phpinfowp-report-priority-body">
438 <div class="phpinfowp-report-priority-head">
439 <?php echo $verdict_pill($p['urgency'], $p['urgency'] === 'critical' ? 'Critical' : 'High'); ?>
440 <span class="phpinfowp-report-priority-cat"><?php echo esc_html($p['category']); ?></span>
441 </div>
442 <div class="phpinfowp-report-priority-title"><?php echo esc_html($p['title']); ?></div>
443 <div class="phpinfowp-report-priority-why">
444 <strong><?php _e('Why it matters:', 'phpinfo-wp'); ?></strong>
445 <?php echo esc_html($p['reason']); ?>
446 </div>
447 <div class="phpinfowp-report-priority-how">
448 <strong><?php _e('How to fix:', 'phpinfo-wp'); ?></strong>
449 <?php echo wp_kses($p['how'], ['code' => [], 'strong' => [], 'em' => []]); ?>
450 </div>
451 </div>
452 </li>
453 <?php endforeach; ?>
454 </ol>
455 <?php else: ?>
456 <h3 class="phpinfowp-report-h"><?php _e('No urgent actions', 'phpinfo-wp'); ?></h3>
457 <p><?php _e('The site is in good shape. Keep an eye on the subscores above and review again next month.', 'phpinfo-wp'); ?></p>
458 <?php endif; ?>
459
460 <!-- Snapshot grid with verdict pills + plain-English captions -->
461 <h3 class="phpinfowp-report-h"><?php _e('Snapshot', 'phpinfo-wp'); ?></h3>
462 <div class="phpinfowp-report-grid">
463 <div class="phpinfowp-report-stat">
464 <div class="phpinfowp-report-stat-label">PHP version <?php echo $verdict_pill($verdict_eol($r['eol'])); ?></div>
465 <div class="phpinfowp-report-stat-value"><?php echo esc_html($r['php']); ?></div>
466 <div class="phpinfowp-report-stat-sub">
467 <?php if ($r['eol']['status'] === 'eol'): ?>
468 End of life
469 <?php elseif ($r['eol']['status'] === 'warning'): ?>
470 EOL in <?php echo (int) $r['eol']['days']; ?> days (<?php echo esc_html($r['eol']['eol']); ?>)
471 <?php else: ?>
472 Supported until <?php echo esc_html($r['eol']['eol']); ?>
473 <?php endif; ?>
474 </div>
475 <div class="phpinfowp-report-stat-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('php_version')); ?></div>
476 </div>
477 <div class="phpinfowp-report-stat">
478 <div class="phpinfowp-report-stat-label">WordPress <?php echo $verdict_pill(($r['extras']['updates']['core'] ?? 0) > 0 ? 'warn' : 'ok'); ?></div>
479 <div class="phpinfowp-report-stat-value"><?php echo esc_html($r['wp']); ?></div>
480 <div class="phpinfowp-report-stat-sub">
481 <?php if (($r['extras']['updates']['core'] ?? 0) > 0): ?>
482 Update available
483 <?php else: ?>
484 Up to date
485 <?php endif; ?>
486 </div>
487 <div class="phpinfowp-report-stat-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('wp_version')); ?></div>
488 </div>
489 <div class="phpinfowp-report-stat">
490 <div class="phpinfowp-report-stat-label">PHP Config <?php echo $verdict_pill($verdict_score((int) ($r['grader']['score'] ?? 0))); ?></div>
491 <div class="phpinfowp-report-stat-value"><?php echo esc_html($r['grader']['grade']); ?> <span class="phpinfowp-report-stat-value-sub"><?php echo (int) $r['grader']['score']; ?>/100</span></div>
492 <div class="phpinfowp-report-stat-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('config_grade')); ?></div>
493 </div>
494 <?php if (!isset($r['headers']['error'])): ?>
495 <div class="phpinfowp-report-stat">
496 <div class="phpinfowp-report-stat-label">Security Headers <?php echo $verdict_pill($verdict_score((int) ($r['headers']['score'] ?? 0))); ?></div>
497 <div class="phpinfowp-report-stat-value"><?php echo esc_html($r['headers']['grade']); ?> <span class="phpinfowp-report-stat-value-sub"><?php echo (int) $r['headers']['score']; ?>/100</span></div>
498 <div class="phpinfowp-report-stat-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('headers')); ?></div>
499 </div>
500 <?php endif; ?>
501 <?php if ($r['db']):
502 $db_v = !empty($r['db']['eol']) && ($db_ts = strtotime($r['db']['eol']))
503 ? (($db_ts - time()) / DAY_IN_SECONDS < 0 ? 'critical' : (($db_ts - time()) / DAY_IN_SECONDS < 90 ? 'critical' : (($db_ts - time()) / DAY_IN_SECONDS < 365 ? 'warn' : 'ok')))
504 : 'ok';
505 ?>
506 <div class="phpinfowp-report-stat">
507 <div class="phpinfowp-report-stat-label">Database <?php echo $verdict_pill($db_v); ?></div>
508 <div class="phpinfowp-report-stat-value"><?php echo esc_html($r['db']['engine'] . ' ' . $r['db']['version']); ?></div>
509 <div class="phpinfowp-report-stat-sub">
510 <?php if (!empty($r['db']['eol'])): ?>
511 EOL <?php echo esc_html($r['db']['eol']); ?>
512 <?php else: ?>
513 <?php echo size_format($r['db_size']['total']); ?> total
514 <?php endif; ?>
515 </div>
516 <div class="phpinfowp-report-stat-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('db_engine')); ?></div>
517 </div>
518 <?php endif; ?>
519 <?php if ($r['opcache']): ?>
520 <div class="phpinfowp-report-stat">
521 <div class="phpinfowp-report-stat-label">OPcache hit rate <?php echo $verdict_pill($verdict_hit($r['opcache']['hit_rate'] ?? 0)); ?></div>
522 <div class="phpinfowp-report-stat-value"><?php echo $r['opcache']['hit_rate'] !== null ? esc_html($r['opcache']['hit_rate']) . '%' : ''; ?></div>
523 <div class="phpinfowp-report-stat-sub"><?php _e('Target 95% +', 'phpinfo-wp'); ?></div>
524 <div class="phpinfowp-report-stat-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('opcache_hit')); ?></div>
525 </div>
526 <?php endif; ?>
527 </div>
528
529 <!-- Site configuration (new section) -->
530 <?php $ex = $r['extras']; ?>
531 <h3 class="phpinfowp-report-h"><?php _e('Site configuration', 'phpinfo-wp'); ?></h3>
532 <table class="phpinfowp-report-table">
533 <tbody>
534 <tr>
535 <td style="width:40%"><?php _e('HTTPS', 'phpinfo-wp'); ?></td>
536 <td><?php echo $ex['https']['is_https'] && !$ex['https']['mixed_risk']
537 ? $verdict_pill('ok', 'Fully on HTTPS')
538 : $verdict_pill('warn', $ex['https']['is_https'] ? 'Mixed-content risk' : 'Not on HTTPS'); ?></td>
539 </tr>
540 <tr>
541 <td>WP_DEBUG_DISPLAY <span class="phpinfowp-report-inline-cap">(<?php echo esc_html(Phpinfo_WP_Report::plain_english('wp_debug_display')); ?>)</span></td>
542 <td>
543 <?php if ($ex['wp_debug']['debug_display']): ?>
544 <?php echo $verdict_pill('critical', 'On — errors visible to visitors'); ?>
545 <?php else: ?>
546 <?php echo $verdict_pill('ok', 'Off (safe)'); ?>
547 <?php endif; ?>
548 </td>
549 </tr>
550 <tr>
551 <td><?php _e('Pending updates', 'phpinfo-wp'); ?></td>
552 <td>
553 <?php
554 $u = $ex['updates'];
555 $tot = $u['core'] + $u['plugins'] + $u['themes'];
556 if ($tot === 0) {
557 echo $verdict_pill('ok', 'All up to date');
558 } else {
559 $bits = [];
560 if ($u['core']) $bits[] = 'WP core';
561 if ($u['plugins']) $bits[] = "{$u['plugins']} plugin" . ($u['plugins'] === 1 ? '' : 's');
562 if ($u['themes']) $bits[] = "{$u['themes']} theme" . ($u['themes'] === 1 ? '' : 's');
563 echo $verdict_pill($u['core'] ? 'warn' : 'warn', implode(' · ', $bits) . ' pending');
564 }
565 ?>
566 </td>
567 </tr>
568 <tr>
569 <td><?php _e('Backup plugin', 'phpinfo-wp'); ?></td>
570 <td>
571 <?php if ($ex['backup']['detected']): ?>
572 <?php echo $verdict_pill('ok', esc_html($ex['backup']['plugin']) . ' detected'); ?>
573 <?php else: ?>
574 <?php echo $verdict_pill('warn', 'None detected'); ?>
575 <?php endif; ?>
576 </td>
577 </tr>
578 <?php foreach ($ex['permissions'] as $name => $info):
579 if (empty($info['exists'])) continue; ?>
580 <tr>
581 <td><code><?php echo esc_html($name); ?></code> permissions</td>
582 <td>
583 <?php echo $verdict_pill($info['verdict'], $info['perms']); ?>
584 <?php if ($info['verdict'] !== 'ok'): ?>
585 <span class="phpinfowp-report-inline-cap"> consider <code>chmod <?php echo $name === 'wp-config.php' ? '600' : '644'; ?> <?php echo esc_html($name); ?></code></span>
586 <?php endif; ?>
587 </td>
588 </tr>
589 <?php endforeach; ?>
590 </tbody>
591 </table>
592
593 <!-- Failing config checks -->
594 <?php $failing = array_filter($r['grader']['checks'], function ($c) {
595 return $c['status'] === 'fail';
596 }); ?>
597 <?php if ($failing): ?>
598 <h3 class="phpinfowp-report-h">Config issues (<?php echo count($failing); ?>)</h3>
599 <table class="phpinfowp-report-table">
600 <thead><tr><th><?php _e('Directive', 'phpinfo-wp'); ?></th><th><?php _e('Current', 'phpinfo-wp'); ?></th><th><?php _e('Recommended', 'phpinfo-wp'); ?></th><th style="width:90px"><?php _e('Verdict', 'phpinfo-wp'); ?></th></tr></thead>
601 <tbody>
602 <?php foreach ($failing as $c): ?>
603 <tr>
604 <td><code><?php echo esc_html($c['key']); ?></code><?php if (!empty($c['why'])): ?><div class="phpinfowp-report-inline-cap"><?php echo esc_html($c['why']); ?></div><?php endif; ?></td>
605 <td style="color:#8e1414"><?php echo esc_html($c['value']); ?></td>
606 <td style="color:#0a7d2e"><?php echo esc_html($c['good']); ?></td>
607 <td><?php echo $verdict_pill('critical', 'Fix'); ?></td>
608 </tr>
609 <?php endforeach; ?>
610 </tbody>
611 </table>
612 <?php endif; ?>
613
614 <!-- Missing security headers -->
615 <?php if (!isset($r['headers']['error'])):
616 $missing = array_filter($r['headers']['results'], function ($h) {
617 return !$h['present'];
618 });
619 ?>
620 <?php if ($missing): ?>
621 <h3 class="phpinfowp-report-h">Missing security headers (<?php echo count($missing); ?>)</h3>
622 <ul class="phpinfowp-report-list">
623 <?php foreach ($missing as $h): ?>
624 <li>
625 <?php echo $verdict_pill('warn', 'Missing'); ?>
626 <strong><?php echo esc_html($h['label']); ?></strong> &mdash; <?php echo esc_html($h['desc']); ?>
627 </li>
628 <?php endforeach; ?>
629 </ul>
630 <?php endif; ?>
631 <?php endif; ?>
632
633 <!-- SSL summary -->
634 <?php if ($r['ssl']): ?>
635 <h3 class="phpinfowp-report-h"><?php _e('SSL certificates', 'phpinfo-wp'); ?></h3>
636 <table class="phpinfowp-report-table">
637 <thead><tr><th><?php _e('Host', 'phpinfo-wp'); ?></th><th><?php _e('Issuer', 'phpinfo-wp'); ?></th><th><?php _e('Expires', 'phpinfo-wp'); ?></th><th><?php _e('Status', 'phpinfo-wp'); ?></th></tr></thead>
638 <tbody>
639 <?php foreach ($r['ssl'] as $c):
640 if (!empty($c['error'])) continue;
641 $sv = $c['days'] < 0 ? 'critical' : ($c['days'] < 14 ? 'critical' : ($c['days'] < 30 ? 'warn' : 'ok'));
642 ?>
643 <tr>
644 <td><?php echo esc_html($c['host']); ?></td>
645 <td><?php echo esc_html($c['issuer']); ?></td>
646 <td><?php echo esc_html($c['expiry']); ?></td>
647 <td><?php echo $verdict_pill($sv, $c['days'] < 0 ? 'Expired' : $c['days'] . ' days'); ?></td>
648 </tr>
649 <?php endforeach; ?>
650 </tbody>
651 </table>
652 <?php endif; ?>
653
654 <!-- DB summary -->
655 <?php if ($r['db']): ?>
656 <h3 class="phpinfowp-report-h"><?php _e('Database', 'phpinfo-wp'); ?></h3>
657 <table class="phpinfowp-report-table">
658 <tbody>
659 <tr><td style="width:40%"><?php _e('Engine', 'phpinfo-wp'); ?></td><td><?php echo esc_html($r['db']['engine'] . ' ' . $r['db']['version']); ?></td></tr>
660 <tr><td><?php _e('End-of-life', 'phpinfo-wp'); ?></td><td><?php echo esc_html($r['db']['eol'] ?? ''); ?></td></tr>
661 <tr><td><?php _e('Total size', 'phpinfo-wp'); ?></td><td><?php echo size_format($r['db_size']['total']); ?> across <?php echo (int) $r['db_size']['tables']; ?> tables</td></tr>
662 <tr>
663 <td><?php _e('Autoload data', 'phpinfo-wp'); ?></td>
664 <td>
665 <?php
666 $al_bytes = (int) $r['autoload']['bytes'];
667 $al_v = $al_bytes > 1048576 ? 'warn' : 'ok';
668 ?>
669 <?php echo size_format($al_bytes); ?> across <?php echo (int) $r['autoload']['count']; ?> options
670 <?php echo $verdict_pill($al_v, $al_v === 'ok' ? 'Tiny (good)' : 'Large'); ?>
671 <div class="phpinfowp-report-inline-cap"><?php echo esc_html(Phpinfo_WP_Report::plain_english('autoload')); ?></div>
672 </td>
673 </tr>
674 </tbody>
675 </table>
676 <?php endif; ?>
677
678 <!-- Cron -->
679 <?php if ($r['cron'] && ($r['cron']['overdue'] || $r['cron']['orphan'])): ?>
680 <h3 class="phpinfowp-report-h"><?php _e('Cron health', 'phpinfo-wp'); ?></h3>
681 <ul class="phpinfowp-report-list">
682 <li><?php echo (int) $r['cron']['total']; ?> total scheduled events</li>
683 <?php if ($r['cron']['overdue']): ?>
684 <li><?php echo $verdict_pill('critical', 'Overdue'); ?> <?php echo (int) $r['cron']['overdue']; ?> event(s) <span class="phpinfowp-report-inline-cap"> <?php echo esc_html(Phpinfo_WP_Report::plain_english('overdue_cron')); ?></span></li>
685 <?php endif; ?>
686 <?php if ($r['cron']['orphan']): ?>
687 <li><?php echo $verdict_pill('warn', 'Orphan'); ?> <?php echo (int) $r['cron']['orphan']; ?> hook(s) <span class="phpinfowp-report-inline-cap"> <?php echo esc_html(Phpinfo_WP_Report::plain_english('orphan_cron')); ?></span></li>
688 <?php endif; ?>
689 </ul>
690 <?php endif; ?>
691
692 <!-- Compat -->
693 <?php if ($r['compat'] && !isset($r['compat']['error']) && !empty($r['compat']['total'])): ?>
694 <h3 class="phpinfowp-report-h">PHP compatibility (target PHP <?php echo esc_html($r['compat']['target']); ?>)</h3>
695 <p><?php echo $verdict_pill('warn', $r['compat']['total'] . ' issues'); ?> across <?php echo (int) $r['compat']['with_issues']; ?> plugins/themes. Open the <strong><?php _e('PHP Compatibility', 'phpinfo-wp'); ?></strong> page for the full file list.</p>
696 <?php endif; ?>
697
698 <?php endif; // end Pro-only detail block ?>
699
700
701 <div class="phpinfowp-report-footer">
702 <?php if ($b['enabled'] && $b['footer_note']): ?>
703 <?php echo nl2br(esc_html($b['footer_note'])); ?>
704 <?php else: ?>
705 Report generated by <strong><?php echo $brand_name; ?></strong> &middot; <?php echo esc_html(get_site_url()); ?>
706 <?php endif; ?>
707 </div>
708
709 </div>
710 </div>
711