The Upload of zip-Files with CMB2 as suggested by Michael works fine. Thanks again! Just for the case someone likes to do the same:
I found two solutions for the unzip problem.
First solution: use the updated_post_meta
action hook to unzip if the value and validate whenever zipfieldname_id
changes (Note here that the _id
suffix must be attached to the field name!)
class Unzipper {
/**
* Run all Hooks.
*
* @return void
*/
public function init() {
require_once ABSPATH . 'wp-admin/includes/file.php';
WP_Filesystem();
add_action( 'updated_post_meta', array( $this, 'unzip_and_validate' ), 10, 4 );
}
/**
* Executed when a new zipfile is assigned to my_cpt
* Unpacks the zip-file checks if its content meets the requirements.
*
* @param int $meta_id ID of updated metadata entry.
* @param int $post_id ID of the object metadata is for.
* @param string $meta_key Metadata key.
* @param mixed $meta_value Metadata value.
*
* @return void
*/
public function unzip_and_validate( $meta_id, $post_id, $meta_key, $meta_value ) {
if ( 'panorama_zip_id' !== $meta_key ) {
return;
}
write_log( "new panorama_zip_id: {$meta_value}" );
global $wp_filesystem;
$zip_file_path = get_attached_file( $meta_value );
$upload_array = wp_upload_dir();
$upload_folder = $upload_array['basedir'];
$dest_folder = "{$upload_folder}/my_plugin_folder/{$post_id}";
$wp_filesystem->delete( $dest_folder, true, 'd' );
wp_mkdir_p( $dest_folder );
unzip_file( $zip_file_path, $dest_folder );
// validation
...
2. Solution (More complicated but also more user friendly): Use a JS-Observer and Ajax. The advantage here is that we can give the user validation feedback as soon as he selects a new zip file.
In your admin JavaScript file use an observer to run a function everytime the value of the hidden field zipfieldname_id
(Again: Note the id
suffix) changes. (A simple change event wont fire, if the value is not changed by the user but by WordPress scripts, so you have to use an observer here).
The function called by the Observer will send an Ajax request, that sends the zipfile id. The invoked PHP-Funktion will find the zipfile by its id and unzips it, validates the unzipped folder and sends a validation message or code in response.
Use this response in JavaScript to add a validation message to output a validation feedback to the admin page.
If your validation prosses works fine, you can prevent the zip file to be unziped and validated more the one time, by storing a validation code in the meta table after validation and check if such a code is stored in the meta table, before you unzip and validat it.
Admittedly: quite elaborate, but as user friendly as possible.
Here is my JS-Code
jQuery( function( $ ) {
if ( $( 'body.post-type-tree_panorama' ).length ) {
const url = zipAjaxObject.ajaxURL;
const data = {};
data.action = 'zip-validation';
data.nonce = zipAjaxObject.nonce;
// Observe if the zip file is changing
const hiddenZip = document.getElementById( 'panorama_zip_id' );
const validationMessage = $( 'div.cmb2-id-panorama-zip div.validation' );
function ajaxValidationRequest() {
let zipId = hiddenZip.getAttribute( 'value' );
data.zipId = zipId;
$.ajax({
url: url,
type: 'post',
data: data,
success( response ) {
response = JSON.parse( response );
console.log( response.notice );
validationMessage.html( response.notice );
}
});
}
ajaxValidationRequest();
const mutationCallback = ( mutationsList ) => {
for ( const mutation of mutationsList ) {
if ( 'attributes' === mutation.type && 'value' === mutation.attributeName ) {
ajaxValidationRequest();
}
}
};
const observer = new MutationObserver( mutationCallback );
observer.observe( hiddenZip, { attributes: true });
}
});
And some of my php code:
<?php
/**
* Ajax-Resopnse on panorama_zip_id change.
*
* @link https://digidrom.de
* @since 1.0.0
*
* @package digipano
* @subpackage digipano/includes
*/
namespace digidrom\digipano;
use digidrom\digipano\ZipError;
/**
* Handels Ajax requests that is send when ever the panorama_zip_id of a panoaram_tree changes.
*
* @package Digipano
* @author Jan Tack <[email protected]>
*/
class ZipAjax {
/**
* Run all Hooks.
*
* @return void
*/
public function init() {
add_action( 'admin_enqueue_scripts', array( $this, 'load' ) );
add_action( 'wp_ajax_zip-validation', array( $this, 'response_ajax' ) );
}
/**
* The user should get an instant feedback when selecting a zip file for a tree-panoram,
* even before adas post form is submitted.
*
* Therefore this function is called via Ajax whenever the user selects a new zip file.
* The zip file is then unpacked and validated. The response to the Ajax request then contains
* a div container with a matching success or error message.
*
* @return void
*/
public function response_ajax() {
// phpcs:ignore
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'ajax-nonce' ) ) {
die( 'YOU SHALL NOT PASS!' );
}
if ( isset( $_POST['zipId'] ) ) {
$zip_id = sanitize_text_field( wp_unslash( $_POST['zipId'] ) );
// $note = get_post_meta( $zip_id, 'zip-validation', true );
$note = '';
if ( '' === $note ) {
$note = $this->unzip_and_validate( $zip_id );
if ( 'allright' === $note ) {
$upload_array = wp_upload_dir();
$upload_folder = $upload_array['basedir'];
$source_dir = "{$upload_folder}/unzip/{$zip_id}/";
}
update_post_meta( $zip_id, 'zip-validation', $note );
}
$response = array(
'zipId' => $zip_id,
'note' => $note,
'notice' => ZipError::get_notice( $note ),
);
echo wp_json_encode( $response );
}
die();
}
/**
* Enqueue script and inject data
*
* @return void
*/
public function load() {
wp_enqueue_script( 'zip-script', plugins_url( '/js/zipvalidation.js', DIGIPANO_FILE ), array( 'jquery' ), '1.0.0', true );
$zip_ajax_object = array(
'ajaxURL' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'ajax-nonce' ),
);
wp_localize_script( 'zip-script', 'zipAjaxObject', $zip_ajax_object );
}
/**
* Unzip and validate
*
* @param int $zip_id The id of the zip file.
*
* @return ZipError
*/
private function unzip_and_validate( $zip_id ) {
global $wp_filesystem;
$upload_array = wp_upload_dir();
$upload_folder = $upload_array['basedir'];
$dest_folder = "{$upload_folder}/unzip/{$zip_id}/";
if ( $wp_filesystem->exists( $dest_folder ) ) {
$wp_filesystem->delete( $dest_folder, true, 'd' );
}
wp_mkdir_p( $dest_folder );
$zip_file = get_attached_file( $zip_id );
unzip_file( $zip_file, $dest_folder );
require_once __DIR__ . '/class-unzipvalidator.php';
$validator = new UnzipValidator( $dest_folder );
return $validator->validate_and_sanitize();
}
}