├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── asset
└── js
│ └── script.js
├── class
└── wpsdb-media-files.php
├── composer.json
├── languages
└── wp-migrate-db-pro-media-files-en.pot
├── template
└── migrate.php
├── version.php
└── wp-sync-db-media-files.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | indent_style = space
12 | indent_size = 2
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .htaccess
2 | wp-config.php
3 | wp-content/uploads/
4 | wp-content/blogs.dir/
5 | wp-content/upgrade/
6 | wp-content/backup-db/
7 | wp-content/advanced-cache.php
8 | wp-content/wp-cache-config.php
9 | sitemap.xml
10 | *.log
11 | wp-content/cache/
12 | wp-content/backups/
13 | sitemap.xml.gz
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WP Sync DB Media Files
2 | An addon for [WP Sync DB](https://github.com/wp-sync-db/wp-sync-db) that lets you sync media libraries between WordPress installations.
3 |
4 |

5 |
--------------------------------------------------------------------------------
/asset/js/script.js:
--------------------------------------------------------------------------------
1 | // functions
2 | var determine_media_to_migrate;
3 | var remote_media_files_unavailable = false;
4 | var remote_connection_data;
5 | var connection_info;
6 | var media_successfully_determined;
7 |
8 | (function($) {
9 |
10 | // .length doesn't work on JS "associative arrays" i.e. objects with key/value elements, this does
11 | Object.size = function(obj) {
12 | var size = 0,
13 | key;
14 | for (key in obj) {
15 | if (obj.hasOwnProperty(key)) size++;
16 | }
17 | return size;
18 | };
19 |
20 | $(document).ready(function() {
21 |
22 | if (migration_type() == 'savefile') {
23 | $('.media-files-options').hide();
24 | }
25 |
26 | var disable_media_files_option = function() {
27 | $('#media-files').attr('data-available', '0');
28 | $('#media-files').prop('checked', false);
29 | $('#media-files').attr('disabled', 'disabled');
30 | $('.media-files').addClass('disabled');
31 | $('.media-files-options .expandable-content').hide();
32 | };
33 |
34 | var hide_show_options = function(unavailable) {
35 | var mig_type = migration_type();
36 |
37 | if ('savefile' == mig_type) {
38 | $('.media-files-options').hide();
39 | return;
40 | }
41 |
42 | $('.media-files-options').show();
43 | $('.media-files-push').hide();
44 |
45 | if (unavailable) {
46 | $('.media-files-options ul').hide();
47 | $('.media-migration-unavailable').show();
48 | disable_media_files_option();
49 | return;
50 | }
51 |
52 | if (typeof remote_connection_data != 'undefined' &&
53 | wpsdb_media_files_version != remote_connection_data.media_files_version
54 | ) {
55 | $('.media-files-remote-location').html(remote_connection_data.url);
56 | $('.media-file-remote-version').html(remote_connection_data.media_files_version);
57 | $('.media-files-different-plugin-version-notice').show();
58 | disable_media_files_option();
59 | return;
60 | }
61 |
62 | $('.media-files-options ul').show();
63 | $('.media-migration-unavailable').hide();
64 | $('.media-files-different-plugin-version-notice').hide();
65 | $('#media-files').removeAttr('disabled');
66 | $('.media-files').removeClass('disabled');
67 | $('#media-files').attr('data-available', '1');
68 | };
69 |
70 | $.wpsdb.add_action('move_connection_info_box', function() {
71 | hide_show_options(remote_media_files_unavailable);
72 | $('.remove-scope-1').html('remote');
73 | $('.remove-scope-2').html('local');
74 | if (migration_type() == 'pull') {
75 | $('.remove-scope-1').html('local');
76 | $('.remove-scope-2').html('remote');
77 | }
78 | });
79 |
80 | $.wpsdb.add_action('verify_connection_to_remote_site', function(
81 | connection_data) {
82 | remote_connection_data = connection_data;
83 | remote_media_files_unavailable = (typeof connection_data.media_files_available ==
84 | 'undefined');
85 | hide_show_options(remote_media_files_unavailable);
86 | });
87 |
88 | $.wpsdb.add_filter('wpsdb_before_migration_complete_hooks', function(
89 | hooks) {
90 | if (false == is_media_migration() || 'savefile' == migration_type())
91 | return hooks;
92 | hooks.push('determine_media_to_migrate');
93 | return hooks;
94 | });
95 |
96 | determine_media_to_migrate = function() {
97 | connection_info = $.trim($('.pull-push-connection-info').val()).split(
98 | "\n");
99 | $('.progress-text').html(wpsdbmf_strings.determining);
100 |
101 | var remove_local_media = 0;
102 |
103 | if ($('#remove-local-media').is(':checked')) {
104 | remove_local_media = 1;
105 | }
106 |
107 | $.ajax({
108 | url: ajaxurl,
109 | type: 'POST',
110 | dataType: 'text',
111 | cache: false,
112 | data: {
113 | action: 'wpsdbmf_determine_media_to_migrate',
114 | remove_local_media: remove_local_media,
115 | intent: migration_type(),
116 | url: connection_info[0],
117 | key: connection_info[1],
118 | temp_prefix: connection_data.temp_prefix,
119 | nonce: wpsdb_nonces.determine_media_to_migrate,
120 | },
121 | error: function(jqXHR, textStatus, errorThrown) {
122 | $('.progress-title').html(wpsdbmf_strings.migration_failed);
123 | $('.progress-text').html(wpsdbmf_strings.error_determining +
124 | ' (#101mf)');
125 | $('.progress-text').addClass('migration-error');
126 | console.log(jqXHR);
127 | console.log(textStatus);
128 | console.log(errorThrown);
129 | migration_error = true;
130 | migration_complete_events();
131 | return;
132 | },
133 | success: function(data) {
134 | original_data = data;
135 | data = JSON.parse(data.trim());
136 | if (false == data) {
137 | migration_failed(original_data);
138 | return;
139 | }
140 |
141 | next_step_in_migration = {
142 | fn: media_successfully_determined,
143 | args: [data]
144 | };
145 | execute_next_step();
146 | }
147 |
148 | });
149 |
150 | }
151 |
152 | function migration_failed(data) {
153 | $('.progress-title').html(wpsdbmf_strings.migration_failed);
154 | $('.progress-text').html(data);
155 | $('.progress-text').addClass('migration-error');
156 | migration_error = true;
157 | migration_complete_events();
158 | }
159 |
160 | media_successfully_determined = function(data) {
161 | if (typeof data.wpsdb_error != 'undefined' && data.wpsdb_error == 1) {
162 | non_fatal_errors += data.body;
163 | next_step_in_migration = {
164 | fn: wpsdb_call_next_hook
165 | };
166 | execute_next_step();
167 | return;
168 | }
169 |
170 | var args = {};
171 | args.media_progress = 0;
172 | args.media_progress_image_number = 0;
173 | args.media_total_size = data.total_size;
174 | args.remote_uploads_url = data.remote_uploads_url;
175 | args.files_to_migrate = data.files_to_migrate;
176 |
177 | args.bottleneck = wpsdb_max_request;
178 |
179 | if (Object.size(args.files_to_migrate) > 0) {
180 | $('.progress-bar').width('0px');
181 | }
182 |
183 | $('.progress-tables').empty();
184 | $('.progress-tables-hover-boxes').empty();
185 |
186 | $('.progress-tables').prepend('' +
188 | wpsdbmf_strings.media_files +
189 | ' (0 / ' +
190 | wpsdb_add_commas(Object.size(args.files_to_migrate)) +
191 | ')
');
192 |
193 | next_step_in_migration = {
194 | fn: migrate_media_files_recursive,
195 | args: [args]
196 | };
197 | execute_next_step();
198 | }
199 |
200 | function migrate_media_files_recursive(args) {
201 | if (0 == Object.size(args.files_to_migrate)) {
202 | wpsdb_call_next_hook();
203 | return;
204 | }
205 |
206 | var file_chunk_to_migrate = [];
207 | var file_chunk_size = 0;
208 | var number_of_files_to_migrate = 0;
209 |
210 | $.each(args.files_to_migrate, function(index, value) {
211 | if (!file_chunk_to_migrate.length) {
212 | file_chunk_to_migrate.push(index);
213 | file_chunk_size += value;
214 | delete args.files_to_migrate[index];
215 | ++args.media_progress_image_number;
216 | ++number_of_files_to_migrate;
217 | } else {
218 | if ((file_chunk_size + value) > args.bottleneck ||
219 | number_of_files_to_migrate >= remote_connection_data.media_files_max_file_uploads
220 | ) {
221 | return false;
222 | } else {
223 | file_chunk_to_migrate.push(index);
224 | file_chunk_size += value;
225 | delete args.files_to_migrate[index];
226 | ++args.media_progress_image_number;
227 | ++number_of_files_to_migrate;
228 | }
229 | }
230 | });
231 |
232 | var connection_info = $.trim($('.pull-push-connection-info').val()).split(
233 | "\n");
234 |
235 | $.ajax({
236 | url: ajaxurl,
237 | type: 'POST',
238 | dataType: 'text',
239 | cache: false,
240 | data: {
241 | action: 'wpsdbmf_migrate_media',
242 | file_chunk: file_chunk_to_migrate,
243 | remote_uploads_url: args.remote_uploads_url,
244 | intent: migration_type(),
245 | url: connection_info[0],
246 | key: connection_info[1],
247 | nonce: wpsdb_nonces.migrate_media,
248 | },
249 | error: function(jqXHR, textStatus, errorThrown) {
250 | $('.progress-title').html('Migration failed');
251 | $('.progress-text').html(wpsdbmf_strings.problem_migrating_media +
252 | ' (#102mf)');
253 | $('.progress-text').addClass('migration-error');
254 | console.log(jqXHR);
255 | console.log(textStatus);
256 | console.log(errorThrown);
257 | migration_error = true;
258 | migration_complete_events();
259 | return;
260 | },
261 | success: function(data) {
262 | original_data = data;
263 | data = JSON.parse(data.trim());
264 | if (false == data) {
265 | migration_failed(original_data);
266 | return;
267 | }
268 |
269 | if (typeof data.wpsdb_error != 'undefined' && data.wpsdb_error ==
270 | 1) {
271 | non_fatal_errors += data.body;
272 | }
273 |
274 | args.media_progress += file_chunk_size;
275 |
276 | var percent = 100 * args.media_progress / args.media_total_size;
277 | $('.progress-bar').width(percent + '%');
278 | overall_percent = Math.floor(percent);
279 |
280 | $('.progress-text').html(overall_percent + '% - ' +
281 | wpsdbmf_strings.migrating_media_files);
282 | $('.media-migration-current-image').html(wpsdb_add_commas(args.media_progress_image_number));
283 |
284 | next_step_in_migration = {
285 | fn: migrate_media_files_recursive,
286 | args: [args]
287 | };
288 | execute_next_step();
289 | }
290 | });
291 | }
292 |
293 | function is_media_migration() {
294 | return $('#media-files').attr('data-available') == '1' && $(
295 | '#media-files').is(':checked') ? true : false;
296 | }
297 |
298 | function migration_type() {
299 | return $('input[name=action]:checked').val();
300 | }
301 | });
302 | })(jQuery);
303 |
--------------------------------------------------------------------------------
/class/wpsdb-media-files.php:
--------------------------------------------------------------------------------
1 | plugin_slug = 'wp-sync-db-media-files';
10 | $this->plugin_version = $GLOBALS['wpsdb_meta']['wp-sync-db-media-files']['version'];
11 |
12 | if( ! $this->meets_version_requirements( '1.4b1' ) ) return;
13 |
14 | add_action( 'wpsdb_after_advanced_options', array( $this, 'migration_form_controls' ) );
15 | add_action( 'wpsdb_load_assets', array( $this, 'load_assets' ) );
16 | add_action( 'wpsdb_js_variables', array( $this, 'js_variables' ) );
17 | add_filter( 'wpsdb_accepted_profile_fields', array( $this, 'accepted_profile_fields' ) );
18 | add_filter( 'wpsdb_establish_remote_connection_data', array( $this, 'establish_remote_connection_data' ) );
19 | add_filter( 'wpsdb_nonces', array( $this, 'add_nonces' ) );
20 |
21 | // compatibility with CLI migraitons
22 | add_filter( 'wpsdb_cli_finalize_migration', array( $this, 'cli_migration' ), 10, 4 );
23 |
24 | // internal AJAX handlers
25 | add_action( 'wp_ajax_wpsdbmf_determine_media_to_migrate', array( $this, 'ajax_determine_media_to_migrate' ) );
26 | add_action( 'wp_ajax_wpsdbmf_migrate_media', array( $this, 'ajax_migrate_media' ) );
27 |
28 | // external AJAX handlers
29 | add_action( 'wp_ajax_nopriv_wpsdbmf_get_remote_media_listing', array( $this, 'respond_to_get_remote_media_listing' ) );
30 | add_action( 'wp_ajax_nopriv_wpsdbmf_push_request', array( $this, 'respond_to_push_request' ) );
31 | add_action( 'wp_ajax_nopriv_wpsdbmf_remove_local_attachments', array( $this, 'respond_to_remove_local_attachments' ) );
32 | }
33 |
34 | function get_local_attachments() {
35 | global $wpdb;
36 | $prefix = $wpdb->prefix;
37 | $temp_prefix = stripslashes( $_POST['temp_prefix'] );
38 |
39 | /*
40 | * We determine which media files need migrating BEFORE the database migration is finalized.
41 | * Because of this we need to scan the *_post & *_postmeta that are prefixed using the temporary prefix.
42 | * Though this should only happen when we're responding to a get_remote_media_listing() call AND it's a push OR
43 | * we're scanning local files AND it's a pull.
44 | */
45 |
46 | if(
47 | ( true == $this->responding_to_get_remote_media_listing && $_POST['intent'] == 'push' ) ||
48 | ( false == $this->responding_to_get_remote_media_listing && $_POST['intent'] == 'pull' )
49 | ) {
50 |
51 | $local_tables = array_flip( $this->get_tables() );
52 |
53 | $posts_table_name = "{$temp_prefix}{$prefix}posts";
54 | $postmeta_table_name = "{$temp_prefix}{$prefix}postmeta";
55 |
56 | if( isset( $local_tables[$posts_table_name] ) && isset( $local_tables[$postmeta_table_name] ) ) {
57 | $prefix = $temp_prefix . $prefix;
58 | }
59 |
60 | }
61 |
62 | $local_attachments = $wpdb->get_results(
63 | "SELECT `{$prefix}posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata'
64 | FROM `{$prefix}posts`
65 | INNER JOIN `{$prefix}postmeta` pm1 ON `{$prefix}posts`.`ID` = pm1.`post_id` AND pm1.`meta_key` = '_wp_attached_file'
66 | LEFT OUTER JOIN `{$prefix}postmeta` pm2 ON `{$prefix}posts`.`ID` = pm2.`post_id` AND pm2.`meta_key` = '_wp_attachment_metadata'
67 | WHERE `{$prefix}posts`.`post_type` = 'attachment'", ARRAY_A
68 | );
69 |
70 | if( is_multisite() ) {
71 | $blogs = $this->get_blogs();
72 | $prefix = $wpdb->prefix;
73 | foreach( $blogs as $blog ) {
74 | $posts_table_name = "{$temp_prefix}{$prefix}{$blog}_posts";
75 | $postmeta_table_name = "{$temp_prefix}{$prefix}{$blog}_postmeta";
76 | if( isset( $local_tables[$posts_table_name] ) && isset( $local_tables[$postmeta_table_name] ) ) {
77 | $prefix = $temp_prefix . $prefix;
78 | }
79 | $attachments = $wpdb->get_results(
80 | "SELECT `{$prefix}{$blog}_posts`.`post_modified_gmt` AS 'date', pm1.`meta_value` AS 'file', pm2.`meta_value` AS 'metadata', {$blog} AS 'blog_id'
81 | FROM `{$prefix}{$blog}_posts`
82 | INNER JOIN `{$prefix}{$blog}_postmeta` pm1 ON `{$prefix}{$blog}_posts`.`ID` = pm1.`post_id` AND pm1.`meta_key` = '_wp_attached_file'
83 | LEFT OUTER JOIN `{$prefix}{$blog}_postmeta` pm2 ON `{$prefix}{$blog}_posts`.`ID` = pm2.`post_id` AND pm2.`meta_key` = '_wp_attachment_metadata'
84 | WHERE `{$prefix}{$blog}_posts`.`post_type` = 'attachment'", ARRAY_A
85 | );
86 |
87 | $local_attachments = array_merge( $attachments, $local_attachments );
88 | }
89 | }
90 |
91 | $local_attachments = array_map( array( $this, 'process_attachment_data' ), $local_attachments );
92 | $local_attachments = array_filter( $local_attachments );
93 |
94 | return $local_attachments;
95 | }
96 |
97 | function get_flat_attachments( $attachments ) {
98 | $flat_attachments = array();
99 | foreach( $attachments as $attachment ) {
100 | $flat_attachments[] = $attachment['file'];
101 | if( isset( $attachment['sizes'] ) ) {
102 | $flat_attachments = array_merge( $flat_attachments, $attachment['sizes'] );
103 | }
104 | }
105 | return $flat_attachments;
106 | }
107 |
108 | function process_attachment_data( $attachment ) {
109 | if ( isset( $attachment['blog_id'] ) ) { // used for multisite
110 | if ( defined( 'UPLOADBLOGSDIR' ) ) {
111 | $upload_dir = sprintf( '%s/files/', $attachment['blog_id'] );
112 | } else {
113 | $upload_dir = sprintf( 'sites/%s/', $attachment['blog_id'] );
114 | }
115 | $attachment['file'] = $upload_dir . $attachment['file'];
116 | }
117 | $upload_dir = str_replace( basename( $attachment['file'] ), '', $attachment['file'] );
118 | if ( ! empty( $attachment['metadata'] ) ) {
119 | $attachment['metadata'] = @unserialize( $attachment['metadata'] );
120 | if ( ! empty( $attachment['metadata']['sizes'] ) && is_array( $attachment['metadata']['sizes'] ) ) {
121 | foreach ( $attachment['metadata']['sizes'] as $size ) {
122 | if ( empty( $size['file'] ) ) continue;
123 | $attachment['sizes'][] = $upload_dir . $size['file'];
124 | }
125 | }
126 | }
127 | unset( $attachment['metadata'] );
128 | return $attachment;
129 | }
130 |
131 | function uploads_dir() {
132 | if( defined( 'UPLOADBLOGSDIR' ) ) {
133 | $upload_dir = trailingslashit( ABSPATH ) . UPLOADBLOGSDIR;
134 | }
135 | else {
136 | $upload_dir = wp_upload_dir();
137 | $upload_dir = $upload_dir['basedir'];
138 | }
139 | return trailingslashit( $upload_dir );
140 | }
141 |
142 | function get_local_media() {
143 | $upload_dir = untrailingslashit( $this->uploads_dir() );
144 | if( ! file_exists( $upload_dir ) ) return array();
145 |
146 | $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $upload_dir ), RecursiveIteratorIterator::SELF_FIRST );
147 | $local_media = array();
148 |
149 | foreach( $files as $name => $object ){
150 | $name = str_replace( array( $upload_dir . DS, '\\' ), array( '', '/' ), $name );
151 | $local_media[$name] = $object->getSize();
152 | }
153 |
154 | return $local_media;
155 | }
156 |
157 | function ajax_migrate_media() {
158 | $this->check_ajax_referer( 'migrate-media' );
159 | $this->set_time_limit();
160 |
161 | if ( $_POST['intent'] == 'pull' ) {
162 | $result = $this->process_pull_request();
163 | return $result;
164 | }
165 |
166 | $result = $this->process_push_request();
167 | return $result;
168 | }
169 |
170 | function process_pull_request() {
171 | $files_to_download = $_POST['file_chunk'];
172 | $remote_uploads_url = trailingslashit( $_POST['remote_uploads_url'] );
173 | $parsed = parse_url( $_POST['url'] );
174 | if( ! empty( $parsed['user'] ) ) {
175 | $credentials = sprintf( '%s:%s@', $parsed['user'], $parsed['pass'] );
176 | $remote_uploads_url = str_replace( '://', '://' . $credentials, $remote_uploads_url );
177 | }
178 |
179 | $upload_dir = $this->uploads_dir();
180 |
181 | $errors = array();
182 | foreach( $files_to_download as $file_to_download ) {
183 | $temp_file_path = $this->download_url( $remote_uploads_url . $file_to_download );
184 |
185 | if( is_wp_error( $temp_file_path ) ) {
186 | $download_error = $temp_file_path->get_error_message();
187 | $errors[] = __( sprintf( 'Could not download file: %1$s - %2$s', $remote_uploads_url . $file_to_download, $download_error ), 'wp-sync-db-media-files' );
188 | continue;
189 | }
190 |
191 | $date = str_replace( basename( $file_to_download ), '', $file_to_download );
192 | $new_path = $upload_dir . $date . basename( $file_to_download );
193 |
194 | $move_result = @rename( $temp_file_path, $new_path );
195 |
196 | if( false === $move_result ) {
197 | $folder = dirname( $new_path );
198 | if( @file_exists( $folder ) ) {
199 | $errors[] = __( sprintf( 'Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', $temp_file_path, $new_path ), 'wp-sync-db-media-files' ) . ' (#103mf)';
200 | }
201 | else{
202 | if( false === @mkdir( $folder, 0755, true ) ) {
203 | $errors[] = __( sprintf( 'Error attempting to create required directory: %s', $folder ), 'wp-sync-db-media-files' ) . ' (#104mf)';
204 | }
205 | else {
206 | $move_result = @rename( $temp_file_path, $new_path );
207 | if( false === $move_result ) {
208 | $errors[] = __( sprintf( 'Error attempting to move downloaded file. Temp path: %1$s - New Path: %2$s', $temp_file_path, $new_path ), 'wp-sync-db-media-files' ) . ' (#105mf)';
209 | }
210 | }
211 | }
212 | }
213 | }
214 |
215 | if( ! empty( $errors ) ) {
216 | $return = array(
217 | 'wpsdb_error' => 1,
218 | 'body' => implode( '
', $errors ) . '
'
219 | );
220 | $result = $this->end_ajax( json_encode( $return ) );
221 | return $result;
222 | }
223 |
224 | // not required, just here because we have to return something otherwise the AJAX fails
225 | $return['success'] = 1;
226 | $result = $this->end_ajax( json_encode( $return ) );
227 | return $result;
228 | }
229 |
230 | function process_push_request() {
231 | $files_to_migrate = $_POST['file_chunk'];
232 |
233 | $upload_dir = $this->uploads_dir();
234 |
235 | $body = '';
236 | foreach( $files_to_migrate as $file_to_migrate ) {
237 | $body .= $this->file_to_multipart( $upload_dir . $file_to_migrate );
238 | }
239 |
240 | $post_args = array(
241 | 'action' => 'wpsdbmf_push_request',
242 | 'files' => serialize( $files_to_migrate )
243 | );
244 |
245 | $post_args['sig'] = $this->create_signature( $post_args, $_POST['key'] );
246 |
247 | $body .= $this->array_to_multipart( $post_args );
248 |
249 | $args['body'] = $body;
250 | $ajax_url = trailingslashit( $_POST['url'] ) . 'wp-admin/admin-ajax.php';
251 | $response = $this->remote_post( $ajax_url, '', __FUNCTION__, $args );
252 | $response = $this->verify_remote_post_response( $response );
253 |
254 | $result = $this->end_ajax( json_encode( $response ) );
255 | return $result;
256 | }
257 |
258 | function respond_to_push_request() {
259 | $filtered_post = $this->filter_post_elements( $_POST, array( 'action', 'files' ) );
260 | $filtered_post['files'] = stripslashes( $filtered_post['files'] );
261 | if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
262 | $return = array(
263 | 'wpsdb_error' => 1,
264 | 'body' => $this->invalid_content_verification_error . ' (#101mf)',
265 | );
266 | $result = $this->end_ajax( serialize( $return ) );
267 | return $result;
268 | }
269 |
270 | if( ! isset( $_FILES['media'] ) ) {
271 | $return = array(
272 | 'wpsdb_error' => 1,
273 | 'body' => __( '$_FILES is empty, the upload appears to have failed', 'wp-sync-db-media-files' ) . ' (#106mf)',
274 | );
275 | $result = $this->end_ajax( serialize( $return ) );
276 | return $result;
277 | }
278 |
279 | $upload_dir = $this->uploads_dir();
280 |
281 | $files = $this->diverse_array( $_FILES['media'] );
282 | $file_paths = unserialize( $filtered_post['files'] );
283 | $i = 0;
284 | $errors = array();
285 | foreach( $files as &$file ) {
286 | $destination = $upload_dir . $file_paths[$i];
287 | $folder = dirname( $destination );
288 |
289 | if( false === @file_exists( $folder ) && false === @mkdir( $folder, 0755, true ) ) {
290 | $errors[] = __( sprintf( 'Error attempting to create required directory: %s', $folder ), 'wp-sync-db-media-files' ) . ' (#108mf)';
291 | ++$i;
292 | continue;
293 | }
294 |
295 | if( false === @move_uploaded_file( $file['tmp_name'], $destination ) ) {
296 | $errors[] = __( sprintf( 'A problem occurred when attempting to move the temp file "%1$s" to "%2$s"', $file['tmp_name'], $destination ), 'wp-sync-db-media-files' ) . ' (#107mf)';
297 | }
298 | ++$i;
299 | }
300 |
301 | $return = array( 'success' => 1 );
302 | if( ! empty( $errors ) ) {
303 | $return = array(
304 | 'wpsdb_error' => 1,
305 | 'body' => implode( '
', $errors ) . '
'
306 | );
307 | }
308 | $result = $this->end_ajax( serialize( $return ) );
309 | return $result;
310 | }
311 |
312 | function ajax_determine_media_to_migrate() {
313 | $this->check_ajax_referer( 'determine-media-to-migrate' );
314 | $this->set_time_limit();
315 |
316 | $local_attachments = $this->get_local_attachments();
317 | $local_media = $this->get_local_media();
318 |
319 | $data = array();
320 | $data['action'] = 'wpsdbmf_get_remote_media_listing';
321 | $data['temp_prefix'] = $this->temp_prefix;
322 | $data['intent'] = $_POST['intent'];
323 | $data['sig'] = $this->create_signature( $data, $_POST['key'] );
324 | $ajax_url = trailingslashit( $_POST['url'] ) . 'wp-admin/admin-ajax.php';
325 | $response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
326 | $response = $this->verify_remote_post_response( $response );
327 |
328 | $upload_dir = $this->uploads_dir();
329 |
330 | $remote_attachments = $response['remote_attachments'];
331 | $remote_media = $response['remote_media'];
332 |
333 | $this->files_to_migrate = array();
334 |
335 | if( $_POST['intent'] == 'pull' ) {
336 | $this->media_diff( $local_attachments, $remote_attachments, $local_media, $remote_media );
337 | }
338 | else {
339 | $this->media_diff( $remote_attachments, $local_attachments, $remote_media, $local_media );
340 | }
341 |
342 | $return['files_to_migrate'] = $this->files_to_migrate;
343 | $return['total_size'] = array_sum( $this->files_to_migrate );
344 | $return['remote_uploads_url'] = $response['remote_uploads_url'];
345 |
346 | // remove local/remote media if it doesn't exist on the local/remote site
347 | if( $_POST['remove_local_media'] == '1' ) {
348 | if( $_POST['intent'] == 'pull' ) {
349 | $this->remove_local_attachments( $remote_attachments );
350 | }
351 | else {
352 | $data = array();
353 | $data['action'] = 'wpsdbmf_remove_local_attachments';
354 | $data['remote_attachments'] = serialize( $local_attachments );
355 | $data['sig'] = $this->create_signature( $data, $_POST['key'] );
356 | $ajax_url = trailingslashit( $_POST['url'] ) . 'wp-admin/admin-ajax.php';
357 | $response = $this->remote_post( $ajax_url, $data, __FUNCTION__ );
358 | // the response is ignored here (for now) as this is not a critical task
359 | }
360 | }
361 |
362 | $result = $this->end_ajax( json_encode( $return ) );
363 | return $result;
364 | }
365 |
366 | function respond_to_remove_local_attachments() {
367 | $filtered_post = $this->filter_post_elements( $_POST, array( 'action', 'remote_attachments' ) );
368 | $filtered_post['remote_attachments'] = stripslashes( $filtered_post['remote_attachments'] );
369 | if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
370 | $return = array(
371 | 'wpsdb_error' => 1,
372 | 'body' => $this->invalid_content_verification_error . ' (#109mf)',
373 | );
374 | $result = $this->end_ajax( serialize( $return ) );
375 | return $result;
376 | }
377 |
378 | $remote_attachments = @unserialize( $filtered_post['remote_attachments'] );
379 | if( false === $remote_attachments ) {
380 | $return = array(
381 | 'wpsdb_error' => 1,
382 | 'body' => __( 'Error attempting to unserialize the remote attachment data', 'wp-sync-db-media-files' ) . ' (#110mf)',
383 | );
384 | $result = $this->end_ajax( serialize( $return ) );
385 | return $result;
386 | }
387 |
388 | $this->remove_local_attachments( $remote_attachments );
389 |
390 | $return = array(
391 | 'success' => 1,
392 | );
393 | $result = serialize( json_encode( $return ) );
394 | return $result;
395 | }
396 |
397 | function remove_local_attachments( $remote_attachments ) {
398 | $flat_remote_attachments = array_flip( $this->get_flat_attachments( $remote_attachments ) );
399 | $local_media = $this->get_local_media();
400 | // remove local media if it doesn't exist on the remote site
401 | $temp_local_media = array_keys( $local_media );
402 | $allowed_mime_types = array_flip( get_allowed_mime_types() );
403 | $upload_dir = $this->uploads_dir();
404 | foreach( $temp_local_media as $local_media_file ) {
405 | // don't remove folders
406 | if( false === is_file( $upload_dir . $local_media_file ) ) continue;
407 | $filetype = wp_check_filetype( $local_media_file );
408 | // don't remove files that we shouldn't remove, e.g. .php, .sql, etc
409 | if( false === isset( $allowed_mime_types[$filetype['type']] ) ) continue;
410 | // don't remove files that exist on the remote site
411 | if( true === isset( $flat_remote_attachments[$local_media_file] ) ) continue;
412 |
413 | @unlink( $upload_dir . $local_media_file );
414 | }
415 | }
416 |
417 | function media_diff( $site_a_attachments, $site_b_attachments, $site_a_media, $site_b_media ) {
418 | foreach( $site_b_attachments as $attachment ) {
419 | $local_attachment_key = $this->multidimensional_search( array( 'file' => $attachment['file'] ), $site_a_attachments );
420 | if( false === $local_attachment_key ) continue;
421 | $remote_timestamp = strtotime( $attachment['date'] );
422 | $local_timestamp = strtotime( $site_a_attachments[$local_attachment_key]['date'] );
423 | if( $local_timestamp >= $remote_timestamp ) {
424 | if( ! isset( $site_a_media[$attachment['file']] ) ) {
425 | $this->add_files_to_migrate( $attachment, $site_b_media );
426 | }
427 | else {
428 | $this->maybe_add_resized_images( $attachment, $site_b_media, $site_a_media );
429 | }
430 | }
431 | else {
432 | $this->add_files_to_migrate( $attachment, $site_b_media );
433 | }
434 | }
435 | }
436 |
437 | function add_files_to_migrate( $attachment, $remote_media ) {
438 | if( isset( $remote_media[$attachment['file']] ) ) {
439 | $this->files_to_migrate[$attachment['file']] = $remote_media[$attachment['file']];
440 | }
441 | if( empty( $attachment['sizes'] ) || apply_filters( 'wpsdb_exclude_resized_media', false ) ) return;
442 | foreach( $attachment['sizes'] as $size ) {
443 | if( isset( $remote_media[$size] ) ) {
444 | $this->files_to_migrate[$size] = $remote_media[$size];
445 | }
446 | }
447 | }
448 |
449 | function maybe_add_resized_images( $attachment, $site_b_media, $site_a_media ) {
450 | if( empty( $attachment['sizes'] ) || apply_filters( 'wpsdb_exclude_resized_media', false ) ) return;
451 | foreach( $attachment['sizes'] as $size ) {
452 | if( isset( $site_b_media[$size] ) && ! isset( $site_a_media[$size] ) ) {
453 | $this->files_to_migrate[$size] = $site_b_media[$size];
454 | }
455 | }
456 | }
457 |
458 | function respond_to_get_remote_media_listing() {
459 | $filtered_post = $this->filter_post_elements( $_POST, array( 'action', 'temp_prefix', 'intent' ) );
460 | if ( ! $this->verify_signature( $filtered_post, $this->settings['key'] ) ) {
461 | $return = array(
462 | 'wpsdb_error' => 1,
463 | 'body' => $this->invalid_content_verification_error . ' (#100mf)',
464 | );
465 | $result = $this->end_ajax( serialize( $return ) );
466 | return $result;
467 | }
468 |
469 | if( defined( 'UPLOADBLOGSDIR' ) ) {
470 | $upload_url = home_url( UPLOADBLOGSDIR );
471 | }
472 | else {
473 | $upload_dir = wp_upload_dir();
474 | $upload_url = $upload_dir['baseurl'];
475 | }
476 |
477 | $this->responding_to_get_remote_media_listing = true;
478 |
479 | $return['remote_attachments'] = $this->get_local_attachments();
480 | $return['remote_media'] = $this->get_local_media();
481 | $return['remote_uploads_url'] = $upload_url;
482 |
483 | $result = $this->end_ajax( serialize( $return ) );
484 | return $result;
485 | }
486 |
487 | function migration_form_controls() {
488 | $this->template( 'migrate' );
489 | }
490 |
491 | function accepted_profile_fields( $profile_fields ) {
492 | $profile_fields[] = 'media_files';
493 | $profile_fields[] = 'remove_local_media';
494 | return $profile_fields;
495 | }
496 |
497 | function load_assets() {
498 | $plugins_url = trailingslashit( plugins_url() ) . trailingslashit( $this->plugin_folder_name );
499 | $src = $plugins_url . 'asset/js/script.js';
500 | $version = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? time() : $this->plugin_version;
501 | wp_enqueue_script( 'wp-sync-db-media-files-script', $src, array( 'jquery', 'wp-sync-db-common', 'wp-sync-db-hook', 'wp-sync-db-script' ), $version, true );
502 |
503 | wp_localize_script( 'wp-sync-db-media-files-script', 'wpsdbmf_strings', array(
504 | 'determining' => __( "Determining which media files to migrate, please wait...", 'wp-sync-db-media-files' ),
505 | 'error_determining' => __( "Error while attempting to determine which media files to migrate.", 'wp-sync-db-media-files' ),
506 | 'migration_failed' => __( "Migration failed", 'wp-sync-db-media-files' ),
507 | 'problem_migrating_media' => __( "A problem occurred when migrating the media files.", 'wp-sync-db-media-files' ),
508 | 'media_files' => __( "Media Files", 'wp-sync-db-media-files' ),
509 | 'migrating_media_files' => __( "Migrating media files", 'wp-sync-db-media-files' ),
510 | ) );
511 |
512 | }
513 |
514 | function establish_remote_connection_data( $data ) {
515 | $data['media_files_available'] = '1';
516 | $data['media_files_version'] = $this->plugin_version;
517 | if( function_exists( 'ini_get' ) ) {
518 | $max_file_uploads = ini_get( 'max_file_uploads' );
519 | }
520 | $max_file_uploads = ( empty( $max_file_uploads ) ) ? 20 : $max_file_uploads;
521 | $data['media_files_max_file_uploads'] = apply_filters( 'wpsdbmf_max_file_uploads', $max_file_uploads );
522 | return $data;
523 | }
524 |
525 | function multidimensional_search( $needle, $haystack ) {
526 | if( empty( $needle ) || empty( $haystack ) ) return false;
527 |
528 | foreach( $haystack as $key => $value ) {
529 | foreach ( $needle as $skey => $svalue ) {
530 | $exists = ( isset( $haystack[$key][$skey] ) && $haystack[$key][$skey] === $svalue );
531 | }
532 | if( $exists ) return $key;
533 | }
534 |
535 | return false;
536 | }
537 |
538 | function get_blogs() {
539 | global $wpdb;
540 |
541 | $blogs = $wpdb->get_results(
542 | "SELECT blog_id
543 | FROM {$wpdb->blogs}
544 | WHERE spam = '0'
545 | AND deleted = '0'
546 | AND archived = '0'
547 | AND blog_id != 1
548 | ");
549 |
550 | $clean_blogs = array();
551 | foreach( $blogs as $blog ) {
552 | $clean_blogs[] = $blog->blog_id;
553 | }
554 |
555 | return $clean_blogs;
556 | }
557 |
558 | function download_url( $url, $timeout = 300 ) {
559 | //WARNING: The file is not automatically deleted, The script must unlink() the file.
560 | if ( ! $url )
561 | return new WP_Error('http_no_url', __('Invalid URL Provided.'));
562 |
563 | $tmpfname = wp_tempnam($url);
564 | if ( ! $tmpfname )
565 | return new WP_Error('http_no_file', __('Could not create Temporary file.'));
566 |
567 | $response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname, 'reject_unsafe_urls' => false ) );
568 |
569 | if ( is_wp_error( $response ) ) {
570 | unlink( $tmpfname );
571 | return $response;
572 | }
573 |
574 | if ( 200 != wp_remote_retrieve_response_code( $response ) ){
575 | unlink( $tmpfname );
576 | return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) );
577 | }
578 |
579 | return $tmpfname;
580 | }
581 |
582 | function js_variables() {
583 | ?>
584 | var wpsdb_media_files_version = 'plugin_version; ?>';
585 | 1, 'body' => $this->error );
591 | $result = $this->end_ajax( json_encode( $return ) );
592 | return $result;
593 | }
594 |
595 | if ( ! is_serialized( trim( $response ) ) ) {
596 | $return = array( 'wpsdb_error' => 1, 'body' => $response );
597 | $result = $this->end_ajax( json_encode( $return ) );
598 | return $result;
599 | }
600 |
601 | $response = unserialize( trim( $response ) );
602 |
603 | if ( isset( $response['wpsdb_error'] ) ) {
604 | $result = $this->end_ajax( json_encode( $response ) );
605 | return $result;
606 | }
607 | return $response;
608 | }
609 |
610 | function add_nonces( $nonces ) {
611 | $nonces['migrate_media'] = wp_create_nonce( 'migrate-media' );
612 | $nonces['determine_media_to_migrate'] = wp_create_nonce( 'determine-media-to-migrate' );
613 | return $nonces;
614 | }
615 |
616 | function cli_migration( $outcome, $profile, $verify_connection_response, $initiate_migration_response ) {
617 | global $wpsdb, $wpsdb_cli;
618 | if ( true !== $outcome ) return $outcome;
619 | if ( !isset( $profile['media_files'] ) || '1' !== $profile['media_files'] ) return $outcome;
620 |
621 | if ( !isset( $verify_connection_response['media_files_max_file_uploads'] ) ) {
622 | return $wpsdb_cli->cli_error( __( 'WP Sync DB Media Files does not seems to be installed/active on the remote website.', 'wp-sync-db-media-files' ) );
623 | }
624 |
625 | $this->set_time_limit();
626 | $wpsdb->set_cli_migration();
627 | $this->set_cli_migration();
628 |
629 | $connection_info = explode( "\n", $profile['connection_info'] );
630 |
631 | $_POST['intent'] = $profile['action'];
632 | $_POST['url'] = trim( $connection_info[0] );
633 | $_POST['key'] = trim( $connection_info[1] );
634 | $_POST['remove_local_media'] = ( isset( $profile['remove_local_media'] ) ) ? 1 : 0;
635 | $_POST['temp_prefix'] = $verify_connection_response['temp_prefix'];
636 |
637 | do_action( 'wpsdb_cli_before_determine_media_to_migrate', $profile, $verify_connection_response, $initiate_migration_response );
638 |
639 | $response = $this->ajax_determine_media_to_migrate();
640 | if( is_wp_error( $determine_media_to_migrate_response = $wpsdb_cli->verify_cli_response( $response, 'ajax_determine_media_to_migrate()' ) ) ) return $determine_media_to_migrate_response;
641 |
642 | $remote_uploads_url = $determine_media_to_migrate_response['remote_uploads_url'];
643 | $files_to_migrate = $determine_media_to_migrate_response['files_to_migrate'];
644 | // seems like this value needs to be different depending on pull/push?
645 | $bottleneck = $wpsdb->get_bottleneck();
646 |
647 | while ( !empty( $files_to_migrate ) ) {
648 | $file_chunk_to_migrate = array();
649 | $file_chunk_size = 0;
650 | $number_of_files_to_migrate = 0;
651 | foreach ( $files_to_migrate as $file_to_migrate => $file_size ) {
652 | if ( empty( $file_chunk_to_migrate ) ) {
653 | $file_chunk_to_migrate[] = $file_to_migrate;
654 | $file_chunk_size += $file_size;
655 | unset( $files_to_migrate[$file_to_migrate] );
656 | ++$number_of_files_to_migrate;
657 | } else {
658 | if ( ( $file_chunk_size + $file_size ) > $bottleneck || $number_of_files_to_migrate >= $verify_connection_response['media_files_max_file_uploads'] ) {
659 | break;
660 | } else {
661 | $file_chunk_to_migrate[] = $file_to_migrate;
662 | $file_chunk_size += $file_size;
663 | unset( $files_to_migrate[$file_to_migrate] );
664 | ++$number_of_files_to_migrate;
665 | }
666 | }
667 |
668 | $_POST['file_chunk'] = $file_chunk_to_migrate;
669 | $_POST['remote_uploads_url'] = $remote_uploads_url;
670 |
671 | $response = $this->ajax_migrate_media();
672 | if( is_wp_error( $migrate_media_response = $wpsdb_cli->verify_cli_response( $response, 'ajax_migrate_media()' ) ) ) return $migrate_media_response;
673 | }
674 | }
675 | return true;
676 | }
677 | }
678 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wp-sync-db/wp-sync-db-media-files",
3 | "type": "wordpress-plugin",
4 | "homepage": "https://github.com/wp-sync-db/wp-sync-db-media-files",
5 | "license": "GPL-2.0",
6 | "description": "WP Sync DB Media File Addon for WP Sync DB",
7 | "keywords": ["plugin","wordpress","wp-sync-db","media"],
8 | "require": {
9 | "composer/installers": "~1.0.6"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/languages/wp-migrate-db-pro-media-files-en.pot:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "Project-Id-Version: WP Sync DB Media Files\n"
4 | "POT-Creation-Date: 2014-05-28 13:46+1000\n"
5 | "PO-Revision-Date: 2014-05-28 13:46+1000\n"
6 | "Last-Translator: Delicious Brains \n"
7 | "Language-Team: Delicious Brains \n"
8 | "Language: en\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=UTF-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "X-Generator: Poedit 1.6.5\n"
13 | "X-Poedit-Basepath: ..\n"
14 | "X-Poedit-SourceCharset: UTF-8\n"
15 | "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
16 | "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
17 | "_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19 | "X-Poedit-SearchPath-0: .\n"
20 |
21 | #: class/wpsdb-media-files.php:273
22 | msgid "$_FILES is empty, the upload appears to have failed"
23 | msgstr ""
24 |
25 | #: class/wpsdb-media-files.php:382
26 | msgid "Error attempting to unserialize the remote attachment data"
27 | msgstr ""
28 |
29 | #: class/wpsdb-media-files.php:504
30 | msgid "Determining which media files to migrate, please wait..."
31 | msgstr ""
32 |
33 | #: class/wpsdb-media-files.php:505
34 | msgid "Error while attempting to determine which media files to migrate."
35 | msgstr ""
36 |
37 | #: class/wpsdb-media-files.php:506
38 | msgid "Migration failed"
39 | msgstr ""
40 |
41 | #: class/wpsdb-media-files.php:507
42 | msgid "A problem occurred when migrating the media files."
43 | msgstr ""
44 |
45 | #: class/wpsdb-media-files.php:508 template/migrate.php:6
46 | msgid "Media Files"
47 | msgstr ""
48 |
49 | #: class/wpsdb-media-files.php:509
50 | msgid "Migrating media files"
51 | msgstr ""
52 |
53 | #: class/wpsdb-media-files.php:561
54 | msgid "Invalid URL Provided."
55 | msgstr ""
56 |
57 | #: class/wpsdb-media-files.php:565
58 | msgid "Could not create Temporary file."
59 | msgstr ""
60 |
61 | #: class/wpsdb-media-files.php:623
62 | msgid ""
63 | "WP Sync DB Media Files does not seems to be installed/active on the "
64 | "remote website."
65 | msgstr ""
66 |
67 | #: template/migrate.php:15
68 | msgid ""
69 | "Remove local media files that are not "
70 | "found on the remote site"
71 | msgstr ""
72 |
73 | #: template/migrate.php:23
74 | msgid "Addon Missing"
75 | msgstr ""
76 |
77 | #: template/migrate.php:23
78 | msgid ""
79 | "The Media Files addon is inactive on the remote site. "
80 | "Please install and activate it to enable media file migration."
81 | msgstr ""
82 |
83 | #: template/migrate.php:27
84 | msgid "Version Mismatch"
85 | msgstr ""
86 |
--------------------------------------------------------------------------------
/template/migrate.php:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/version.php:
--------------------------------------------------------------------------------
1 |