<?php
/**
 * Genesis Framework.
 *
 * WARNING: This file is part of the core Genesis Framework. DO NOT edit this file under any circumstances.
 * Please do all modifications in the form of a child theme.
 *
 * @package Genesis\Admin
 * @author  StudioPress
 * @license GPL-2.0-or-later
 * @link    https://my.studiopress.com/themes/genesis/
 */

/**
 * Register a new admin page, providing content and corresponding menu item for the Import / Export page.
 *
 * Although this class was added in 1.8.0, some of the methods were originally standalone functions added in previous
 * versions of Genesis.
 *
 * @package Genesis\Admin
 *
 * @since 1.8.0
 */
class Genesis_Admin_Import_Export extends Genesis_Admin_Basic {

	/**
	 * Create an admin menu item and settings page.
	 *
	 * Also hook in the handling of file imports and exports.
	 *
	 * @since 1.8.0
	 *
	 * @see \Genesis_Admin_Import_Export::export() Handle settings file exports.
	 * @see \Genesis_Admin_Import_Export::import() Handle settings file imports.
	 */
	public function __construct() {

		$this->help_base = GENESIS_VIEWS_DIR . '/help/import-export-';

		$page_id = 'genesis-import-export';

		$menu_ops = [
			'submenu' => [
				'parent_slug' => 'genesis',
				'page_title'  => __( 'Genesis - Import/Export', 'genesis' ),
				'menu_title'  => __( 'Import/Export', 'genesis' ),
			],
		];

		$this->create( $page_id, $menu_ops );

		add_action( 'admin_init', [ $this, 'export' ] );
		add_action( 'admin_init', [ $this, 'import' ] );

	}

	/**
	 * Contextual help content.
	 *
	 * @since 2.0.0
	 */
	public function help() {

		$this->add_help_tab( 'general', __( 'Import/Export', 'genesis' ) );
		$this->add_help_tab( 'import', __( 'Import', 'genesis' ) );
		$this->add_help_tab( 'export', __( 'Export', 'genesis' ) );

		// Add help sidebar.
		$this->set_help_sidebar();

	}

	/**
	 * Callback for displaying the Genesis Import / Export admin page.
	 *
	 * Call the genesis_import_export_form action after the last default table row.
	 *
	 * @since 1.4.0
	 */
	public function admin() {

		include GENESIS_VIEWS_DIR . '/pages/genesis-admin-import-export.php';

	}

	/**
	 * Add custom notices that display after successfully importing or exporting the settings.
	 *
	 * @since 1.4.0
	 *
	 * @return void Return early if not on the correct admin page.
	 */
	public function notices() {

		if ( ! genesis_is_menu_page( 'genesis-import-export' ) ) {
			return;
		}

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- We don't need nonce verification here
		if ( isset( $_REQUEST['imported'] ) && 'true' === $_REQUEST['imported'] ) {
			printf( '<div id="message" class="updated" role="alert"><p><strong>%s</strong></p></div>', esc_html__( 'Settings successfully imported.', 'genesis' ) );
		} elseif ( isset( $_REQUEST['error'] ) && 'true' === $_REQUEST['error'] ) {
			printf( '<div id="message" class="error" role="alert"><p><strong>%s</strong></p></div>', esc_html__( 'There was a problem importing your settings. Please try again.', 'genesis' ) );
		}
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

	}

	/**
	 * Return array of export options and their arguments.
	 *
	 * Plugins and themes can hook into the genesis_export_options filter to add
	 * their own settings to the exporter.
	 *
	 * @since 1.6.0
	 *
	 * @return array Export options.
	 */
	protected function get_export_options() {

		$options = [
			'theme' => [
				'label'          => __( 'Theme Settings', 'genesis' ),
				'settings-field' => GENESIS_SETTINGS_FIELD,
			],
			'seo'   => [
				'label'          => __( 'SEO Settings', 'genesis' ),
				'settings-field' => GENESIS_SEO_SETTINGS_FIELD,
			],
		];

		return (array) apply_filters( 'genesis_export_options', $options );

	}

	/**
	 * Echo out the checkboxes for the export options.
	 *
	 * @since 1.6.0
	 *
	 * @return void Return early if there are no options to export.
	 */
	protected function export_checkboxes() {

		$options = $this->get_export_options();

		if ( ! $options ) {
			// Not even the Genesis theme / seo export options were returned from the filter.
			printf( '<p><em>%s</em></p>', esc_html__( 'No export options available.', 'genesis' ) );
			return;
		}

		foreach ( $options as $name => $args ) {
			// Ensure option item has an array key, and that label and settings-field appear populated.
			if ( is_int( $name ) || ! isset( $args['label'], $args['settings-field'] ) || '' === $args['label'] || '' === $args['settings-field'] ) {
				return;
			}

			printf( '<p><label for="genesis-export-%1$s"><input id="genesis-export-%1$s" name="genesis-export[%1$s]" type="checkbox" value="1" /> %2$s</label></p>', esc_attr( $name ), esc_html( $args['label'] ) );

		}

	}

	/**
	 * Generate the export file, if requested, in JSON format.
	 *
	 * After checking we're on the right page, and trying to export, loop through the list of requested options to
	 * export, grabbing the settings from the database, and building up a file name that represents that collection of
	 * settings.
	 *
	 * A .json file is then sent to the browser, named with "genesis" at the start and ending with the current
	 * date-time.
	 *
	 * The genesis_export action is fired after checking we can proceed, but before the array of export options are
	 * retrieved.
	 *
	 * @since 1.4.0
	 *
	 * @return null Return early if not on the correct page, or we're not exporting settings.
	 */
	public function export() {

		if ( ! genesis_is_menu_page( 'genesis-import-export' ) ) {
			return;
		}

		if ( empty( $_REQUEST['genesis-export'] ) ) {
			return;
		}

		check_admin_referer( 'genesis-export', 'genesis-export-nonce' );

		$export_data = $_REQUEST['genesis-export'];

		/**
		 * Fires before export happens, after admin referer has been checked.
		 *
		 * @since 1.4.0
		 *
		 * @param string Value of `genesis-export` request variable, containing a list of which settings to export.
		 */
		do_action( 'genesis_export', $export_data );

		$options = $this->get_export_options();

		$settings = [];

		// Exported file name always starts with "genesis".
		$prefix = [ 'genesis' ];

		// Loop through set(s) of options.
		foreach ( (array) $export_data as $export => $value ) {
			// Grab settings field name (key).
			$settings_field = $options[ $export ]['settings-field'];

			// Grab all of the settings from the database under that key.
			$settings[ $settings_field ] = get_option( $settings_field );

			// Add name of option set to build up export file name.
			$prefix[] = $export;
		}

		if ( ! $settings ) {
			return;
		}

		// Complete the export file name by joining parts together.
		$prefix = implode( '-', $prefix );

		$output = wp_json_encode( $settings );

		// Prepare and send the export file to the browser.
		header( 'Content-Description: File Transfer' );
		header( 'Cache-Control: public, must-revalidate' );
		header( 'Pragma: hack' );
		header( 'Content-Type: text/plain' );
		header( 'Content-Disposition: attachment; filename="' . $prefix . '-' . gmdate( 'Ymd-His' ) . '.json"' );
		header( 'Content-Length: ' . mb_strlen( $output ) );
		echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		exit;

	}

	/**
	 * Handle the file uploaded to import settings.
	 *
	 * Upon upload, the file contents are JSON-decoded. If there were errors, or no options to import, then reload the
	 * page to show an error message.
	 *
	 * Otherwise, loop through the array of option sets, and update the data under those keys in the database.
	 * Afterwards, reload the page with a success message.
	 *
	 * Calls genesis_import action is fired after checking we can proceed, but before attempting to extract the contents
	 * from the uploaded file.
	 *
	 * @since 1.4.0
	 *
	 * @return null Return early if not on the correct admin page, or we're not importing settings.
	 */
	public function import() {

		if ( ! genesis_is_menu_page( 'genesis-import-export' ) ) {
			return;
		}

		if ( empty( $_REQUEST['genesis-import'] ) ) {
			return;
		}

		check_admin_referer( 'genesis-import', 'genesis-import-nonce' );

		/**
		 * Fires before importing settings, but after admin referer has been checked
		 *
		 * @since 1.4.0
		 *
		 * @param string $import Value of `genesis-import` request variable, containing list of which settings to import.
		 * @param array  $file   Reference to uploaded file to import.
		 */
		do_action( 'genesis_import', $_REQUEST['genesis-import'], $_FILES['genesis-import-upload'] );

		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
		$upload = file_get_contents( $_FILES['genesis-import-upload']['tmp_name'] );

		$options = json_decode( $upload, true );

		// Check for errors.
		if ( ! $options || $_FILES['genesis-import-upload']['error'] ) {
			genesis_admin_redirect(
				'genesis-import-export',
				[
					'error' => 'true',
				]
			);
			exit;
		}

		// Identify the settings keys that we should import.
		$exportables     = $this->get_export_options();
		$importable_keys = [];
		foreach ( $exportables as $exportable ) {
			$importable_keys[] = $exportable['settings-field'];
		}

		// Cycle through data, import Genesis settings.
		foreach ( (array) $options as $key => $settings ) {
			if ( in_array( $key, $importable_keys, true ) ) {
				update_option( $key, $settings );
			}
		}

		// Redirect, add success flag to the URI.
		genesis_admin_redirect(
			'genesis-import-export',
			[
				'imported' => 'true',
			]
		);
		exit;

	}

}
