Export Media Library - Version 1.1.0

Version Description

  • expose API::export function for easier reuse by 3rd-party code
  • set last modify time for each file in zip to match the timestamp on disk
Download this release

Release Info

Developer andrej.pavlovic
Plugin Icon wp plugin Export Media Library
Version 1.1.0
Comparing to
See all releases

Code changes from version 1.0.1 to 1.1.0

composer.json CHANGED
@@ -1,10 +1,11 @@
1
  {
2
  "name": "massedge/wordpress-plugin-export-media-library",
3
  "description": "Wordpress plugin that allows admins to export media library files as a compressed zip archive.",
4
- "version": "1.0.1",
5
  "type": "wordpress-plugin",
6
  "license": "GPL-3.0",
7
  "require": {
 
8
  "maennchen/zipstream-php": "0.4.1"
9
  },
10
  "autoload": {
1
  {
2
  "name": "massedge/wordpress-plugin-export-media-library",
3
  "description": "Wordpress plugin that allows admins to export media library files as a compressed zip archive.",
4
+ "version": "1.1.0",
5
  "type": "wordpress-plugin",
6
  "license": "GPL-3.0",
7
  "require": {
8
+ "php": ">=5.6",
9
  "maennchen/zipstream-php": "0.4.1"
10
  },
11
  "autoload": {
composer.lock CHANGED
@@ -1,10 +1,10 @@
1
  {
2
  "_readme": [
3
  "This file locks the dependencies of your project to a known state",
4
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
  "This file is @generated automatically"
6
  ],
7
- "content-hash": "7ca0a374585c1909c1c51ac1de7e41fa",
8
  "packages": [
9
  {
10
  "name": "maennchen/zipstream-php",
@@ -65,6 +65,8 @@
65
  "stability-flags": [],
66
  "prefer-stable": false,
67
  "prefer-lowest": false,
68
- "platform": [],
 
 
69
  "platform-dev": []
70
  }
1
  {
2
  "_readme": [
3
  "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
  "This file is @generated automatically"
6
  ],
7
+ "content-hash": "a853daad251f8773c406fcc9f1e7a7a0",
8
  "packages": [
9
  {
10
  "name": "maennchen/zipstream-php",
65
  "stability-flags": [],
66
  "prefer-stable": false,
67
  "prefer-lowest": false,
68
+ "platform": {
69
+ "php": ">=5.6"
70
+ },
71
  "platform-dev": []
72
  }
index.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Export Media Library
4
  Plugin URI: https://github.com/massedge/wordpress-plugin-export-media-library
5
  Description: Allows admins to export media library files as a compressed zip archive.
6
- Version: 1.0.1
7
  Author: Mass Edge Inc.
8
  Author URI: https://www.massedge.com/
9
  License: GPL3
3
  Plugin Name: Export Media Library
4
  Plugin URI: https://github.com/massedge/wordpress-plugin-export-media-library
5
  Description: Allows admins to export media library files as a compressed zip archive.
6
+ Version: 1.1.0
7
  Author: Mass Edge Inc.
8
  Author URI: https://www.massedge.com/
9
  License: GPL3
lib/MassEdge/WordPress/Plugin/ExportMediaLibrary/API.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MassEdge\WordPress\Plugin\ExportMediaLibrary;
4
+
5
+ use ZipStream\ZipStream;
6
+
7
+ class API {
8
+ const FOLDER_STRUCTURE_NESTED = 'nested';
9
+ const FOLDER_STRUCTURE_FLAT = 'flat';
10
+
11
+ static function defaultExportOptions() {
12
+ return [
13
+ 'filename' => 'export.zip',
14
+ 'root_path' => null, // defaults to `filename` without extension
15
+ 'folder_structure' => self::FOLDER_STRUCTURE_NESTED,
16
+ 'compress' => false,
17
+ 'upload_basedir' => self::getUploadBasedir(),
18
+ 'query_args' => [
19
+ 'post_type' => 'attachment',
20
+ 'post_status' => 'inherit',
21
+ 'fields' => 'ids',
22
+ 'posts_per_page' => -1,
23
+ ],
24
+ 'add_attachment_callback' => function($value, $params) { return $value; },
25
+ 'add_attachment_failed_callback' => function($params) {},
26
+ 'add_extra_files_callback' => function($params) {},
27
+ ];
28
+ }
29
+
30
+ /**
31
+ * Stream zip file comprised of all attachments directly to output stream.
32
+ * @param array $options
33
+ * @return void
34
+ */
35
+ static function export(array $options = array()) {
36
+ $options = array_merge(self::defaultExportOptions(), $options);
37
+
38
+ if ($options['root_path'] === null) {
39
+ // default to `filename` without extension
40
+ $options['root_path'] = pathinfo($options['filename'], PATHINFO_FILENAME);
41
+ }
42
+ // ensure path doesn't end in slash
43
+ if ($options['root_path']) $options['root_path'] = rtrim($options['root_path'], '/\\');
44
+
45
+ // create a new zipstream object
46
+ $zip = new ZipStream($options['filename'], [
47
+ // WORKAROUND: treat each file as large in order to use STORE method, thereby avoiding compression
48
+ ZipStream::OPTION_LARGE_FILE_SIZE => ($options['compress']) ? 20 * 1024 * 1024 : 1,
49
+ ]);
50
+
51
+ $query = new \WP_Query();
52
+ $attachmentIds = $query->query($options['query_args']);
53
+
54
+ $flatFilenames = [];
55
+
56
+ foreach($attachmentIds as $attachmentId) {
57
+ $attachmentPath = get_attached_file($attachmentId);
58
+
59
+ if ($attachmentPath) {
60
+ switch($options['folder_structure']) {
61
+ case self::FOLDER_STRUCTURE_NESTED:
62
+ // check if attachment in upload folder
63
+ if (substr($attachmentPath, 0, strlen($options['upload_basedir'])) === $options['upload_basedir']) {
64
+ $file = substr($attachmentPath, strlen($options['upload_basedir']) + 1);
65
+ } else {
66
+ if (0 == strpos( $attachmentPath, '/' )) {
67
+ $file = substr($attachmentPath, 1);
68
+ } else if (preg_match( '|^.:\\\|', $attachmentPath )) {
69
+ $file = substr($attachmentPath, 3);
70
+ } else {
71
+ $file = $attachmentPath;
72
+ }
73
+ }
74
+ break;
75
+
76
+ case self::FOLDER_STRUCTURE_FLAT:
77
+ default:
78
+ $file = basename($attachmentPath, PATHINFO_BASENAME);
79
+ $filename = pathinfo($file, PATHINFO_FILENAME);
80
+ $ext = pathinfo($file, PATHINFO_EXTENSION);
81
+
82
+ // append a number to file name, of another with same name is already present
83
+ for($i = 0; in_array($file, $flatFilenames); $i++) {
84
+ $file = $filename . $i . (($ext !== null) ? '.' . $ext : '');
85
+ }
86
+
87
+ // keep track of file name, so another file doesn't over write it
88
+ $flatFilenames[] = $file;
89
+ }
90
+
91
+ $file = ($options['root_path'])
92
+ ? "{$options['root_path']}/{$file}"
93
+ : "{$file}";
94
+
95
+ $time = @filectime($attachmentPath);
96
+ } else {
97
+ $file = null;
98
+ $time = false;
99
+ }
100
+
101
+ // opportunity to manipulate adding of attachment to zip
102
+ $result = $options['add_attachment_callback']([
103
+ 'name' => $file,
104
+ 'path' => $attachmentPath,
105
+ 'options' => [
106
+ 'time' => $time,
107
+ ],
108
+ ], [
109
+ 'attachment_id' => $attachmentId,
110
+ ]);
111
+
112
+ // skip attachment if result not specified
113
+ if (!$result || empty($result['name']) || empty($result['path'])) continue;
114
+
115
+ try {
116
+ $zip->addFileFromPath($result['name'], $result['path'], $result['options']);
117
+ } catch (\Exception $ex) {
118
+ $options['add_attachment_failed_callback']([
119
+ 'name' => $result['name'],
120
+ 'path' => $result['path'],
121
+ 'options' => $result['options'],
122
+ 'exception' => $ex,
123
+ ]);
124
+
125
+ // skip files that fail to be added to zip
126
+ continue;
127
+ }
128
+ }
129
+
130
+ // give opportunity to add extra files before finishing the stream
131
+ $options['add_extra_files_callback']([
132
+ 'add_file_callback' => function($name, $path, array $options = []) use ($zip) {
133
+ return $zip->addFileFromPath($name, $path, $options);
134
+ },
135
+ ]);
136
+
137
+ // finish the zip stream
138
+ $zip->finish();
139
+ }
140
+
141
+ private static function getUploadBasedir() {
142
+ $uploads = wp_get_upload_dir();
143
+ if ($uploads['error'] !== false) {
144
+ throw new \Exception($uploads['error']);
145
+ }
146
+ return $uploads['basedir'];
147
+ }
148
+ }
lib/MassEdge/WordPress/Plugin/ExportMediaLibrary/Module/AdminPageExport.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  namespace MassEdge\WordPress\Plugin\ExportMediaLibrary\Module;
4
 
5
- use ZipStream\ZipStream;
6
 
7
  class AdminPageExport extends Base {
8
  const FIELD_SUBMIT_DOWNLOAD = 'massedge-wp-plugin-eml-ape-submit-download';
@@ -10,9 +10,6 @@ class AdminPageExport extends Base {
10
  const FIELD_FOLDER_STRUCTURE = 'folder_structure';
11
  const FIELD_COMPRESS = 'compress';
12
 
13
- const FOLDER_STRUCTURE_NESTED = 'nested';
14
- const FOLDER_STRUCTURE_FLAT = 'flat';
15
-
16
  const REQUIRED_CAPABILITY = 'upload_files';
17
 
18
  function registerHooks() {
@@ -38,22 +35,34 @@ class AdminPageExport extends Base {
38
  if (!check_admin_referer(self::FIELD_NONCE_DOWNLOAD_ACTION)) return;
39
 
40
  // create name for download
41
- $filename = sprintf('%s_media_library_export', current_time('Y-m-d_H-i-s'));
42
 
43
  // set folder structure
44
  $folderStructure = (
45
  empty($_POST[self::FIELD_FOLDER_STRUCTURE]) ||
46
- !in_array($_POST[self::FIELD_FOLDER_STRUCTURE], [self::FOLDER_STRUCTURE_NESTED, self::FOLDER_STRUCTURE_FLAT])
47
  )
48
- ? self::FOLDER_STRUCTURE_NESTED
49
  : $_POST['folder_structure'];
50
 
51
  // set compress option
52
  $compress = !empty($_POST[self::FIELD_COMPRESS]);
53
 
54
  try {
55
- // export
56
- $this->export($filename, $folderStructure, $compress);
 
 
 
 
 
 
 
 
 
 
 
 
57
  } catch (\Exception $ex) {
58
  add_action('admin_notices', function() use ($ex) {
59
  echo sprintf('<div class="error"><p>%s</p></div>', esc_html($ex->getMessage()));
@@ -79,8 +88,8 @@ class AdminPageExport extends Base {
79
  <th>Folder Structure</th>
80
  <td>
81
  <select name="<?php echo esc_attr(self::FIELD_FOLDER_STRUCTURE) ?>">
82
- <option value="<?php echo esc_attr(self::FOLDER_STRUCTURE_FLAT) ?>">Single folder with all files</option>
83
- <option value="<?php echo esc_attr(self::FOLDER_STRUCTURE_NESTED) ?>">Nested folders</option>
84
  </select>
85
  </td>
86
  </tr>
@@ -104,80 +113,4 @@ class AdminPageExport extends Base {
104
  <?php
105
  echo ob_get_clean();
106
  }
107
-
108
- function export($exportName, $folderStructure, $compress) {
109
- $exportFilename = "{$exportName}.zip";
110
- $basedir = self::getUploadBasedir();
111
-
112
- # create a new zipstream object
113
- $zip = new ZipStream($exportFilename, [
114
- // WORKAROUND: treat each file as large in order to use STORE method, thereby avoiding compression
115
- ZipStream::OPTION_LARGE_FILE_SIZE => ($compress) ? 20 * 1024 * 1024 : 1,
116
- ]);
117
-
118
- $query = new \WP_Query();
119
- $attachmentIds = $query->query([
120
- 'post_type' => 'attachment',
121
- 'post_status' => 'inherit',
122
- 'fields' => 'ids',
123
- 'posts_per_page' => -1,
124
- ]);
125
-
126
- $flatFilenames = [];
127
-
128
- foreach($attachmentIds as $attachmentId) {
129
- $attachmentPath = get_attached_file($attachmentId);
130
-
131
- if (!$attachmentPath) continue;
132
-
133
- switch($folderStructure) {
134
- case self::FOLDER_STRUCTURE_NESTED:
135
- // check if attachment in upload folder
136
- if (substr($attachmentPath, 0, strlen($basedir)) === $basedir) {
137
- $file = substr($attachmentPath, strlen($basedir) + 1);
138
- } else {
139
- if (0 == strpos( $attachmentPath, '/' )) {
140
- $file = substr($attachmentPath, 1);
141
- } else if (preg_match( '|^.:\\\|', $attachmentPath )) {
142
- $file = substr($attachmentPath, 3);
143
- } else {
144
- $file = $attachmentPath;
145
- }
146
- }
147
- break;
148
-
149
- case self::FOLDER_STRUCTURE_FLAT:
150
- default:
151
- $file = basename($attachmentPath, PATHINFO_BASENAME);
152
- $filename = pathinfo($file, PATHINFO_FILENAME);
153
- $ext = pathinfo($file, PATHINFO_EXTENSION);
154
-
155
- // append a number to file name, of another with same name is already present
156
- for($i = 0; in_array($file, $flatFilenames); $i++) {
157
- $file = $filename . $i . (($ext !== null) ? '.' . $ext : '');
158
- }
159
-
160
- // keep track of file name, so another file doesn't over write it
161
- $flatFilenames[] = $file;
162
- }
163
-
164
- try {
165
- $zip->addFileFromPath("{$exportName}/{$file}", $attachmentPath);
166
- } catch (\Exception $ex) {
167
- // skip files that fail to be added to zip
168
- continue;
169
- }
170
- }
171
-
172
- # finish the zip stream
173
- $zip->finish();
174
- }
175
-
176
- private static function getUploadBasedir() {
177
- $uploads = wp_get_upload_dir();
178
- if ($uploads['error'] !== false) {
179
- throw new \Exception($uploads['error']);
180
- }
181
- return $uploads['basedir'];
182
- }
183
  }
2
 
3
  namespace MassEdge\WordPress\Plugin\ExportMediaLibrary\Module;
4
 
5
+ use MassEdge\WordPress\Plugin\ExportMediaLibrary\API;
6
 
7
  class AdminPageExport extends Base {
8
  const FIELD_SUBMIT_DOWNLOAD = 'massedge-wp-plugin-eml-ape-submit-download';
10
  const FIELD_FOLDER_STRUCTURE = 'folder_structure';
11
  const FIELD_COMPRESS = 'compress';
12
 
 
 
 
13
  const REQUIRED_CAPABILITY = 'upload_files';
14
 
15
  function registerHooks() {
35
  if (!check_admin_referer(self::FIELD_NONCE_DOWNLOAD_ACTION)) return;
36
 
37
  // create name for download
38
+ $filename = sprintf('%s_media_library_export.zip', current_time('Y-m-d_H-i-s'));
39
 
40
  // set folder structure
41
  $folderStructure = (
42
  empty($_POST[self::FIELD_FOLDER_STRUCTURE]) ||
43
+ !in_array($_POST[self::FIELD_FOLDER_STRUCTURE], [API::FOLDER_STRUCTURE_NESTED, API::FOLDER_STRUCTURE_FLAT])
44
  )
45
+ ? API::FOLDER_STRUCTURE_NESTED
46
  : $_POST['folder_structure'];
47
 
48
  // set compress option
49
  $compress = !empty($_POST[self::FIELD_COMPRESS]);
50
 
51
  try {
52
+ API::export([
53
+ 'filename' => $filename,
54
+ 'folder_structure' => $folderStructure,
55
+ 'compress' => $compress,
56
+ 'add_attachment_callback' => function($value, $params) {
57
+ return apply_filters('massedge-wp-eml/export/add_attachment', $value, $params);
58
+ },
59
+ 'add_attachment_failed_callback' => function($params) {
60
+ do_action('massedge-wp-eml/export/add_attachment_failed', $params);
61
+ },
62
+ 'add_extra_files_callback' => function($params) {
63
+ do_action('massedge-wp-eml/export/add_extra_files', $params);
64
+ }
65
+ ]);
66
  } catch (\Exception $ex) {
67
  add_action('admin_notices', function() use ($ex) {
68
  echo sprintf('<div class="error"><p>%s</p></div>', esc_html($ex->getMessage()));
88
  <th>Folder Structure</th>
89
  <td>
90
  <select name="<?php echo esc_attr(self::FIELD_FOLDER_STRUCTURE) ?>">
91
+ <option value="<?php echo esc_attr(API::FOLDER_STRUCTURE_FLAT) ?>">Single folder with all files</option>
92
+ <option value="<?php echo esc_attr(API::FOLDER_STRUCTURE_NESTED) ?>">Nested folders</option>
93
  </select>
94
  </td>
95
  </tr>
113
  <?php
114
  echo ob_get_clean();
115
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: export media library, download media library, media library, export, downl
4
  Requires at least: 4.7.10
5
  Tested up to: 4.9.6
6
  Requires PHP: 5.6
7
- Stable tag: 1.0.1
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -26,6 +26,10 @@ Allows users to export media library files as a compressed zip archive.
26
 
27
  == Changelog ==
28
 
 
 
 
 
29
  = 1.0.1 =
30
  Fixed title of plugin in readme.
31
 
4
  Requires at least: 4.7.10
5
  Tested up to: 4.9.6
6
  Requires PHP: 5.6
7
+ Stable tag: 1.1.0
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
26
 
27
  == Changelog ==
28
 
29
+ = 1.1.0 =
30
+ * expose API::export function for easier reuse by 3rd-party code
31
+ * set last modify time for each file in zip to match the timestamp on disk
32
+
33
  = 1.0.1 =
34
  Fixed title of plugin in readme.
35