' . esc_html( __( 'Options saved.', 'adventurebook' ) ) . '

'; } // process export form submission if ( isset( $_POST['export'] ) ) { // security check if ( ! isset( $_POST['adventurebook_export_nonce'] ) || ! wp_verify_nonce( ab_get('adventurebook_export_nonce' ), 'adventurebook_export' ) ) { echo '

' . esc_html( __( 'Security check failed. Please try again.', 'adventurebook' ) ) . '

'; } elseif ( isset( $_POST['adventurebook_id'] ) ) { $adventurebook_id = intval( ab_get('adventurebook_id') ); // redirect to export handler $export_url = add_query_arg( array('action' => 'adventurebook_export'), admin_url( 'admin-post.php' ) ); wp_safe_redirect( $export_url ); exit; } else { echo '

' . esc_html( __( 'Please select an Adventurebook to export.', 'adventurebook' ) ) . '

'; } } // process maintenance form submission if ( isset( $_POST['maintenance'] ) ) { // security check check_admin_referer( 'adventurebook_maintenance' ); // delete orphaned characters if ( isset( $_POST['adventurebook_delete_orphaned_characters'] ) ) { $deleted_count = Character::delete_orphaned_characters(); /* translators: %d = number of deleted characters */ echo '

' . esc_html( sprintf( __( 'Deleted %d orphaned characters.', 'adventurebook' ), $deleted_count ) ) . '

'; } // delete orphaned spawns if ( isset( $_POST['adventurebook_delete_orphaned_spawns'] ) ) { // remove all entries in enemy_spawns that are associated with a non existing character (character_id) $deleted_count = Enemy_Spawn::delete_orphaned_enemy_spawn_points(); /* translators: %d = number of deleted spawn points */ echo '

' . esc_html( sprintf( __( 'Deleted %d orphaned enemy spawn points.', 'adventurebook' ), $deleted_count ) ) . '

'; // remove all entries in item_spawns that are associated with a non existing character (character_id) $deleted_count = Item_Spawn::delete_orphaned_item_spawn_points(); /* translators: %d = number of deleted spawn points */ echo '

' . esc_html( sprintf( __( 'Deleted %d orphaned item spawn points.', 'adventurebook' ), $deleted_count ) ) . '

'; // remove all entries in items that are associated with a non existing spawn_id $deleted_count = Item::delete_orphaned_items(); /* translators: %d = number of deleted items */ echo '

' . esc_html( sprintf( __( 'Deleted %d orphaned items.', 'adventurebook' ), $deleted_count ) ) . '

'; // remove all entries in enemies that are associated with a non existing spawn_id $deleted_count = Enemy::delete_orphaned_enemies(); /* translators: %d = number of deleted enemies */ echo '

' . esc_html( sprintf( __( 'Deleted %d orphaned enemies.', 'adventurebook' ), $deleted_count ) ) . '

'; } } // get current option value $current_page_id = get_option( 'adventurebook_character_page_id', '' ); ?>





open( $zip_path ) === true ) { $zip->extractTo( $import_dir ); $zip->close(); } else { return false; } // JSON-Datei suchen (erste .json im Hauptverzeichnis) $json_files = glob( $import_dir . '/*.json' ); if ( empty( $json_files ) ) { adventurebook_rrmdir( $import_dir ); return false; } $json_path = $json_files[0]; $json_data = file_get_contents( $json_path ); $data = json_decode( $json_data, true ); if ( ! is_array( $data ) ) { adventurebook_rrmdir( $import_dir ); return false; } // Bilder importieren und URLs korrigieren adventurebook_import_images_and_relink( $import_dir, $data ); // temporären Ordner löschen adventurebook_rrmdir( $import_dir ); return $data; } /** * Lädt alle Bilder im /images/-Ordner in die Mediathek * und korrigiert die URLs in $data (pages, items, enemies). */ function adventurebook_import_images_and_relink( $import_dir, &$data ) { $images_dir = trailingslashit( $import_dir ) . 'images/'; if ( is_dir( $images_dir ) ) { $upload_dir = wp_upload_dir(); $filename_to_id = []; $filename_to_url = []; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; global $wpdb; $image_files = glob( $images_dir . '*.*' ); foreach ( $image_files as $image_path ) { $filename = basename( $image_path ); // 🔍 Bestehendes Attachment anhand Dateiname suchen (egal welcher Monatsordner) $like = '%' . $wpdb->esc_like( '/' . $filename ) . '%'; $existing_id = $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_wp_attached_file' AND meta_value LIKE %s LIMIT 1", $like ) ); if ( $existing_id ) { // ✅ Bereits in Mediathek $attachment_id = intval( $existing_id ); $attachment_url = wp_get_attachment_url( $attachment_id ); } else { // ❌ Noch nicht vorhanden → neu hochladen $file_array = [ 'name' => $filename, 'tmp_name' => $image_path, ]; $attachment_id = media_handle_sideload( $file_array, 0 ); if ( is_wp_error( $attachment_id ) ) { continue; } $attachment_url = wp_get_attachment_url( $attachment_id ); } // 🧩 Beide Mappings speichern $filename_to_id[ $filename ] = $attachment_id; $filename_to_url[ $filename ] = $attachment_url; } do_log( 'Import: Processed ' . count( $filename_to_url ) . ' images.' ); } // Schritt 2: URLs in JSON korrigieren // Seiten if ( isset( $data['pages'] ) && is_array( $data['pages'] ) ) { foreach ( $data['pages'] as &$page ) { $found = false; if ( ! empty( $page['additional_image_filename'] ) ) { $basename = $page['additional_image_filename']; if ( isset( $filename_to_id[ $basename ] ) ) { $page['meta']['_additional_image'] = $filename_to_id[ $basename ]; $found = true; } } // if ( ! $found && isset( $page['meta']['_additional_image'] ) ) { // // Fallback // $old_url = $page['meta']['_additional_image']; // if ( ! is_string( $old_url ) || empty( $old_url ) ) { // continue; // } // $basename = basename( $old_url ); // if ( isset( $filename_to_id[ $basename ] ) ) { // $page['meta']['_additional_image'] = $filename_to_id[ $basename ]; // $found = true; // error_log( '> (2) Import Images: Found image by _additional_image URL for page ID ' . intval( $page['ID'] ) ); // } // } if ( ! $found && isset( $page['additional_image_url'] ) ) { // last resort: try to get the remote image from the URL in 'additional_image_url' $old_url = $page['additional_image_url']; if ( ! is_string( $old_url ) || empty( $old_url ) ) { $basename = ''; } else { // load the image from the URL and save it to the media library $page['meta']['_additional_image'] = adventurebook_copy_remote_file_to_media_library( $old_url ); $basename = basename( $old_url ); if ( $page['meta']['_additional_image'] ) { $found = true; } else { } } } if ( ! is_string( $basename ) || empty( $basename ) ) { $basename = ''; } } } // Items if ( isset( $data['item_definitions'] ) && is_array( $data['item_definitions'] ) ) { foreach ( $data['item_definitions'] as &$item ) { if ( ! empty( $item['image_url'] ) ) { $basename = basename( $item['image_url'] ); if ( isset( $filename_to_url[ $basename ] ) ) { $item['image_url'] = $filename_to_url[ $basename ]; } } } } // Enemies if ( isset( $data['enemy_definitions'] ) && is_array( $data['enemy_definitions'] ) ) { foreach ( $data['enemy_definitions'] as &$enemy ) { if ( ! empty( $enemy['image_url'] ) ) { $basename = basename( $enemy['image_url'] ); if ( isset( $filename_to_url[ $basename ] ) ) { $enemy['image_url'] = $filename_to_url[ $basename ]; } } } } } function adventurebook_copy_remote_file_to_media_library( $url ): int { if ( empty( $url ) || ! filter_var( $url, FILTER_VALIDATE_URL ) ) { return 0; } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; // Lade die Datei temporär herunter $temp_file = download_url( $url ); if ( is_wp_error( $temp_file ) ) { return 0; } // Bereite das Dateiarraay für media_handle_sideload vor $file_array = [ 'name' => basename( wp_parse_url( $url, PHP_URL_PATH ) ), 'tmp_name' => $temp_file, ]; // Füge die Datei der Mediathek hinzu $attachment_id = media_handle_sideload( $file_array, 0 ); if ( is_wp_error( $attachment_id ) ) { wp_delete_file( $temp_file ); // Lösche die temporäre Datei return 0; } return $attachment_id; } /** * Utility function: remove a directory and all its contents recursively * * @param string $dir path to directory * * @return void */ function adventurebook_rrmdir( $dir ) { global $wp_filesystem; // initialize the WP filesystem, no more using file_put_contents etc. if ( empty( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } if ( ! $wp_filesystem->is_dir( $dir ) ) { return; } // content of directory $files = $wp_filesystem->dirlist( $dir ); if ( ! empty( $files ) ) { foreach ( $files as $name => $info ) { $path = trailingslashit( $dir ) . $name; if ( 'f' === $info['type'] ) { // delete file $wp_filesystem->delete( $path ); } elseif ( 'd' === $info['type'] ) { // delete recursively adventurebook_rrmdir( $path ); } } } // now delete the empty directory $wp_filesystem->rmdir( $dir ); } // handle import request add_action( 'admin_post_adventurebook_import', 'adventurebook_handle_import' ); function adventurebook_handle_import(): void { if ( ! isset( $_POST['adventurebook_import_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['adventurebook_import_nonce'] ) ), 'adventurebook_import' ) ) { wp_die( esc_html__( 'Security check failed.', 'adventurebook' ) ); } if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to import an Adventurebook.', 'adventurebook' ) ); } if ( ! isset( $_FILES['adventurebook_import_file'] ) || isset( $_FILES['adventurebook_import_file']['error'] ) && $_FILES['adventurebook_import_file']['error'] !== UPLOAD_ERR_OK ) { wp_die( esc_html__( 'File upload failed. Please try again.', 'adventurebook' ) ); } if ( empty( $_FILES['adventurebook_import_file']['tmp_name'] ) ) { wp_die( esc_html__( 'No file uploaded. Please try again.', 'adventurebook' ) ); } // get import options $create_new = isset( $_POST['adventurebook_import_create_new'] ); if ( is_uploaded_file( $_FILES['adventurebook_import_file']['tmp_name'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $file = sanitize_text_field( wp_unslash( $_FILES['adventurebook_import_file']['tmp_name'] ) ); } else { wp_die( esc_html__( 'Invalid file upload.', 'adventurebook' ) ); } $import_data = adventurebook_import_from_zip( $file ); if ( ! $import_data || ! is_array( $import_data ) ) { wp_die( esc_html__( 'Failed to import Adventurebook. Invalid file format.', 'adventurebook' ) ); } // check if we should create a new adventurebook or add to an existing one if ( ! $create_new && isset( $import_data['title'] ) ) { // try to find an existing adventurebook with the same title $existing_books = get_posts( array( 'post_type' => 'adventurebook', 'posts_per_page' => 1, 'title' => $import_data['title'], 'post_status' => 'any', ) ); if ( $existing_books && count( $existing_books ) > 0 ) { $existing_book = $existing_books[0]; $new_book_id = $existing_book->ID; } else { // we haven't found an existing book, so we need to create a new one // but only if the import file contains pages // if it does not contain pages, the import probably only contains item definitions or similar if ( isset( $import_data['pages'] ) && is_array( $import_data['pages'] ) ) { $create_new = true; } } } else { $create_new = true; } if ( $create_new ) { // create a new adventurebook $new_book_id = null; // create a new adventurebook post $adventurebook_title = sanitize_text_field( $import_data['title'] . ' (imported)' ); $new_book_id = wp_insert_post( array( 'post_type' => 'adventurebook', 'post_title' => $adventurebook_title, 'post_content' => wp_kses_post( $import_data['content'] ), 'post_status' => 'publish', ) ); if ( is_wp_error( $new_book_id ) ) { wp_die( esc_html__( 'Failed to create new Adventurebook.', 'adventurebook' ) ); } // add meta data if ( isset( $import_data['meta'] ) && is_array( $import_data['meta'] ) ) { foreach ( $import_data['meta'] as $key => $value ) { update_post_meta( $new_book_id, sanitize_text_field( $key ), maybe_unserialize( $value[0] ) ); } } } // mapping of old page IDs to new page IDs $old_to_new_page_ids = array(); // create pages if ( isset( $import_data['pages'] ) && is_array( $import_data['pages'] ) ) { foreach ( $import_data['pages'] as $page_data ) { $new_page_id = wp_insert_post( array( 'post_type' => 'adventurebook_page', 'post_title' => sanitize_text_field( $page_data['title'] ), 'post_content' => wp_kses_post( $page_data['content'] ), 'post_status' => 'publish', 'post_parent' => $new_book_id, ) ); if ( ! is_wp_error( $new_page_id ) && isset( $page_data['meta'] ) && is_array( $page_data['meta'] ) ) { foreach ( $page_data['meta'] as $key => $value ) { // check if value is an array and only take the first element // if it is not an array, just take the value if ( is_array( $value ) ) { update_post_meta( $new_page_id, sanitize_text_field( $key ), maybe_unserialize( $value[0] ) ); } else { update_post_meta( $new_page_id, sanitize_text_field( $key ), maybe_unserialize( $value ) ); } } } // save the old page_id to new page_id mapping for later use $old_to_new_page_ids[ $page_data['ID'] ] = $new_page_id; } } // after all pages are created, set the new startpage_id on the adventurebook if ( isset( $import_data['meta']['_adventurebook_start_page'] ) ) { $old_startpage_id = intval( $import_data['meta']['_adventurebook_start_page'][0] ); if ( isset( $old_to_new_page_ids[ $old_startpage_id ] ) ) { update_post_meta( $new_book_id, '_adventurebook_start_page', $old_to_new_page_ids[ $old_startpage_id ] ); } } // after all pages are created, we need to iterate all decisions in all pages again and update the $option['target'] with the new page_id if ( isset( $import_data['pages'] ) && is_array( $import_data['pages'] ) ) { foreach ( $import_data['pages'] as $page_data ) { if ( isset( $page_data['meta']['_adventurebook_decision_options'] ) ) { $old_page_id = $page_data['ID']; if ( isset( $old_to_new_page_ids[ $old_page_id ] ) ) { $new_page_id = $old_to_new_page_ids[ $old_page_id ]; $decisions = maybe_unserialize( $page_data['meta']['_adventurebook_decision_options'][0] ); if ( is_array( $decisions ) ) { $updated = false; foreach ( $decisions as &$option ) { if ( isset( $option['target'] ) && isset( $old_to_new_page_ids[ intval( $option['target'] ) ] ) ) { $option['target'] = $old_to_new_page_ids[ intval( $option['target'] ) ]; $updated = true; } } if ( $updated ) { update_post_meta( $new_page_id, '_adventurebook_decision_options', $decisions ); } } } } } } // import item definitions if ( isset( $import_data['item_definitions'] ) && is_array( $import_data['item_definitions'] ) ) { foreach ( $import_data['item_definitions'] as $item_definition ) { // check if an item with the same code already exists $existing_item = Item_Definition::get_item_definition_by_code( $item_definition['code'] ); if ( ! $existing_item ) { Item_Definition::create_item_definition( $item_definition['code'], $item_definition['name'], $item_definition['description'], $item_definition['stats'], $item_definition['image_url'] ); } } } // import enemy definitions if ( isset( $import_data['enemy_definitions'] ) && is_array( $import_data['enemy_definitions'] ) ) { foreach ( $import_data['enemy_definitions'] as $enemy_definition ) { // check if an enemy with the same code already exists $existing_enemy = Enemy_Definition::get_enemy_definition_by_code( $enemy_definition['code'] ); if ( ! $existing_enemy ) { Enemy_Definition::create_enemy_definition( $enemy_definition['code'], $enemy_definition['name'], $enemy_definition['is_hostile'], $enemy_definition['can_be_attacked'], $enemy_definition['xp_value'], $enemy_definition['description'], $enemy_definition['stats'], $enemy_definition['inventory'], $enemy_definition['image_url'] ); } } } // import item spawns if ( isset( $import_data['item_spawns'] ) && is_array( $import_data['item_spawns'] ) ) { foreach ( $import_data['item_spawns'] as $spawn_data ) { $old_page_id = intval( $spawn_data['page_id'] ); if ( isset( $old_to_new_page_ids[ $old_page_id ] ) ) { $new_page_id = $old_to_new_page_ids[ $old_page_id ]; Item_Spawn::create_item_spawn( $spawn_data['code'], $new_page_id ); } } } // import enemy spawns if ( isset( $import_data['enemy_spawns'] ) && is_array( $import_data['enemy_spawns'] ) ) { foreach ( $import_data['enemy_spawns'] as $spawn_data ) { $old_page_id = intval( $spawn_data['page_id'] ); if ( isset( $old_to_new_page_ids[ $old_page_id ] ) ) { $new_page_id = $old_to_new_page_ids[ $old_page_id ]; Enemy_Spawn::create_enemy_spawn( $spawn_data['code'], $new_page_id, $spawn_data['respawn_time'], $spawn_data['alternate_page_ids'] ); } } } // import scripts if ( isset( $import_data['scripts'] ) && is_array( $import_data['scripts'] ) ) { foreach ( $import_data['scripts'] as $script_data ) { // check if a script with the same code already exists $existing_script = Script::get_script_by_code( $script_data['code'] ); if ( $existing_script ) { continue; } Script::create_script( $script_data['code'], $script_data['name'], $script_data['commands'] ); } } // import characters if ( isset( $import_data['characters'] ) && is_array( $import_data['characters'] ) ) { foreach ( $import_data['characters'] as $character_data ) { Character::create_character_by_params( 0, 0, $new_book_id, $character_data['name'], $character_data['location_id'], $character_data['stats'], $character_data['inventory'], ); } } // for now, just show a success message echo '

' . esc_html__( 'Adventurebook imported successfully. Please review the imported data.', 'adventurebook' ) . '

'; // redirect back to settings page $redirect_url = add_query_arg( array( 'page' => 'adventurebook-settings', 'message' => 'import_success' ), admin_url( 'admin.php' ) ); wp_safe_redirect( $redirect_url ); exit; } // handle export request add_action( 'admin_post_adventurebook_export', 'adventurebook_handle_export' ); function adventurebook_handle_export(): void { if ( ! isset( $_POST['adventurebook_export_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['adventurebook_export_nonce'] ) ), 'adventurebook_export' ) ) { wp_die( esc_html__( 'Security check failed.', 'adventurebook' ) ); } $adventurebook_id = isset( $_POST['adventurebook_id'] ) ? intval( ab_get('adventurebook_id') ) : 0; if ( ! $adventurebook_id ) { wp_die( esc_html__( 'Invalid Adventurebook ID.', 'adventurebook' ) ); } if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to export this Adventurebook.', 'adventurebook' ) ); } $post = get_post( $adventurebook_id ); if ( ! $post ) { wp_die( esc_html__( 'Adventurebook not found.', 'adventurebook' ) ); } // prepare data for export $data = array( 'title' => $post->post_title, 'content' => $post->post_content, 'meta' => get_post_meta( $adventurebook_id ), 'date_export' => current_time( 'mysql' ), ); // check if the option to include book & pages is set ('include_book') $include_book = ab_get( 'adventurebook_export_include_book' ); $include_items = ab_get( 'adventurebook_export_include_items' ); $include_enemies = ab_get( 'adventurebook_export_include_enemies' ); $include_characters = ab_get( 'adventurebook_export_include_characters' ); $include_scripts = ab_get( 'adventurebook_export_include_scripts' ); $image_ids = array(); if ( $include_book ) { // export book & pages // get all pages of this adventurebook (CPT adventurebook_page) // get all items of this adventurebook - this means we get all adventurebook_item_spawns entries from the database // that have an item (code matches an entry in adventurebook_items) that belongs to this adventurebook // the item belongs to this adventurebook if the found item_spawns entry has a page_id that belongs to this adventurebook global $wpdb; $page_ids = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'adventurebook_page' AND post_parent = %d", $adventurebook_id ) ); if ( ! empty( $page_ids ) ) { // first get all item spawns and enemy spawns for these pages global $wpdb; $placeholders = implode( ',', array_fill( 0, count( $page_ids ), '%d' ) ); // Query 1: Item Spawns $query_item_spawns = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}adventurebook_item_spawns WHERE page_id IN ($placeholders)", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare ...$page_ids // unpack array values ); $result_item_spawns = $wpdb->get_results( $query_item_spawns, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $data['item_spawns'] = Item_Spawn::get_item_spawns_by_result( $result_item_spawns ); // Query 2: Enemy Spawns but only with parent_id = 0 (no character-specific spawns) $query_enemy_spawns = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}adventurebook_enemy_spawns WHERE page_id IN ($placeholders) AND parent_id = 0", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare ...$page_ids // unpack array values ); $result_enemy_spawns = $wpdb->get_results( $query_enemy_spawns, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $data['enemy_spawns'] = Enemy_Spawn::get_enemy_spawns_by_result( $result_enemy_spawns ); } // get all pages for this adventurebook $pages = get_posts( array( 'post_type' => 'adventurebook_page', 'posts_per_page' => - 1, 'post_status' => 'any', 'post_parent' => $adventurebook_id, 'orderby' => 'ID', 'order' => 'ASC' ) ); $data['pages'] = array(); foreach ( $pages as $page ) { // we need the file_url for the additional_image for later import (get_post_meta( $page->ID, '_additional_image', true)) $additional_image_id = get_post_meta( $page->ID, '_additional_image', true ); $additional_image_url = ''; $additional_image_filename = ''; if ( $additional_image_id ) { $additional_image_url = wp_get_attachment_url( $additional_image_id ); if ( $additional_image_url ) { $additional_image_filename = basename( $additional_image_url ); } } $data['pages'][] = array( 'title' => $page->post_title, 'content' => $page->post_content, 'meta' => get_post_meta( $page->ID ), 'ID' => $page->ID, 'additional_image_id' => $additional_image_id, 'additional_image_url' => $additional_image_url, 'additional_image_filename' => $additional_image_filename, ); } // we need to .zip all images that are used in pages, items and enemies and provide them in a folder inside the .zip // pages: post_meta( $post_id, '_additional_image', $image_id ); // iterate through the pages and collect all image IDs foreach ( $pages as $page ) { $image_id = get_post_meta( $page->ID, '_additional_image', true ); if ( $image_id && ! in_array( $image_id, $image_ids, true ) ) { $image_ids[] = $image_id; } } } if ( $include_items ) { // export item definitions $data['item_definitions'] = Item_Definition::get_all_item_definitions_by_adventurebook_id( $adventurebook_id ); // items: image_url foreach ( $data['item_definitions'] as $item ) { do_log( 'Exporting item image: ' . $item->code ); if ( isset( $item->image_url ) && ! empty( $item->image_url ) ) { do_log( '...image url: ' . $item->image_url ); // try to get the attachment ID from the URL $attachment_id = adventurebook_ensure_attachment_from_url( $item->image_url ); if ( $attachment_id && ! in_array( $attachment_id, $image_ids, true ) ) { do_log( '...found attachment ID: ' . $attachment_id ); $image_ids[] = $attachment_id; } else { do_log( '...no attachment ID found' ); } } else { do_log( '...no image url set' ); } } } if ( $include_enemies ) { // export enemy definitions $data['enemy_definitions'] = Enemy_Definition::get_all_enemy_definitions_by_adventurebook_id( $adventurebook_id ); // enemies: image_url foreach ( $data['enemy_definitions'] as $enemy ) { if ( isset( $enemy->image_url ) && ! empty( $enemy->image_url ) ) { // try to get the attachment ID from the URL $attachment_id = adventurebook_ensure_attachment_from_url( $enemy->image_url ); if ( $attachment_id && ! in_array( $attachment_id, $image_ids, true ) ) { $image_ids[] = $attachment_id; } } } } if ( $include_characters ) { // export character definitions $data['characters'] = Character::get_characters_by_adventurebook_id( $adventurebook_id ); // set the user_id of all characters to 0 for export (we don't want to export user data) foreach ( $data['characters'] as &$character ) { $character->user_id = 0; // TODO: export all items this character owns // we don't export the items table, so fill up the inventory with the items data // in the import script, create new entries in the items table for the character's inventory } } if ( $include_scripts ) { $data['scripts'] = Script::get_all_scripts_by_adventurebook_id( $adventurebook_id ); } // now we have all image IDs in the $image_ids array // we will create a folder 'images' and copy all images there // and then create a .zip file with the JSON file and the images folder // for now, we will just provide the image URLs in an array $data['images'] = array(); foreach ( $image_ids as $image_id ) { $image_url = wp_get_attachment_url( $image_id ); if ( $image_url ) { $data['images'][] = $image_url; } } // output the JSON file for download // set the appropriate headers // application/json // content-disposition: attachment; filename="adventurebook_export_{adventurebook_id}.json" // no-cache // expires: 0 // finally, output the JSON data // and exit // Pfade vorbereiten $upload_dir = wp_upload_dir(); $tmp_dir = trailingslashit( $upload_dir['basedir'] ) . 'adventurebook_export_tmp_' . $adventurebook_id . '/'; $images_dir = $tmp_dir . 'images/'; // delete old temp directory if it exists if ( file_exists( $tmp_dir ) ) { adventurebook_rrmdir( $tmp_dir ); } // create temp directories wp_mkdir_p( $images_dir ); // save JSON file $json_path = $tmp_dir . 'adventurebook_export_' . $adventurebook_id . '.json'; file_put_contents( $json_path, wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) ); // copy images to images directory foreach ( $image_ids as $image_id ) { $file_path = get_attached_file( $image_id ); if ( $file_path && file_exists( $file_path ) ) { $basename = basename( $file_path ); copy( $file_path, $images_dir . $basename ); } } // create zip file $zip_path = trailingslashit( $upload_dir['basedir'] ) . 'adventurebook_export_' . $adventurebook_id . '.zip'; $zip = new ZipArchive(); if ( $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) { // JSON file add $zip->addFile( $json_path, basename( $json_path ) ); // Images add $image_files = glob( $images_dir . '*.*' ); foreach ( $image_files as $file ) { $zip->addFile( $file, 'images/' . basename( $file ) ); } $zip->close(); } // remove temp directory adventurebook_rrmdir( $tmp_dir ); global $wp_filesystem; // initialize the WP filesystem, no more using file_put_contents etc. if ( empty( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } // read file contents $file_contents = $wp_filesystem->get_contents( $zip_path ); // serve download if ( $file_contents !== false ) { // needed headers nocache_headers(); header( 'Content-Type: application/zip' ); header( 'Content-Disposition: attachment; filename="adventurebook_export_' . $adventurebook_id . '.zip"' ); header( 'Content-Length: ' . strlen( $file_contents ) ); header( 'Pragma: no-cache' ); header( 'Expires: 0' ); // output file echo $file_contents; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } // remove temp zip file $wp_filesystem->delete( $zip_path ); exit; } function adventurebook_ensure_attachment_from_url( $image_url ) { // Versuch, eine Attachment-ID zu ermitteln $attachment_id = attachment_url_to_postid( $image_url ); if ( $attachment_id ) { return $attachment_id; } // Lokalen Pfad bestimmen (falls im uploads-Verzeichnis) $upload_dir = wp_upload_dir(); $local_path = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $image_url ); if ( file_exists( $local_path ) ) { // Datei existiert lokal, in Mediathek einfügen $filetype = wp_check_filetype( basename( $local_path ), null ); $attachment = [ 'guid' => $upload_dir['url'] . '/' . basename( $local_path ), 'post_mime_type' => $filetype['type'], 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $local_path ) ), 'post_content' => '', 'post_status' => 'inherit', ]; $attachment_id = wp_insert_attachment( $attachment, $local_path ); require_once( ABSPATH . 'wp-admin/includes/image.php' ); $attach_data = wp_generate_attachment_metadata( $attachment_id, $local_path ); wp_update_attachment_metadata( $attachment_id, $attach_data ); return $attachment_id; } // file is a remote URL, try to download and sideload if ( strpos( $image_url, 'http' ) === 0 ) { $tmp = download_url( $image_url ); if ( is_wp_error( $tmp ) ) { return 0; // Download fehlgeschlagen } $file_array = [ 'name' => basename( $image_url ), 'tmp_name' => $tmp, ]; // Insert file into media library $attachment_id = media_handle_sideload( $file_array, 0 ); if ( is_wp_error( $attachment_id ) ) { wp_delete_file( $tmp ); return 0; } return $attachment_id; } return 0; }