PluginProbe
Code Snippets / 3.0.0
Code Snippets v3.0.0
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / php / admin-menus / class-edit-menu.php

class-edit-menu.php in Code Snippets 3.0.0, at php/admin-menus/class-edit-menu.php

850 lines 25.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Code_Snippets;
4
5 use function Code_Snippets\Settings\get_setting;
6
7 /**
8 * This class handles the add/edit menu
9 */
10 class Edit_Menu extends Admin_Menu {
11
12 /**
13 * The snippet object currently being edited
14 *
15 * @var Snippet
16 * @see Edit_Menu::load_snippet_data()
17 */
18 protected $snippet = null;
19
20 /**
21 * Constructor
22 */
23 public function __construct() {
24
25 parent::__construct(
26 'edit',
27 _x( 'Edit Snippet', 'menu label', 'code-snippets' ),
28 __( 'Edit Snippet', 'code-snippets' )
29 );
30 }
31
32 /**
33 * Register action and filter hooks
34 */
35 public function run() {
36 parent::run();
37 $this->remove_debug_bar_codemirror();
38 }
39
40 /**
41 * Register the admin menu
42 */
43 public function register() {
44 parent::register();
45
46 /* Only preserve the edit menu if we are currently editing a snippet */
47 if ( ! isset( $_REQUEST['page'] ) || $_REQUEST['page'] !== $this->slug ) {
48 remove_submenu_page( $this->base_slug, $this->slug );
49 }
50
51 /* Add New Snippet menu */
52 $this->add_menu(
53 code_snippets()->get_menu_slug( 'add' ),
54 _x( 'Add New', 'menu label', 'code-snippets' ),
55 __( 'Add New Snippet', 'code-snippets' )
56 );
57 }
58
59 /**
60 * Executed when the menu is loaded
61 */
62 public function load() {
63 parent::load();
64
65 // Retrieve the current snippet object
66 $this->load_snippet_data();
67
68 $screen = get_current_screen();
69 $edit_hook = get_plugin_page_hookname( $this->slug, $this->base_slug );
70 if ( $screen->in_admin( 'network' ) ) {
71 $edit_hook .= '-network';
72 }
73
74 // Disallow visiting the edit snippet page without a valid ID
75 if ( $screen->base === $edit_hook && ( ! isset( $_REQUEST['id'] ) || 0 === $this->snippet->id ) ) {
76 wp_redirect( code_snippets()->get_menu_url( 'add' ) );
77 exit;
78 }
79
80 // Process any submitted actions
81 $this->process_actions();
82
83 // Load the contextual help tabs
84 $contextual_help = new Contextual_Help( 'edit' );
85 $contextual_help->load();
86
87 // Register action hooks
88 if ( get_setting( 'general', 'enable_description' ) ) {
89 add_action( 'code_snippets_edit_snippet', array( $this, 'render_description_editor' ), 9 );
90 }
91
92 if ( get_setting( 'general', 'enable_tags' ) ) {
93 add_action( 'code_snippets_edit_snippet', array( $this, 'render_tags_editor' ) );
94 }
95
96 add_action( 'code_snippets_below_editor', array( $this, 'render_priority_setting' ), 0 );
97
98 if ( is_network_admin() ) {
99 add_action( 'code_snippets_edit_snippet', array( $this, 'render_multisite_sharing_setting' ), 1 );
100 }
101
102 if ( apply_filters( 'code_snippets/extra_save_buttons', true ) ) {
103 add_action( 'code_snippets/admin/code_editor_toolbar', array( $this, 'render_extra_submit_buttons' ) );
104 }
105
106 if ( apply_filters( 'code_snippets/enable_code_direction', is_rtl() ) ) {
107 add_action( 'code_snippets/admin/code_editor_toolbar', array( $this, 'render_direction_setting' ), 11, 0 );
108 }
109
110 $this->process_actions();
111 }
112
113 /**
114 * Load the data for the snippet currently being edited
115 */
116 public function load_snippet_data() {
117 $edit_id = isset( $_REQUEST['id'] ) && intval( $_REQUEST['id'] ) ? absint( $_REQUEST['id'] ) : 0;
118
119 $this->snippet = get_snippet( $edit_id );
120 $snippet = $this->snippet;
121
122 if ( 0 === $edit_id && isset( $_GET['type'] ) && $_GET['type'] !== $snippet->type ) {
123 if ( 'php' === $_GET['type'] ) {
124 $snippet->scope = 'global';
125 } elseif ( 'css' === $_GET['type'] ) {
126 $snippet->scope = 'site-css';
127 } elseif ( 'html' === $_GET['type'] ) {
128 $snippet->scope = 'content';
129 } elseif ( 'js' === $_GET['type'] ) {
130 $snippet->scope = 'site-head-js';
131 }
132 }
133 }
134
135 /**
136 * Process data sent from the edit page
137 */
138 private function process_actions() {
139
140 /* Check for a valid nonce */
141 if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'save_snippet' ) ) {
142 return;
143 }
144
145 if ( isset( $_POST['save_snippet'] ) || isset( $_POST['save_snippet_execute'] ) ||
146 isset( $_POST['save_snippet_activate'] ) || isset( $_POST['save_snippet_deactivate'] ) ) {
147 $this->save_posted_snippet();
148 }
149
150 if ( isset( $_POST['snippet_id'] ) ) {
151 $snippet_id = intval( $_POST['snippet_id'] );
152
153 /* Delete the snippet if the button was clicked */
154 if ( isset( $_POST['delete_snippet'] ) ) {
155 delete_snippet( $snippet_id );
156 wp_redirect( add_query_arg( 'result', 'delete', code_snippets()->get_menu_url( 'manage' ) ) );
157 exit;
158 }
159
160 /* Export the snippet if the button was clicked */
161 if ( isset( $_POST['export_snippet'] ) ) {
162 $export = new Export( $snippet_id );
163 $export->export_snippets();
164 }
165
166 /* Download the snippet if the button was clicked */
167 if ( isset( $_POST['download_snippet'] ) ) {
168 $export = new Export( $snippet_id );
169 $export->download_snippets();
170 }
171 }
172 }
173
174 /**
175 * Remove the sharing status from a network snippet
176 *
177 * @param int $snippet_id Snippet ID.
178 *
179 * @phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
180 */
181 private function unshare_network_snippet( $snippet_id ) {
182 $shared_snippets = get_site_option( 'shared_network_snippets', array() );
183
184 if ( ! in_array( $snippet_id, $shared_snippets, true ) ) {
185 return;
186 }
187
188 /* Remove the snippet ID from the array */
189 $shared_snippets = array_diff( $shared_snippets, array( $snippet_id ) );
190 update_site_option( 'shared_network_snippets', array_values( $shared_snippets ) );
191
192 /* Deactivate on all sites */
193 global $wpdb;
194 $sites = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
195 if ( $sites ) {
196 foreach ( $sites as $site ) {
197 switch_to_blog( $site );
198 $active_shared_snippets = get_option( 'active_shared_network_snippets' );
199
200 if ( is_array( $active_shared_snippets ) ) {
201 $active_shared_snippets = array_diff( $active_shared_snippets, array( $snippet_id ) );
202 update_option( 'active_shared_network_snippets', $active_shared_snippets );
203 }
204 }
205
206 restore_current_blog();
207 }
208 }
209
210 /**
211 * Display a custom error message when a code error is encountered
212 *
213 * @param string $out Error message content.
214 *
215 * @return string New error message.
216 */
217 private function code_error_callback( $out ) {
218 $error = error_get_last();
219
220 if ( is_null( $error ) ) {
221 return $out;
222 }
223
224 $m = '<h3>' . esc_html__( "Don't Panic", 'code-snippets' ) . '</h3>';
225 /* translators: %d: line where error was produced */
226 $m .= '<p>' . sprintf( esc_html__( 'The code snippet you are trying to save produced a fatal error on line %d:', 'code-snippets' ), intval( $error['line'] ) ) . '</p>';
227 $m .= '<strong>' . esc_html( $error['message'] ) . '</strong>';
228 $m .= '<p>' . esc_html__( 'The previous version of the snippet is unchanged, and the rest of this site should be functioning normally as before.', 'code-snippets' ) . '</p>';
229 $m .= '<p>' . esc_html__( 'Please use the back button in your browser to return to the previous page and try to fix the code error.', 'code-snippets' );
230 $m .= ' ' . esc_html__( 'If you prefer, you can close this page and discard the changes you just made. No changes will be made to this site.', 'code-snippets' ) . '</p>';
231
232 return $m;
233 }
234
235 /**
236 * Validate the snippet code before saving to database
237 *
238 * @param Snippet $snippet Snippet object.
239 *
240 * @return bool Whether the code produces errors.
241 */
242 private function test_code( Snippet $snippet ) {
243
244 if ( empty( $snippet->code ) || 'php' !== $snippet->type ) {
245 return false;
246 }
247
248 ob_start( array( $this, 'code_error_callback' ) );
249
250 $result = eval( $snippet->code );
251
252 ob_end_clean();
253
254 do_action( 'code_snippets/after_execute_snippet', $snippet->id, $snippet->code, $result );
255
256 return false === $result;
257 }
258
259 /**
260 * Save the posted snippet data to the database and redirect
261 */
262 private function save_posted_snippet() {
263
264 /* Build snippet object from fields with 'snippet_' prefix */
265 $snippet = new Snippet();
266
267 foreach ( $_POST as $field => $value ) {
268 if ( 'snippet_' === substr( $field, 0, 8 ) ) {
269
270 /* Remove the 'snippet_' prefix from field name and set it on the object */
271 $snippet->set_field( substr( $field, 8 ), stripslashes( $value ) );
272 }
273 }
274
275 if ( isset( $_POST['save_snippet_execute'] ) && 'single-use' !== $snippet->scope ) {
276 unset( $_POST['save_snippet_execute'] );
277 $_POST['save_snippet'] = 'yes';
278 }
279
280 /* Activate or deactivate the snippet before saving if we clicked the button */
281
282 if ( isset( $_POST['save_snippet_execute'] ) ) {
283 $snippet->active = 1;
284 } elseif ( isset( $_POST['snippet_sharing'] ) && 'on' === $_POST['snippet_sharing'] ) {
285 // Shared network snippets cannot be network-activated
286 $snippet->active = 0;
287 unset( $_POST['save_snippet_activate'], $_POST['save_snippet_deactivate'] );
288 } elseif ( isset( $_POST['save_snippet_activate'] ) ) {
289 $snippet->active = 1;
290 } elseif ( isset( $_POST['save_snippet_deactivate'] ) ) {
291 $snippet->active = 0;
292 }
293
294 if ( 'php' === $snippet->type ) {
295
296 /* Remove <?php and <? from beginning of snippet */
297 $snippet->code = preg_replace( '|^\s*<\?(php)?|', '', $snippet->code );
298 /* Remove ?> from end of snippet */
299 $snippet->code = preg_replace( '|\?>\s*$|', '', $snippet->code );
300
301 /* Deactivate snippet if code contains errors */
302 if ( $snippet->active && 'single-use' !== $snippet->scope ) {
303 $validator = new Validator( $snippet->code );
304 $code_error = $validator->validate();
305
306 if ( ! $code_error ) {
307 $code_error = $this->test_code( $snippet );
308 }
309
310 if ( $code_error ) {
311 $snippet->active = 0;
312 }
313 }
314 }
315
316 /* Save the snippet to the database */
317 $snippet_id = save_snippet( $snippet );
318
319 /* Update the shared network snippets if necessary */
320 if ( $snippet_id && is_network_admin() ) {
321
322 if ( isset( $_POST['snippet_sharing'] ) && 'on' === $_POST['snippet_sharing'] ) {
323 $shared_snippets = get_site_option( 'shared_network_snippets', array() );
324
325 /* Add the snippet ID to the array if it isn't already */
326 if ( ! in_array( $snippet_id, $shared_snippets, true ) ) {
327 $shared_snippets[] = $snippet_id;
328 update_site_option( 'shared_network_snippets', array_values( $shared_snippets ) );
329 }
330 } else {
331 $this->unshare_network_snippet( $snippet_id );
332 }
333 }
334
335 /* If the saved snippet ID is invalid, display an error message */
336 if ( ! $snippet_id || $snippet_id < 1 ) {
337 /* An error occurred */
338 wp_redirect( add_query_arg( 'result', 'save-error', code_snippets()->get_menu_url( 'add' ) ) );
339 exit;
340 }
341
342 /* Display message if a parse error occurred */
343 if ( isset( $code_error ) && $code_error ) {
344 wp_redirect(
345 add_query_arg(
346 array(
347 'id' => $snippet_id,
348 'result' => 'code-error',
349 ),
350 code_snippets()->get_menu_url( 'edit' )
351 )
352 );
353 exit;
354 }
355
356 /* Set the result depending on if the snippet was just added */
357 $result = isset( $_POST['snippet_id'] ) ? 'updated' : 'added';
358
359 /* Append a suffix if the snippet was activated or deactivated */
360 if ( isset( $_POST['save_snippet_activate'] ) ) {
361 $result .= '-and-activated';
362 } elseif ( isset( $_POST['save_snippet_deactivate'] ) ) {
363 $result .= '-and-deactivated';
364 } elseif ( isset( $_POST['save_snippet_execute'] ) ) {
365 $result .= '-and-executed';
366 }
367
368 /* Redirect to edit snippet page */
369 $redirect_uri = add_query_arg(
370 array(
371 'id' => $snippet_id,
372 'result' => $result,
373 ),
374 code_snippets()->get_menu_url( 'edit' )
375 );
376
377 wp_redirect( esc_url_raw( $redirect_uri ) );
378 exit;
379 }
380
381 /**
382 * Add a description editor to the single snippet page
383 *
384 * @param Snippet $snippet The snippet being used for this page.
385 */
386 public function render_description_editor( Snippet $snippet ) {
387 $settings = Settings\get_settings_values();
388 $settings = $settings['description_editor'];
389
390 echo '<h2><label for="snippet_description">', esc_html__( 'Description', 'code-snippets' ), '</label></h2>';
391
392 remove_editor_styles(); // stop custom theme styling interfering with the editor
393
394 wp_editor(
395 $snippet->desc,
396 'description',
397 apply_filters( 'code_snippets/admin/description_editor_settings',
398 array(
399 'textarea_name' => 'snippet_description',
400 'textarea_rows' => $settings['rows'],
401 'teeny' => ! $settings['use_full_mce'],
402 'media_buttons' => $settings['media_buttons'],
403 )
404 )
405 );
406 }
407
408 /**
409 * Render the interface for editing snippet tags
410 *
411 * @param Snippet $snippet The snippet currently being edited.
412 */
413 public function render_tags_editor( Snippet $snippet ) {
414
415 ?>
416 <h2 style="margin: 25px 0 10px;">
417 <label for="snippet_tags" style="cursor: auto;">
418 <?php esc_html_e( 'Tags', 'code-snippets' ); ?>
419 </label>
420 </h2>
421
422 <input type="text" id="snippet_tags" name="snippet_tags" style="width: 100%;"
423 placeholder="<?php esc_html_e( 'Enter a list of tags; separated by commas', 'code-snippets' ); ?>"
424 value="<?php echo esc_attr( $snippet->tags_list ); ?>"/>
425 <?php
426 }
427
428 /**
429 * Render the snippet priority setting
430 *
431 * @param Snippet $snippet The snippet currently being edited.
432 */
433 public function render_priority_setting( Snippet $snippet ) {
434 if ( 'html' === $snippet->type ) {
435 return;
436 }
437
438 ?>
439 <p class="snippet-priority"
440 title="<?php esc_attr_e( 'Snippets with a lower priority number will run before those with a higher number.', 'code-snippets' ); ?>">
441 <label for="snippet_priority"><?php esc_html_e( 'Priority', 'code-snippets' ); ?></label>
442
443 <input name="snippet_priority" type="number" id="snippet_priority"
444 value="<?php echo esc_attr( $snippet->priority ); ?>">
445 </p>
446 <?php
447 }
448
449 /**
450 * Render the setting for shared network snippets
451 *
452 * @param Snippet $snippet The snippet currently being edited.
453 */
454 public function render_multisite_sharing_setting( $snippet ) {
455 $shared_snippets = get_site_option( 'shared_network_snippets', array() );
456 ?>
457
458 <div class="snippet-sharing-setting">
459 <h2 class="screen-reader-text"><?php esc_html_e( 'Sharing Settings', 'code-snippets' ); ?></h2>
460 <label for="snippet_sharing">
461 <input type="checkbox" name="snippet_sharing"
462 <?php checked( in_array( $snippet->id, $shared_snippets, true ) ); ?>>
463 <?php esc_html_e( 'Allow this snippet to be activated on individual sites on the network', 'code-snippets' ); ?>
464 </label>
465 </div>
466
467 <?php
468 }
469
470 /**
471 * Render additional save buttons above the snippet editor.
472 *
473 * @param Snippet $snippet Snippet currently being edited.
474 */
475 public function render_extra_submit_buttons( Snippet $snippet ) {
476
477 $actions['save_snippet'] = array(
478 __( 'Save Changes', 'code-snippets' ),
479 __( 'Save Snippet', 'code-snippets' ),
480 );
481
482 if ( 'html' !== $snippet->type ) {
483
484 if ( 'single-use' === $snippet->scope ) {
485 $actions['save_snippet_execute'] = array(
486 __( 'Execute Once', 'code-snippets' ),
487 __( 'Save Snippet and Execute Once', 'code-snippets' ),
488 );
489
490 } elseif ( ! $snippet->shared_network || ! is_network_admin() ) {
491
492 if ( $snippet->active ) {
493 $actions['save_snippet_deactivate'] = array(
494 __( 'Deactivate', 'code-snippets' ),
495 __( 'Save Snippet and Deactivate', 'code-snippets' ),
496 );
497
498 } else {
499 $actions['save_snippet_activate'] = array(
500 __( 'Activate', 'code-snippets' ),
501 __( 'Save Snippet and Activate', 'code-snippets' ),
502 );
503 }
504 }
505 }
506
507 foreach ( $actions as $action => $labels ) {
508 $other_attributes = array(
509 'title' => $labels[1],
510 'id' => $action . '_extra',
511 );
512 submit_button( $labels[0], 'secondary small', $action, false, $other_attributes );
513 }
514 }
515
516 /**
517 * Render a control for changing the code editor text direction
518 */
519 public function render_direction_setting() {
520 ?>
521 <label class="screen-reader-text" for="snippet-code-direction">
522 <?php esc_html_e( 'Code Direction', 'code-snippets' ); ?>
523 </label>
524 <select id="snippet-code-direction">
525 <option value="ltr"><?php esc_html_e( 'LTR', 'code-snippets' ); ?></option>
526 <option value="rtl"><?php esc_html_e( 'RTL', 'code-snippets' ); ?></option>
527 </select>
528 <?php
529 }
530
531 /**
532 * Retrieve the first error in a snippet's code
533 *
534 * @param int $snippet_id Snippet ID.
535 *
536 * @return array|bool Error if execution failed, otherwise false.
537 */
538 private function get_snippet_error( $snippet_id ) {
539
540 if ( ! intval( $snippet_id ) ) {
541 return false;
542 }
543
544 $snippet = get_snippet( intval( $snippet_id ) );
545
546 if ( '' === $snippet->code ) {
547 return false;
548 }
549
550 $validator = new Validator( $snippet->code );
551 $error = $validator->validate();
552
553 if ( $error ) {
554 return $error;
555 }
556
557 ob_start();
558 $result = eval( $snippet->code );
559 ob_end_clean();
560
561 if ( false !== $result ) {
562 return false;
563 }
564
565 $error = error_get_last();
566
567 if ( is_null( $error ) ) {
568 return false;
569 }
570
571 return $error;
572 }
573
574 /**
575 * Print the status and error messages
576 */
577 protected function print_messages() {
578
579 if ( ! isset( $_REQUEST['result'] ) ) {
580 return;
581 }
582
583 $result = sanitize_key( $_REQUEST['result'] );
584
585 if ( 'code-error' === $result ) {
586 $error = isset( $_REQUEST['id'] ) ? $this->get_snippet_error( intval( $_REQUEST['id'] ) ) : false;
587
588 if ( $error ) {
589 /* translators: %d: line of file where error originated */
590 $text = __( 'The snippet has been deactivated due to an error on line %d:', 'code-snippets' );
591
592 printf(
593 '<div id="message" class="error fade"><p>%s</p><p><strong>%s</strong></p></div>',
594 sprintf( esc_html( $text ), intval( $error['line'] ) ),
595 wp_kses_post( $error['message'] )
596 );
597
598 } else {
599 echo '<div id="message" class="error fade"><p>';
600 esc_html_e( 'The snippet has been deactivated due to an error in the code.', 'code-snippets' );
601 echo '</p></div>';
602 }
603
604 return;
605 }
606
607 if ( 'save-error' === $result ) {
608 echo '<div id="message" class="error fade"><p>';
609 esc_html_e( 'An error occurred when saving the snippet.', 'code-snippets' );
610 echo '</p></div>';
611 return;
612 }
613
614 $messages = array(
615 'added' => __( 'Snippet <strong>added</strong>.', 'code-snippets' ),
616 'updated' => __( 'Snippet <strong>updated</strong>.', 'code-snippets' ),
617 'added-and-activated' => __( 'Snippet <strong>added</strong> and <strong>activated</strong>.', 'code-snippets' ),
618 'updated-and-executed' => __( 'Snippet <strong>added</strong> and <strong>executed</strong>.', 'code-snippets' ),
619 'updated-and-activated' => __( 'Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets' ),
620 'updated-and-deactivated' => __( 'Snippet <strong>updated</strong> and <strong>deactivated</strong>.', 'code-snippets' ),
621 );
622
623 if ( isset( $messages[ $result ] ) ) {
624 echo '<div id="message" class="updated fade"><p>', wp_kses( $messages[ $result ], [ 'strong' => [] ] ), '</p></div>';
625
626 }
627 }
628
629 /**
630 * Enqueue assets for the edit menu
631 */
632 public function enqueue_assets() {
633 $plugin = code_snippets();
634 $rtl = is_rtl() ? '-rtl' : '';
635
636 enqueue_code_editor( $this->snippet->type );
637
638 wp_enqueue_style(
639 'code-snippets-edit',
640 plugins_url( "css/min/edit$rtl.css", $plugin->file ),
641 [ 'code-editor' ],
642 $plugin->version
643 );
644
645 wp_enqueue_script(
646 'code-snippets-edit-menu',
647 plugins_url( 'js/min/edit.js', $plugin->file ),
648 [ 'code-snippets-code-editor' ],
649 $plugin->version,
650 true
651 );
652
653 wp_localize_script(
654 'code-snippets-edit-menu',
655 'code_snippets_edit_i18n',
656 [
657 'missing_title_code' => esc_attr__( 'This snippet has no code or title. Continue?', 'code-snippets' ),
658 'missing_title' => esc_attr__( 'This snippet has no title. Continue?', 'code-snippets' ),
659 'missing_code' => esc_attr__( 'This snippet has no snippet code. Continue?', 'code-snippets' ),
660 ]
661 );
662
663 $this->enqueue_tag_assets();
664 }
665
666 /**
667 * Enqueue the necessary assets for the tag editor
668 */
669 protected function enqueue_tag_assets() {
670
671 if ( ! get_setting( 'general', 'enable_tags' ) ) {
672 return;
673 }
674
675 wp_enqueue_script(
676 'code-snippets-edit-menu-tags',
677 plugins_url( 'js/min/tags.js', code_snippets()->file ),
678 [],
679 code_snippets()->version,
680 true
681 );
682
683 $options = apply_filters( 'code_snippets/tag_editor_options',
684 array(
685 'allow_spaces' => true,
686 'available_tags' => get_all_snippet_tags(),
687 )
688 );
689
690 $inline_script = 'var code_snippets_tags = ' . wp_json_encode( $options ) . ';';
691
692 wp_add_inline_script( 'code-snippets-edit-menu-tags', $inline_script, 'before' );
693 }
694
695 /**
696 * Remove the old CodeMirror version used by the Debug Bar Console plugin
697 * that is messing up the snippet editor
698 */
699 public function remove_debug_bar_codemirror() {
700
701 /* Try to discern if we are on the single snippet page as good as we can at this early time */
702 if ( ! is_admin() || 'admin.php' !== $GLOBALS['pagenow'] ) {
703 return;
704 }
705
706 if ( ! isset( $_GET['page'] ) || code_snippets()->get_menu_slug( 'edit' ) !== $_GET['page'] && code_snippets()->get_menu_slug( 'settings' ) !== $_GET['page'] ) {
707 return;
708 }
709
710 remove_action( 'debug_bar_enqueue_scripts', 'debug_bar_console_scripts' );
711 }
712
713 /**
714 * Retrieve a list of submit actions for a given snippet
715 *
716 * @param Snippet $snippet The snippet currently being edited.
717 * @param bool $extra_actions Whether to include additional actions alongside save actions.
718 *
719 * @return array Two-dimensional array with action name keyed to description.
720 */
721 public function get_actions_list( $snippet, $extra_actions = true ) {
722 $actions = [ 'save_snippet' => __( 'Save Changes', 'code-snippets' ) ];
723
724 if ( 'single-use' === $snippet->scope ) {
725 $actions['save_snippet_execute'] = __( 'Save Changes and Execute Once', 'code-snippets' );
726
727 } elseif ( ! $snippet->shared_network || ! is_network_admin() ) {
728
729 if ( $snippet->active ) {
730 $actions['save_snippet_deactivate'] = __( 'Save Changes and Deactivate', 'code-snippets' );
731 } else {
732 $actions['save_snippet_activate'] = __( 'Save Changes and Activate', 'code-snippets' );
733 }
734 }
735
736 // Make the 'Save and Activate' button the default if the setting is enabled
737 if ( ! $snippet->active && 'single-use' !== $snippet->scope &&
738 get_setting( 'general', 'activate_by_default' ) ) {
739 $actions = array_reverse( $actions );
740 }
741
742 if ( $extra_actions && 0 !== $snippet->id ) {
743
744 if ( apply_filters( 'code_snippets/enable_downloads', true ) ) {
745 $actions['download_snippet'] = __( 'Download', 'code-snippets' );
746 }
747
748 $actions['export_snippet'] = __( 'Export', 'code-snippets' );
749 $actions['delete_snippet'] = __( 'Delete', 'code-snippets' );
750 }
751
752 return apply_filters( 'code_snippets/admin/submit_actions', $actions, $snippet, $extra_actions );
753 }
754
755 /**
756 * Render the submit buttons for a code snippet
757 *
758 * @param Snippet $snippet The snippet currently being edited.
759 * @param string $size Additional size classes to pass to button.
760 * @param bool $extra_actions Whether to include additional buttons alongside save buttons.
761 */
762 public function render_submit_buttons( $snippet, $size = '', $extra_actions = true ) {
763
764 $actions = $this->get_actions_list( $snippet, $extra_actions );
765 $type = 'primary';
766 $size = $size ? ' ' . $size : '';
767
768 foreach ( $actions as $action => $label ) {
769 $other = null;
770
771 if ( 'delete_snippet' === $action ) {
772 $other = sprintf(
773 'onclick="%s"',
774 esc_js(
775 sprintf(
776 'return confirm("%s");',
777 esc_html__( 'You are about to permanently delete this snippet.', 'code-snippets' ) . "\n" .
778 esc_html__( "'Cancel' to stop, 'OK' to delete.", 'code-snippets' )
779 )
780 )
781 );
782 }
783
784 submit_button( $label, $type . $size, $action, false, $other );
785
786 if ( 'primary' === $type ) {
787 $type = 'secondary';
788 }
789 }
790 }
791
792 /**
793 * Render a list of scopes as ratio controls
794 *
795 * @param array $scopes List of scopes to render, with scope name keyed to label.
796 */
797 public function print_scopes_list( $scopes ) {
798 $scope_icons = Snippet::get_scope_icons();
799
800 foreach ( $scopes as $scope => $label ) {
801 printf( '<label><input type="radio" name="snippet_scope" value="%s"', esc_attr( $scope ) );
802 checked( $scope, $this->snippet->scope );
803 printf( '> <span class="dashicons dashicons-%s"></span> %s</label>', esc_attr( $scope_icons[ $scope ] ), esc_html( $label ) );
804 }
805 }
806
807 /**
808 * Render a keyboard shortcut as HTML.
809 *
810 * @param array|string $modifiers Modifier keys. Can be 'Cmd', 'Ctrl', 'Shift', 'Option', 'Alt'.
811 * @param string $key Keyboard key.
812 *
813 * @return void
814 */
815 protected function render_keyboard_shortcut( $modifiers, $key ) {
816 static $keys = null;
817
818 if ( is_null( $keys ) ) {
819 $keys = array(
820 'Cmd' => _x( 'Cmd', 'keyboard key', 'code-snippets' ),
821 'Ctrl' => _x( 'Ctrl', 'keyboard key', 'code-snippets' ),
822 'Shift' => _x( 'Shift', 'keyboard key', 'code-snippets' ),
823 'Option' => _x( 'Option', 'keyboard key', 'code-snippets' ),
824 'Alt' => _x( 'Alt', 'keyboard key', 'code-snippets' ),
825 'F' => _x( 'F', 'keyboard key', 'code-snippets' ),
826 'G' => _x( 'G', 'keyboard key', 'code-snippets' ),
827 'R' => _x( 'R', 'keyboard key', 'code-snippets' ),
828 'S' => _x( 'S', 'keyboard key', 'code-snippets' ),
829 );
830 }
831
832 if ( ! is_array( $modifiers ) ) {
833 $modifiers = array( $modifiers );
834 }
835
836 foreach ( $modifiers as $modifier ) {
837 if ( 'Ctrl' === $modifier || 'Cmd' === $modifier ) {
838 echo '<kbd class="pc-key">', esc_html( $keys['Ctrl'] ), '</kbd>';
839 echo '<kbd class="mac-key">', esc_html( $keys['Cmd'] ), '</kbd>&hyphen;';
840 } elseif ( 'Option' === $modifier ) {
841 echo '<span class="mac-key"><kbd class="mac-key">', esc_html( $keys['Option'] ), '</kbd>&hyphen;</span>';
842 } else {
843 echo '<kbd>', esc_html( $keys[ $modifier ] ), '</kbd>&hyphen;';
844 }
845 }
846
847 echo '<kbd>', esc_html( $keys[ $key ] ), '</kbd>';
848 }
849 }
850