PluginProbe
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts / 2.7.0
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts v2.7.0
2.7.6 2.7.5 2.7.4 trunk 1.3 2.0.4 2.0.6 2.1.91 2.2.4 2.2.7 2.2.9 2.3.1 2.3.10 2.4.10 2.4.2 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.6.0 2.6.1 2.7.0 2.7.1 All 27 releases
insert-php / includes / class.admin-bar.php

class.admin-bar.php in Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts 2.7.0, at includes/class.admin-bar.php

724 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin Bar functionality
4 *
5 * @package Woody_Code_Snippets
6 */
7
8 // Exit if accessed directly.
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Class WINP_Admin_Bar
15 *
16 * Adds Woody Code Snippets menu to WordPress admin bar.
17 */
18 class WINP_Admin_Bar {
19
20 /**
21 * Class instance.
22 *
23 * @var self|null
24 */
25 private static $instance;
26
27 /**
28 * Active snippets on current page.
29 *
30 * @var array<int, array{id: int, name: string, type: string, location: string, scope: string}>
31 */
32 private $active_snippets = [];
33
34 /**
35 * Get instance.
36 *
37 * @return self
38 */
39 public static function instance() {
40 if ( null === self::$instance ) {
41 self::$instance = new self();
42 }
43
44 return self::$instance;
45 }
46
47 /**
48 * Constructor.
49 */
50 public function __construct() {
51 add_action( 'admin_bar_menu', [ $this, 'add_admin_bar_menu' ], 100 );
52 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ] );
53 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] );
54 add_action( 'wp_footer', [ $this, 'update_admin_bar_with_js' ], 9999 );
55 add_action( 'admin_footer', [ $this, 'update_admin_bar_with_js' ], 9999 );
56 }
57
58 /**
59 * Enqueue admin bar styles.
60 *
61 * @return void
62 */
63 public function enqueue_styles() {
64 if ( ! is_admin_bar_showing() ) {
65 return;
66 }
67
68 wp_add_inline_style(
69 'admin-bar',
70 '
71 /* Main menu item */
72 #wp-admin-bar-woody-snippets .ab-icon:before {
73 content: "{ }";
74 font-family: monospace;
75 font-size: 16px;
76 font-weight: bold;
77 top: -2px;
78 }
79
80 #wp-admin-bar-woody-snippets > .ab-item .ab-label {
81 padding-left: 4px;
82 }
83
84 #wp-admin-bar-woody-snippets .woody-count {
85 display: inline-block;
86 background: #10b981;
87 color: #fff;
88 border-radius: 50%;
89 width: 20px;
90 height: 20px;
91 line-height: 20px;
92 text-align: center;
93 font-size: 11px;
94 font-weight: 600;
95 margin-left: 6px;
96 vertical-align: middle;
97 }
98
99 #wp-admin-bar-woody-snippets .woody-safe-mode {
100 display: inline-block;
101 background: #dba617;
102 color: #fff;
103 border-radius: 4px;
104 padding: 0px 6px;
105 font-size: 8px;
106 font-weight: 600;
107 margin-left: 6px;
108 vertical-align: middle;
109 text-transform: uppercase;
110 letter-spacing: 0.5px;
111 }
112
113 /* Dropdown submenu */
114 #wp-admin-bar-woody-snippets > .ab-sub-wrapper {
115 width: 320px;
116 }
117
118 #wp-admin-bar-woody-snippets .ab-submenu {
119 background: #23272a;
120 }
121
122 /* Header section */
123 #wp-admin-bar-woody-snippets-header {
124 background: #23272a !important;
125 border-bottom: 2px solid #3b82f6 !important;
126 }
127
128 #wp-admin-bar-woody-snippets-header > .ab-item {
129 color: #60a5fa !important;
130 background: transparent !important;
131 font-weight: 600;
132 text-transform: uppercase;
133 font-size: 11px;
134 letter-spacing: 0.1em;
135 cursor: default !important;
136 padding: 4px 16px !important;
137 line-height: 1.2;
138 min-height: auto !important;
139 }
140
141 #wp-admin-bar-woody-snippets-header:hover > .ab-item {
142 background: transparent !important;
143 color: #60a5fa !important;
144 }
145
146 /* Snippet items */
147 #wp-admin-bar-woody-snippets .woody-snippet-item > .ab-item {
148 padding: 8px 16px !important;
149 height: auto !important;
150 line-height: 1.4;
151 min-height: auto !important;
152 background: #2c3338 !important;
153 border-bottom: 1px solid rgb(60 67 74);
154 }
155
156 #wp-admin-bar-woody-snippets .woody-snippet-item {
157 border-bottom: 1px solid rgba(255, 255, 255, 0.05);
158 }
159
160 #wp-admin-bar-woody-snippets .woody-snippet-item:hover > .ab-item {
161 background: #32373c !important;
162 }
163
164 #wp-admin-bar-woody-snippets li.woody-snippet-item:last-of-type {
165 border-bottom: none;
166 }
167
168 /* Snippet content wrapper */
169 #wp-admin-bar-woody-snippets .woody-snippet-content {
170 display: flex;
171 flex-direction: column;
172 gap: 8px;
173 }
174
175 #wp-admin-bar-woody-snippets .woody-snippet-name {
176 font-weight: 400;
177 font-size: 15px;
178 color: #e8eaed;
179 white-space: wrap;
180 overflow: hidden;
181 text-overflow: ellipsis;
182 line-height: 1.3;
183 }
184
185 /* Snippet meta (type + location) */
186 #wp-admin-bar-woody-snippets .woody-snippet-meta {
187 display: flex;
188 align-items: center;
189 gap: 10px;
190 }
191
192 /* Type badge */
193 #wp-admin-bar-woody-snippets .woody-snippet-type {
194 display: inline-flex;
195 align-items: center;
196 justify-content: center;
197 padding: 5px 11px;
198 border-radius: 4px;
199 font-weight: 700;
200 font-size: 10px;
201 text-transform: uppercase;
202 letter-spacing: 0.02em;
203 flex-shrink: 0;
204 line-height: 1;
205 }
206
207 #wp-admin-bar-woody-snippets .woody-snippet-type.type-js {
208 background: #f7df1e;
209 color: #655b0e;
210 }
211
212 #wp-admin-bar-woody-snippets .woody-snippet-type.type-html {
213 background: #e44d26;
214 color: #fffffd;
215 }
216
217 #wp-admin-bar-woody-snippets .woody-snippet-type.type-php {
218 background: #777bb3;
219 color: #ffffff;
220 }
221
222 #wp-admin-bar-woody-snippets .woody-snippet-type.type-universal {
223 background: #607D8B;
224 color: #ffffff;
225 }
226
227 #wp-admin-bar-woody-snippets .woody-snippet-type.type-css {
228 background: #1572b6;
229 color: #ffffff;
230 }
231
232 #wp-admin-bar-woody-snippets .woody-snippet-type.type-text {
233 background: #4CAF50;
234 color: #fffffd;
235 }
236
237 #wp-admin-bar-woody-snippets .woody-snippet-type.type-ad {
238 background: #26aa5d;
239 color: #fffffd;
240 }
241
242 #wp-admin-bar-woody-snippets .woody-snippet-location {
243 color: #a0a5aa;
244 font-size: 12px;
245 font-weight: 400;
246 white-space: nowrap;
247 overflow: hidden;
248 text-overflow: ellipsis;
249 line-height: 1.3;
250 }
251
252 /* View all link */
253 #wp-admin-bar-woody-snippets-view-all {
254 border-top: 1px solid rgba(255, 255, 255, 0.05);
255 background: #23272a !important;
256 }
257
258 #wp-admin-bar-woody-snippets-view-all > .ab-item {
259 text-align: center;
260 color: #60a5fa !important;
261 font-weight: 500;
262 padding: 13px 16px !important;
263 min-height: auto !important;
264 background: transparent !important;
265 padding: 3px 0 !important;
266 }
267
268 #wp-admin-bar-woody-snippets-view-all > .ab-item:after {
269 content: " →";
270 }
271
272 #wp-admin-bar-woody-snippets-view-all:hover > .ab-item {
273 color: #93c5fd !important;
274 background: rgba(59, 130, 246, 0.1) !important;
275 }
276
277 /* Empty state */
278 #wp-admin-bar-woody-snippets-empty > .ab-item {
279 color: #a0a5aa !important;
280 font-style: italic;
281 padding: 16px !important;
282 min-height: auto !important;
283 background: #2c3338 !important;
284 }
285
286 /* Safe mode message */
287 #wp-admin-bar-woody-snippets-safe-mode-message > .ab-item {
288 padding: 16px !important;
289 min-height: auto !important;
290 background: #2c3338 !important;
291 line-height: 1.5;
292 color: #e8eaed;
293 font-size: 14px;
294 white-space: wrap !important;
295 height: auto !important;
296 }
297
298 /* Disable safe mode button (styled like view all) */
299 #wp-admin-bar-woody-snippets-disable-safe-mode {
300 border-top: 1px solid rgba(255, 255, 255, 0.05);
301 background: #23272a !important;
302 }
303
304 #wp-admin-bar-woody-snippets-disable-safe-mode > .ab-item {
305 text-align: center;
306 color: #60a5fa !important;
307 font-weight: 500;
308 padding: 13px 16px !important;
309 min-height: auto !important;
310 background: transparent !important;
311 padding: 3px 0 !important;
312 }
313
314 #wp-admin-bar-woody-snippets-disable-safe-mode:hover > .ab-item {
315 color: #93c5fd !important;
316 background: rgba(59, 130, 246, 0.1) !important;
317 }
318
319 /* Pagination */
320 #wp-admin-bar-woody-snippets-pagination {
321 background: #23272a !important;
322 border-top: 1px solid rgba(255, 255, 255, 0.05);
323 border-bottom: 1px solid rgba(255, 255, 255, 0.05);
324 }
325
326 #wp-admin-bar-woody-snippets-pagination > .ab-item {
327 display: flex !important;
328 align-items: center;
329 justify-content: space-between;
330 padding: 8px 16px !important;
331 min-height: auto !important;
332 background: transparent !important;
333 cursor: default !important;
334 }
335
336 #wp-admin-bar-woody-snippets-pagination .woody-pagination-buttons {
337 display: flex;
338 gap: 8px;
339 }
340
341 #wp-admin-bar-woody-snippets-pagination .woody-pagination-btn {
342 background: #32373c;
343 color: #a0a5aa;
344 border: 1px solid rgba(255, 255, 255, 0.1);
345 border-radius: 3px;
346 padding: 4px 10px;
347 font-size: 11px;
348 font-weight: 500;
349 cursor: pointer;
350 transition: all 0.2s;
351 }
352
353 #wp-admin-bar-woody-snippets-pagination .woody-pagination-btn:hover:not(:disabled) {
354 background: #3b82f6;
355 color: #fff;
356 border-color: #3b82f6;
357 }
358
359 #wp-admin-bar-woody-snippets-pagination .woody-pagination-btn:disabled {
360 opacity: 0.4;
361 cursor: not-allowed;
362 }
363
364 #wp-admin-bar-woody-snippets-pagination .woody-pagination-info {
365 color: #a0a5aa;
366 font-size: 12px;
367 }
368 '
369 );
370 }
371
372 /**
373 * Add admin bar menu.
374 *
375 * @param WP_Admin_Bar $wp_admin_bar Admin bar instance.
376 * @return void
377 */
378 public function add_admin_bar_menu( $wp_admin_bar ) {
379 if ( ! current_user_can( 'manage_options' ) ) {
380 return;
381 }
382
383 // Get active snippets (static for now).
384 $this->collect_active_snippets();
385
386 $count = count( $this->active_snippets );
387
388 // Check if safe mode is enabled.
389 $is_safe_mode = WINP_Helper::is_safe_mode();
390
391 // Add parent menu item.
392 $wp_admin_bar->add_node(
393 [
394 'id' => 'woody-snippets',
395 'parent' => 'top-secondary',
396 'title' => $is_safe_mode
397 ? sprintf(
398 '<span class="ab-icon"></span><span class="ab-label">%s</span><span class="woody-safe-mode">%s</span>',
399 esc_html__( 'Woody', 'insert-php' ),
400 esc_html__( 'Safe Mode', 'insert-php' )
401 )
402 : sprintf(
403 '<span class="ab-icon"></span><span class="ab-label">%s</span><span class="woody-count">%d</span>',
404 esc_html__( 'Woody', 'insert-php' ),
405 $count
406 ),
407 'href' => admin_url( 'edit.php?post_type=' . WINP_SNIPPETS_POST_TYPE ),
408 'meta' => [
409 'title' => $is_safe_mode
410 ? __( 'Safe mode is enabled - snippets are not executing', 'insert-php' )
411 : __( 'Active snippets on this page', 'insert-php' ),
412 ],
413 ]
414 );
415
416 // Add header.
417 $wp_admin_bar->add_node(
418 [
419 'parent' => 'woody-snippets',
420 'id' => 'woody-snippets-header',
421 'title' => $is_safe_mode
422 ? __( 'Safe Mode Enabled', 'insert-php' )
423 : __( 'Active Snippets on This Page', 'insert-php' ),
424 'meta' => [
425 'class' => 'woody-snippets-header',
426 ],
427 ]
428 );
429
430 // Add safe mode message if enabled.
431 if ( $is_safe_mode ) {
432 $wp_admin_bar->add_node(
433 [
434 'parent' => 'woody-snippets',
435 'id' => 'woody-snippets-safe-mode-message',
436 'title' => __( 'Safe Mode is active. All snippets are temporarily disabled. You can re-enable them after fixing any issues.', 'insert-php' ),
437 'meta' => [
438 'class' => 'woody-safe-mode-message',
439 ],
440 ]
441 );
442 }
443
444 // Add snippet items (limited to 5 per page).
445 if ( ! empty( $this->active_snippets ) ) {
446 $snippets_to_show = array_slice( $this->active_snippets, 0, 5 );
447 foreach ( $snippets_to_show as $snippet ) {
448 $wp_admin_bar->add_node(
449 [
450 'parent' => 'woody-snippets',
451 'id' => 'woody-snippet-' . $snippet['id'],
452 'title' => $this->render_snippet_item( $snippet ),
453 'href' => admin_url( 'post.php?post=' . $snippet['id'] . '&action=edit' ),
454 'meta' => [
455 'class' => 'woody-snippet-item',
456 ],
457 ]
458 );
459 }
460
461 // Add pagination if more than 5 snippets.
462 if ( count( $this->active_snippets ) > 5 ) {
463 $wp_admin_bar->add_node(
464 [
465 'parent' => 'woody-snippets',
466 'id' => 'woody-snippets-pagination',
467 'title' => $this->render_pagination( 1, count( $this->active_snippets ) ),
468 ]
469 );
470 }
471 } elseif ( ! $is_safe_mode ) {
472 // Only show empty state if not in safe mode.
473 $wp_admin_bar->add_node(
474 [
475 'parent' => 'woody-snippets',
476 'id' => 'woody-snippets-empty',
477 'title' => __( 'No active snippets on this page', 'insert-php' ),
478 ]
479 );
480 }
481
482 // Add disable safe mode button or view all link.
483 if ( $is_safe_mode ) {
484 $disable_url = admin_url( 'edit.php?post_type=wbcr-snippets&wbcr-php-snippets-disable-safe-mode=1' );
485 $wp_admin_bar->add_node(
486 [
487 'parent' => 'woody-snippets',
488 'id' => 'woody-snippets-disable-safe-mode',
489 'title' => __( 'Disable Safe Mode', 'insert-php' ),
490 'href' => $disable_url,
491 ]
492 );
493 } else {
494 $wp_admin_bar->add_node(
495 [
496 'parent' => 'woody-snippets',
497 'id' => 'woody-snippets-view-all',
498 'title' => __( 'View All Snippets', 'insert-php' ),
499 'href' => admin_url( 'edit.php?post_type=' . WINP_SNIPPETS_POST_TYPE ),
500 ]
501 );
502 }
503 }
504
505 /**
506 * Render snippet item HTML.
507 *
508 * @param array{id: int, name: string, type: string, location: string, scope: string} $snippet Snippet data.
509 * @return string
510 */
511 private function render_snippet_item( $snippet ) {
512 $type_class = 'type-' . strtolower( $snippet['type'] );
513
514 return sprintf(
515 '<div class="woody-snippet-content">
516 <div class="woody-snippet-name">%s</div>
517 <div class="woody-snippet-meta">
518 <span class="woody-snippet-type %s">%s</span>
519 <span class="woody-snippet-location">%s</span>
520 </div>
521 </div>',
522 esc_html( $snippet['name'] ),
523 esc_attr( $type_class ),
524 esc_html( $snippet['type'] ),
525 esc_html( $snippet['location'] )
526 );
527 }
528
529 /**
530 * Render pagination controls.
531 *
532 * @param int $current_page Current page number.
533 * @param int $total_count Total number of snippets.
534 * @return string
535 */
536 private function render_pagination( $current_page, $total_count ) {
537 $total_pages = ceil( $total_count / 5 );
538 $start = ( ( $current_page - 1 ) * 5 ) + 1;
539 $end = min( $current_page * 5, $total_count );
540
541 return sprintf(
542 '<div style="display: flex; justify-content: space-between; align-items: center; width: 100%%;">
543 <span class="woody-pagination-info">%s</span>
544 <div class="woody-pagination-buttons">
545 <button class="woody-pagination-btn" data-page="prev" %s>← %s</button>
546 <button class="woody-pagination-btn" data-page="next" %s>%s →</button>
547 </div>
548 </div>',
549 // translators: 1: start number, 2: end number, 3: total count.
550 sprintf( __( '%1$d-%2$d of %3$d', 'insert-php' ), $start, $end, $total_count ),
551 $current_page <= 1 ? 'disabled' : '',
552 esc_html__( 'Previous', 'insert-php' ),
553 $current_page >= $total_pages ? 'disabled' : '',
554 esc_html__( 'Next', 'insert-php' )
555 );
556 }
557
558 /**
559 * Collect active snippets on current page.
560 *
561 * Gets the actually executed snippets from the execution tracker.
562 *
563 * @return void
564 */
565 private function collect_active_snippets() {
566 // Get executed snippets from the execution tracker.
567 if ( class_exists( 'WINP_Execute_Snippet' ) ) {
568 $execute_instance = WINP_Execute_Snippet::app();
569 if ( $execute_instance ) {
570 $executed = $execute_instance->get_executed_snippets();
571 $this->active_snippets = array_values( $executed );
572 }
573 }
574 }
575
576 /**
577 * Update admin bar with JavaScript after all snippets have executed.
578 *
579 * Outputs inline JavaScript that updates the admin bar with the final
580 * list of executed snippets, including footer snippets and shortcodes.
581 *
582 * @return void
583 */
584 public function update_admin_bar_with_js() {
585 if ( ! is_admin_bar_showing() || ! current_user_can( 'manage_options' ) ) {
586 return;
587 }
588
589 // Check if safe mode is enabled.
590 $is_safe_mode = WINP_Helper::is_safe_mode();
591
592 // If safe mode is enabled, don't update the snippets list.
593 if ( $is_safe_mode ) {
594 return;
595 }
596
597 // Get the final list of executed snippets.
598 $this->collect_active_snippets();
599 $count = count( $this->active_snippets );
600 $total_pages = ceil( $count / 5 );
601
602 // Prepare snippet data for JavaScript.
603 $snippets_data = [];
604 foreach ( $this->active_snippets as $snippet ) {
605 $snippets_data[] = [
606 'id' => $snippet['id'],
607 'name' => $snippet['name'],
608 'type' => $snippet['type'],
609 'location' => $snippet['location'],
610 'edit_url' => admin_url( 'post.php?post=' . $snippet['id'] . '&action=edit' ),
611 ];
612 }
613
614 ?>
615 <script type="text/javascript">
616 (function() {
617 const snippetsData = <?php echo wp_json_encode( $snippets_data ); ?>;
618 const totalCount = <?php echo intval( $count ); ?>;
619 const perPage = 5;
620 let currentPage = 1;
621
622 function renderSnippetItem(snippet) {
623 const typeClass = 'type-' + snippet.type.toLowerCase();
624 return `<div class="woody-snippet-content">
625 <div class="woody-snippet-name">${snippet.name}</div>
626 <div class="woody-snippet-meta">
627 <span class="woody-snippet-type ${typeClass}">${snippet.type}</span>
628 <span class="woody-snippet-location">${snippet.location}</span>
629 </div>
630 </div>`;
631 }
632
633 function renderPagination() {
634 const totalPages = Math.ceil(totalCount / perPage);
635 const start = ((currentPage - 1) * perPage) + 1;
636 const end = Math.min(currentPage * perPage, totalCount);
637 const prevDisabled = currentPage <= 1 ? 'disabled' : '';
638 const nextDisabled = currentPage >= totalPages ? 'disabled' : '';
639
640 return `<div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
641 <span class="woody-pagination-info">${start}-${end} of ${totalCount}</span>
642 <div class="woody-pagination-buttons">
643 <button class="woody-pagination-btn" data-page="prev" ${prevDisabled}>← Prev</button>
644 <button class="woody-pagination-btn" data-page="next" ${nextDisabled}>Next →</button>
645 </div>
646 </div>`;
647 }
648
649 function updateDisplay() {
650 const submenu = document.querySelector('#wp-admin-bar-woody-snippets .ab-submenu');
651 if (!submenu) return;
652
653 let html = '<li id="wp-admin-bar-woody-snippets-header"><a class="ab-item" href="javascript:void(0)"><?php echo esc_js( __( 'Active Snippets on This Page', 'insert-php' ) ); ?></a></li>';
654
655 if (snippetsData.length === 0) {
656 html += '<li id="wp-admin-bar-woody-snippets-empty"><a class="ab-item" href="javascript:void(0)"><?php echo esc_js( __( 'No active snippets on this page', 'insert-php' ) ); ?></a></li>';
657 } else {
658 const start = (currentPage - 1) * perPage;
659 const end = start + perPage;
660 const pageSnippets = snippetsData.slice(start, end);
661
662 pageSnippets.forEach(snippet => {
663 html += `<li id="wp-admin-bar-woody-snippet-${snippet.id}" class="woody-snippet-item"><a class="ab-item" href="${snippet.edit_url}">${renderSnippetItem(snippet)}</a></li>`;
664 });
665
666 if (snippetsData.length > perPage) {
667 html += `<li id="wp-admin-bar-woody-snippets-pagination"><a class="ab-item" href="javascript:void(0)">${renderPagination()}</a></li>`;
668 }
669 }
670
671 html += '<li id="wp-admin-bar-woody-snippets-view-all"><a class="ab-item" href="<?php echo esc_js( admin_url( 'edit.php?post_type=' . WINP_SNIPPETS_POST_TYPE ) ); ?>"><?php echo esc_js( __( 'View All Snippets', 'insert-php' ) ); ?></a></li>';
672
673 submenu.innerHTML = html;
674
675 // Attach pagination handlers
676 const prevBtn = submenu.querySelector('[data-page="prev"]');
677 const nextBtn = submenu.querySelector('[data-page="next"]');
678
679 if (prevBtn) {
680 prevBtn.addEventListener('click', (e) => {
681 e.preventDefault();
682 e.stopPropagation();
683 if (currentPage > 1) {
684 currentPage--;
685 updateDisplay();
686 }
687 });
688 }
689
690 if (nextBtn) {
691 nextBtn.addEventListener('click', (e) => {
692 e.preventDefault();
693 e.stopPropagation();
694 const totalPages = Math.ceil(totalCount / perPage);
695 if (currentPage < totalPages) {
696 currentPage++;
697 updateDisplay();
698 }
699 });
700 }
701 }
702
703 function updateAdminBar() {
704 // Update the count badge
705 const countBadge = document.querySelector('#wp-admin-bar-woody-snippets .woody-count');
706 if (countBadge) {
707 countBadge.textContent = totalCount;
708 }
709
710 // Update the submenu with pagination
711 updateDisplay();
712 }
713
714 // Try immediately
715 updateAdminBar();
716
717 // Also try after a small delay in case admin bar loads late
718 setTimeout(updateAdminBar, 100);
719 })();
720 </script>
721 <?php
722 }
723 }
724