PluginProbe
Code Snippets / 3.1.0
Code Snippets v3.1.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.1.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 && ( empty( $_REQUEST['id'] ) || 0 === $this->snippet->id || null === $this->snippet->id ) ) {
76 wp_safe_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_safe_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 private function unshare_network_snippet( $snippet_id ) {
180 $shared_snippets = get_site_option( 'shared_network_snippets', array() );
181
182 if ( ! in_array( $snippet_id, $shared_snippets, true ) ) {
183 return;
184 }
185
186 /* Remove the snippet ID from the array */
187 $shared_snippets = array_diff( $shared_snippets, array( $snippet_id ) );
188 update_site_option( 'shared_network_snippets', array_values( $shared_snippets ) );
189
190 /* Deactivate on all sites */
191 $sites = get_sites( [ 'fields' => 'ids' ] );
192
193 foreach ( $sites as $site ) {
194 switch_to_blog( $site );
195 $active_shared_snippets = get_option( 'active_shared_network_snippets' );
196
197 if ( is_array( $active_shared_snippets ) ) {
198 $active_shared_snippets = array_diff( $active_shared_snippets, array( $snippet_id ) );
199 update_option( 'active_shared_network_snippets', $active_shared_snippets );
200 }
201
202 clean_active_snippets_cache( code_snippets()->db->ms_table );
203 }
204
205 restore_current_blog();
206 }
207
208 /**
209 * Display a custom error message when a code error is encountered
210 *
211 * @param string $out Error message content.
212 *
213 * @return string New error message.
214 */
215 private function code_error_callback( $out ) {
216 $error = error_get_last();
217
218 if ( is_null( $error ) ) {
219 return $out;
220 }
221
222 $m = '<h3>' . esc_html__( "Don't Panic", 'code-snippets' ) . '</h3>';
223 /* translators: %d: line where error was produced */
224 $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>';
225 $m .= '<strong>' . esc_html( $error['message'] ) . '</strong>';
226 $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>';
227 $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' );
228 $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>';
229
230 return $m;
231 }
232
233 /**
234 * Validate the snippet code before saving to database
235 *
236 * @param Snippet $snippet Snippet object.
237 *
238 * @return bool Whether the code produces errors.
239 */
240 private function test_code( Snippet $snippet ) {
241
242 if ( empty( $snippet->code ) || 'php' !== $snippet->type ) {
243 return false;
244 }
245
246 ob_start( array( $this, 'code_error_callback' ) );
247
248 $result = eval( $snippet->code );
249
250 ob_end_clean();
251
252 do_action( 'code_snippets/after_execute_snippet', $snippet->id, $snippet->code, $result );
253
254 return false === $result;
255 }
256
257 /**
258 * Save the posted snippet data to the database and redirect
259 */
260 private function save_posted_snippet() {
261
262 /* Build snippet object from fields with 'snippet_' prefix */
263 $snippet = new Snippet();
264
265 foreach ( $_POST as $field => $value ) {
266 if ( 'snippet_' === substr( $field, 0, 8 ) ) {
267
268 /* Remove the 'snippet_' prefix from field name and set it on the object */
269 $snippet->set_field( substr( $field, 8 ), stripslashes( $value ) );
270 }
271 }
272
273 if ( isset( $_POST['save_snippet_execute'] ) && 'single-use' !== $snippet->scope ) {
274 unset( $_POST['save_snippet_execute'] );
275 $_POST['save_snippet'] = 'yes';
276 }
277
278 /* Activate or deactivate the snippet before saving if we clicked the button */
279
280 if ( isset( $_POST['save_snippet_execute'] ) ) {
281 $snippet->active = 1;
282 } elseif ( isset( $_POST['snippet_sharing'] ) && 'on' === $_POST['snippet_sharing'] ) {
283 // Shared network snippets cannot be network-activated.
284 $snippet->active = 0;
285 unset( $_POST['save_snippet_activate'], $_POST['save_snippet_deactivate'] );
286 } elseif ( isset( $_POST['save_snippet_activate'] ) ) {
287 $snippet->active = 1;
288 } elseif ( isset( $_POST['save_snippet_deactivate'] ) ) {
289 $snippet->active = 0;
290 }
291
292 if ( 'php' === $snippet->type ) {
293
294 /* Remove <?php and <? from beginning of snippet */
295 $snippet->code = preg_replace( '|^\s*<\?(php)?|', '', $snippet->code );
296 /* Remove ?> from end of snippet */
297 $snippet->code = preg_replace( '|\?>\s*$|', '', $snippet->code );
298
299 /* Deactivate snippet if code contains errors */
300 if ( $snippet->active && 'single-use' !== $snippet->scope ) {
301 $validator = new Validator( $snippet->code );
302 $code_error = $validator->validate();
303
304 if ( ! $code_error ) {
305 $code_error = $this->test_code( $snippet );
306 }
307
308 if ( $code_error ) {
309 $snippet->active = 0;
310 }
311 }
312 }
313
314 /* Save the snippet to the database */
315 $snippet_id = save_snippet( $snippet );
316
317 /* Update the shared network snippets if necessary */
318 if ( $snippet_id && is_network_admin() ) {
319
320 if ( isset( $_POST['snippet_sharing'] ) && 'on' === $_POST['snippet_sharing'] ) {
321 $shared_snippets = get_site_option( 'shared_network_snippets', array() );
322
323 /* Add the snippet ID to the array if it isn't already */
324 if ( ! in_array( $snippet_id, $shared_snippets, true ) ) {
325 $shared_snippets[] = $snippet_id;
326 update_site_option( 'shared_network_snippets', array_values( $shared_snippets ) );
327 }
328 } else {
329 $this->unshare_network_snippet( $snippet_id );
330 }
331 }
332
333 /* If the saved snippet ID is invalid, display an error message */
334 if ( ! $snippet_id || $snippet_id < 1 ) {
335 /* An error occurred */
336 wp_safe_redirect( add_query_arg( 'result', 'save-error', code_snippets()->get_menu_url( 'add' ) ) );
337 exit;
338 }
339
340 /* Display message if a parse error occurred */
341 if ( isset( $code_error ) && $code_error ) {
342 wp_safe_redirect(
343 add_query_arg(
344 array(
345 'id' => $snippet_id,
346 'result' => 'code-error',
347 ),
348 code_snippets()->get_menu_url( 'edit' )
349 )
350 );
351 exit;
352 }
353
354 /* Set the result depending on if the snippet was just added */
355 $result = isset( $_POST['snippet_id'] ) ? 'updated' : 'added';
356
357 /* Append a suffix if the snippet was activated or deactivated */
358 if ( isset( $_POST['save_snippet_activate'] ) ) {
359 $result .= '-and-activated';
360 } elseif ( isset( $_POST['save_snippet_deactivate'] ) ) {
361 $result .= '-and-deactivated';
362 } elseif ( isset( $_POST['save_snippet_execute'] ) ) {
363 $result .= '-and-executed';
364 }
365
366 /* Redirect to edit snippet page */
367 $redirect_uri = add_query_arg(
368 array(
369 'id' => $snippet_id,
370 'result' => $result,
371 ),
372 code_snippets()->get_menu_url( 'edit' )
373 );
374
375 wp_safe_redirect( esc_url_raw( $redirect_uri ) );
376 exit;
377 }
378
379 /**
380 * Add a description editor to the single snippet page
381 *
382 * @param Snippet $snippet The snippet being used for this page.
383 */
384 public function render_description_editor( Snippet $snippet ) {
385 $settings = Settings\get_settings_values();
386 $settings = $settings['description_editor'];
387
388 echo '<h2><label for="snippet_description">', esc_html__( 'Description', 'code-snippets' ), '</label></h2>';
389
390 remove_editor_styles(); // Stop custom theme styling interfering with the editor.
391
392 wp_editor(
393 $snippet->desc,
394 'description',
395 apply_filters(
396 'code_snippets/admin/description_editor_settings',
397 array(
398 'textarea_name' => 'snippet_description',
399 'textarea_rows' => $settings['rows'],
400 'teeny' => ! $settings['use_full_mce'],
401 'media_buttons' => $settings['media_buttons'],
402 )
403 )
404 );
405 }
406
407 /**
408 * Render the interface for editing snippet tags
409 *
410 * @param Snippet $snippet The snippet currently being edited.
411 */
412 public function render_tags_editor( Snippet $snippet ) {
413
414 ?>
415 <h2 style="margin: 25px 0 10px;">
416 <label for="snippet_tags" style="cursor: auto;">
417 <?php esc_html_e( 'Tags', 'code-snippets' ); ?>
418 </label>
419 </h2>
420
421 <input type="text" id="snippet_tags" name="snippet_tags" style="width: 100%;"
422 placeholder="<?php esc_html_e( 'Enter a list of tags; separated by commas', 'code-snippets' ); ?>"
423 value="<?php echo esc_attr( $snippet->tags_list ); ?>"/>
424 <?php
425 }
426
427 /**
428 * Render the snippet priority setting
429 *
430 * @param Snippet $snippet The snippet currently being edited.
431 */
432 public function render_priority_setting( Snippet $snippet ) {
433 if ( 'html' === $snippet->type ) {
434 return;
435 }
436
437 ?>
438 <p class="snippet-priority"
439 title="<?php esc_attr_e( 'Snippets with a lower priority number will run before those with a higher number.', 'code-snippets' ); ?>">
440 <label for="snippet_priority"><?php esc_html_e( 'Priority', 'code-snippets' ); ?></label>
441
442 <input name="snippet_priority" type="number" id="snippet_priority"
443 value="<?php echo esc_attr( $snippet->priority ); ?>">
444 </p>
445 <?php
446 }
447
448 /**
449 * Render the setting for shared network snippets
450 *
451 * @param Snippet $snippet The snippet currently being edited.
452 */
453 public function render_multisite_sharing_setting( $snippet ) {
454 $shared_snippets = get_site_option( 'shared_network_snippets', array() );
455 ?>
456
457 <div class="snippet-sharing-setting">
458 <h2 class="screen-reader-text"><?php esc_html_e( 'Sharing Settings', 'code-snippets' ); ?></h2>
459 <label for="snippet_sharing">
460 <input type="checkbox" name="snippet_sharing"
461 <?php checked( in_array( $snippet->id, $shared_snippets, true ) ); ?>>
462 <?php esc_html_e( 'Allow this snippet to be activated on individual sites on the network', 'code-snippets' ); ?>
463 </label>
464 </div>
465
466 <?php
467 }
468
469 /**
470 * Render additional save buttons above the snippet editor.
471 *
472 * @param Snippet $snippet Snippet currently being edited.
473 */
474 public function render_extra_submit_buttons( Snippet $snippet ) {
475
476 $actions['save_snippet'] = array(
477 __( 'Save Changes', 'code-snippets' ),
478 __( 'Save Snippet', 'code-snippets' ),
479 );
480
481 if ( 'html' !== $snippet->type ) {
482
483 if ( 'single-use' === $snippet->scope ) {
484 $actions['save_snippet_execute'] = array(
485 __( 'Execute Once', 'code-snippets' ),
486 __( 'Save Snippet and Execute Once', 'code-snippets' ),
487 );
488
489 } elseif ( ! $snippet->shared_network || ! is_network_admin() ) {
490
491 if ( $snippet->active ) {
492 $actions['save_snippet_deactivate'] = array(
493 __( 'Deactivate', 'code-snippets' ),
494 __( 'Save Snippet and Deactivate', 'code-snippets' ),
495 );
496
497 } else {
498 $actions['save_snippet_activate'] = array(
499 __( 'Activate', 'code-snippets' ),
500 __( 'Save Snippet and Activate', 'code-snippets' ),
501 );
502 }
503 }
504 }
505
506 foreach ( $actions as $action => $labels ) {
507 $other_attributes = array(
508 'title' => $labels[1],
509 'id' => $action . '_extra',
510 );
511 submit_button( $labels[0], 'secondary small', $action, false, $other_attributes );
512 }
513 }
514
515 /**
516 * Render a control for changing the code editor text direction
517 */
518 public function render_direction_setting() {
519 ?>
520 <label class="screen-reader-text" for="snippet-code-direction">
521 <?php esc_html_e( 'Code Direction', 'code-snippets' ); ?>
522 </label>
523 <select id="snippet-code-direction">
524 <option value="ltr"><?php esc_html_e( 'LTR', 'code-snippets' ); ?></option>
525 <option value="rtl"><?php esc_html_e( 'RTL', 'code-snippets' ); ?></option>
526 </select>
527 <?php
528 }
529
530 /**
531 * Retrieve the first error in a snippet's code
532 *
533 * @param int $snippet_id Snippet ID.
534 *
535 * @return array|bool Error if execution failed, otherwise false.
536 */
537 private function get_snippet_error( $snippet_id ) {
538
539 if ( ! intval( $snippet_id ) ) {
540 return false;
541 }
542
543 $snippet = get_snippet( intval( $snippet_id ) );
544
545 if ( '' === $snippet->code ) {
546 return false;
547 }
548
549 $validator = new Validator( $snippet->code );
550 $error = $validator->validate();
551
552 if ( $error ) {
553 return $error;
554 }
555
556 ob_start();
557 $result = eval( $snippet->code );
558 ob_end_clean();
559
560 if ( false !== $result ) {
561 return false;
562 }
563
564 $error = error_get_last();
565
566 if ( is_null( $error ) ) {
567 return false;
568 }
569
570 return $error;
571 }
572
573 /**
574 * Print the status and error messages
575 */
576 protected function print_messages() {
577
578 if ( ! isset( $_REQUEST['result'] ) ) {
579 return;
580 }
581
582 $result = sanitize_key( $_REQUEST['result'] );
583
584 if ( 'code-error' === $result ) {
585 $error = isset( $_REQUEST['id'] ) ? $this->get_snippet_error( intval( $_REQUEST['id'] ) ) : false;
586
587 if ( $error ) {
588 /* translators: %d: line of file where error originated */
589 $text = __( 'The snippet has been deactivated due to an error on line %d:', 'code-snippets' );
590
591 printf(
592 '<div id="message" class="error fade"><p>%s</p><p><strong>%s</strong></p></div>',
593 sprintf( esc_html( $text ), intval( $error['line'] ) ),
594 wp_kses_post( $error['message'] )
595 );
596
597 } else {
598 echo '<div id="message" class="error fade"><p>';
599 esc_html_e( 'The snippet has been deactivated due to an error in the code.', 'code-snippets' );
600 echo '</p></div>';
601 }
602
603 return;
604 }
605
606 if ( 'save-error' === $result ) {
607 echo '<div id="message" class="error fade"><p>';
608 esc_html_e( 'An error occurred when saving the snippet.', 'code-snippets' );
609 echo '</p></div>';
610 return;
611 }
612
613 $messages = array(
614 'added' => __( 'Snippet <strong>added</strong>.', 'code-snippets' ),
615 'updated' => __( 'Snippet <strong>updated</strong>.', 'code-snippets' ),
616 'added-and-activated' => __( 'Snippet <strong>added</strong> and <strong>activated</strong>.', 'code-snippets' ),
617 'updated-and-executed' => __( 'Snippet <strong>added</strong> and <strong>executed</strong>.', 'code-snippets' ),
618 'updated-and-activated' => __( 'Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets' ),
619 'updated-and-deactivated' => __( 'Snippet <strong>updated</strong> and <strong>deactivated</strong>.', 'code-snippets' ),
620 );
621
622 if ( isset( $messages[ $result ] ) ) {
623 echo '<div id="message" class="updated fade"><p>', wp_kses( $messages[ $result ], [ 'strong' => [] ] ), '</p></div>';
624
625 }
626 }
627
628 /**
629 * Enqueue assets for the edit menu
630 */
631 public function enqueue_assets() {
632 $plugin = code_snippets();
633 $rtl = is_rtl() ? '-rtl' : '';
634
635 enqueue_code_editor( $this->snippet->type );
636
637 wp_enqueue_style(
638 'code-snippets-edit',
639 plugins_url( "css/min/edit$rtl.css", $plugin->file ),
640 [ 'code-editor' ],
641 $plugin->version
642 );
643
644 wp_enqueue_script(
645 'code-snippets-edit-menu',
646 plugins_url( 'js/min/edit.js', $plugin->file ),
647 [ 'code-snippets-code-editor' ],
648 $plugin->version,
649 true
650 );
651
652 wp_localize_script(
653 'code-snippets-edit-menu',
654 'code_snippets_edit_i18n',
655 [
656 'missing_title_code' => esc_attr__( 'This snippet has no code or title. Continue?', 'code-snippets' ),
657 'missing_title' => esc_attr__( 'This snippet has no title. Continue?', 'code-snippets' ),
658 'missing_code' => esc_attr__( 'This snippet has no snippet code. Continue?', 'code-snippets' ),
659 ]
660 );
661
662 $this->enqueue_tag_assets();
663 }
664
665 /**
666 * Enqueue the necessary assets for the tag editor
667 */
668 protected function enqueue_tag_assets() {
669
670 if ( ! get_setting( 'general', 'enable_tags' ) ) {
671 return;
672 }
673
674 wp_enqueue_script(
675 'code-snippets-edit-menu-tags',
676 plugins_url( 'js/min/tags.js', code_snippets()->file ),
677 [],
678 code_snippets()->version,
679 true
680 );
681
682 $options = apply_filters(
683 '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