| | 80 | // Handle theme download action: create a zip of the theme and send it to the browser. |
| | 81 | if ( 'download_theme' === $action ) { |
| | 82 | if ( ! current_user_can( 'edit_themes' ) ) { |
| | 83 | wp_die( '<p>' . __( 'Sorry, you are not allowed to download themes for this site.' ) . '</p>' ); |
| | 84 | } |
| | 85 | |
| | 86 | // Verify nonce (accept either _wpnonce or nonce parameter for compatibility). |
| | 87 | $nonce_name = isset( $_REQUEST['_wpnonce'] ) ? '_wpnonce' : 'nonce'; |
| | 88 | if ( ! check_admin_referer( 'download-theme_' . $stylesheet, $nonce_name, false ) ) { |
| | 89 | wp_die( '<p>' . __( 'Security check failed.' ) . '</p>' ); |
| | 90 | } |
| | 91 | |
| | 92 | $theme_dir = $theme->get_stylesheet_directory(); |
| | 93 | |
| | 94 | if ( ! is_dir( $theme_dir ) ) { |
| | 95 | wp_die( '<p>' . __( 'Theme directory not found.' ) . '</p>' ); |
| | 96 | } |
| | 97 | |
| | 98 | $zipname = sanitize_file_name( $theme->get( 'Name' ) ); |
| | 99 | if ( ! $zipname ) { |
| | 100 | $zipname = $stylesheet; |
| | 101 | } |
| | 102 | $tmpfile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $zipname . '-' . time() . '.zip'; |
| | 103 | |
| | 104 | // Try native ZipArchive first, fall back to PclZip if needed. |
| | 105 | if ( class_exists( 'ZipArchive' ) ) { |
| | 106 | $zip = new ZipArchive(); |
| | 107 | if ( true !== $zip->open( $tmpfile, ZipArchive::CREATE ) ) { |
| | 108 | wp_die( '<p>' . __( 'Could not create zip archive.' ) . '</p>' ); |
| | 109 | } |
| | 110 | |
| | 111 | $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $theme_dir ) ); |
| | 112 | foreach ( $files as $file ) { |
| | 113 | if ( $file->isDir() ) { |
| | 114 | continue; |
| | 115 | } |
| | 116 | $filePath = $file->getRealPath(); |
| | 117 | $relativePath = substr( $filePath, strlen( $theme_dir ) + 1 ); |
| | 118 | $zip->addFile( $filePath, $relativePath ); |
| | 119 | } |
| | 120 | |
| | 121 | $zip->close(); |
| | 122 | } else { |
| | 123 | // Use PclZip fallback bundled with WordPress admin. |
| | 124 | require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; |
| | 125 | |
| | 126 | $filelist = array(); |
| | 127 | $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $theme_dir ) ); |
| | 128 | foreach ( $files as $file ) { |
| | 129 | if ( $file->isDir() ) { |
| | 130 | continue; |
| | 131 | } |
| | 132 | $filelist[] = $file->getRealPath(); |
| | 133 | } |
| | 134 | |
| | 135 | $archive = new PclZip( $tmpfile ); |
| | 136 | $result = $archive->create( $filelist, PCLZIP_OPT_REMOVE_PATH, $theme_dir ); |
| | 137 | if ( 0 === $result ) { |
| | 138 | wp_die( '<p>' . __( 'Could not create zip archive.' ) . ' ' . esc_html( $archive->errorInfo( true ) ) . '</p>' ); |
| | 139 | } |
| | 140 | } |
| | 141 | |
| | 142 | if ( ! file_exists( $tmpfile ) ) { |
| | 143 | wp_die( '<p>' . __( 'Failed to create theme archive.' ) . '</p>' ); |
| | 144 | } |
| | 145 | |
| | 146 | // Send the file to the browser. |
| | 147 | header( 'Content-Type: application/zip' ); |
| | 148 | header( 'Content-Disposition: attachment; filename="' . $zipname . '.zip"' ); |
| | 149 | header( 'Content-Length: ' . filesize( $tmpfile ) ); |
| | 150 | readfile( $tmpfile ); |
| | 151 | @unlink( $tmpfile ); |
| | 152 | exit; |
| | 153 | } |
| | 154 | |