Adventurebook Dashboard'; }, 'dashicons-book', 6 ); } ); /** * Register Adventurebook meta boxes reliably for all screens */ add_action( 'add_meta_boxes', 'adventurebook_register_metaboxes', 99 ); function adventurebook_register_metaboxes( $post_type ) { if ( $post_type !== 'adventurebook_page' ) { return; } /** * add a meta box to the adventurebook_page post type to add additional resources "wood:100;stone:50;gold:20" * resources can be harvested with the appropriate skill ("woodcutting" for wood, "mining" for stone or gold) * the number after the colon indicates how many resources can be harvested on this page within 1 hour. * it is capped by the character's skill (10 * level max per harvest action). */ // Resource Harvesting Box add_meta_box( 'adventurebook_page_resources', __( 'Resource Harvesting', 'adventurebook' ), 'adventurebook_page_resources_html', 'adventurebook_page', 'normal', 'high' ); // Falls sie aus einem fehlerhaften Benutzerlayout "verschwunden" ist: global $wp_meta_boxes; if ( isset( $wp_meta_boxes['adventurebook_page']['side']['default']['adventurebook_page_resources'] ) ) { $box = $wp_meta_boxes['adventurebook_page']['side']['default']['adventurebook_page_resources']; unset( $wp_meta_boxes['adventurebook_page']['side']['default']['adventurebook_page_resources'] ); $wp_meta_boxes['adventurebook_page']['normal']['high']['adventurebook_page_resources'] = $box; } } function adventurebook_admin_enqueue_select2( $hook ): void { // check if a CPT adventurebook_page is edited if ( $hook !== 'post.php' && $hook !== 'post-new.php' ) { return; } $base_url = plugin_dir_url( __FILE__ ); // Select2 lokal laden wp_enqueue_style( 'adventurebook-select2', $base_url . '../../css/select2.min.css', [], '4.1.0' ); wp_enqueue_script( 'adventurebook-select2', $base_url . '../../js/select2.min.js', [ 'jquery' ], '4.1.0', true ); // Eigenes Init-Skript wp_enqueue_script( 'adventurebook-select2-init', $base_url . '../../js/admin-select2-init.js', [ 'adventurebook-select2' ], '1.0.0', true ); // Übersetzbare Platzhaltertexte bereitstellen wp_localize_script( 'adventurebook-select2-init', 'adventurebook_select2', [ 'placeholder' => __( 'Select items...', 'adventurebook' ), ] ); } add_action( 'admin_enqueue_scripts', 'adventurebook_admin_enqueue_select2' ); // Media Uploader Scripts nur auf den Adventurebook Items und Enemies Seiten laden add_action( 'admin_enqueue_scripts', 'adventurebook_enqueue_media_scripts' ); function adventurebook_enqueue_media_scripts( $hook_suffix ): void { // check if this the adventurebook items or enemies page if ( $hook_suffix !== 'adventurebook_page_adventurebook_items' && $hook_suffix !== 'adventurebook_page_adventurebook-enemies' ) { return; } // Media Uploader Scripts laden wp_enqueue_media(); } // ----------------- PAGES ----------------- function adventurebook_set_parent_on_new_page( $post_id ): void { // prevent autosave effects if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // check if the post type is 'adventurebook_page' if ( get_post_type( $post_id ) != 'adventurebook_page' ) { return; } if ( ! isset( $_GET['_adventurebook_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_adventurebook_nonce'] ) ), 'adventurebook_set_parent_action' ) ) { return; } // check for editing rights if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // check if the parent_adventurebook parameter is set in the URL if ( isset( $_GET['parent_adventurebook'] ) && isset( $_GET['post_type'] ) && $_GET['post_type'] === 'adventurebook_page' ) { $parent_id = intval( $_GET['parent_adventurebook'] ); // temporarily remove the hook to prevent infinite loops remove_action( 'save_post', 'adventurebook_set_parent_on_new_page', 10, 3 ); // set the parent value wp_update_post( array( 'ID' => $post_id, 'post_parent' => $parent_id, ) ); // add the hook back add_action( 'save_post', 'adventurebook_set_parent_on_new_page', 10, 3 ); } } function adventurebook_page_resources_html(): void { global $post; wp_nonce_field('adventurebook_save_page_resources', 'adventurebook_nonce_page_resources'); // Combat System laden $parent_id = wp_get_post_parent_id($post->ID); // get the current combat system of the parent adventurebook $combat_system = adventurebook_get_combat_system_for_adventurebook($parent_id); $resources = $combat_system ? $combat_system->get_available_resources() : []; // gespeicherte Werte $stored = get_post_meta($post->ID, '_adventurebook_page_resources', true); $parsed = []; if ($stored) { foreach (explode(';', $stored) as $pair) { if (strpos($pair, ':') !== false) { [$k, $v] = explode(':', $pair); $parsed[$k] = intval($v); } } } $preselected_keys = array_keys($parsed); ?>

$amount): ?>
0) { $result[] = sanitize_key($key) . ':' . $amount; } } update_post_meta($post_id, '_adventurebook_page_resources', implode(';', $result)); } /** * add a meta box to the adventurebook_page post type to select a script */ function adventurebook_add_page_script_meta_box(): void { add_meta_box( 'adventurebook_page_script', // Unique ID __( 'Select Script', 'adventurebook' ), // Box title 'adventurebook_page_script_html', // Content callback 'adventurebook_page', // Post type 'side', // Context 'default' // Priority ); } function adventurebook_page_script_html( $post ): void { // retrieve the currently selected script $script_code = get_post_meta( $post->ID, '_adventurebook_page_script', true ); // Nonce for security wp_nonce_field( 'adventurebook_save_page_script', 'adventurebook_nonce_page_script' ); echo ''; echo ''; } function adventurebook_save_page_script_meta_box_data( $post_id ): void { // Check if the user has permission to edit the post if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Verify the nonce if ( ! isset( $_POST['adventurebook_nonce_page_script'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['adventurebook_nonce_page_script'] ) ), 'adventurebook_save_page_script' ) ) { return; } // check for editing rights if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Check if the script dropdown is set if ( isset( $_POST['adventurebook_page_script'] ) ) { $script_code = sanitize_text_field( wp_unslash( $_POST['adventurebook_page_script'] ) ); update_post_meta( $post_id, '_adventurebook_page_script', $script_code ); } } add_action( 'save_post', 'adventurebook_save_page_script_meta_box_data' ); add_action( 'add_meta_boxes', 'adventurebook_add_page_script_meta_box' ); /** * add a meta box to the adventurebook post type with information about the adventurebook * e.g. which enemy_definitions are used in this adventurebook */ add_action( 'add_meta_boxes', 'adventurebook_add_book_info_meta_box' ); function adventurebook_add_book_info_meta_box(): void { add_meta_box( 'adventurebook_book_info', // Unique ID __( 'Adventurebook Info', 'adventurebook' ), // Box title 'adventurebook_book_info_html', // Content callback 'adventurebook', // Post type 'normal', // Context 'default' // Priority ); } function adventurebook_book_info_html( $post ): void { // Enemies echo '

' . esc_html( __( 'Used Enemies in this Adventurebook', 'adventurebook' ) ) . '

'; $enemy_definitions = Enemy_Definition::get_all_enemy_definitions_by_adventurebook_id( $post->ID ); if ( empty( $enemy_definitions ) ) { echo '

' . esc_html( __( 'No enemy definitions found.', 'adventurebook' ) ) . '

'; } else { // sort by name usort( $enemy_definitions, function ( $a, $b ) { return strcmp( $a->name, $b->name ); } ); echo ''; } // Items echo '

' . esc_html( __( 'Used Items in this Adventurebook', 'adventurebook' ) ) . '

'; $item_definitions = Item_Definition::get_all_item_definitions_by_adventurebook_id( $post->ID ); if ( empty( $item_definitions ) ) { echo '

' . esc_html( __( 'No item definitions found.', 'adventurebook' ) ) . '

'; } else { // sort by name usort( $item_definitions, function ( $a, $b ) { return strcmp( $a->name, $b->name ); } ); echo ''; } // Scripts echo '

' . esc_html( __( 'Used Scripts in this Adventurebook', 'adventurebook' ) ) . '

'; $scripts = Script::get_all_scripts_by_adventurebook_id( $post->ID ); if ( empty( $scripts ) ) { echo '

' . esc_html( __( 'No scripts found.', 'adventurebook' ) ) . '

'; } else { // sort by code usort( $scripts, function ( $a, $b ) { return strcmp( $a->code, $b->code ); } ); echo ''; } } /** add a meta box to select the type of page * available types: * default (normal page) * shop (page with shop functionality) - will display an additional meta box with shop settings */ function adventurebook_add_page_type_meta_box() { // only add meta box if the parent adventurebook is of type "game" $post = get_post(); $adventurebook_type = adventurebook_get_type( "adventurebook_page", $post ); if ( $adventurebook_type !== 'game' ) { return; } add_meta_box( 'adventurebook_page_type', // Unique ID __( 'Select Page Type', 'adventurebook' ), // Box title 'adventurebook_page_type_html', // Content callback 'adventurebook_page', // Post type 'side', // Context 'default' // Priority ); } function adventurebook_page_type_html( $post ) { // retrieve the currently selected page type $page_type = get_post_meta( $post->ID, '_adventurebook_page_type', true ); // Nonce for security wp_nonce_field( 'adventurebook_save_page_type', 'adventurebook_nonce_page_type' ); echo ''; echo ''; } function adventurebook_save_page_type_meta_box_data( $post_id ) { // Check if the user has permission to edit the post if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Verify the nonce if ( ! isset( $_POST['adventurebook_nonce_page_type'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['adventurebook_nonce_page_type'] ) ), 'adventurebook_save_page_type' ) ) { return; } // check for editing rights if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Check if the page type dropdown is set if ( isset( $_POST['adventurebook_page_type'] ) ) { $page_type = sanitize_text_field( wp_unslash( $_POST['adventurebook_page_type'] ) ); update_post_meta( $post_id, '_adventurebook_page_type', $page_type ); } } add_action( 'save_post', 'adventurebook_save_page_type_meta_box_data' ); add_action( 'add_meta_boxes', 'adventurebook_add_shop_meta_box' ); function adventurebook_add_shop_meta_box( $post_type, $post = null ) { // adventurebook must be game $adventurebook_type = adventurebook_get_type( "adventurebook_page", $post ); if ( $adventurebook_type === 'game' ) { add_meta_box( 'adventurebook_shop_options', __( 'Shop Options', 'adventurebook' ), 'adventurebook_shop_options_meta_box', 'adventurebook_page', 'normal', 'default' ); } } function adventurebook_shop_options_meta_box( $post ) { // Sicherheits-Nonce wp_nonce_field( 'adventurebook_shop_options_save', 'adventurebook_shop_options_nonce' ); $page_type = get_post_meta( $post->ID, '_adventurebook_page_type', true ); // Nur anzeigen, wenn Typ bereits "Shop" ist if ( $page_type !== 'shop' ) { echo '

' . esc_html__( 'Shop Options will be displayed once the Page Type is set to "Shop" and the page is saved.', 'adventurebook' ) . '

'; return; } // Gespeicherte Werte $items_list = get_post_meta( $post->ID, '_adventurebook_shop_items', true ); $price_factor = get_post_meta( $post->ID, '_adventurebook_shop_price_factor', true ) ?: '1.0'; $selected_items = array_filter( array_map( 'trim', explode( ',', (string) $items_list ) ) ); // Alle Item-Definitionen laden $items = Adventurebook\Item_Definition::get_all_item_definitions(); ?>





post_parent; // Abrufen aller Adventurebooks $books = get_posts( array( 'post_type' => 'adventurebook', 'numberposts' => - 1, 'post_status' => 'publish', 'orderby' => 'title', 'order' => 'ASC', ) ); echo ''; // show a button to edit the selected parent adventurebook if ( $current_parent_id ) { $edit_link = get_edit_post_link( $current_parent_id ); echo '' . esc_html__( 'Edit Parent Book', 'adventurebook' ) . '

'; } } /** * Filtert die Post-Daten vor dem Speichern und setzt den Parent, * falls das Dropdown-Feld übergeben wurde. */ add_filter( 'wp_insert_post_data', 'adventurebook_handle_parent_dropdown', 10, 2 ); function adventurebook_handle_parent_dropdown( $data, $postarr ) { // Nur für unseren CPT if ( $data['post_type'] !== 'adventurebook_page' ) { return $data; } // Wenn kein POST-Kontext existiert (z. B. WP-CLI), abbrechen if ( empty( $_POST ) ) { return $data; } // Nonce prüfen if ( ! isset( $_POST['adventurebook_nonce_parent'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['adventurebook_nonce_parent'] ) ), 'adventurebook_save_parent' ) ) { return $data; } // Rechte prüfen if ( ! current_user_can( 'edit_post', $postarr['ID'] ) ) { return $data; } // Wenn das Dropdown-Feld vorhanden ist, Parent übernehmen if ( isset( $_POST['adventurebook_parent_dropdown'] ) ) { $data['post_parent'] = intval( $_POST['adventurebook_parent_dropdown'] ); } return $data; } // Callback-Funktion, um das Metabox-HTML anzuzeigen function adventurebook_type_meta_box_callback( $post ) { // Hole den gespeicherten Wert aus der Post-Meta $adventurebook_type = get_post_meta( $post->ID, '_adventurebook_type', true ); // Sicherheit: Nonce-Feld für die Verifizierung beim Speichern wp_nonce_field( 'adventurebook_save_type', 'adventurebook_nonce_type' ); ?> ID, '_additional_image', true ); $image_url = wp_get_attachment_url( $image_id ); ?>

'text/html' ) ); wp_enqueue_script( 'code-editor' ); wp_enqueue_style( 'code-editor' ); // vis.js for map visualization wp_enqueue_script( 'vis-network', $plugin_url .'../vis-4.21.0/dist/vis-network.min.js', [], '9.1.6', // oder deine Version true ); // include the custom script $version = filemtime( $plugin_path . '../../js/adventurebook_admin.js' ); wp_enqueue_script( 'adventurebook-custom-script', $plugin_url . '../../js/adventurebook_admin.js', array( 'jquery' ), $version, true ); // AJAX URL und Nonce an das JavaScript übergeben wp_localize_script( 'adventurebook-custom-script', 'adventurebook_ajax', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), // WordPress AJAX-URL 'nonce' => wp_create_nonce( 'adventurebook_ajax_nonce' ) // Nonce zur Sicherheit ) ); // PHP-Daten vorbereiten global $post; // get the parent page id of this post $book_id = wp_get_post_parent_id( $post ); // get the game type of the parent book $book_type = get_post_meta( $book_id, '_adventurebook_type', true ); // get all pages of the same book $pages = get_posts( array( 'post_type' => 'adventurebook_page', 'post_parent' => $book_id, 'post_status' => 'publish', 'numberposts' => - 1, 'orderby' => 'title', 'order' => 'ASC', ) ); $pages_data = array_map( function ( $page ) { return [ 'ID' => $page->ID, 'title' => $page->post_title, ]; }, $pages ); // include the decision options script // the script needs the $adventurebook_decision_dirs variable from admin_decisions.php wp_enqueue_script( 'adventurebook-decision-options', $plugin_url . '../../js/adventurebook_admin_decision_options.js', array( 'jquery' ), $version, true ); wp_localize_script( 'adventurebook-decision-options', 'adventurebook_decision_dirs', $adventurebook_decision_dirs ); // Übersetzungen und Seiten als JS-Variablen verfügbar machen wp_localize_script( 'adventurebook-decision-options', 'adventurebook_pages', $pages_data ); // JS also needs the available restriction types global $adventurebook_restriction_types; wp_localize_script( 'adventurebook-decision-options', 'adventurebook_restriction_types', $adventurebook_restriction_types ); wp_localize_script( 'adventurebook-decision-options', 'adventurebook_book_type', [$book_type] ); wp_localize_script( 'adventurebook-decision-options', 'adventurebook_i18n', [ 'label' => __( 'Label', 'adventurebook' ), 'target' => __( 'Target Page', 'adventurebook' ), 'select_page' => __( 'Select a page', 'adventurebook' ), 'generate_new_page' => __( 'Generate new page', 'adventurebook' ), 'remove' => __( 'Remove', 'adventurebook' ), 'new_page_title' => __( 'New Page Title', 'adventurebook' ), 'new_page_backlink' => __( 'Backlink Label', 'adventurebook' ), 'restriction_type' => __( 'Restriction Type', 'adventurebook' ), 'restriction_value' => __( 'Restriction Value', 'adventurebook' ), 'add_decision' => __( 'Add Decision', 'adventurebook' ), 'decision_dir' => __( 'Direction', 'adventurebook' ), ] ); // translate strings for JS $selected = isset($_GET['book_id']) ? intval(ab_get('book_id')) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $graph = $selected ? adventurebook_generate_map_graph_for_book($selected) : ['nodes'=>[], 'edges'=>[]]; wp_localize_script('adventurebook-custom-script', 'adventurebookMapData', [ 'graph' => $graph, 'ajax_url' => admin_url('admin-ajax.php'), 'selected' => $selected, 'edit_url' => admin_url('post.php?action=edit&post='), ]); } // --- Dropdown for Game-type Adventurebook Combat Systems --- add_action( 'add_meta_boxes', function () { add_meta_box( 'adventurebook_combat_system', __( 'Combat System', 'adventurebook' ), 'adventurebook_render_adventurebook_combat_system_meta_box', 'adventurebook', 'side', 'default' ); } ); function adventurebook_render_adventurebook_combat_system_meta_box($post ) { wp_nonce_field( 'adventurebook_combat_system_save', 'adventurebook_combat_system_nonce' ); // Aktueller Typ $adventurebook_type = get_post_meta( $post->ID, '_adventurebook_type', true ); // Nur anzeigen, wenn Typ == "Game" if ( $adventurebook_type !== 'game' ) { echo '

' . esc_html( __( 'No Combat System, Adventurebook-Type is not "Game".', 'adventurebook' ) ) . '

'; return; } // Gespeicherter Wert $selected_system = get_post_meta( $post->ID, '_adventurebook_combat_system', true ); $systems = adventurebook_get_all_combat_systems(); // Dropdown ?> get_name(); } } } return $systems; } /** * returns an instance of the combat system for the given adventurebook * or null if no combat system is set or the class does not exist * * @param $adventurebook_id * * @return Adventurebook\Combat_System|null */ function adventurebook_get_combat_system_for_adventurebook($adventurebook_id ): ?Adventurebook\Combat_System { $adventurebook_combat_system_class = "Adventurebook\\" . get_post_meta( $adventurebook_id, '_adventurebook_combat_system', true ); if ( $adventurebook_combat_system_class && class_exists( $adventurebook_combat_system_class ) ) { return new $adventurebook_combat_system_class(); } return null; } function adventurebook_get_combat_system_for_character_id($character_id ): ?Adventurebook\Combat_System { $character = Character::get_character_by_id( $character_id ); if ( $character ) { return adventurebook_get_combat_system_for_adventurebook( $character->adventurebook_id ); } return null; } #region --- CUSTOM POST TYPE "adventurebook" --- // 1. Add a new column to the CPT admin table add_filter( 'manage_adventurebook_posts_columns', function ( $columns ) { // Insert new column after the Title column $new = []; foreach ( $columns as $key => $value ) { $new[ $key ] = $value; if ( 'title' === $key ) { $new['page_id'] = __( 'ID', 'adventurebook' ); } } return $new; } ); // 2. Render the column content add_action( 'manage_adventurebook_posts_custom_column', function ( $column_name, $post_id ) { if ( 'page_id' === $column_name ) { echo intval( $post_id ); } }, 10, 2 ); // 3. (Optional) Make it sortable add_filter( 'manage_edit-adventurebook_sortable_columns', function ( $columns ) { $columns['page_id'] = 'ID'; return $columns; } ); /** * Register the 'adventurebook' custom post type * An Adventure Book is a custom post type that represents a book that can be read online. * It has pages that can be navigated through. */ function adventurebook_register_book_post_type(): void { $labels = array( 'name' => __( 'Adventurebooks', 'adventurebook' ), 'singular_name' => __( 'Adventurebook', 'adventurebook' ), 'menu_name' => __( 'Adventurebooks', 'adventurebook' ), 'name_admin_bar' => __( 'Adventurebooks', 'adventurebook' ), 'add_new' => __( 'Add New Book', 'adventurebook' ), 'add_new_item' => __( 'Add New Adventurebook', 'adventurebook' ), 'new_item' => __( 'New Adventurebook', 'adventurebook' ), 'edit_item' => __( 'Edit Adventurebook', 'adventurebook' ), 'view_item' => __( 'View Adventurebook', 'adventurebook' ), 'all_items' => __( 'Adventurebooks', 'adventurebook' ), 'search_items' => __( 'Search Adventurebooks', 'adventurebook' ), 'not_found' => __( 'No Adventurebooks found.', 'adventurebook' ), 'not_found_in_trash' => __( 'No Adventurebooks found in Trash.', 'adventurebook' ) ); $args = array( 'labels' => $labels, 'public' => false, 'publicly_queryable' => false, 'exclude_from_search' => true, 'show_ui' => true, 'show_in_menu' => 'adventurebook_main', 'query_var' => true, 'rewrite' => false, 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => true, 'menu_position' => 5, 'menu_icon' => 'dashicons-book-alt', 'supports' => array( 'title' ), ); register_post_type( 'adventurebook', $args ); } // add custom columns to the adventurebook post type function adventurebook_add_custom_columns( $columns ) { $columns['game_type'] = __( 'Type / System', 'adventurebook' ); $columns['page_count'] = __( 'Page Count', 'adventurebook' ); $columns['start_page'] = __( 'Start Page', 'adventurebook' ); return $columns; } function adventurebook_custom_column_content( $column, $post_id ) { if ( $column === 'game_type' ) { $type = get_post_meta( $post_id, '_adventurebook_type', true ); $combat_system = get_post_meta( $post_id, '_adventurebook_combat_system', true ); echo esc_html( ucfirst( $type ) ) . '
' . esc_html( ucfirst( str_replace( '_', ' ', $combat_system ) ) ); } if ( $column === 'page_count' ) { // show the number of pages that belong to this adventurebook // but first remove the "adventurebook_page_filter_by_parent" pre_get_post filter to avoid interference remove_action( 'pre_get_posts', 'adventurebook_page_filter_by_parent' ); $pages = get_posts( array( 'post_type' => 'adventurebook_page', 'post_parent' => $post_id, 'numberposts' => - 1, 'post_status' => 'publish', ) ); add_action( 'pre_get_posts', 'adventurebook_page_filter_by_parent' ); $page_count = count( $pages ); echo esc_html( $page_count ); } if ( $column === 'start_page' ) { // Abrufen der ID der Startseite für das Adventurebook $start_page_id = get_post_meta( $post_id, '_adventurebook_start_page', true ); if ( $start_page_id ) { // Abrufen des Titels der Startseite $start_page = get_post( $start_page_id ); if ( $start_page ) { // show an edit link to the start page (CTP adventurebook_page) $edit_link = get_edit_post_link( $start_page_id ); echo '' . esc_html( $start_page->post_title ) . ' (' . esc_html( $start_page_id ) . ')'; } else { esc_html_e( 'No Start Page', 'adventurebook' ); } } else { esc_html_e( 'No Start Page', 'adventurebook' ); } } } function adventurebook_add_new_page_link_meta_box() { add_meta_box( 'adventurebook_new_page_link', // Unique ID __( 'Add New Book Page', 'adventurebook' ), // Box title 'adventurebook_new_page_link_html', // Content callback 'adventurebook', // Post type 'side', // Context 'high' // Priority ); } function adventurebook_new_page_link_html( $post ) { $link = add_query_arg( array( 'post_type' => 'adventurebook_page', 'parent_adventurebook' => $post->ID, '_adventurebook_nonce' => wp_create_nonce( 'adventurebook_set_parent_action' ) ), admin_url( 'post-new.php' ) ); echo ''; esc_html_e( 'Add New Book Page', 'adventurebook' ); echo ''; } // Füge eine Metabox hinzu, um die Startseite auszuwählen function adventurebook_add_start_page_meta_box() { add_meta_box( 'adventurebook_start_page', // ID der Metabox esc_html( __( 'Select Target Page', 'adventurebook' ) ), // Titel der Metabox 'adventurebook_start_page_meta_box_html', // Callback zur Ausgabe des Inhalts 'adventurebook', // Post-Typ 'side', // Kontext 'default' // Priorität ); } // HTML für das Dropdown-Menü in der Metabox function adventurebook_start_page_meta_box_html( $post ) { wp_nonce_field( 'adventurebook_save_startpage', 'adventurebook_nonce_startpage' ); // retrieve the currently selected start page $selected_page_id = get_post_meta( $post->ID, '_adventurebook_start_page', true ); // get all pages, sorted by title, and with parent as the current book $pages = get_posts( array( 'post_type' => 'adventurebook_page', 'post_parent' => $post->ID, 'numberposts' => - 1, 'post_status' => 'publish', 'orderby' => 'title', 'order' => 'ASC', ) ); if ( empty( $pages ) ) { echo esc_html( __( 'No pages found. Please create pages first.', 'adventurebook' ) ); return; } echo ''; } function adventurebook_save_start_page_meta_box( $post_id ) { // Verhindere Autosave-Effekte if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Sicherstellen, dass es sich um den richtigen Post-Typ handelt if ( get_post_type( $post_id ) != 'adventurebook' ) { return; } // Check Nonce if ( ! isset( $_POST['adventurebook_nonce_startpage'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['adventurebook_nonce_startpage'] ) ), 'adventurebook_save_startpage' ) ) { return; } // check for editing rights if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } remove_action( 'save_post', 'adventurebook_save_start_page_meta_box' ); // Überprüfen, ob eine Startseite ausgewählt wurde if ( isset( $_POST['adventurebook_start_page'] ) ) { $start_page_id = intval( $_POST['adventurebook_start_page'] ); update_post_meta( $post_id, '_adventurebook_start_page', $start_page_id ); } add_action( 'save_post', 'adventurebook_save_start_page_meta_box' ); } #endregion add_action( 'deleted_post', 'adventurebook_deleted_post' ); function adventurebook_deleted_post( $post_id ) { $post_type = get_post_type( $post_id ); // delete a whole adventurebook if ( $post_type === 'adventurebook' ) { // delete all adventurebook_page posts that belong to this adventurebook $pages = get_posts( array( 'post_type' => 'adventurebook_page', 'posts_per_page' => - 1, 'post_status' => 'any', 'post_parent' => $post_id, ) ); foreach ( $pages as $page ) { $item_spwan = Item_Spawn::get_item_spawns_by_page_id( $page->ID, true ); foreach ( $item_spwan as $spawn ) { /* @var $spawn Item_Spawn */ Item_Spawn::delete_item_spawn_point( $spawn->id ); } $enemy_spwan = Enemy_Spawn::get_enemy_spawns_by_page_id( $page->ID, 0 ); foreach ( $enemy_spwan as $spawn ) { /* @var $spawn Enemy_Spawn */ Enemy_Spawn::delete_enemy_spawn_point( $spawn->id ); } wp_delete_post( $page->ID, true ); } // now delete the adventurebook itself (already done by WP) // finally, delete all characters that belong to this adventurebook Character::delete_orphaned_characters(); } // delete an adventurebook_page if ( $post_type === 'adventurebook_page' ) { // remove any links that point to this page $all_pages = get_posts( array( 'post_type' => 'adventurebook_page', 'posts_per_page' => - 1, 'post_status' => 'any', ) ); global $wpdb; // fetch all adventurebook_page posts with the meta key _adventurebook_decision_options from the database $meta_key = '_adventurebook_decision_options'; $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->prepare( "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s", $meta_key ), ARRAY_A ); if ( empty( $results ) ) { return; } foreach ( $results as $row ) { $meta_value = maybe_unserialize( $row['meta_value'] ); $post_id_with_meta = (int) $row['post_id']; $modified = false; if ( is_array( $meta_value ) ) { foreach ( $meta_value as $index => $option ) { if ( isset( $option['target'] ) && (int) $option['target'] === $post_id ) { unset( $meta_value[ $index ] ); $modified = true; } } } if ( $modified ) { // Neu speichern oder löschen, wenn leer if ( empty( $meta_value ) ) { delete_post_meta( $post_id_with_meta, $meta_key ); } else { update_post_meta( $post_id_with_meta, $meta_key, array_values( $meta_value ) ); } } } } } /** * Adventurebook – custom plugin deactivation confirmation flow */ function adventurebook_enqueue_deactivation_script( $hook ) { // Load only on the Plugins list page if ( 'plugins.php' !== $hook ) { return; } wp_enqueue_script( 'adventurebook-deactivate', plugin_dir_url( __FILE__ ) . '../../js/adventurebook_deactivate.js', array( 'jquery' ), '1.0', true ); // Pass the custom confirmation URL to JS wp_localize_script( 'adventurebook-deactivate', 'adventurebookDeactivate', array( 'confirm_url' => admin_url( 'admin.php?page=adventurebook-deactivate-confirm' ), 'confirm_text' => __( 'Are you sure you want to deactivate Adventurebook?', 'adventurebook' ), ) ); } function adventurebook_add_deactivate_confirm_page() { add_submenu_page( 'admin.php', __( 'Confirm Adventurebook Deactivation', 'adventurebook' ), __( 'Confirm Adventurebook Deactivation', 'adventurebook' ), 'manage_options', 'adventurebook-deactivate-confirm', 'adventurebook_render_deactivate_confirm_page' ); } // 3. Render the confirmation page function adventurebook_render_deactivate_confirm_page() { if ( ! current_user_can( 'activate_plugins' ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'adventurebook' ) ); } $plugin_file = 'adventurebook/adventurebook.php'; $deactivate_url = wp_nonce_url( admin_url( 'plugins.php?action=deactivate&plugin=' . urlencode( $plugin_file ) ), 'deactivate-plugin_' . $plugin_file ); $cancel_url = admin_url( 'plugins.php' ); ?>

'adventurebook_page', 'posts_per_page' => - 1, 'post_status' => 'any', ) ); foreach ( $pages as $page ) { $additional_image_id = get_post_meta( $page->ID, '_additional_image', true ); if ( $additional_image_id ) { wp_delete_attachment( $additional_image_id, true ); } wp_delete_post( $page->ID, true ); } // now delete all adventurebooks $adventurebooks = get_posts( array( 'post_type' => 'adventurebook', 'posts_per_page' => - 1, 'post_status' => 'any', ) ); foreach ( $adventurebooks as $book ) { wp_delete_post( $book->ID, true ); } } if ( $delete_data ) { // before we drop all tables, delete all images that are used by item_definition or enemy_definition adventurebook_delete_all_used_images(); adventurebook_drop_all_tables(); delete_option( 'adventurebook_settings' ); delete_option( 'adventurebook_version' ); } } /** Delete all images that are used by item_definition or enemy_definition * This function is called before dropping all tables when the plugin is deactivated with data deletion * * the image is referenced by the image_url field in the item_definition and enemy_definition tables * we need to find the attachment ID from the URL and then delete the attachment from the media library */ function adventurebook_delete_all_used_images() { global $wpdb; // get all image URLs from item_definition $item_image_urls = $wpdb->get_col( "SELECT image_url FROM {$wpdb->prefix}adventurebook_item_definitions WHERE image_url IS NOT NULL AND image_url != ''" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // get all image URLs from enemy_definition $enemy_image_urls = $wpdb->get_col( "SELECT image_url FROM {$wpdb->prefix}adventurebook_enemy_definitions WHERE image_url IS NOT NULL AND image_url != ''" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $all_image_urls = array_unique( array_merge( $item_image_urls, $enemy_image_urls ) ); foreach ( $all_image_urls as $image_url ) { $attachment_id = adventurebook_ensure_attachment_from_url( $image_url ); if ( $attachment_id ) { wp_delete_attachment( $attachment_id, true ); } } } function adventurebook_settings_page(): void { add_submenu_page( 'adventurebook_main', __( 'Adventurebook Settings', 'adventurebook' ), __( 'Settings', 'adventurebook' ), 'manage_options', 'adventurebook-settings', 'adventurebook_settings_page_callback', ); } add_action( 'admin_menu', 'adventurebook_settings_page' ); add_action( 'add_meta_boxes', function() { add_meta_box( 'adventurebook_play_box', 'Quick Access', function( $post ) { // PLAY BUTTON // get the active character for this user and adventurebook // get the adventurebook id - this is the page id of the parent post $adventurebook_id = wp_get_post_parent_id( $post->ID ); $user_id = get_current_user_id(); $current_character_id = intval(get_user_meta( $user_id, 'adventurebook_current_character_id_' . $adventurebook_id, true )); // get the current character $character = Character::get_character_by_id( $current_character_id ); if ( $character ) { // move the character to the current page ($character->location_id = $post->ID) $character->move_to_zone( $post->ID ); } // now just call the play url for this book // find the page where the shortcode [adventurebook id="$post->ID"] is used $startpage = adventurebook_get_first_page_using_shortcode( $adventurebook_id ); // get the url for this page if( ! $startpage ) { echo '

'.esc_html__('No page with [adventurebook id="..."] shortcode found for this book. Please create a page with this shortcode first.', 'adventurebook').'

'; return; } $url = get_permalink( $startpage->ID ); echo ''.esc_html__('Open Adventure', 'adventurebook').''; // SHOW ON MAP BUTTON (premium only) if (defined('ADVENTUREBOOK_PREMIUM') && ADVENTUREBOOK_PREMIUM) { $map_url = admin_url( 'admin.php?page=adventurebook_map&book_id=' . intval( $adventurebook_id ) . '&page_id=' . intval( $post->ID ) ); echo ''.esc_html__('Show on Map','adventurebook').''; } }, 'adventurebook_page', 'side', // oder 'normal' 'high' ); }); if ( defined( 'ADVENTUREBOOK_PREMIUM' ) && ADVENTUREBOOK_PREMIUM ) { require_once __DIR__ . '/../premium/admin_map.php'; }