PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 7.6.1
LiteSpeed Cache v7.6.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / src / activation.cls.php
litespeed-cache / src Last commit date
cdn 7 months ago data_structure 7 months ago activation.cls.php 7 months ago admin-display.cls.php 7 months ago admin-settings.cls.php 7 months ago admin.cls.php 7 months ago api.cls.php 7 months ago avatar.cls.php 7 months ago base.cls.php 7 months ago cdn.cls.php 7 months ago cloud.cls.php 7 months ago conf.cls.php 7 months ago control.cls.php 7 months ago core.cls.php 7 months ago crawler-map.cls.php 7 months ago crawler.cls.php 7 months ago css.cls.php 7 months ago data.cls.php 7 months ago data.upgrade.func.php 7 months ago db-optm.cls.php 7 months ago debug2.cls.php 7 months ago doc.cls.php 7 months ago error.cls.php 7 months ago esi.cls.php 7 months ago file.cls.php 7 months ago gui.cls.php 7 months ago health.cls.php 7 months ago htaccess.cls.php 7 months ago img-optm.cls.php 7 months ago import.cls.php 7 months ago import.preset.cls.php 7 months ago lang.cls.php 7 months ago localization.cls.php 7 months ago media.cls.php 7 months ago metabox.cls.php 7 months ago object-cache-wp.cls.php 7 months ago object-cache.cls.php 7 months ago object.lib.php 7 months ago optimize.cls.php 7 months ago optimizer.cls.php 7 months ago placeholder.cls.php 7 months ago purge.cls.php 7 months ago report.cls.php 7 months ago rest.cls.php 7 months ago root.cls.php 7 months ago router.cls.php 7 months ago str.cls.php 7 months ago tag.cls.php 7 months ago task.cls.php 7 months ago tool.cls.php 7 months ago ucss.cls.php 7 months ago utility.cls.php 7 months ago vary.cls.php 7 months ago vpi.cls.php 7 months ago
activation.cls.php
722 lines
1 <?php
2 /**
3 * The plugin activation class.
4 *
5 * @since 1.1.0
6 * @since 1.5 Moved into /inc
7 * @package LiteSpeed
8 * @subpackage LiteSpeed/inc
9 * @author LiteSpeed Technologies <info@litespeedtech.com>
10 */
11
12 namespace LiteSpeed;
13
14 defined( 'WPINC' ) || exit();
15
16 /**
17 * Class Activation
18 *
19 * Handles plugin activation, deactivation, and related file management.
20 *
21 * @since 1.1.0
22 */
23 class Activation extends Base {
24
25 const TYPE_UPGRADE = 'upgrade';
26 const TYPE_INSTALL_3RD = 'install_3rd';
27 const TYPE_INSTALL_ZIP = 'install_zip';
28 const TYPE_DISMISS_RECOMMENDED = 'dismiss_recommended';
29
30 const NETWORK_TRANSIENT_COUNT = 'lscwp_network_count';
31
32 /**
33 * Data file path for configuration.
34 *
35 * @since 4.1
36 * @var string
37 */
38 private static $data_file;
39
40 /**
41 * Construct
42 *
43 * Initializes the data file path.
44 *
45 * @since 4.1
46 */
47 public function __construct() {
48 self::$data_file = LSCWP_CONTENT_DIR . '/' . self::CONF_FILE;
49 }
50
51 /**
52 * The activation hook callback.
53 *
54 * Handles plugin activation tasks, including file creation and multisite setup.
55 *
56 * @since 1.0.0
57 * @access public
58 */
59 public static function register_activation() {
60 $count = 0;
61 ! defined( 'LSCWP_LOG_TAG' ) && define( 'LSCWP_LOG_TAG', 'Activate_' . get_current_blog_id() );
62
63 /* Network file handler */
64 if ( is_multisite() ) {
65 $count = self::get_network_count();
66 if ( false !== $count ) {
67 $count = (int) $count + 1;
68 set_site_transient( self::NETWORK_TRANSIENT_COUNT, $count, DAY_IN_SECONDS );
69 }
70
71 if ( ! is_network_admin() ) {
72 if ( 1 === $count ) {
73 // Only itself is activated, set .htaccess with only CacheLookUp
74 try {
75 Htaccess::cls()->insert_ls_wrapper();
76 } catch ( \Exception $ex ) {
77 Admin_Display::error( $ex->getMessage() );
78 }
79 }
80 }
81 }
82 self::cls()->update_files();
83
84 if ( defined( 'LSCWP_REF' ) && 'whm' === LSCWP_REF ) {
85 GUI::update_option( GUI::WHM_MSG, GUI::WHM_MSG_VAL );
86 }
87 }
88
89 /**
90 * Uninstall plugin
91 *
92 * Removes all LiteSpeed Cache settings and data.
93 *
94 * @since 1.1.0
95 * @since 7.3 Updated to remove all settings.
96 * @access public
97 */
98 public static function uninstall_litespeed_cache() {
99 Task::destroy();
100
101 if ( is_multisite() ) {
102 // Save main site id
103 $current_blog = get_current_blog_id();
104
105 // get all sites
106 $sub_sites = get_sites();
107
108 // clear foreach site
109 foreach ( $sub_sites as $sub_site ) {
110 $sub_blog_id = (int) $sub_site->blog_id;
111 if ( $sub_blog_id !== $current_blog ) {
112 // Switch to blog
113 switch_to_blog( $sub_blog_id );
114
115 // Delete site options
116 self::delete_settings();
117
118 // Delete site tables
119 Data::cls()->tables_del();
120 }
121 }
122
123 // Return to main site
124 switch_to_blog( $current_blog );
125 }
126
127 // Delete current blog/site
128 // Delete options
129 self::delete_settings();
130
131 // Delete site tables
132 Data::cls()->tables_del();
133
134 if ( file_exists( LITESPEED_STATIC_DIR ) ) {
135 File::rrmdir( LITESPEED_STATIC_DIR );
136 }
137
138 Cloud::version_check( 'uninstall' );
139 }
140
141 /**
142 * Remove all litespeed settings.
143 *
144 * Deletes all LiteSpeed Cache options from the database.
145 *
146 * @since 7.3
147 * @access private
148 */
149 private static function delete_settings() {
150 global $wpdb;
151
152 // phpcs:ignore WordPress.DB.DirectDatabaseQuery
153 $wpdb->query($wpdb->prepare("DELETE FROM `$wpdb->options` WHERE option_name LIKE %s", 'litespeed.%'));
154 }
155
156 /**
157 * Get the blog ids for the network. Accepts function arguments.
158 *
159 * @since 1.0.12
160 * @access public
161 * @param array $args Arguments for get_sites().
162 * @return array The array of blog ids.
163 */
164 public static function get_network_ids( $args = array() ) {
165 $args['fields'] = 'ids';
166 $blogs = get_sites( $args );
167
168 return $blogs;
169 }
170
171 /**
172 * Gets the count of active litespeed cache plugins on multisite.
173 *
174 * @since 1.0.12
175 * @access private
176 * @return int|false Number of active LSCWP or false if none.
177 */
178 private static function get_network_count() {
179 $count = get_site_transient( self::NETWORK_TRANSIENT_COUNT );
180 if ( false !== $count ) {
181 return (int) $count;
182 }
183 // need to update
184 $default = array();
185 $count = 0;
186
187 $sites = self::get_network_ids( array( 'deleted' => 0 ) );
188 if ( empty( $sites ) ) {
189 return false;
190 }
191
192 foreach ( $sites as $site ) {
193 $bid = is_object( $site ) && property_exists( $site, 'blog_id' ) ? $site->blog_id : $site;
194 $plugins = get_blog_option( $bid, 'active_plugins', $default );
195 if ( ! empty( $plugins ) && in_array( LSCWP_BASENAME, $plugins, true ) ) {
196 ++$count;
197 }
198 }
199
200 /**
201 * In case this is called outside the admin page
202 *
203 * @see https://codex.wordpress.org/Function_Reference/is_plugin_active_for_network
204 * @since 2.0
205 */
206 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
207 require_once ABSPATH . '/wp-admin/includes/plugin.php';
208 }
209
210 if ( is_plugin_active_for_network( LSCWP_BASENAME ) ) {
211 ++$count;
212 }
213 return $count;
214 }
215
216 /**
217 * Is this deactivate call the last active installation on the multisite network?
218 *
219 * @since 1.0.12
220 * @access private
221 */
222 private static function is_deactivate_last() {
223 $count = self::get_network_count();
224 if ( false === $count ) {
225 return false;
226 }
227 if ( 1 !== $count ) {
228 // Not deactivating the last one.
229 --$count;
230 set_site_transient( self::NETWORK_TRANSIENT_COUNT, $count, DAY_IN_SECONDS );
231 return false;
232 }
233
234 delete_site_transient( self::NETWORK_TRANSIENT_COUNT );
235 return true;
236 }
237
238 /**
239 * The deactivation hook callback.
240 *
241 * Initializes all clean up functionalities.
242 *
243 * @since 1.0.0
244 * @access public
245 */
246 public static function register_deactivation() {
247 Task::destroy();
248
249 ! defined( 'LSCWP_LOG_TAG' ) && define( 'LSCWP_LOG_TAG', 'Deactivate_' . get_current_blog_id() );
250
251 Purge::purge_all();
252
253 if ( is_multisite() ) {
254 if ( ! self::is_deactivate_last() ) {
255 if ( is_network_admin() ) {
256 // Still other activated subsite left, set .htaccess with only CacheLookUp
257 try {
258 Htaccess::cls()->insert_ls_wrapper();
259 } catch ( \Exception $ex ) {
260 Admin_Display::error($ex->getMessage());
261 }
262 }
263 return;
264 }
265 }
266
267 /* 1) wp-config.php; */
268
269 try {
270 self::cls()->manage_wp_cache_const( false );
271 } catch ( \Exception $ex ) {
272 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.PHP.DevelopmentFunctions.error_log_error_log
273 error_log( 'In wp-config.php: WP_CACHE could not be set to false during deactivation!' );
274
275 Admin_Display::error( $ex->getMessage() );
276 }
277
278 /* 2) adv-cache.php; Dropped in v3.0.4 */
279
280 /* 3) object-cache.php; */
281
282 Object_Cache::cls()->del_file();
283
284 /* 4) .htaccess; */
285
286 try {
287 Htaccess::cls()->clear_rules();
288 } catch ( \Exception $ex ) {
289 Admin_Display::error( $ex->getMessage() );
290 }
291
292 /* 5) .litespeed_conf.dat; */
293
294 self::del_conf_data_file();
295
296 /* 6) delete option lscwp_whm_install */
297
298 // delete in case it's not deleted prior to deactivation.
299 GUI::dismiss_whm();
300 }
301
302 /**
303 * Manage related files based on plugin latest conf
304 *
305 * Handle files:
306 * 1) wp-config.php;
307 * 2) adv-cache.php;
308 * 3) object-cache.php;
309 * 4) .htaccess;
310 * 5) .litespeed_conf.dat;
311 *
312 * @since 3.0
313 * @access public
314 */
315 public function update_files() {
316 Debug2::debug( '🗂️ [Activation] update_files' );
317
318 // Update cache setting `_CACHE`
319 $this->cls( 'Conf' )->define_cache();
320
321 // Site options applied already
322 $options = $this->get_options();
323
324 /* 1) wp-config.php; */
325
326 try {
327 $this->manage_wp_cache_const( $options[ self::_CACHE ] );
328 } catch ( \Exception $ex ) {
329 // Add msg to admin page or CLI
330 Admin_Display::error( wp_kses_post( $ex->getMessage() ) );
331 }
332
333 /* 2) adv-cache.php; Dropped in v3.0.4 */
334
335 /* 3) object-cache.php; */
336
337 if ( $options[ self::O_OBJECT ] && ( ! $options[ self::O_DEBUG_DISABLE_ALL ] || is_multisite() ) ) {
338 $this->cls( 'Object_Cache' )->update_file( $options );
339 } else {
340 $this->cls( 'Object_Cache' )->del_file(); // Note: because it doesn't reconnect, which caused setting page OC option changes delayed, thus may meet Connect Test Failed issue (Next refresh will correct it). Not a big deal, will keep as is.
341 }
342
343 /* 4) .htaccess; */
344
345 try {
346 $this->cls( 'Htaccess' )->update( $options );
347 } catch ( \Exception $ex ) {
348 Admin_Display::error( wp_kses_post( $ex->getMessage() ) );
349 }
350
351 /* 5) .litespeed_conf.dat; */
352
353 if ( ( $options[ self::O_GUEST ] || $options[ self::O_OBJECT ] ) && ( ! $options[ self::O_DEBUG_DISABLE_ALL ] || is_multisite() ) ) {
354 $this->update_conf_data_file( $options );
355 }
356 }
357
358 /**
359 * Delete data conf file
360 *
361 * Removes the .litespeed_conf.dat file.
362 *
363 * @since 4.1
364 * @access private
365 */
366 private static function del_conf_data_file() {
367 global $wp_filesystem;
368
369 if ( ! $wp_filesystem ) {
370 require_once ABSPATH . 'wp-admin/includes/file.php';
371 WP_Filesystem();
372 }
373
374 if ( $wp_filesystem->exists( self::$data_file ) ) {
375 $wp_filesystem->delete( self::$data_file );
376 }
377 }
378
379 /**
380 * Update data conf file for guest mode & object cache
381 *
382 * Updates the .litespeed_conf.dat file with relevant settings.
383 *
384 * @since 4.1
385 * @access private
386 * @param array $options Plugin options.
387 */
388 private function update_conf_data_file( $options ) {
389 $ids = array();
390 if ( $options[ self::O_OBJECT ] ) {
391 $this_ids = array(
392 self::O_DEBUG,
393 self::O_OBJECT_KIND,
394 self::O_OBJECT_HOST,
395 self::O_OBJECT_PORT,
396 self::O_OBJECT_LIFE,
397 self::O_OBJECT_USER,
398 self::O_OBJECT_PSWD,
399 self::O_OBJECT_DB_ID,
400 self::O_OBJECT_PERSISTENT,
401 self::O_OBJECT_ADMIN,
402 self::O_OBJECT_TRANSIENTS,
403 self::O_OBJECT_GLOBAL_GROUPS,
404 self::O_OBJECT_NON_PERSISTENT_GROUPS,
405 );
406 $ids = array_merge( $ids, $this_ids );
407 }
408
409 if ( $options[ self::O_GUEST ] ) {
410 $this_ids = array(
411 self::HASH,
412 self::O_CACHE_LOGIN_COOKIE,
413 self::O_DEBUG_IPS,
414 self::O_UTIL_NO_HTTPS_VARY,
415 self::O_GUEST_UAS,
416 self::O_GUEST_IPS,
417 );
418 $ids = array_merge( $ids, $this_ids );
419 }
420
421 $data = array();
422 foreach ( $ids as $v ) {
423 $data[ $v ] = $options[ $v ];
424 }
425 $data = wp_json_encode( $data );
426
427 $old_data = File::read( self::$data_file );
428 if ( $old_data !== $data ) {
429 defined( 'LSCWP_LOG' ) && Debug2::debug( '[Activation] Updating .litespeed_conf.dat' );
430 File::save( self::$data_file, $data );
431 }
432 }
433
434 /**
435 * Update the WP_CACHE variable in the wp-config.php file.
436 *
437 * If enabling, check if the variable is defined, and if not, define it.
438 * Vice versa for disabling.
439 *
440 * @since 1.0.0
441 * @since 3.0 Refactored
442 * @param bool $enable Whether to enable WP_CACHE.
443 * @throws \Exception If wp-config.php cannot be modified.
444 * @return bool True if updated, false if no change needed.
445 */
446 public function manage_wp_cache_const( $enable ) {
447 if ( $enable ) {
448 if ( defined( 'WP_CACHE' ) && WP_CACHE ) {
449 return false;
450 }
451 } elseif ( ! defined( 'WP_CACHE' ) || ( defined( 'WP_CACHE' ) && ! WP_CACHE ) ) {
452 return false;
453 }
454
455 if ( apply_filters( 'litespeed_wpconfig_readonly', false ) ) {
456 throw new \Exception( 'wp-config file is forbidden to modify due to API hook: litespeed_wpconfig_readonly' );
457 }
458
459 /**
460 * Follow WP's logic to locate wp-config file
461 *
462 * @see wp-load.php
463 */
464 $conf_file = ABSPATH . 'wp-config.php';
465 if ( ! file_exists( $conf_file ) ) {
466 $conf_file = dirname( ABSPATH ) . '/wp-config.php';
467 }
468
469 $content = File::read( $conf_file );
470 if ( ! $content ) {
471 throw new \Exception( 'wp-config file content is empty: ' . wp_kses_post( $conf_file ) );
472 }
473
474 // Remove the line `define('WP_CACHE', true/false);` first
475 if ( defined( 'WP_CACHE' ) ) {
476 $content = preg_replace( '/define\(\s*([\'"])WP_CACHE\1\s*,\s*\w+\s*\)\s*;/sU', '', $content );
477 }
478
479 // Insert const
480 if ( $enable ) {
481 $content = preg_replace( '/^<\?php/', "<?php\ndefine( 'WP_CACHE', true );", $content );
482 }
483
484 $res = File::save( $conf_file, $content, false, false, false );
485
486 if ( true !== $res ) {
487 throw new \Exception( 'wp-config.php operation failed when changing `WP_CACHE` const: ' . wp_kses_post( $res ) );
488 }
489
490 return true;
491 }
492
493 /**
494 * Handle auto update
495 *
496 * Enables auto-updates for the plugin if configured.
497 *
498 * @since 2.7.2
499 * @access public
500 */
501 public function auto_update() {
502 if ( ! $this->conf( Base::O_AUTO_UPGRADE ) ) {
503 return;
504 }
505
506 add_filter( 'auto_update_plugin', array( $this, 'auto_update_hook' ), 10, 2 );
507 }
508
509 /**
510 * Auto upgrade hook
511 *
512 * Determines whether to auto-update the plugin.
513 *
514 * @since 3.0
515 * @access public
516 * @param bool $update Whether to update.
517 * @param object $item Plugin data.
518 * @return bool Whether to update.
519 */
520 public function auto_update_hook( $update, $item ) {
521 if ( ! empty( $item->slug ) && 'litespeed-cache' === $item->slug ) {
522 $auto_v = Cloud::version_check( 'auto_update_plugin' );
523
524 if ( ! empty( $auto_v['latest'] ) && ! empty( $item->new_version ) && $auto_v['latest'] === $item->new_version ) {
525 return true;
526 }
527 }
528
529 return $update; // Else, use the normal API response to decide whether to update or not
530 }
531
532 /**
533 * Upgrade LSCWP
534 *
535 * Upgrades the LiteSpeed Cache plugin.
536 *
537 * @since 2.9
538 * @access public
539 */
540 public function upgrade() {
541 $plugin = Core::PLUGIN_FILE;
542
543 /**
544 * Load upgrade cls
545 *
546 * @see wp-admin/update.php
547 */
548 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
549 include_once ABSPATH . 'wp-admin/includes/file.php';
550 include_once ABSPATH . 'wp-admin/includes/misc.php';
551
552 try {
553 ob_start();
554 $skin = new \WP_Ajax_Upgrader_Skin();
555 $upgrader = new \Plugin_Upgrader( $skin );
556 $result = $upgrader->upgrade( $plugin );
557 if ( ! is_plugin_active( $plugin ) ) {
558 // todo: upgrade should reactivate the plugin again by WP. Need to check why disabled after upgraded.
559 activate_plugin( $plugin, '', is_multisite() );
560 }
561 ob_end_clean();
562 } catch ( \Exception $e ) {
563 Admin_Display::error( __( 'Failed to upgrade.', 'litespeed-cache' ) );
564 return;
565 }
566
567 if ( is_wp_error( $result ) ) {
568 Admin_Display::error( __( 'Failed to upgrade.', 'litespeed-cache' ) );
569 return;
570 }
571
572 Admin_Display::success( __( 'Upgraded successfully.', 'litespeed-cache' ) );
573 }
574
575 /**
576 * Detect if the plugin is active or not
577 *
578 * @since 1.0
579 * @access public
580 * @param string $plugin Plugin slug.
581 * @return bool True if active, false otherwise.
582 */
583 public function dash_notifier_is_plugin_active( $plugin ) {
584 include_once ABSPATH . 'wp-admin/includes/plugin.php';
585
586 $plugin_path = $plugin . '/' . $plugin . '.php';
587
588 return is_plugin_active( $plugin_path );
589 }
590
591 /**
592 * Detect if the plugin is installed or not
593 *
594 * @since 1.0
595 * @access public
596 * @param string $plugin Plugin slug.
597 * @return bool True if installed, false otherwise.
598 */
599 public function dash_notifier_is_plugin_installed( $plugin ) {
600 include_once ABSPATH . 'wp-admin/includes/plugin.php';
601
602 $plugin_path = $plugin . '/' . $plugin . '.php';
603
604 $valid = validate_plugin( $plugin_path );
605
606 return ! is_wp_error( $valid );
607 }
608
609 /**
610 * Grab a plugin info from WordPress
611 *
612 * @since 1.0
613 * @access public
614 * @param string $slug Plugin slug.
615 * @return object|false Plugin info or false on failure.
616 */
617 public function dash_notifier_get_plugin_info( $slug ) {
618 include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
619 $result = plugins_api( 'plugin_information', array( 'slug' => $slug ) );
620
621 if ( is_wp_error( $result ) ) {
622 return false;
623 }
624
625 return $result;
626 }
627
628 /**
629 * Install the 3rd party plugin
630 *
631 * Installs and activates a third-party plugin.
632 *
633 * @since 1.0
634 * @access public
635 */
636 public function dash_notifier_install_3rd() {
637 ! defined( 'SILENCE_INSTALL' ) && define( 'SILENCE_INSTALL', true );
638
639 // phpcs:ignore
640 $slug = ! empty( $_GET['plugin'] ) ? wp_unslash( sanitize_text_field( $_GET['plugin'] ) ) : false;
641
642 // Check if plugin is installed already
643 if ( ! $slug || $this->dash_notifier_is_plugin_active( $slug ) ) {
644 return;
645 }
646
647 /**
648 * Load upgrade cls
649 *
650 * @see wp-admin/update.php
651 */
652 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
653 include_once ABSPATH . 'wp-admin/includes/file.php';
654 include_once ABSPATH . 'wp-admin/includes/misc.php';
655
656 $plugin_path = $slug . '/' . $slug . '.php';
657
658 if ( ! $this->dash_notifier_is_plugin_installed( $slug ) ) {
659 $plugin_info = $this->dash_notifier_get_plugin_info( $slug );
660 if ( ! $plugin_info ) {
661 return;
662 }
663 // Try to install plugin
664 try {
665 ob_start();
666 $skin = new \Automatic_Upgrader_Skin();
667 $upgrader = new \Plugin_Upgrader( $skin );
668 $result = $upgrader->install( $plugin_info->download_link );
669 ob_end_clean();
670 } catch ( \Exception $e ) {
671 return;
672 }
673 }
674
675 if ( ! is_plugin_active( $plugin_path ) ) {
676 activate_plugin( $plugin_path );
677 }
678 }
679
680 /**
681 * Handle all request actions from main cls
682 *
683 * Processes various activation-related actions.
684 *
685 * @since 2.9
686 * @access public
687 */
688 public function handler() {
689 $type = Router::verify_type();
690
691 switch ( $type ) {
692 case self::TYPE_UPGRADE:
693 $this->upgrade();
694 break;
695
696 case self::TYPE_INSTALL_3RD:
697 $this->dash_notifier_install_3rd();
698 break;
699
700 case self::TYPE_DISMISS_RECOMMENDED:
701 Cloud::reload_summary();
702 Cloud::save_summary( array( 'news.new' => 0 ) );
703 break;
704
705 case self::TYPE_INSTALL_ZIP:
706 Cloud::reload_summary();
707 $summary = Cloud::get_summary();
708 if ( ! empty( $summary['news.zip'] ) ) {
709 Cloud::save_summary( array( 'news.new' => 0 ) );
710
711 $this->cls( 'Debug2' )->beta_test( $summary['zip'] );
712 }
713 break;
714
715 default:
716 break;
717 }
718
719 Admin::redirect();
720 }
721 }
722