'Script', 'plural' => 'Scripts', 'ajax' => false ] ); } function get_columns() { return [ 'cb' => '', 'name' => __( 'Name', 'adventurebook' ), 'id' => 'ID', 'code' => __( 'Code', 'adventurebook' ), 'commands' => __( 'Commands', 'adventurebook' ), ]; } function get_sortable_columns() { return [ 'id' => [ 'id', false ], 'code' => [ 'code', false ], 'name' => [ 'name', false ], ]; } function column_cb( $item ) { return sprintf( '', intval( $item['id'] ) ); } function column_default( $item, $column_name ) { /** @var Script $item */ switch ( $column_name ) { case 'id': return intval( $item['id'] ); case 'code': return esc_html( $item['code'] ); case 'name': return $this->column_name( $item ); case 'commands': // just show the number of commands if ( empty( $item['commands'] ) ) { return __( 'No Commands', 'adventurebook' ); } $commands = json_decode( $item['commands'], true ); if ( is_array( $commands ) ) { /* translators: %d: Number of commands */ return sprintf( _n( '%d Command', '%d Commands', count( $commands ), 'adventurebook' ), count( $commands ) ); } else { return __( 'No Commands', 'adventurebook' ); } default: return ''; } } function column_name( $script ): string { $actions = [ 'edit' => sprintf( '' . __( 'Edit', 'adventurebook' ) . '', intval( $script['id'] ) ), 'edit_as_textfield' => sprintf( '' . __( 'Edit as textfield', 'adventurebook' ) . '', intval( $script['id'] ) ), 'delete' => sprintf( '' . __( 'Delete', 'adventurebook' ) . '', intval( $script['id'] ), wp_create_nonce( 'adventurebook_delete_script_' . intval( $script['id'] ) ) ), ]; return sprintf( '%s %s', esc_html( $script['name'] ), $this->row_actions( $actions ) ); } function get_bulk_actions() { return [ 'bulk-delete' => __( 'Delete', 'adventurebook' ) ]; } function process_bulk_action() { global $wpdb; $action = $this->current_action(); if ( $action === 'bulk-delete' ) { // WP-Standard Bulk-Nonce für Items if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'bulk-scripts' ) ) { $this->message = __( 'Nonce invalid.', 'adventurebook' ); return; } $script_ids = isset( $_POST['script_ids'] ) ? array_map( 'intval', wp_unslash( $_POST['script_ids'] ) ) : []; if ( $script_ids ) { foreach ( $script_ids as $id ) { $wpdb->delete( "{$wpdb->prefix}adventurebook_scripts", [ 'id' => $id ] );// phpcs:ignore WordPress.DB.DirectDatabaseQuery } $this->message = sprintf( '%d Scripts deleted.', count( $script_ids ) ); } } } function prepare_scripts() { if ( isset ( $_REQUEST['s'] ) ) { // did we get ANY search request? If yes, check the nonce check_admin_referer( 'adventurebook_search_script' ); } global $wpdb; $this->process_bulk_action(); $per_page = 10; $current_page = $this->get_pagenum(); $offset = ( $current_page - 1 ) * $per_page; $allowed_orderby = [ 'id', 'code', 'name' ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : 'id'; if ( ! in_array( $orderby, $allowed_orderby, true ) ) { $orderby = 'id'; } $allowed_order = [ 'ASC', 'DESC' ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order = isset( $_GET['order'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ) : 'DESC'; if ( ! in_array( $order, $allowed_order, true ) ) { $order = 'DESC'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $search = isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : ''; if ( $search !== '' ) { $like = '%' . $wpdb->esc_like( $search ) . '%'; $total_items = (int) $wpdb->get_var( $wpdb->prepare(// phpcs:ignore WordPress.DB.DirectDatabaseQuery "SELECT COUNT(*) FROM {$wpdb->prefix}adventurebook_scripts WHERE code LIKE %s OR name LIKE %s", $like, $like ) ); $this->items_data = $wpdb->get_results( $wpdb->prepare(// phpcs:ignore WordPress.DB.DirectDatabaseQuery "SELECT * FROM " . $wpdb->prefix . "adventurebook_scripts WHERE code LIKE %s OR name LIKE %s ORDER BY $orderby $order LIMIT %d OFFSET %d",// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $like, $like, $per_page, $offset ), ARRAY_A ); } else { $total_items = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}adventurebook_scripts" );// phpcs:ignore WordPress.DB.DirectDatabaseQuery $this->items_data = $wpdb->get_results( $wpdb->prepare(// phpcs:ignore WordPress.DB.DirectDatabaseQuery "SELECT * FROM {$wpdb->prefix}adventurebook_scripts ORDER BY $orderby $order LIMIT %d OFFSET %d",// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $per_page, $offset ), ARRAY_A ); } $this->set_pagination_args( [ 'total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil( $total_items / $per_page ), ] ); $this->_column_headers = [ $this->get_columns(), [], $this->get_sortable_columns() ]; $this->items = $this->items_data; } public function get_message() { return $this->message; } } function adventurebook_sanitize_script_text( string $str ): string { // Entferne Slashes aus dem POST $str = wp_unslash( $str ); // Entferne unsichtbare Steuerzeichen, aber NICHT \n oder \r $str = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $str ); // Trim nur am Ende – nicht in der Mitte return rtrim( $str ); } // Admin-Seite function adventurebook_scripts_page() { // check for admin permissions if ( ! current_user_can( 'manage_options' ) ) { return; } global $wpdb; $message = ''; $is_list_view = ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], [ 'new', 'edit', 'edit_as_textfield' ], true ); $error = false; // Speichern if ( isset( $_POST['adventurebook_save_script'] ) ) { $nonce = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : ''; if ( $nonce && wp_verify_nonce( $nonce, 'adventurebook_save_script' ) ) { $id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : 0; $code = isset( $_POST['code'] ) ? sanitize_text_field( wp_unslash( $_POST['code'] ) ) : ''; $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; // code has to be unique if( $id == 0 && Script::get_script_by_code( $code ) ) { $message = __('Script code must be unique.', 'adventurebook' ); $error = true; } $commands_info = []; // check if a script_command_textarea is set (edit as textfield) if ( isset( $_POST['script_commands_textarea'] ) ) { // Sanitize textarea input and split into lines $lines = explode( "\n", adventurebook_sanitize_script_text( wp_unslash( $_POST['script_commands_textarea'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $lines as $line ) { list( $key, $command, $parameters ) = explode( '|', trim( $line ), 3 ); $commands_info[] = [ 'key' => $key, 'command' => $command, 'parameters' => $parameters, ]; } } else { if ( isset( $_POST['script_commands_keys'] ) && isset( $_POST['script_commands_commands'] ) && isset( $_POST['script_commands_parameters'] ) ) { // Sanitize keys and values if ( is_array( $_POST['script_commands_keys'] ) ) { $keys = array_map( 'sanitize_text_field', wp_unslash( $_POST['script_commands_keys'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } else { $keys = []; } if ( is_array( $_POST['script_commands_commands'] ) ) { $commands = array_map( 'adventurebook_sanitize_script_text', wp_unslash( $_POST['script_commands_commands'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } else { $commands = []; } if ( is_array( $_POST['script_commands_parameters'] ) ) { $parameters = array_map( 'adventurebook_sanitize_script_text', wp_unslash( $_POST['script_commands_parameters'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } else { $parameters = []; } for ( $i = 0; $i < count( $keys ); $i ++ ) { $commands_info[] = [ 'key' => $keys[ $i ] ?? '', 'command' => $commands[ $i ] ?? '', 'parameters' => $parameters[ $i ] ?? '', ]; } } } // sort commands by key usort( $commands_info, function ( $a, $b ) { return intval( $a['key'] ) <=> intval( $b['key'] ); } ); $commands = json_encode( $commands_info ); if(! $error ) { if ( $id > 0 ) { $wpdb->update( "{$wpdb->prefix}adventurebook_scripts",// phpcs:ignore WordPress.DB.DirectDatabaseQuery compact( 'code', 'name', 'commands' ), [ 'id' => $id ] ); $message = __( 'Script updated.', 'adventurebook' ); } else { $wpdb->insert( "{$wpdb->prefix}adventurebook_scripts",// phpcs:ignore WordPress.DB.DirectDatabaseQuery compact( 'code', 'name', 'commands' ) ); $message = __( 'Script created.', 'adventurebook' ); } } $is_list_view = false; } else { $message = __( 'Invalid nonce.', 'adventurebook' ); } } // Einzel-Löschen if ( isset( $_GET['action'], $_GET['id'] ) && $_GET['action'] === 'delete' ) { // check nonce $id = intval( sanitize_text_field( wp_unslash( $_GET['id'] ) ) ); $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : ''; if ( $nonce && wp_verify_nonce( $nonce, 'adventurebook_delete_script_' . $id ) ) { $wpdb->delete( "{$wpdb->prefix}adventurebook_scripts", [ 'id' => $id ] );// phpcs:ignore WordPress.DB.DirectDatabaseQuery $message = __( 'Script deleted.', 'adventurebook' ); } } echo '
' . esc_html( $message ) . '
'; if ( ! $is_list_view ) { echo '' . esc_html( __( 'Back to Script-List', 'adventurebook' ) ) . '
'; } echo '' . esc_html( $list_table->get_message() ) . '