is_store_screen() ) { return; } $ends = CheckoutHawk_Panic::ends_at(); echo '

'; echo esc_html__( 'CheckoutHawk panic mode is on.', 'checkouthawk' ); echo ' '; if ( $ends ) { /* translators: %s: date and time panic mode ends */ echo esc_html( sprintf( __( 'Stricter checkout rules are in force until %s.', 'checkouthawk' ), CheckoutHawk_Helpers::local_time( gmdate( 'Y-m-d H:i:s', $ends ) ) ) ); } else { echo esc_html__( 'Stricter checkout rules stay in force until you switch them off.', 'checkouthawk' ); } echo ' ' . esc_html__( 'Open CheckoutHawk', 'checkouthawk' ) . ''; echo '

'; } /** * Is the current admin screen one where a store owner would expect to hear about the checkout? * * @return bool */ protected function is_store_screen() { if ( ! function_exists( 'get_current_screen' ) ) { return false; } $screen = get_current_screen(); if ( ! $screen ) { return false; } if ( 'dashboard' === $screen->id || false !== strpos( $screen->id, 'checkouthawk' ) ) { return true; } if ( false !== strpos( $screen->id, 'woocommerce' ) || false !== strpos( $screen->id, 'wc-orders' ) ) { return true; } return 'shop_order' === $screen->post_type; } /** * Verify a request came from an admin form. * * @param string $action Nonce action. * @return void */ protected function verify( $action ) { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'checkouthawk' ), 403 ); } check_admin_referer( $action ); } /** * Redirect back to a plugin page with a status message. * * @param string $page Page slug. * @param string $message Message key. * @return void */ protected function redirect( $page, $message = '' ) { $url = admin_url( 'admin.php?page=' . sanitize_key( $page ) ); if ( $message ) { $url = add_query_arg( 'checkouthawk_message', sanitize_key( $message ), $url ); } wp_safe_redirect( $url ); exit; } /** * Show the message set by a redirect. * * @return void */ protected function render_message() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read only notice text. $key = isset( $_GET['checkouthawk_message'] ) ? sanitize_key( wp_unslash( $_GET['checkouthawk_message'] ) ) : ''; if ( '' === $key ) { return; } $messages = array( 'settings_saved' => __( 'Settings saved.', 'checkouthawk' ), 'panic_on' => __( 'Panic mode is on. Stricter checkout rules are now in force.', 'checkouthawk' ), 'panic_off' => __( 'Panic mode is off. Normal checkout rules are back.', 'checkouthawk' ), 'block_added' => __( 'Block added.', 'checkouthawk' ), 'block_failed' => __( 'That value could not be blocked. Check the format and try again.', 'checkouthawk' ), 'block_removed' => __( 'Block removed.', 'checkouthawk' ), 'cleanup_done' => __( 'Failed orders removed.', 'checkouthawk' ), 'log_cleared' => __( 'Activity log cleared.', 'checkouthawk' ), ); if ( ! isset( $messages[ $key ] ) ) { return; } $class = 'block_failed' === $key ? 'notice-error' : 'notice-success'; echo '

' . esc_html( $messages[ $key ] ) . '

'; } /** * Save settings. * * @return void */ public function handle_save_settings() { $this->verify( 'checkouthawk_save_settings' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nonce and capability verified above; each field is sanitized per type in CheckoutHawk_Settings::sanitize(). $raw = isset( $_POST['checkouthawk'] ) ? (array) wp_unslash( $_POST['checkouthawk'] ) : array(); CheckoutHawk_Settings::save( CheckoutHawk_Settings::sanitize( $raw ) ); $this->redirect( 'checkouthawk-settings', 'settings_saved' ); } /** * Turn panic mode on or off. * * @return void */ public function handle_toggle_panic() { $this->verify( 'checkouthawk_toggle_panic' ); if ( CheckoutHawk_Panic::is_active() ) { CheckoutHawk_Panic::disable( 'manual' ); $this->redirect( 'checkouthawk', 'panic_off' ); } // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above. $minutes = isset( $_POST['panic_minutes'] ) ? absint( wp_unslash( $_POST['panic_minutes'] ) ) : 0; CheckoutHawk_Panic::enable( $minutes, 'manual', __( 'Switched on from the dashboard', 'checkouthawk' ) ); $this->redirect( 'checkouthawk', 'panic_on' ); } /** * Add a manual block. * * @return void */ public function handle_add_block() { $this->verify( 'checkouthawk_add_block' ); // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- verified above. $type = isset( $_REQUEST['block_type'] ) ? sanitize_key( wp_unslash( $_REQUEST['block_type'] ) ) : 'ip'; $value = isset( $_REQUEST['block_value'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['block_value'] ) ) : ''; $hours = isset( $_REQUEST['block_hours'] ) ? absint( wp_unslash( $_REQUEST['block_hours'] ) ) : 0; $note = isset( $_REQUEST['block_reason'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['block_reason'] ) ) : ''; // phpcs:enable $added = CheckoutHawk_Blocklist::add( $type, $value, array( 'reason' => $note ? $note : __( 'Added by hand', 'checkouthawk' ), 'source' => 'manual', 'minutes' => $hours * 60, ) ); $this->redirect( 'checkouthawk-blocks', $added ? 'block_added' : 'block_failed' ); } /** * Remove a block. * * @return void */ public function handle_remove_block() { $this->verify( 'checkouthawk_remove_block' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified above. $id = isset( $_REQUEST['block'] ) ? absint( wp_unslash( $_REQUEST['block'] ) ) : 0; if ( $id ) { CheckoutHawk_Blocklist::remove( $id ); } $this->redirect( 'checkouthawk-blocks', 'block_removed' ); } /** * Run the failed order cleanup now. * * @return void */ public function handle_run_cleanup() { $this->verify( 'checkouthawk_run_cleanup' ); checkouthawk()->cleanup->run( 200 ); $this->redirect( 'checkouthawk-settings', 'cleanup_done' ); } /** * Empty the activity log. * * @return void */ public function handle_clear_log() { $this->verify( 'checkouthawk_clear_log' ); CheckoutHawk_Logger::truncate(); $this->redirect( 'checkouthawk-events', 'log_cleared' ); } /** * Export the activity log as CSV. * * @return void */ public function handle_export_events() { $this->verify( 'checkouthawk_export_events' ); $rows = CheckoutHawk_Logger::get_events( array( 'per_page' => 200, 'page' => 1, ) ); $lines = array( $this->csv_line( array( 'time_utc', 'event', 'severity', 'ip', 'email', 'country', 'order_id', 'reason' ) ) ); foreach ( $rows as $row ) { $lines[] = $this->csv_line( array( $row['event_time'], $row['event_type'], $row['severity'], $row['ip'], $row['email'], $row['country'], $row['order_id'], $row['reason'], ) ); } nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=checkouthawk-log-' . gmdate( 'Y-m-d' ) . '.csv' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV download, each field is escaped by csv_line(). echo implode( "\r\n", $lines ); exit; } /** * Build one CSV row. * * @param array $fields Field values. * @return string */ protected function csv_line( $fields ) { $out = array(); foreach ( (array) $fields as $field ) { $field = (string) $field; // Stop spreadsheet formula injection. if ( '' !== $field && in_array( $field[0], array( '=', '+', '-', '@' ), true ) ) { $field = "'" . $field; } $out[] = '"' . str_replace( '"', '""', $field ) . '"'; } return implode( ',', $out ); } /** * Dashboard screen. * * @return void */ public function render_dashboard() { $this->render_message(); $summary = CheckoutHawk_Logger::summary( 24 ); $summary_week = CheckoutHawk_Logger::summary( 24 * 7 ); $offenders = CheckoutHawk_Logger::top_offenders( 24 * 7, 8 ); $daily = CheckoutHawk_Logger::daily_totals( 14 ); $recent = CheckoutHawk_Logger::get_events( array( 'per_page' => 10 ) ); $active_blocks = CheckoutHawk_Blocklist::active_count(); $failed_orders = CheckoutHawk_Cleanup::failed_order_count(); $panic = CheckoutHawk_Panic::is_active(); include CHECKOUTHAWK_PATH . 'admin/views/dashboard.php'; } /** * Activity log screen. * * @return void */ public function render_events() { $this->render_message(); $table = new CheckoutHawk_Events_Table(); $table->prepare_items(); include CHECKOUTHAWK_PATH . 'admin/views/events.php'; } /** * Block list screen. * * @return void */ public function render_blocks() { $this->render_message(); $table = new CheckoutHawk_Blocks_Table(); $table->prepare_items(); include CHECKOUTHAWK_PATH . 'admin/views/blocks.php'; } /** * Settings screen. * * @return void */ public function render_settings() { $this->render_message(); $settings = CheckoutHawk_Settings::all(); include CHECKOUTHAWK_PATH . 'admin/views/settings.php'; } }