PluginProbe
Code Snippets / 3.10.2
Code Snippets v3.10.2
4.0.0-beta.2 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 All 65 releases
code-snippets / php / Admin / Menus / Manage / Manage_Menu.php

Manage_Menu.php in Code Snippets 3.10.2, at php/Admin/Menus/Manage/Manage_Menu.php

322 lines 9.2 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\Admin\Menus\Manage;
4
5 use Code_Snippets\Admin\Contextual_Help;
6 use Code_Snippets\Admin\Menus\Admin_Menu;
7 use Code_Snippets\Controller\Cloud_Search_Controller;
8 use function Code_Snippets\activate_snippet;
9 use function Code_Snippets\code_snippets;
10 use function Code_Snippets\get_snippet;
11 use function Code_Snippets\Settings\get_setting;
12 use const Code_Snippets\PLUGIN_FILE;
13 use const Code_Snippets\PLUGIN_VERSION;
14
15 /**
16 * Provides the manage snippets admin menu.
17 */
18 class Manage_Menu extends Admin_Menu {
19
20 /**
21 * Default number of snippets shown per page in the manage table.
22 */
23 private const DEFAULT_SNIPPETS_PER_PAGE = 100;
24
25 /**
26 * Manage menu Screen Options.
27 *
28 * @var Manage_Menu_Screen_Options
29 */
30 private Manage_Menu_Screen_Options $screen_options;
31
32 /**
33 * Class constructor.
34 */
35 public function __construct() {
36 parent::__construct(
37 'manage',
38 _x( 'All Snippets', 'menu label', 'code-snippets' ),
39 __( 'Snippets', 'code-snippets' )
40 );
41
42 if ( code_snippets()->is_compact_menu() ) {
43 add_action( 'admin_menu', array( $this, 'register_compact_menu' ), 2 );
44 add_action( 'network_admin_menu', array( $this, 'register_compact_menu' ), 2 );
45 }
46
47 $this->screen_options = new Manage_Menu_Screen_Options();
48 new Manage_Menu_Bulk_Download();
49
50 add_action( 'admin_menu', array( $this, 'register_upgrade_menu' ), 500 );
51 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_menu_css' ) );
52 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_menu_css' ] );
53 }
54
55 /**
56 * Register the top-level 'Snippets' menu and associated 'Manage' subpage
57 */
58 public function register() {
59 add_menu_page(
60 __( 'Snippets', 'code-snippets' ),
61 _x( 'Snippets', 'top-level menu label', 'code-snippets' ),
62 code_snippets()->get_cap(),
63 code_snippets()->get_menu_slug(),
64 [ $this, 'render' ],
65 'none', // Added through CSS as a mask to prevent loading 'blinking'.
66 apply_filters( 'code_snippets/admin/menu_position', is_network_admin() ? 21 : 67 )
67 );
68
69 parent::register();
70 }
71
72 /**
73 * Render the snippets table interface.
74 *
75 * @return void
76 */
77 public function render() {
78 echo '<div id="manage-snippets-container" class="wrap"></div>';
79 }
80
81 /**
82 * Register the 'upgrade' menu item.
83 *
84 * @return void
85 */
86 public function register_upgrade_menu() {
87 if ( code_snippets()->licensing->is_licensed() || get_setting( 'general', 'hide_upgrade_menu' ) ) {
88 return;
89 }
90
91 $menu_title = sprintf(
92 '<span class="button button-primary code-snippets-upgrade-button">%s %s</span>',
93 _x( 'Go Pro', 'top-level menu label', 'code-snippets' ),
94 '<span class="dashicons dashicons-external" aria-hidden="true"></span>'
95 );
96
97 $hook = add_submenu_page(
98 code_snippets()->get_menu_slug(),
99 __( 'Upgrade to Pro', 'code-snippets' ),
100 $menu_title,
101 code_snippets()->get_cap(),
102 'code_snippets_upgrade',
103 '__return_empty_string',
104 100
105 );
106
107 add_action( "load-$hook", [ $this, 'load_upgrade_menu' ] );
108 }
109
110 /**
111 * Print CSS required for the admin menu icon.
112 *
113 * @return void
114 */
115 public function enqueue_menu_css() {
116 wp_enqueue_style(
117 'code-snippets-menu',
118 plugins_url( 'dist/menu.css', PLUGIN_FILE ),
119 [],
120 PLUGIN_VERSION
121 );
122 }
123
124 /**
125 * Redirect the user upon opening the upgrade menu.
126 *
127 * @return void
128 */
129 public function load_upgrade_menu() {
130 wp_safe_redirect( 'https://snipco.de/JE2f' );
131 exit;
132 }
133
134 /**
135 * Retrieve every hookname registered by this menu, including the compact
136 * Tools submenu hookname when compact mode is active.
137 *
138 * @return string[]
139 */
140 public function get_hooknames(): array {
141 $hooknames = parent::get_hooknames();
142
143 if ( code_snippets()->is_compact_menu() ) {
144 $hooknames[] = get_plugin_page_hookname( $this->base_slug, 'tools.php' );
145 }
146
147 return $hooknames;
148 }
149
150 /**
151 * Add menu pages for the compact menu
152 */
153 public function register_compact_menu() {
154 if ( ! code_snippets()->is_compact_menu() ) {
155 return;
156 }
157
158 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Value is matched to known classes.
159 $sub = code_snippets()->get_menu_slug( isset( $_GET['sub'] ) ? sanitize_key( $_GET['sub'] ) : 'snippets' );
160 $classmap = [
161 'snippets' => 'manage',
162 'add-snippet' => 'edit',
163 'edit-snippet' => 'edit',
164 'import-code-snippets' => 'import',
165 'snippets-settings' => 'settings',
166 ];
167 $menus = code_snippets()->admin->menus;
168 $class = isset( $classmap[ $sub ], $menus[ $classmap[ $sub ] ] ) ? $menus[ $classmap[ $sub ] ] : $this;
169
170 $hook = add_submenu_page(
171 'tools.php',
172 __( 'Snippets', 'code-snippets' ),
173 _x( 'Snippets', 'tools submenu label', 'code-snippets' ),
174 code_snippets()->get_cap(),
175 code_snippets()->get_menu_slug(),
176 [ $class, 'render' ]
177 );
178
179 add_action( 'load-' . $hook, [ $class, 'load' ] );
180 }
181
182 /**
183 * Nonce action guarding the run-once request.
184 */
185 public const RUN_ONCE_NONCE = 'code_snippets_run_once';
186
187 /**
188 * Run a single-use snippet, when asked to by the snippets list.
189 *
190 * Activating the snippet is all that is required: single-use snippets are
191 * executed and then deactivated again on the next page load, so redirecting
192 * afterwards both runs the code and returns the snippet to its resting
193 * state. This mirrors what the list table did before the snippets list
194 * moved to the REST API, at which point the button was left pointing at a
195 * URL that nothing handled.
196 *
197 * @return void
198 */
199 private function handle_run_once(): void {
200 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified immediately below.
201 $action = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : '';
202
203 if ( 'run-once' !== $action ) {
204 return;
205 }
206
207 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified immediately below.
208 $nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
209
210 if ( ! wp_verify_nonce( $nonce, self::RUN_ONCE_NONCE ) || ! code_snippets()->current_user_can() ) {
211 return;
212 }
213
214 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified above.
215 $snippet_id = isset( $_REQUEST['snippet'] ) ? absint( wp_unslash( $_REQUEST['snippet'] ) ) : 0;
216
217 if ( ! $snippet_id ) {
218 return;
219 }
220
221 // The network context comes from the current screen, never the request,
222 // so a subsite administrator cannot target a network snippet.
223 $network = is_network_admin();
224 $snippet = get_snippet( $snippet_id, $network );
225
226 // Only single-use snippets are run this way. Activating anything else
227 // would leave it permanently on while the notice claimed it ran once.
228 if ( ! $snippet || 0 === $snippet->id || 'single-use' !== $snippet->scope ) {
229 wp_safe_redirect( remove_query_arg( [ 'action', 'snippet', 'network', '_wpnonce', 'result' ] ) );
230 exit;
231 }
232
233 // Safe mode skips execution, so activating here would leave the snippet on
234 // without ever running it, behind a false success notice. Report it instead.
235 if ( \Code_Snippets\Integration\Evaluate_Functions::is_safe_mode_active() ) {
236 wp_safe_redirect(
237 add_query_arg(
238 [ 'result' => 'run-once-safe-mode' ],
239 remove_query_arg( [ 'action', 'snippet', 'network', '_wpnonce', 'result' ] )
240 )
241 );
242 exit;
243 }
244
245 // An already-active snippet has effectively run, so treat it as success.
246 $result = $snippet->active ? $snippet : activate_snippet( $snippet_id, $network );
247
248 wp_safe_redirect(
249 add_query_arg(
250 [ 'result' => is_string( $result ) ? 'run-once-failed' : 'executed' ],
251 remove_query_arg( [ 'action', 'snippet', 'network', '_wpnonce', 'result' ] )
252 )
253 );
254 exit;
255 }
256
257 /**
258 * Executed when the admin page is loaded.
259 */
260 public function load() {
261 parent::load();
262
263 $this->handle_run_once();
264 $this->screen_options->load();
265
266 if ( $this->screen_options->is_upsell_view() ) {
267 return;
268 }
269
270 $contextual_help = new Contextual_Help( 'edit' );
271 $contextual_help->load();
272 }
273
274 /**
275 * Enqueue scripts and stylesheets for the admin page.
276 */
277 public function enqueue_assets() {
278 $assets = new Manage_Menu_Assets( $this->screen_options );
279 $assets->enqueue( self::$script_deps, self::$style_deps );
280 }
281
282 /**
283 * Get the number of snippets to show per page in the cloud search.
284 *
285 * The value defaults to the user's local snippets-per-page preference but can
286 * be overridden independently via the `code_snippets/cloud_search/per_page` filter.
287 * The result is clamped to the REST API's per_page maximum of 100.
288 *
289 * @return int
290 */
291 public static function get_cloud_search_per_page(): int {
292 $per_page = intval( apply_filters( 'code_snippets/cloud_search/per_page', self::get_snippets_per_page() ) );
293 return min( Cloud_Search_Controller::MAX_RESULTS_PER_PAGE, max( 1, $per_page ) );
294 }
295
296 /**
297 * Get the default number of snippets to show per page.
298 *
299 * @return int
300 */
301 public static function get_default_snippets_per_page(): int {
302 $default = apply_filters( 'code_snippets/snippets_per_page_default', self::DEFAULT_SNIPPETS_PER_PAGE );
303 return max( 1, intval( $default ) );
304 }
305
306 /**
307 * Get the number of snippets to show per page.
308 *
309 * @return int
310 */
311 public static function get_snippets_per_page(): int {
312 $per_page = intval( get_user_option( 'snippets_per_page' ) );
313
314 return intval(
315 apply_filters(
316 'snippets_per_page',
317 $per_page > 0 ? $per_page : self::get_default_snippets_per_page()
318 )
319 );
320 }
321 }
322