callable(): array, 'import' => callable( array ): string ). * * @return array> */ private static function content_handlers() { /** * Lets an extension export and import the contents the webmaster built. * * @param array> $handlers Handlers by key. "export" returns a list of data; * "import" receives that data and returns a message. */ return (array) apply_filters( 'vefdk_content_handlers', array() ); } /** * What a reset can put back: array( key => array( label, callback ) ). * * @return array */ private static function reset_groups() { $groups = array( 'journal' => array( 'label' => __( 'The journal and the finished files', 'vefdocskit' ), 'callback' => array( __CLASS__, 'reset_journal' ), ), 'settings' => array( 'label' => __( 'The settings and the style', 'vefdocskit' ), 'callback' => array( __CLASS__, 'reset_settings' ), ), ); /** * Lets an extension add what a reset can put back. * * @param array $groups Groups. */ return (array) apply_filters( 'vefdk_reset_groups', $groups ); } /** * The cards, printed under the settings form. * * @return void */ public static function render() { $handlers = self::content_handlers(); ?>

$group ) : ?>

$free ); /** * Lets an extension add its settings to the file (secrets and ids of this site never belong in it). * * @param array $data Settings by key. */ $data = (array) apply_filters( 'vefdk_export_settings', $data ); } else { foreach ( self::content_handlers() as $key => $handler ) { $data[ $key ] = (array) call_user_func( $handler['export'] ); } } $json = wp_json_encode( array( 'plugin' => 'vefdocskit', 'kind' => $kind, 'version' => VEFDK_VERSION, 'data' => $data, ), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="vefdocskit-' . $kind . '-' . gmdate( 'Y-m-d' ) . '.json"' ); header( 'X-Content-Type-Options: nosniff' ); echo $json; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON file download. exit; } /** * Reads a file of the settings or of the contents. * * @return void */ public static function import() { self::guard( 'vefdk_import' ); $kind = self::kind(); // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- verified in guard(); the file is checked below. $file = isset( $_FILES['file'] ) && is_array( $_FILES['file'] ) ? $_FILES['file'] : array(); // phpcs:enable $tmp = isset( $file['tmp_name'] ) && is_string( $file['tmp_name'] ) ? $file['tmp_name'] : ''; if ( UPLOAD_ERR_OK !== (int) ( $file['error'] ?? UPLOAD_ERR_NO_FILE ) || '' === $tmp || ! is_uploaded_file( $tmp ) ) { self::done( 'error', __( 'No file received. Choose a file and try again.', 'vefdocskit' ) ); } if ( (int) ( $file['size'] ?? 0 ) > self::MAX_BYTES ) { self::done( 'error', __( 'This file is too large.', 'vefdocskit' ) ); } $text = file_get_contents( $tmp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- uploaded file, local path. $json = is_string( $text ) ? json_decode( $text, true ) : null; if ( ! is_array( $json ) || 'vefdocskit' !== ( $json['plugin'] ?? '' ) || ! isset( $json['kind'], $json['data'] ) || ! is_array( $json['data'] ) ) { self::done( 'error', __( 'This file does not come from VEFdocsKit.', 'vefdocskit' ) ); } if ( $kind !== $json['kind'] ) { self::done( 'error', 'settings' === $kind ? __( 'This file holds contents, not settings. Use “Import contents” instead.', 'vefdocskit' ) : __( 'This file holds settings, not contents. Use “Import settings” instead.', 'vefdocskit' ) ); } if ( 'settings' === $kind ) { $free = isset( $json['data']['free'] ) && is_array( $json['data']['free'] ) ? $json['data']['free'] : array(); unset( $free['page_id'] ); VEFDK_Settings::update( VEFDK_Settings::clean( array_merge( VEFDK_Settings::all(), $free ) ) ); /** * An extension reads its part of a settings file (clean every value). * * @param array $data Settings by key. */ do_action( 'vefdk_import_settings', $json['data'] ); self::done( 'success', __( 'Settings imported.', 'vefdocskit' ) ); } $messages = array(); foreach ( self::content_handlers() as $key => $handler ) { if ( isset( $json['data'][ $key ] ) && is_array( $json['data'][ $key ] ) ) { $messages[] = (string) call_user_func( $handler['import'], $json['data'][ $key ] ); } } self::done( $messages ? 'success' : 'error', $messages ? implode( ' ', array_filter( $messages ) ) : __( 'Nothing to import in this file.', 'vefdocskit' ) ); } /** * Puts back what was ticked. * * @return void */ public static function reset() { self::guard( 'vefdk_reset' ); // phpcs:disable WordPress.Security.NonceVerification.Missing -- verified in guard(). $word = isset( $_POST['word'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['word'] ) ) ) : ''; $asked = isset( $_POST['groups'] ) && is_array( $_POST['groups'] ) ? array_map( 'sanitize_key', wp_unslash( $_POST['groups'] ) ) : array(); // phpcs:enable if ( self::CONFIRM_WORD !== $word ) { self::done( 'error', __( 'Nothing was reset: the confirmation word was not typed.', 'vefdocskit' ) ); } $groups = self::reset_groups(); $done = array(); foreach ( $asked as $key ) { if ( isset( $groups[ $key ] ) ) { call_user_func( $groups[ $key ]['callback'] ); $done[] = $groups[ $key ]['label']; } } if ( ! $done ) { self::done( 'error', __( 'Nothing was reset: tick what to put back.', 'vefdocskit' ) ); } self::done( 'success', sprintf( /* translators: %s: list of what was reset */ __( 'Reset: %s.', 'vefdocskit' ), implode( ', ', $done ) ) ); } /** * Empties the journal and removes the finished files. * * @return void */ public static function reset_journal() { global $wpdb; VEFDK_Storage::remove_all(); $wpdb->query( 'DELETE FROM ' . VEFDK_DB::table( 'journal' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- table name built from the prefix. } /** * Puts the settings back to their defaults (the page of the documents stays). * * @return void */ public static function reset_settings() { update_option( VEFDK_Settings::OPTION, VEFDK_Settings::clean( array( 'page_id' => (int) VEFDK_Settings::get( 'page_id' ) ) ), false ); VEFDK_Settings::flush(); /** * The settings of VEFdocsKit were put back to their defaults: an extension does the same with its own. */ do_action( 'vefdk_reset_settings' ); } /** * The kind of file asked for. * * @return string settings or content. */ private static function kind() { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- verified in guard(). return isset( $_REQUEST['kind'] ) && 'content' === sanitize_key( wp_unslash( $_REQUEST['kind'] ) ) ? 'content' : 'settings'; } /** * Rights and nonce of a form: an administrator, never an outside helper. * * @param string $action Nonce action. * @return void */ private static function guard( $action ) { if ( ! current_user_can( 'manage_options' ) || VEFDK_Capabilities::is_external_helper() ) { wp_die( esc_html__( 'You do not have permission to do this.', 'vefdocskit' ), '', array( 'response' => 403 ) ); } check_admin_referer( $action ); } /** * Shows a message on the Settings tab. * * @param string $kind success or error. * @param string $message Text. * @return void */ private static function done( $kind, $message ) { VEFDK_Admin::flash( $kind, $message ); wp_safe_redirect( VEFDK_Admin::url( 'settings' ) ); exit; } }