' . 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;
}