WordPress Attachments ZIP Export for Documents CPT

PHOTO EMBED

Tue May 27 2025 00:40:59 GMT+0000 (Coordinated Universal Time)

Saved by @Y@sir

<?php
/**
 * Create a ZIP of every attachment in a "Documents" CPT,
 * organized into main folders and sub-folders using your exact labels,
 * with "/" replaced by "-" in folder names,
 * and named after the documentโ€™s title.
 */
function create_documents_zip( $post_id ) {
    if ( ! $post_id ) {
        return false;
    }

    // Build ZIP filename from post title
    $post_title = get_the_title( $post_id );
    $slug       = sanitize_title( $post_title );
    $zip_name   = "{$slug}.zip";

    // 1) Define main folders (sections) and sub-folders (fields)
    $sections = [
        'Personal Information' => [
            'passport-sized-photo'                     => 'Passport Sized Photo',
            'valid-passport'                           => 'Valid Passport',
            'national-identity-cards'                  => 'National Identity Cards',
            'employment-pass-s-pass-dependent-pass'    => 'Employment Pass/ S Pass/ Dependent Pass',
            'birth-certificates-household-registration' => 'Birth Certificates/ Household Registration',
            'official-marriage-certificate'            => 'Official Marriage Certificate',
        ],
        'Education & Professional Information' => [
            'education-certificates-transcripts'                    => 'Education Certificates/ Transcripts',
            'child039schildren039s-school-admission-letter-results' => "Child's/Children's School Admission Letter/ Results",
            'additional-certifications-courses-workshops'           => 'Additional Certifications/ Courses/ Workshops',
        ],
        'Social Contributions' => [
            'media-article-s-in-company--newsletters-websites--magazines--showcasing-contributions'
                => 'Media article (s) in Company / Newsletters / Websites / Magazines / Showcasing Contributions',
            'membership-in-clubs--societies'                        => 'Membership in Clubs / Societies',
            'charity-contribution-receipts--letters'               => 'Charity Contribution Receipt(s) / Letter(s)',
            'corporate-social-responsibility--community-participated-events'
                => 'Corporate Social Responsibility / Community Participated Events',
        ],
        'Employment Documents' => [
            'latest-6-months-payslips'                              => 'Latest 6 Months Payslips',
            'valid-business-registration-certificate-acrabizfile'    => 'Valid Business Registration Certificate (ACRABizfile)',
            'current-employer039s-letter-stating-date-of-employment-position-held-and-salary-per-month-for-past-6-months'
                => "Current Employer's Letter Stating Date of Employment, Position Held and Salary Per Month for Past 6 Months",
            'up-to-date-resume'                                     => 'Up To Date Resume',
            'salary-increment-letters--promotion-letter-s'          => 'Salary Increment Letter(s) / Promotion Letter (s)',
            'annex-a-of-form-4a'                                    => 'Annex A of Form 4A',
        ],
        'Economic Contributions' => [
            'summary-of-personal-insurance'                         => 'Summary of Personal Insurance',
            'summary-of-portfolio-of-personal-investments'          => 'Summary of Portfolio of Personal Investments',
            'summary-of-property-investments'                       => 'Summary of Property Investments',
        ],
        'Testimonials' => [
            'testimonial-from-current--previous-employer'           => 'Testimonial from Current / Previous Employer',
            'testimonial-from-others'                               => 'Testimonial from Others',
        ],
        // "Additional Documents" repeater handled below only if it has files
    ];

    // Prepare the ZIP archive
    $upload_dir = wp_upload_dir();
    $zip_path   = trailingslashit( $upload_dir['basedir'] ) . $zip_name;
    $zip        = new ZipArchive();
    if ( $zip->open( $zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) !== true ) {
        return false;
    }

    // Helper: add one file into Main/Sub
    $add_file = function( $att_id, $main_label, $sub_label ) use ( $zip ) {
        $file = get_attached_file( $att_id );
        if ( ! $file || ! file_exists( $file ) ) {
            return;
        }
        // Clean up labels, replace "/" with "-"
        $safe_main = sanitize_file_name( str_replace( '/', '-', $main_label ) );
        $safe_sub  = sanitize_file_name( str_replace( '/', '-', $sub_label ) );

        // Create the folders and add the file
        $zip->addEmptyDir( $safe_main );
        $zip->addEmptyDir( "{$safe_main}/{$safe_sub}" );
        $zip->addFile( $file, "{$safe_main}/{$safe_sub}/" . basename( $file ) );
    };

    // 2) Loop each defined section and its fields
    foreach ( $sections as $main_label => $fields ) {
        foreach ( $fields as $meta_key => $sub_label ) {
            $raw = get_post_meta( $post_id, $meta_key, true );
            if ( empty( $raw ) ) {
                continue;
            }

            // Normalize to array of items
            $items = is_array( $raw )
                ? $raw
                : ( strpos( $raw, ',' ) !== false
                    ? array_map( 'trim', explode( ',', $raw ) )
                    : [ $raw ] );

            foreach ( $items as $item ) {
                if ( is_array( $item ) && ! empty( $item['id'] ) ) {
                    $item = $item['id'];
                }
                if ( is_string( $item ) && filter_var( $item, FILTER_VALIDATE_URL ) ) {
                    $item = attachment_url_to_postid( $item );
                }
                $att_id = intval( $item );
                if ( $att_id ) {
                    $add_file( $att_id, $main_label, $sub_label );
                }
            }
        }
    }

    // 3) Handle the repeater field "upload-additional-documents" only if it has items
    $repeater = get_post_meta( $post_id, 'upload-additional-documents', true );
    if ( is_array( $repeater ) && ! empty( $repeater ) ) {
        foreach ( $repeater as $row ) {
            $sub_label_raw = ! empty( $row['document-about'] )
                ? $row['document-about']
                : 'Miscellaneous';

            $raw_items = $row['select-documents'];
            if ( empty( $raw_items ) ) {
                continue;
            }

            // Normalize repeater gallery values
            $items = is_array( $raw_items )
                ? $raw_items
                : ( strpos( $raw_items, ',' ) !== false
                    ? array_map( 'trim', explode( ',', $raw_items ) )
                    : [ $raw_items ] );

            foreach ( $items as $item ) {
                if ( is_array( $item ) && ! empty( $item['id'] ) ) {
                    $item = $item['id'];
                }
                if ( is_string( $item ) && filter_var( $item, FILTER_VALIDATE_URL ) ) {
                    $item = attachment_url_to_postid( $item );
                }
                $att_id = intval( $item );
                if ( $att_id ) {
                    $add_file( $att_id, 'Additional Documents', $sub_label_raw );
                }
            }
        }
    }

    $zip->close();
    return $zip_path;
}

/**
 * Stream the ZIP when ?download_all=1&post_id=โ€ฆ is requested.
 */
add_action( 'template_redirect', function() {
    if ( isset( $_GET['download_all'], $_GET['post_id'] ) ) {
        $post_id = intval( $_GET['post_id'] );
        // Optional: verify nonce or user capability here

        $zip_file = create_documents_zip( $post_id );
        if ( ! $zip_file || ! file_exists( $zip_file ) ) {
            wp_die( 'Unable to generate ZIP.' );
        }

        header( 'Content-Type: application/zip' );
        header( 'Content-Disposition: attachment; filename="' . basename( $zip_file ) . '"' );
        header( 'Content-Length: ' . filesize( $zip_file ) );
        readfile( $zip_file );
        exit;
    }
});
content_copyCOPY

This script allows you to automatically generate a downloadable ZIP file containing all uploaded documents from a custom post in WordPress. It's perfect for any structured file upload scenario where organizing user submissions into folders is needed. --- ## ๐Ÿ“ Folder Structure Example Hereโ€™s how the ZIP file structure is organized: ``` John-Doe.zip ๐Ÿ”ฝโ€” Personal-Information/ โ””๏ธ Passport-Sized-Photo/ โ””๏ธ photo.jpg โ””๏ธ Valid-Passport/ โ””๏ธ passport.pdf ๐Ÿ”ฝโ€” Education-&-Professional-Information/ โ””๏ธ Education-Certificates-Transcripts/ โ””๏ธ diploma.pdf ๐Ÿ”ฝโ€” Employment-Documents/ โ””๏ธ Resume/ โ””๏ธ cv.docx ๐Ÿ”ฝโ€” Additional-Documents/ โ””๏ธ Property-Agreement/ โ””๏ธ house.pdf ``` * Folder names are based on field labels. * `/` is replaced with `-` to sanitize folder names. * Only folders with files will be included. --- ## ๐Ÿ”น Use Cases Perfect for: * Visa or PR application systems * Job or recruitment platforms * Educational application portals * Legal or documentation forms * Client onboarding or submission systems * Any multi-file upload collection that requires organized export --- ## ๐Ÿ  How It Works 1. You define a set of media and repeater fields inside a custom post type (like `documents`). 2. Each field holds file uploads (images, PDFs, etc.). 3. The script loops through each section and sub-field. 4. It organizes files into folders and subfolders. 5. When a user clicks a download link, a `.zip` file is created and downloaded. --- ## ๐Ÿ“„ Supported Field Types * Media Upload (single/multiple) * Gallery * URL/File field * Repeater (e.g., for "Additional Documents") --- ## ๐Ÿ“Š Setup Instructions ### 1. Add the Script Paste the full PHP code into your theme's `functions.php` or use a plugin like **Code Snippets**. ### 2. Customize the Field Labels and Keys Match the `$sections` array in the script to your actual field keys and labels. ### 3. Create a Download Link Use this URL structure: ```php <a href="?download_all=1&post_id=<?php the_ID(); ?>">Download ZIP</a> ``` Or dynamically generate it: ```php echo add_query_arg([ 'download_all' => 1, 'post_id' => get_the_ID() ]); ``` --- ## โš–๏ธ Security Note If youโ€™re handling sensitive data: * Use `current_user_can()` to restrict access. * Sanitize and validate `$_GET` parameters. * Optionally add a nonce. ```php if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Unauthorized' ); ``` --- ## ๐Ÿ“ฆ Full ZIP Export Features * Organizes files into a hierarchical folder structure. * Uses post title for the ZIP file name. * Includes only non-empty folders. * Supports repeater sub-fields with document name labels. ---