├── LICENSE
├── asset
└── dist
│ ├── css
│ └── styles.css
│ └── js
│ ├── script-113.js
│ └── script-113.min.js
├── class
├── cli
│ └── wpmdbpro-multisite-tools-cli.php
└── wpmdbpro-multisite-tools.php
├── languages
└── wp-migrate-db-pro-multisite-tools-en.pot
├── template
└── migrate.php
├── version.php
└── wp-migrate-db-pro-multisite-tools.php
/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 |
--------------------------------------------------------------------------------
/asset/dist/css/styles.css:
--------------------------------------------------------------------------------
1 | .new-prefix-field{margin-top:10px}.new-prefix-field input{margin-left:10px}
2 |
--------------------------------------------------------------------------------
/asset/dist/js/script-113.js:
--------------------------------------------------------------------------------
1 | var wpmdb = wpmdb || {};
2 | wpmdb.mst = {
3 | remote_mst_unavailable: false
4 | };
5 |
6 | (function( $, wpmdb ) {
7 | var $mst_options = $( '.mst-options' );
8 | var $mst_select_subsite = $( '#mst-select-subsite' );
9 | var $mst_options_content = $( '.mst-options .expandable-content' );
10 | var $mst_selected_subsite = $( '#mst-selected-subsite' );
11 | var $mst_new_prefix_field = $( '.new-prefix-field' );
12 | var $mst_new_prefix = $( '#new-prefix' );
13 | var $mst_new_prefix_hidden = $( '#new-prefix-hidden' );
14 | var $mst_new_prefix_readonly = $( '#new-prefix-readonly' );
15 | var $mst_unavailable = $( '.mst-unavailable' );
16 | var $mst_different_plugin_version_notice = $( '.mst-different-plugin-version-notice' );
17 | var $mst_different_prefix_notice = $( '.mst-different-prefix-notice' );
18 |
19 | var finished_loading = false;
20 | var original_local_url = null;
21 | var reverse_replace = false;
22 | var table_prefix = $( '.table-select-wrap .table-prefix' ).text();
23 |
24 | function doing_mst_select_subsite() {
25 | return '1' === $mst_select_subsite.attr( 'data-available' ) && $mst_select_subsite.is( ':checked' ) ? true : false;
26 | }
27 |
28 | function disable_mst_options() {
29 | $.wpmdb.do_action( 'wpmdb_lock_replace_url', false );
30 | $.wpmdb.do_action( 'wpmdb_enable_table_migration_options' );
31 | $mst_select_subsite.attr( 'data-available', '0' );
32 | $mst_select_subsite.prop( 'checked', false );
33 | $mst_select_subsite.attr( 'disabled', 'disabled' );
34 | $( '.mst' ).addClass( 'disabled' );
35 | $mst_options_content.hide();
36 | }
37 |
38 | function enable_mst_options() {
39 | $mst_select_subsite.attr( 'data-available', '1' );
40 | $mst_select_subsite.removeAttr( 'disabled' );
41 | $( '.mst' ).removeClass( 'disabled' );
42 | }
43 |
44 | function hide_show_options( unavailable ) {
45 |
46 | // MST options can only be used from a multisite install at present.
47 | // TODO: Remove this test if MST is to be usable from a single site install.
48 | if ( 'false' === wpmdb_data.site_details.is_multisite ) {
49 | disable_mst_options();
50 | $mst_options.hide();
51 | return;
52 | }
53 |
54 | // For Pull/Push remote should be a single site install.
55 | // TODO: Remove this test if MST is to manage subsite to subsite.
56 | if ( 'savefile' !== wpmdb_migration_type() &&
57 | 'undefined' !== typeof wpmdb.mst.remote_connection_data &&
58 | 'true' === wpmdb.mst.remote_connection_data.site_details.is_multisite ) {
59 | disable_mst_options();
60 | $mst_options.hide();
61 | return;
62 | }
63 |
64 | // For Pull/Push remote should have same base prefix.
65 | // TODO: Allow migrating to different base prefix.
66 | if ( 'savefile' !== wpmdb_migration_type() &&
67 | false === unavailable &&
68 | 'undefined' !== typeof wpmdb.mst.remote_connection_data &&
69 | 'undefined' !== typeof wpmdb.mst.remote_connection_data.site_details &&
70 | wpmdb_data.site_details.prefix !== wpmdb.mst.remote_connection_data.site_details.prefix ) {
71 | disable_mst_options();
72 | maybe_update_local_url_for_subsite( false );
73 | $( '.mst-remote-location' ).html( wpmdb.mst.remote_connection_data.url );
74 | $( '.mst-remote-prefix' ).html( wpmdb.mst.remote_connection_data.site_details.prefix );
75 | $mst_different_prefix_notice.show();
76 | return;
77 | }
78 | $mst_different_prefix_notice.hide();
79 |
80 | // For Pull/Push remote also needs MST.
81 | if ( unavailable && 'true' === wpmdb_data.site_details.is_multisite && 'savefile' !== wpmdb_migration_type() ) {
82 | disable_mst_options();
83 | maybe_update_local_url_for_subsite( false );
84 | $mst_unavailable.show();
85 | return;
86 | }
87 | $mst_unavailable.hide();
88 |
89 | if ( 'savefile' !== wpmdb_migration_type() &&
90 | 'undefined' !== typeof wpmdb.mst.remote_connection_data &&
91 | wpmdb_data.mst_version !== wpmdb.mst.remote_connection_data.mst_version ) {
92 | disable_mst_options();
93 | maybe_update_local_url_for_subsite( false );
94 | $( '.mst-remote-location' ).html( wpmdb.mst.remote_connection_data.url );
95 | $( '.mst-remote-version' ).html( wpmdb.mst.remote_connection_data.mst_version );
96 | $mst_different_plugin_version_notice.show();
97 | return;
98 | }
99 | $mst_different_plugin_version_notice.hide();
100 |
101 | enable_mst_options();
102 | if ( doing_mst_select_subsite() ) {
103 | $mst_options_content.show();
104 | selected_subsite_changed();
105 | } else {
106 | $mst_options_content.hide();
107 | }
108 |
109 | maybe_lock_replace_url();
110 |
111 | $mst_options.show();
112 | }
113 |
114 | function maybe_lock_replace_url() {
115 | if ( doing_mst_select_subsite() && 'savefile' !== wpmdb_migration_type() ) {
116 | $.wpmdb.do_action( 'wpmdb_lock_replace_url', true );
117 | } else {
118 | $.wpmdb.do_action( 'wpmdb_lock_replace_url', false );
119 | }
120 | }
121 |
122 | function hide_show_new_prefix_field() {
123 | if ( '' === $mst_selected_subsite.val() ) {
124 | $mst_new_prefix_field.hide();
125 | return;
126 | }
127 |
128 | // If not an Export, we know what the new table prefix should be.
129 | if ( 'savefile' !== wpmdb_migration_type() ) {
130 | $mst_new_prefix.hide();
131 | $mst_new_prefix_readonly.show();
132 | $mst_new_prefix_field.children( 'label' ).addClass( 'disabled' );
133 |
134 | var new_prefix = table_prefix;
135 | var selected_subsite = get_selected_subsite();
136 |
137 | if ( 'pull' === wpmdb_migration_type() ) {
138 | if ( undefined !== selected_subsite.blog_id && 1 < selected_subsite.blog_id ) {
139 | new_prefix = new_prefix + selected_subsite.blog_id + '_';
140 | }
141 | } else {
142 |
143 | // TODO: Determine new table prefix if target has different base or is not single site install.
144 | }
145 | $mst_new_prefix.val( new_prefix );
146 | $mst_new_prefix_hidden.val( new_prefix );
147 | $mst_new_prefix_readonly.text( new_prefix );
148 | } else {
149 | $mst_new_prefix.show();
150 | $mst_new_prefix_readonly.hide();
151 | $mst_new_prefix_field.children( 'label' ).removeClass( 'disabled' );
152 | }
153 | $mst_new_prefix_field.show();
154 | }
155 |
156 | function select_subsite_changed() {
157 | var args = doing_mst_select_subsite();
158 |
159 | maybe_lock_replace_url();
160 |
161 | $.wpmdb.do_action( 'wpmdbmst_select_subsite_changed', args );
162 | }
163 |
164 | function get_selected_subsite() {
165 | var details = {};
166 | if ( doing_mst_select_subsite() ) {
167 | var blog_id = $mst_selected_subsite.find( 'option:selected' ).val();
168 |
169 | if ( '' === blog_id ) {
170 | details = false;
171 | } else {
172 | details.blog_id = blog_id;
173 | details.domain_and_path = $mst_selected_subsite.find( 'option:selected' ).text();
174 | }
175 | }
176 |
177 | return details;
178 | }
179 |
180 | function selected_subsite_changed() {
181 | var selected_subsite = get_selected_subsite();
182 | $.wpmdb.do_action( 'wpmdbmst_selected_subsite_changed', selected_subsite );
183 | }
184 |
185 | var subsite_for_tables = false;
186 |
187 | function update_table_selects( selected_subsite ) {
188 |
189 | // Force table select lists to be refreshed (and filtered again).
190 | $.wpmdb.do_action( 'wpmdb_refresh_table_selects' );
191 |
192 | if ( 'pull' === wpmdb_migration_type() ) {
193 | $.wpmdb.do_action( 'wpmdb_update_pull_table_select' );
194 | } else {
195 | $.wpmdb.do_action( 'wpmdb_update_push_table_select' );
196 | }
197 |
198 | // We may need to enable or disable the ability to select the "Migrate all tables with prefix ..." option.
199 | if ( doing_mst_select_subsite() ) {
200 | $.wpmdb.do_action( 'wpmdb_disable_table_migration_options' );
201 |
202 | // When switching subsites select all the tables unless still loading saved profile.
203 | if ( finished_loading &&
204 | (
205 | false === subsite_for_tables ||
206 | ( undefined !== subsite_for_tables.blog_id && undefined !== selected_subsite.blog_id && subsite_for_tables.blog_id !== selected_subsite.blog_id )
207 | )
208 | ) {
209 | $.wpmdb.do_action( 'wpmdb_select_all_tables' );
210 | }
211 | } else {
212 | $.wpmdb.do_action( 'wpmdb_enable_table_migration_options' );
213 | }
214 |
215 | subsite_for_tables = selected_subsite;
216 | }
217 |
218 | function select_subsite_tables_on_change_action( data ) {
219 | if ( undefined !== wpmdb.mst.remote_connection_data &&
220 | 'true' !== wpmdb.mst.remote_connection_data.site_details.is_multisite &&
221 | doing_mst_select_subsite() &&
222 | data.last_migration_type !== data.migration_type
223 | ) {
224 |
225 | // Timeout required otherwise wpmdb_update_push/pull_table_select action runs after this
226 | setTimeout( function() {
227 | $.wpmdb.do_action( 'wpmdb_select_all_tables' );
228 | } );
229 | }
230 | }
231 |
232 | function maybe_update_local_url_for_subsite( selected_subsite ) {
233 | var new_local_url = original_local_url;
234 |
235 | if ( undefined === selected_subsite ) {
236 | return;
237 | } else if ( undefined !== selected_subsite.domain_and_path ) {
238 | new_local_url = '//' + selected_subsite.domain_and_path;
239 | }
240 |
241 | var replace_right = false;
242 | if ( 'pull' === wpmdb_migration_type() ) {
243 | replace_right = true;
244 | }
245 |
246 | if ( reverse_replace ) {
247 | replace_right = !replace_right;
248 | }
249 |
250 | if ( replace_right ) {
251 | $( '.replace-row.pin .replace-right-col input[type="text"]' ).val( new_local_url );
252 | } else {
253 | $( '.replace-row.pin .old-replace-col input[type="text"]' ).val( new_local_url );
254 | }
255 | }
256 |
257 | function is_subsite_table( table_prefix, table_name ) {
258 | var selected_subsite = get_selected_subsite();
259 |
260 | if ( undefined !== selected_subsite.blog_id ) {
261 | if ( 1 < selected_subsite.blog_id ) {
262 | table_prefix = table_prefix + selected_subsite.blog_id + '_';
263 | var pos = table_name.toLowerCase().indexOf( table_prefix.toLowerCase() );
264 |
265 | if ( 0 === pos ) {
266 | return true;
267 | }
268 | } else {
269 | var escaped_table_name = wpmdb.preg_quote( table_name );
270 | var regex = new RegExp( table_prefix + '([0-9]+)_', 'i' );
271 | var results = regex.exec( escaped_table_name );
272 | return null == results; // If null is returned, there was no match which is good in this case.
273 | }
274 | }
275 |
276 | return false;
277 | }
278 |
279 | function filter_table_for_subsite( exclude, table_name ) {
280 | if ( 'false' === wpmdb_data.site_details.is_multisite ) {
281 | return exclude;
282 | }
283 |
284 | if ( doing_mst_select_subsite() ) {
285 |
286 | // If there is no subsite selected then we should exclude all tables.
287 | if ( false === get_selected_subsite() ) {
288 | return true;
289 | }
290 |
291 | // If table does not have correct base table prefix for site then exclude from subsite export.
292 | if ( table_name.substr( 0, table_prefix.length ) !== table_prefix ) {
293 | return true;
294 | }
295 |
296 | if ( 1 === wpmdb.subsite_for_table( table_prefix, table_name ) ) {
297 |
298 | // wp_users and wp_usermeta are relevant to all sites, shortcut out.
299 | if ( wpmdb.table_is( table_prefix, 'users', table_name ) || wpmdb.table_is( table_prefix, 'usermeta', table_name ) ) {
300 | return exclude;
301 | }
302 |
303 | // Following tables are Multisite setup tables and can be excluded from migration.
304 | // We'll handle getting any data we need from these tables elsewhere.
305 | var ms_tables = [ 'blog_versions', 'blogs', 'registration_log', 'signups', 'site', 'sitemeta' ];
306 |
307 | $.each( ms_tables, function( index, ms_table ) {
308 | if ( wpmdb.table_is( table_prefix, ms_table, table_name ) ) {
309 | exclude = true;
310 | }
311 | } );
312 | }
313 |
314 | // If pulling from a single site install, all properly prefixed tables are relevant.
315 | if ( 'pull' === wpmdb_migration_type() &&
316 | 'undefined' !== typeof wpmdb.mst.remote_connection_data &&
317 | 'true' !== wpmdb.mst.remote_connection_data.site_details.is_multisite ) {
318 | return exclude;
319 | }
320 |
321 | if ( false === is_subsite_table( table_prefix, table_name ) ) {
322 | exclude = true;
323 | }
324 | }
325 |
326 | return exclude;
327 | }
328 |
329 | function validate_new_prefix( new_prefix ) {
330 | var escaped_new_prefix = wpmdb.preg_quote( new_prefix );
331 |
332 | var regex = new RegExp( '[^a-z0-9_]', 'i' );
333 | var results = regex.exec( escaped_new_prefix );
334 | return null == results; // If null is returned there was no match, which is good in this case.
335 | }
336 |
337 | function filter_migration_profile_ready( value, args ) {
338 | if ( 'false' === wpmdb_data.site_details.is_multisite || false === doing_mst_select_subsite() ) {
339 | return value;
340 | }
341 |
342 | var new_prefix = $mst_new_prefix.val();
343 |
344 | if ( false === get_selected_subsite() ) {
345 | alert( wpmdbmst_strings.please_select_a_subsite );
346 | value = false;
347 | } else if ( 0 === new_prefix.trim().length ) {
348 | alert( wpmdbmst_strings.please_enter_a_prefix );
349 | value = false;
350 | } else if ( false === validate_new_prefix( new_prefix ) ) {
351 | alert( wpmdbmst_strings.new_prefix_contents );
352 | value = false;
353 | }
354 |
355 | return value;
356 | }
357 |
358 | function filter_backup_selected_tables( selected_tables ) {
359 | if ( doing_mst_select_subsite() ) {
360 | var selected_subsite = get_selected_subsite();
361 |
362 | // If dealing with non-primary subsite tables and a single site install, we may need to adjust table names to be backed up.
363 | if ( undefined !== selected_subsite.blog_id && 1 < selected_subsite.blog_id &&
364 | ( 'false' === wpmdb_data.site_details.is_multisite || 'true' !== wpmdb.mst.remote_connection_data.site_details.is_multisite ) ) {
365 | $.each( selected_tables, function( index, table_name ) {
366 | if ( false === wpmdb.table_is( table_prefix, 'users', table_name ) && false === wpmdb.table_is( table_prefix, 'usermeta', table_name ) ) {
367 |
368 | // Table to backup needs subsite prefix?
369 | if ( false === is_subsite_table( table_prefix, table_name ) && (
370 | ( 'pull' === wpmdb_migration_type() && 'true' === wpmdb_data.site_details.is_multisite ) ||
371 | ( 'push' === wpmdb_migration_type() && 'true' === wpmdb.mst.remote_connection_data.site_details.is_multisite )
372 | ) ) {
373 | selected_tables[ index ] = $mst_new_prefix.val() + table_name.substr( table_prefix.length );
374 | }
375 |
376 | // Table to backup needs subsite prefix removed?
377 | if ( true === is_subsite_table( table_prefix, table_name ) && (
378 | ( 'pull' === wpmdb_migration_type() && 'false' === wpmdb_data.site_details.is_multisite ) ||
379 | ( 'push' === wpmdb_migration_type() && 'true' !== wpmdb.mst.remote_connection_data.site_details.is_multisite )
380 | ) ) {
381 | selected_tables[ index ] = $mst_new_prefix.val() + table_name.substr( ( table_prefix + selected_subsite.blog_id + '_' ).length );
382 | }
383 | }
384 | } );
385 | }
386 | }
387 |
388 | return selected_tables;
389 | }
390 |
391 | function filter_mf_enable_select_subsites( enable ) {
392 | if ( false !== enable && doing_mst_select_subsite() ) {
393 | enable = false;
394 | }
395 |
396 | return enable;
397 | }
398 |
399 | function update_remote_connection_data( connection_data ) {
400 | wpmdb.mst.remote_connection_data = connection_data;
401 | wpmdb.mst.remote_mst_unavailable = ( 'undefined' === typeof connection_data.mst_available );
402 | }
403 |
404 | // IMPORTANT: This action fires before find/replace columns are swapped for pull/push.
405 | $.wpmdb.add_action( 'move_connection_info_box', function( args ) {
406 | table_prefix = $( '.table-select-wrap .table-prefix' ).text();
407 | if ( null === original_local_url ) {
408 | original_local_url = $.wpmdb.apply_filters( 'wpmdb_base_old_url' );
409 | }
410 |
411 | if ( undefined !== args.migration_type && undefined !== args.last_migration_type ) {
412 | if ( args.migration_type !== args.last_migration_type && ( 'pull' === args.migration_type || 'pull' === args.last_migration_type ) ) {
413 | reverse_replace = true;
414 | }
415 | }
416 | wpmdb_toggle_migration_action_text();
417 | hide_show_options( wpmdb.mst.remote_mst_unavailable );
418 | $.wpmdb.do_action( 'wpmdb_refresh_table_selects' );
419 | if ( reverse_replace ) {
420 | reverse_replace = false;
421 | }
422 | } );
423 |
424 | $.wpmdb.add_action( 'verify_connection_to_remote_site', function( connection_data ) {
425 | update_remote_connection_data( connection_data );
426 | hide_show_options( wpmdb.mst.remote_mst_unavailable );
427 | } );
428 |
429 | $.wpmdb.add_action( 'wpmdbmst_select_subsite_changed', selected_subsite_changed );
430 | $.wpmdb.add_action( 'wpmdbmst_selected_subsite_changed', update_table_selects );
431 | $.wpmdb.add_action( 'wpmdbmst_selected_subsite_changed', maybe_update_local_url_for_subsite );
432 | $.wpmdb.add_action( 'wpmdbmst_selected_subsite_changed', hide_show_new_prefix_field );
433 | $.wpmdb.add_action( 'move_connection_info_box', select_subsite_tables_on_change_action );
434 |
435 | $.wpmdb.add_action( 'wpmdb_connection_data_updated', update_remote_connection_data );
436 |
437 | $.wpmdb.add_filter( 'wpmdb_exclude_table', filter_table_for_subsite );
438 | $.wpmdb.add_filter( 'wpmdb_migration_profile_ready', filter_migration_profile_ready );
439 | $.wpmdb.add_filter( 'wpmdb_backup_selected_tables', filter_backup_selected_tables );
440 |
441 | $.wpmdb.add_filter( 'wpmdbmf_enable_select_subsites', filter_mf_enable_select_subsites );
442 |
443 | $( document ).ready( function() {
444 | $( 'body' ).on( 'change', '#mst-select-subsite', function( e ) {
445 | select_subsite_changed();
446 | } );
447 |
448 | $( 'body' ).on( 'change', '#mst-selected-subsite', function( e ) {
449 | selected_subsite_changed();
450 | } );
451 |
452 | hide_show_options( wpmdb.mst.remote_mst_unavailable );
453 | finished_loading = true;
454 | } );
455 |
456 | })( jQuery, wpmdb );
457 |
--------------------------------------------------------------------------------
/asset/dist/js/script-113.min.js:
--------------------------------------------------------------------------------
1 | var wpmdb=wpmdb||{};wpmdb.mst={remote_mst_unavailable:!1},function(a,b){function c(){return"1"===w.attr("data-available")&&w.is(":checked")?!0:!1}function d(){a.wpmdb.do_action("wpmdb_lock_replace_url",!1),a.wpmdb.do_action("wpmdb_enable_table_migration_options"),w.attr("data-available","0"),w.prop("checked",!1),w.attr("disabled","disabled"),a(".mst").addClass("disabled"),x.hide()}function e(){w.attr("data-available","1"),w.removeAttr("disabled"),a(".mst").removeClass("disabled")}function f(f){return"false"===wpmdb_data.site_details.is_multisite?(d(),void v.hide()):"savefile"!==wpmdb_migration_type()&&"undefined"!=typeof b.mst.remote_connection_data&&"true"===b.mst.remote_connection_data.site_details.is_multisite?(d(),void v.hide()):"savefile"!==wpmdb_migration_type()&&!1===f&&"undefined"!=typeof b.mst.remote_connection_data&&"undefined"!=typeof b.mst.remote_connection_data.site_details&&wpmdb_data.site_details.prefix!==b.mst.remote_connection_data.site_details.prefix?(d(),n(!1),a(".mst-remote-location").html(b.mst.remote_connection_data.url),a(".mst-remote-prefix").html(b.mst.remote_connection_data.site_details.prefix),void F.show()):(F.hide(),f&&"true"===wpmdb_data.site_details.is_multisite&&"savefile"!==wpmdb_migration_type()?(d(),n(!1),void D.show()):(D.hide(),"savefile"!==wpmdb_migration_type()&&"undefined"!=typeof b.mst.remote_connection_data&&wpmdb_data.mst_version!==b.mst.remote_connection_data.mst_version?(d(),n(!1),a(".mst-remote-location").html(b.mst.remote_connection_data.url),a(".mst-remote-version").html(b.mst.remote_connection_data.mst_version),void E.show()):(E.hide(),e(),c()?(x.show(),k()):x.hide(),g(),void v.show())))}function g(){c()&&"savefile"!==wpmdb_migration_type()?a.wpmdb.do_action("wpmdb_lock_replace_url",!0):a.wpmdb.do_action("wpmdb_lock_replace_url",!1)}function h(){if(""===y.val())return void z.hide();if("savefile"!==wpmdb_migration_type()){A.hide(),C.show(),z.children("label").addClass("disabled");var a=J,b=j();"pull"===wpmdb_migration_type()&&void 0!==b.blog_id&&1
45 | $mst_select_subsite = false;
46 | $mst_selected_subsite = 0;
47 | if ( isset( $assoc_args['subsite'] ) ) {
48 | if ( ! is_multisite() ) {
49 | return $wpmdbpro_cli->cli_error( __( 'The installation must be a Multisite network to make use of the subsite option', 'wp-migrate-db-pro-multisite-tools' ) );
50 | }
51 | if ( empty( $assoc_args['subsite'] ) ) {
52 | return $wpmdbpro_cli->cli_error( __( 'A valid Blog ID or Subsite URL must be supplied to make use of the subsite option', 'wp-migrate-db-pro-multisite-tools' ) );
53 | }
54 | $mst_selected_subsite = $this->get_subsite_id( $assoc_args['subsite'] );
55 |
56 | if ( false === $mst_selected_subsite ) {
57 | return $wpmdbpro_cli->cli_error( __( 'A valid Blog ID or Subsite URL must be supplied to make use of the subsite option', 'wp-migrate-db-pro-multisite-tools' ) );
58 | }
59 |
60 | $mst_select_subsite = true;
61 | }
62 |
63 | // --prefix=
64 | global $wpdb;
65 | $new_prefix = $wpdb->base_prefix;
66 | if ( isset( $assoc_args['prefix'] ) ) {
67 | if ( false === $mst_select_subsite ) {
68 | return $wpmdbpro_cli->cli_error( __( 'A new table name prefix may only be specified for subsite exports.', 'wp-migrate-db-pro-multisite-tools' ) );
69 | }
70 | if ( empty( $assoc_args['prefix'] ) ) {
71 | return $wpmdbpro_cli->cli_error( __( 'A valid prefix must be supplied to make use of the prefix option', 'wp-migrate-db-pro-multisite-tools' ) );
72 | }
73 | $new_prefix = trim( $assoc_args['prefix'] );
74 |
75 | if ( sanitize_key( $new_prefix ) !== $new_prefix ) {
76 | return $wpmdbpro_cli->cli_error( $this->get_string( 'new_prefix_contents' ) );
77 | }
78 | } elseif ( 1 < $mst_selected_subsite && 'pull' === $profile['action'] ) {
79 | $new_prefix .= $mst_selected_subsite . '_';
80 | }
81 |
82 | // Disable Media Files Select Subsites if using Subsite Migration.
83 | if ( $mst_select_subsite && ! empty( $profile['mf_select_subsites'] ) && ! empty( $profile['mf_selected_subsites'] ) ) {
84 | unset( $profile['mf_select_subsites'], $profile['mf_selected_subsites'] );
85 | }
86 |
87 | $filtered_profile = compact(
88 | 'mst_select_subsite',
89 | 'mst_selected_subsite',
90 | 'new_prefix'
91 | );
92 |
93 | return array_merge( $profile, $filtered_profile );
94 | }
95 |
96 | /**
97 | * Ensure CLI has appropriate default find and replace values when doing MST.
98 | *
99 | * @param array $profile
100 | * @param array $post_data
101 | *
102 | * @return array
103 | */
104 | public function filter_cli_default_find_and_replace( $profile, $post_data ) {
105 | if ( is_wp_error( $profile ) ) {
106 | return $profile;
107 | }
108 |
109 | if ( empty( $profile['mst_select_subsite'] ) || empty( $profile['mst_selected_subsite'] ) || 1 >= $profile['mst_selected_subsite'] ) {
110 | return $profile;
111 | }
112 |
113 | $source = ( 'pull' === $post_data['intent'] ) ? 'remote' : 'local';
114 | $target = ( 'pull' === $post_data['intent'] ) ? 'local' : 'remote';
115 |
116 | if ( 'true' === $post_data['site_details'][ $source ]['is_multisite'] && ! empty( $post_data['site_details'][ $source ]['subsites_info'][ $profile['mst_selected_subsite'] ]['site_url'] ) ) {
117 | $profile['replace_old'][1] = '//' . untrailingslashit( $this->scheme_less_url( $post_data['site_details'][ $source ]['subsites_info'][ $profile['mst_selected_subsite'] ]['site_url'] ) );
118 | }
119 |
120 | if ( 'true' === $post_data['site_details'][ $target ]['is_multisite'] && ! empty( $post_data['site_details'][ $target ]['subsites_info'][ $profile['mst_selected_subsite'] ]['site_url'] ) ) {
121 | $profile['replace_new'][1] = '//' . untrailingslashit( $this->scheme_less_url( $post_data['site_details'][ $target ]['subsites_info'][ $profile['mst_selected_subsite'] ]['site_url'] ) );
122 | }
123 |
124 | return $profile;
125 | }
126 | }
--------------------------------------------------------------------------------
/class/wpmdbpro-multisite-tools.php:
--------------------------------------------------------------------------------
1 | plugin_slug = 'wp-migrate-db-pro-multisite-tools';
13 | $this->plugin_version = $GLOBALS['wpmdb_meta']['wp-migrate-db-pro-multisite-tools']['version'];
14 | if ( ! $this->meets_version_requirements( '1.6.1' ) ) {
15 | return;
16 | }
17 |
18 | $this->accepted_fields = array(
19 | 'multisite_subsite_export', // TODO: Remove backwards compatibility for CLI once Core/MST/CLI dependencies updated.
20 | 'select_subsite', // TODO: Remove backwards compatibility for CLI once Core/MST/CLI dependencies updated.
21 | 'mst_select_subsite',
22 | 'mst_selected_subsite',
23 | 'new_prefix',
24 | 'keep_active_plugins',
25 | );
26 |
27 | add_action( 'wpmdb_before_migration_options', array( $this, 'migration_form_controls' ) );
28 | add_action( 'wpmdb_load_assets', array( $this, 'load_assets' ) );
29 | add_action( 'wpmdb_diagnostic_info', array( $this, 'diagnostic_info' ) );
30 | add_filter( 'wpmdb_accepted_profile_fields', array( $this, 'accepted_profile_fields' ) );
31 | add_filter( 'wpmdb_establish_remote_connection_data', array( $this, 'establish_remote_connection_data' ) );
32 | add_filter( 'wpmdb_data', array( $this, 'js_variables' ) );
33 |
34 | add_filter( 'wpmdb_exclude_table', array( $this, 'filter_table_for_subsite' ), 10, 2 );
35 | add_filter( 'wpmdb_tables', array( $this, 'filter_tables_for_subsite' ), 10, 2 );
36 | add_filter( 'wpmdb_table_sizes', array( $this, 'filter_table_sizes_for_subsite' ), 10, 2 );
37 | add_filter( 'wpmdb_target_table_name', array( $this, 'filter_target_table_name' ), 10, 4 );
38 | add_filter( 'wpmdb_table_row', array( $this, 'filter_table_row' ), 10, 4 );
39 | add_filter( 'wpmdb_find_and_replace', array( $this, 'filter_find_and_replace' ), 10, 3 );
40 | add_filter( 'wpmdb_finalize_target_table_name', array( $this, 'filter_finalize_target_table_name' ), 10, 3 );
41 | add_filter( 'wpmdb_preserved_options', array( $this, 'filter_preserved_options' ), 10, 2 );
42 | add_filter( 'wpmdb_preserved_options_data', array( $this, 'filter_preserved_options_data' ), 10, 2 );
43 | add_filter( 'wpmdb_get_alter_queries', array( $this, 'filter_get_alter_queries' ) );
44 |
45 | global $wpmdbpro;
46 | $this->wpmdbpro = $wpmdbpro;
47 |
48 | if ( class_exists( 'WPMDBPro_Media_Files' ) ) {
49 | add_filter( 'wpmdbmf_include_subsite', array( $this, 'include_subsite' ), 10, 3 );
50 | add_filter( 'wpmdbmf_destination_file_path', array( $this, 'filter_mf_destination_file_path' ), 10, 3 );
51 | add_filter( 'wpmdbmf_file_not_on_local', array( $this, 'filter_mf_file_not_on_local' ), 10, 3 );
52 | add_filter( 'wpmdbmf_get_remote_attachment_batch_response', array( $this, 'filter_mf_get_remote_attachment_batch_response', ), 10, 3 );
53 | add_filter( 'wpmdbmf_exclude_local_media_file_from_removal', array( $this, 'filter_mf_exclude_local_media_file_from_removal', ), 10, 4 );
54 | add_filter( 'wpmdbmf_file_to_download', array( $this, 'filter_mf_file_to_download', ), 10, 3 );
55 | }
56 | }
57 |
58 | /**
59 | * Does the given user need to be migrated?
60 | *
61 | * @param int $user_id
62 | * @param int $blog_id Optional.
63 | *
64 | * @return bool
65 | */
66 | private function is_user_required_for_blog( $user_id, $blog_id = 0 ) {
67 | static $users = array();
68 |
69 | if ( empty( $user_id ) ) {
70 | $user_id = 0;
71 | }
72 |
73 | if ( empty( $blog_id ) ) {
74 | $blog_id = 0;
75 | }
76 |
77 | if ( isset( $users[ $blog_id ][ $user_id ] ) ) {
78 | return $users[ $blog_id ][ $user_id ];
79 | }
80 |
81 | if ( ! is_multisite() ) {
82 | $users[ $blog_id ][ $user_id ] = true;
83 |
84 | return $users[ $blog_id ][ $user_id ];
85 | }
86 |
87 | $subsites = $this->subsites_list();
88 |
89 | if ( empty( $subsites ) || ! array_key_exists( $blog_id, $subsites ) ) {
90 | $users[ $blog_id ][ $user_id ] = false;
91 |
92 | return $users[ $blog_id ][ $user_id ];
93 | }
94 |
95 | if ( is_user_member_of_blog( $user_id, $blog_id ) ) {
96 | $users[ $blog_id ][ $user_id ] = true;
97 |
98 | return $users[ $blog_id ][ $user_id ];
99 | }
100 |
101 | // If the user has any posts that are going to be migrated, we need the user regardless of whether they still have access.
102 | switch_to_blog( $blog_id );
103 | $user_posts = count_user_posts( $user_id );
104 | restore_current_blog();
105 |
106 | if ( 0 < $user_posts ) {
107 | $users[ $blog_id ][ $user_id ] = true;
108 |
109 | return $users[ $blog_id ][ $user_id ];
110 | }
111 |
112 | // If here, user not required.
113 | $users[ $blog_id ][ $user_id ] = false;
114 |
115 | return $users[ $blog_id ][ $user_id ];
116 | }
117 |
118 | /**
119 | * Return subsite id if subsite selected.
120 | *
121 | * @param WPMDB_Base $plugin_instance
122 | *
123 | * @return int Will return 0 if not doing MST migration.
124 | *
125 | * Will return 0 if not doing MST migration.
126 | */
127 | public function selected_subsite( &$plugin_instance = null ) {
128 | $blog_id = 0;
129 |
130 | if ( empty( $plugin_instance ) ) {
131 | $plugin_instance = &$this->wpmdbpro;
132 | }
133 |
134 | $this->state_data = $plugin_instance->set_post_data();
135 |
136 | if ( ! empty( $this->state_data['form_data'] ) ) {
137 | $this->form_data = $this->parse_migration_form_data( $this->state_data['form_data'] );
138 |
139 | $select_subsite = $this->profile_value( 'mst_select_subsite' );
140 | $selected_subsite = $this->profile_value( 'mst_selected_subsite' );
141 |
142 | // TODO: Remove backwards compatibility for CLI once Core/MST/CLI dependencies updated.
143 | if ( empty( $select_subsite ) && empty( $selected_subsite ) ) {
144 | $select_subsite = $this->profile_value( 'multisite_subsite_export' );
145 | $selected_subsite = $this->profile_value( 'select_subsite' );
146 | }
147 |
148 | // During a migration, this is where the subsite's id will be derived.
149 | if ( empty( $blog_id ) &&
150 | ! empty( $select_subsite ) &&
151 | ! empty( $selected_subsite ) &&
152 | is_numeric( $selected_subsite )
153 | ) {
154 | $blog_id = $selected_subsite;
155 | }
156 | }
157 |
158 | // When loading a saved migration profile, this is where the subsite's id will be derived.
159 | global $loaded_profile;
160 | if ( empty( $blog_id ) &&
161 | ! empty( $loaded_profile['mst_select_subsite'] ) &&
162 | ! empty( $loaded_profile['mst_selected_subsite'] ) &&
163 | is_numeric( $loaded_profile['mst_selected_subsite'] )
164 | ) {
165 | $blog_id = $loaded_profile['mst_selected_subsite'];
166 | }
167 |
168 | // If on multisite we can check that selected blog exists as all scenarios would require it.
169 | if ( 1 < $blog_id && is_multisite() && ! $this->subsite_exists( $blog_id ) ) {
170 | $blog_id = 0;
171 | }
172 |
173 | return $blog_id;
174 | }
175 |
176 | /**
177 | * Adds the multisite tools settings to the migration setting page in core.
178 | */
179 | public function migration_form_controls() {
180 | $this->template( 'migrate' );
181 | }
182 |
183 | /**
184 | * Whitelist multisite tools setting fields for use in AJAX save in core
185 | *
186 | * @param array $profile_fields
187 | *
188 | * @return array
189 | */
190 | public function accepted_profile_fields( $profile_fields ) {
191 | return array_merge( $profile_fields, $this->accepted_fields );
192 | }
193 |
194 | /**
195 | * Check the remote site has the multisite tools addon setup
196 | *
197 | * @param array $data Connection data
198 | *
199 | * @return array Updated connection data
200 | */
201 | public function establish_remote_connection_data( $data ) {
202 | $data['mst_available'] = '1';
203 | $data['mst_version'] = $this->plugin_version;
204 |
205 | return $data;
206 | }
207 |
208 | /**
209 | * Add multisite tools related javascript variables to the page
210 | *
211 | * @param array $data
212 | *
213 | * @return array
214 | */
215 | public function js_variables( $data ) {
216 | $data['mst_version'] = $this->plugin_version;
217 |
218 | return $data;
219 | }
220 |
221 | /**
222 | * Get translated strings for javascript and other functions.
223 | *
224 | * @return array
225 | */
226 | public function get_strings() {
227 | static $strings;
228 |
229 | if ( ! empty( $strings ) ) {
230 | return $strings;
231 | }
232 |
233 | $strings = array(
234 | 'migration_failed' => __( 'Migration failed', 'wp-migrate-db-pro-multisite-tools' ),
235 | 'please_select_a_subsite' => __( 'Please select a subsite.', 'wp-migrate-db-pro-multisite-tools' ),
236 | 'please_enter_a_prefix' => __( 'Please enter a new table prefix.', 'wp-migrate-db-pro-multisite-tools' ),
237 | 'new_prefix_contents' => __( 'Please only enter letters, numbers or underscores for the new table prefix.', 'wp-migrate-db-pro-multisite-tools' ),
238 | 'export_subsite_option' => __( 'Export a subsite as a single site install', 'wp-migrate-db-pro-multisite-tools' ),
239 | 'pull_subsite_option' => __( 'Pull into a specific subsite', 'wp-migrate-db-pro-multisite-tools' ),
240 | 'push_subsite_option' => __( 'Push a specific subsite', 'wp-migrate-db-pro-multisite-tools' ),
241 | );
242 |
243 | return $strings;
244 | }
245 |
246 | /**
247 | * Retrieve a specific translated string.
248 | *
249 | * @param string $key
250 | *
251 | * @return string
252 | */
253 | public function get_string( $key ) {
254 | $strings = $this->get_strings();
255 |
256 | return ( isset( $strings[ $key ] ) ) ? $strings[ $key ] : '';
257 | }
258 |
259 | /**
260 | * Load multisite tools related assets in core plugin.
261 | */
262 | public function load_assets() {
263 | $plugins_url = trailingslashit( plugins_url() ) . trailingslashit( $this->plugin_folder_name );
264 | $version = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? time() : $this->plugin_version;
265 | $ver_string = '-' . str_replace( '.', '', $this->plugin_version );
266 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
267 |
268 | $src = $plugins_url . 'asset/dist/css/styles.css';
269 | wp_enqueue_style( 'wp-migrate-db-pro-multisite-tools-styles', $src, array( 'wp-migrate-db-pro-styles' ), $version );
270 |
271 | $src = $plugins_url . "asset/dist/js/script{$ver_string}{$min}.js";
272 | wp_enqueue_script( 'wp-migrate-db-pro-multisite-tools-script',
273 | $src,
274 | array(
275 | 'jquery',
276 | 'wp-migrate-db-pro-common',
277 | 'wp-migrate-db-pro-hook',
278 | 'wp-migrate-db-pro-script',
279 | ),
280 | $version,
281 | true );
282 |
283 | wp_localize_script( 'wp-migrate-db-pro-multisite-tools-script', 'wpmdbmst_strings', $this->get_strings() );
284 | }
285 |
286 | /**
287 | * Adds extra information to the core plugin's diagnostic info
288 | */
289 | public function diagnostic_info() {
290 | if ( is_multisite() ) {
291 | echo 'Sites: ';
292 | echo number_format( get_blog_count() );
293 | echo "\r\n";
294 | }
295 | }
296 |
297 | /**
298 | * Should the given table be excluded from a subsite migration.
299 | *
300 | * @param bool $exclude
301 | * @param string $table_name
302 | *
303 | * @return bool
304 | */
305 | public function filter_table_for_subsite( $exclude, $table_name ) {
306 | if ( ! is_multisite() ) {
307 | return $exclude;
308 | }
309 |
310 | $blog_id = $this->selected_subsite();
311 |
312 | if ( 0 < $blog_id ) {
313 | // wp_users and wp_usermeta are relevant to all sites, shortcut out.
314 | if ( $this->wpmdbpro->table_is( '', $table_name, 'non_ms_global' ) ) {
315 | return $exclude;
316 | }
317 |
318 | // Following tables are Multisite setup tables and can be excluded from migration.
319 | if ( $this->wpmdbpro->table_is( '', $table_name, 'ms_global' ) ) {
320 | return true;
321 | }
322 |
323 | global $wpdb;
324 | $prefix = $wpdb->base_prefix;
325 | $prefix_escaped = preg_quote( $prefix );
326 |
327 | if ( 1 == $blog_id ) {
328 | // Exclude tables from non-primary subsites.
329 | if ( preg_match( '/^' . $prefix_escaped . '([0-9]+)_/', $table_name, $matches ) ) {
330 | $exclude = true;
331 | }
332 | } else {
333 | $prefix .= $blog_id . '_';
334 | if ( 0 !== stripos( $table_name, $prefix ) ) {
335 | $exclude = true;
336 | }
337 | }
338 | }
339 |
340 | return $exclude;
341 | }
342 |
343 | /**
344 | * Filter the given tables if doing a subsite migration.
345 | *
346 | * @param array $tables
347 | * @param string $scope
348 | *
349 | * @return array
350 | */
351 | public function filter_tables_for_subsite( $tables, $scope = 'regular' ) {
352 | if ( ! is_multisite() || empty( $tables ) ) {
353 | return $tables;
354 | }
355 |
356 | // We will not alter backup or temp tables list.
357 | if ( in_array( $scope, array( 'backup', 'temp' ) ) ) {
358 | return $tables;
359 | }
360 |
361 | $filtered_tables = array();
362 | $blog_id = $this->selected_subsite();
363 |
364 | if ( 0 < $blog_id ) {
365 | foreach ( $tables as $key => $value ) {
366 | if ( false === $this->filter_table_for_subsite( false, $value ) ) {
367 | $filtered_tables[ $key ] = $value;
368 | }
369 | }
370 | } else {
371 | $filtered_tables = $tables;
372 | }
373 |
374 | return $filtered_tables;
375 | }
376 |
377 | /**
378 | * Filter the given tables with sizes if doing a subsite migration.
379 | *
380 | * @param array $table_sizes
381 | * @param string $scope
382 | *
383 | * @return array
384 | */
385 | public function filter_table_sizes_for_subsite( $table_sizes, $scope = 'regular' ) {
386 | if ( ! is_multisite() || empty( $table_sizes ) ) {
387 | return $table_sizes;
388 | }
389 |
390 | $tables = $this->filter_tables_for_subsite( array_keys( $table_sizes ), $scope );
391 |
392 | return array_intersect_key( $table_sizes, array_flip( $tables ) );
393 | }
394 |
395 | /**
396 | * Change the name of the given table if subsite selected and migration profile has new prefix.
397 | *
398 | * @param string $table_name
399 | * @param string $action
400 | * @param string $stage
401 | * @param array $site_details
402 | *
403 | * @return string
404 | */
405 | public function filter_target_table_name( $table_name, $action, $stage, $site_details = array() ) {
406 | $blog_id = $this->selected_subsite();
407 |
408 | if ( 1 > $blog_id || 'backup' == $stage ) {
409 | return $table_name;
410 | }
411 |
412 | $new_prefix = $this->wpmdbpro->profile_value( 'new_prefix' );
413 |
414 | if ( empty( $new_prefix ) ) {
415 | return $table_name;
416 | }
417 |
418 | global $wpdb;
419 | $old_prefix = $wpdb->base_prefix;
420 | if ( is_multisite() && 1 < $blog_id && ! $this->wpmdbpro->table_is( '', $table_name, 'global', '', $blog_id ) ) {
421 | $old_prefix .= $blog_id . '_';
422 | }
423 |
424 | // We do not want to overwrite the global tables unless exporting or target is a single site install.
425 | if ( 'savefile' !== $action &&
426 | (
427 | ( 'pull' === $action && 'true' === $site_details['local']['is_multisite'] ) ||
428 | ( 'push' === $action && 'true' === $site_details['remote']['is_multisite'] )
429 | ) &&
430 | $this->wpmdbpro->table_is( '', $table_name, 'global' )
431 | ) {
432 | $new_prefix .= 'wpmdbglobal_';
433 | }
434 |
435 | if ( 0 === stripos( $table_name, $old_prefix ) ) {
436 | $table_name = substr_replace( $table_name, $new_prefix, 0, strlen( $old_prefix ) );
437 | }
438 |
439 | return $table_name;
440 | }
441 |
442 | /**
443 | * Handler for the wpmdb_table_row filter.
444 | * The given $row can be modified, but if we return false the row will not be used.
445 | *
446 | * @param stdClass $row
447 | * @param string $table_name
448 | * @param string $action
449 | * @param string $stage
450 | *
451 | * @return bool
452 | */
453 | public function filter_table_row( $row, $table_name, $action, $stage ) {
454 | $use = true;
455 | $blog_id = $this->selected_subsite();
456 |
457 | if ( 1 > $blog_id || 'backup' == $stage ) {
458 | return $use;
459 | }
460 |
461 | $new_prefix = $this->wpmdbpro->profile_value( 'new_prefix' );
462 |
463 | if ( empty( $new_prefix ) ) {
464 | return $row;
465 | }
466 |
467 | global $wpdb;
468 |
469 | $old_prefix = $wpdb->base_prefix;
470 | if ( is_multisite() && 1 < $blog_id ) {
471 | $old_prefix .= $blog_id . '_';
472 | }
473 |
474 | if ( $this->wpmdbpro->table_is( 'options', $table_name ) ) {
475 | // Rename options records like wp_X_user_roles to wp_Y_user_roles otherwise no users can do anything in the migrated site.
476 | if ( 0 === stripos( $row->option_name, $old_prefix ) ) {
477 | $row->option_name = substr_replace( $row->option_name, $new_prefix, 0, strlen( $old_prefix ) );
478 | }
479 | }
480 |
481 | if ( $this->wpmdbpro->table_is( 'usermeta', $table_name ) ) {
482 | if ( ! $this->is_user_required_for_blog( $row->user_id, $blog_id ) ) {
483 | $use = false;
484 | } elseif ( 1 == $blog_id ) {
485 | $prefix_escaped = preg_quote( $wpdb->base_prefix );
486 | if ( 1 === preg_match( '/^' . $prefix_escaped . '([0-9]+)_/', $row->meta_key, $matches ) ) {
487 | // Remove non-primary subsite records from usermeta when migrating primary subsite.
488 | $use = false;
489 | } elseif ( 0 === stripos( $row->meta_key, $old_prefix ) ) {
490 | // Rename prefixed keys.
491 | $row->meta_key = substr_replace( $row->meta_key, $new_prefix, 0, strlen( $old_prefix ) );
492 | }
493 | } else {
494 | if ( 0 === stripos( $row->meta_key, $old_prefix ) ) {
495 | // Rename prefixed keys.
496 | $row->meta_key = substr_replace( $row->meta_key, $new_prefix, 0, strlen( $old_prefix ) );
497 | } elseif ( 0 === stripos( $row->meta_key, $wpdb->base_prefix ) ) {
498 | // Remove wp_* records from usermeta not for extracted subsite.
499 | $use = false;
500 | }
501 | }
502 | }
503 |
504 | if ( $this->wpmdbpro->table_is( 'users', $table_name ) ) {
505 | if ( ! $this->is_user_required_for_blog( $row->ID, $blog_id ) ) {
506 | $use = false;
507 | }
508 | }
509 |
510 | return $use;
511 | }
512 |
513 | /**
514 | * Handler for the wpmdb_find_and_replace filter.
515 | *
516 | * @param array $tmp_find_replace_pairs
517 | * @param string $intent
518 | * @param string $site_url
519 | *
520 | * @return array
521 | */
522 | public function filter_find_and_replace( $tmp_find_replace_pairs, $intent, $site_url ) {
523 | // TODO: Remove this condition when MST usable from single site install.
524 | if ( ! is_multisite() ) {
525 | return $tmp_find_replace_pairs;
526 | }
527 |
528 | $blog_id = $this->selected_subsite();
529 |
530 | if ( 1 > $blog_id ) {
531 | return $tmp_find_replace_pairs;
532 | }
533 |
534 | $source = ( 'pull' === $intent ) ? 'remote' : 'local';
535 | $target = ( 'pull' === $intent ) ? 'local' : 'remote';
536 |
537 | if ( 'true' === $this->state_data['site_details'][ $source ]['is_multisite'] ) {
538 | $source_site_url = $this->state_data['site_details'][ $source ]['subsites_info'][ $blog_id ]['site_url'];
539 | $source_uploads_baseurl = $this->state_data['site_details'][ $source ]['subsites_info'][ $blog_id ]['uploads']['baseurl'];
540 | $source_short_basedir = $this->state_data['site_details'][ $source ]['subsites_info'][ $blog_id ]['uploads']['short_basedir'];
541 | } else {
542 | $source_site_url = $this->state_data['site_details'][ $source ]['site_url'];
543 | $source_uploads_baseurl = $this->state_data['site_details'][ $source ]['uploads']['baseurl'];
544 | $source_short_basedir = '';
545 | }
546 | $source_site_url = '//' . untrailingslashit( $this->scheme_less_url( $source_site_url ) );
547 | $source_uploads_baseurl = '//' . untrailingslashit( $this->scheme_less_url( $source_uploads_baseurl ) );
548 |
549 | if ( 'savefile' === $intent ) {
550 | $target_site_url = '';
551 | $target_uploads_baseurl = '';
552 | $target_short_basedir = '';
553 |
554 | foreach ( $tmp_find_replace_pairs as $find => $replace ) {
555 | if ( $find == $source_site_url ) {
556 | $target_site_url = $replace;
557 | break;
558 | }
559 | }
560 |
561 | // Append extra path elements from uploads url, removing unneeded subsite specific elements.
562 | if ( ! empty( $target_site_url ) ) {
563 | $target_uploads_baseurl = $target_site_url . substr( $source_uploads_baseurl, strlen( $source_site_url ) );
564 |
565 | if ( ! empty( $source_short_basedir ) ) {
566 | $target_uploads_baseurl = substr( untrailingslashit( $target_uploads_baseurl ), 0, -strlen( untrailingslashit( $source_short_basedir ) ) );
567 | }
568 | }
569 | } elseif ( 'true' === $this->state_data['site_details'][ $target ]['is_multisite'] ) {
570 | $target_site_url = $this->state_data['site_details'][ $target ]['subsites_info'][ $blog_id ]['site_url'];
571 | $target_uploads_baseurl = $this->state_data['site_details'][ $target ]['subsites_info'][ $blog_id ]['uploads']['baseurl'];
572 | $target_short_basedir = $this->state_data['site_details'][ $target ]['subsites_info'][ $blog_id ]['uploads']['short_basedir'];
573 | } else {
574 | $target_site_url = $this->state_data['site_details'][ $target ]['site_url'];
575 | $target_uploads_baseurl = $this->state_data['site_details'][ $target ]['uploads']['baseurl'];
576 | $target_short_basedir = '';
577 | }
578 |
579 | // If we have a target uploads url, we can add in the find/replace we need.
580 | if ( ! empty( $target_uploads_baseurl ) ) {
581 | $target_site_url = '//' . untrailingslashit( $this->scheme_less_url( $target_site_url ) );
582 | $target_uploads_baseurl = '//' . untrailingslashit( $this->scheme_less_url( $target_uploads_baseurl ) );
583 |
584 | // As we're appending to the find/replace rows, we need to use the already replaced values for altering uploads url.
585 | $old_uploads_url = substr_replace( $source_uploads_baseurl, $target_site_url, 0, strlen( $source_site_url ) );
586 | $tmp_find_replace_pairs[ $old_uploads_url ] = $target_uploads_baseurl;
587 | }
588 |
589 | return $tmp_find_replace_pairs;
590 | }
591 |
592 | /**
593 | * Change the name of the given table depending on migration profile settings and source and target site setup.
594 | *
595 | * @param string $table_name
596 | * @param string $intent
597 | * @param array $site_details
598 | *
599 | * @return string
600 | *
601 | * This is run in response to the wpmdb_finalize_target_table_name filter on the target site.
602 | */
603 | public function filter_finalize_target_table_name( $table_name, $intent, $site_details ) {
604 | $blog_id = $this->selected_subsite();
605 |
606 | if ( 1 > $blog_id ) {
607 | return $table_name;
608 | }
609 |
610 | $new_prefix = $this->wpmdbpro->profile_value( 'new_prefix' );
611 |
612 | if ( empty( $new_prefix ) ) {
613 | return $table_name;
614 | }
615 |
616 | // During a MST migration we add a custom prefix to the global tables so that we can manipulate their data before use.
617 | if ( is_multisite() && $this->wpmdbpro->table_is( '', $table_name, 'global', $new_prefix, $blog_id ) ) {
618 | $new_prefix .= 'wpmdbglobal_';
619 | }
620 |
621 | $old_prefix = ( 'pull' === $intent ? $site_details['remote']['prefix'] : $site_details['local']['prefix'] );
622 | if ( ! is_multisite() && 1 < $blog_id && ! $this->wpmdbpro->table_is( '', $table_name, 'global', $new_prefix, $blog_id ) ) {
623 | $old_prefix .= $blog_id . '_';
624 | }
625 |
626 | if ( 0 === stripos( $table_name, $old_prefix ) ) {
627 | $table_name = substr_replace( $table_name, $new_prefix, 0, strlen( $old_prefix ) );
628 | }
629 |
630 | return $table_name;
631 | }
632 |
633 | /**
634 | * Returns validated and sanitized form data.
635 | *
636 | * @param array|string $data
637 | *
638 | * @return array|string
639 | */
640 | public function parse_migration_form_data( $data ) {
641 | $form_data = parent::parse_migration_form_data( $data );
642 |
643 | $form_data = array_intersect_key( $form_data, array_flip( $this->accepted_fields ) );
644 |
645 | return $form_data;
646 | }
647 |
648 | /**
649 | * Alter given destination file path depending on local and remote site setup.
650 | *
651 | * @param string $file_path
652 | * @param string $intent
653 | * @param WPMDBPro_Media_Files_Base $wpmdbmf
654 | *
655 | * @return string
656 | */
657 | public function filter_mf_destination_file_path( $file_path, $intent, $wpmdbmf ) {
658 | $blog_id = $this->selected_subsite( $wpmdbmf );
659 |
660 | if ( 1 > $blog_id ) {
661 | return $file_path;
662 | }
663 |
664 | if ( ! is_multisite() && 'push' === $intent && ! empty( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) ) {
665 | $file_path = substr_replace( $file_path, '', 0, strlen( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) );
666 | }
667 |
668 | return $file_path;
669 | }
670 |
671 | /**
672 | * Alter given destination file path depending on local and remote site setup.
673 | *
674 | * @param string $file
675 | * @param string $intent
676 | * @param WPMDBPro_Media_Files_Base $wpmdbmf
677 | *
678 | * @return string
679 | */
680 | public function filter_mf_file_not_on_local( $file, $intent, $wpmdbmf ) {
681 | $blog_id = $this->selected_subsite( $wpmdbmf );
682 |
683 | if ( 1 > $blog_id ) {
684 | return $file;
685 | }
686 |
687 | if ( is_multisite() && 'push' === $intent &&
688 | $this->state_data['site_details']['local']['is_multisite'] !== $this->state_data['site_details']['remote']['is_multisite'] &&
689 | ! empty( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] )
690 | ) {
691 | $file = $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] . $file;
692 | } elseif ( ! is_multisite() && 'pull' === $intent &&
693 | $this->state_data['site_details']['local']['is_multisite'] !== $this->state_data['site_details']['remote']['is_multisite'] &&
694 | ! empty( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] )
695 | ) {
696 | $file = substr( $file, strlen( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) );
697 | }
698 |
699 | return $file;
700 | }
701 |
702 | /**
703 | * Alter given source file path depending on local and remote site setup.
704 | *
705 | * @param array $response
706 | * @param string $intent
707 | * @param WPMDBPro_Media_Files_Base $wpmdbmf
708 | *
709 | * @return string
710 | */
711 | public function filter_mf_get_remote_attachment_batch_response( $response, $intent, $wpmdbmf ) {
712 | $blog_id = $this->selected_subsite( $wpmdbmf );
713 |
714 | if ( 1 > $blog_id ) {
715 | return $response;
716 | }
717 |
718 | if ( is_multisite() && 'pull' === $intent &&
719 | $this->state_data['site_details']['local']['is_multisite'] !== $this->state_data['site_details']['remote']['is_multisite']
720 | ) {
721 | $remote_attachments = unserialize( stripslashes( $response['remote_attachments'] ) );
722 |
723 | if ( ! empty( $remote_attachments[1] ) ) {
724 | foreach ( $remote_attachments[1] as $index => $attachment ) {
725 | $attachment['blog_id'] = $blog_id;
726 | $attachment['file'] = ltrim( trailingslashit( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) . $attachment['file'], '/' );
727 |
728 | if ( ! empty( $attachment['sizes'] ) ) {
729 | foreach ( $attachment['sizes'] as $size_idx => $size ) {
730 | $attachment['sizes'][ $size_idx ]['file'] = ltrim( trailingslashit( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) . $size['file'], '/' );
731 | }
732 | }
733 |
734 | $remote_attachments[1][ $index ] = $attachment;
735 | }
736 | }
737 | $response['remote_attachments'] = addslashes( serialize( $remote_attachments ) );
738 | }
739 |
740 | return $response;
741 | }
742 |
743 | /**
744 | * Alter given source file path depending on local and remote site setup.
745 | *
746 | * @param string $file
747 | * @param string $intent
748 | * @param WPMDBPro_Media_Files_Base $wpmdbmf
749 | *
750 | * @return string
751 | */
752 | public function filter_mf_file_to_download( $file, $intent, $wpmdbmf ) {
753 | $blog_id = $this->selected_subsite( $wpmdbmf );
754 |
755 | if ( 1 > $blog_id ) {
756 | return $file;
757 | }
758 |
759 | if ( is_multisite() && 'pull' === $intent &&
760 | $this->state_data['site_details']['local']['is_multisite'] !== $this->state_data['site_details']['remote']['is_multisite'] &&
761 | ! empty( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] )
762 | ) {
763 | $file = substr( $file, strlen( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) );
764 | }
765 |
766 | return $file;
767 | }
768 |
769 | /**
770 | * Should the given file be excluded from removal?
771 | *
772 | * @param bool $value
773 | * @param string $upload_dir
774 | * @param string $short_file_path
775 | * @param WPMDBPro_Media_Files_Base $wpmdbmf
776 | *
777 | * @return bool
778 | */
779 | public function filter_mf_exclude_local_media_file_from_removal( $value, $upload_dir, $short_file_path, $wpmdbmf ) {
780 | // Already excluded, don't override.
781 | if ( $value ) {
782 | return $value;
783 | }
784 |
785 | $blog_id = $this->selected_subsite( $wpmdbmf );
786 |
787 | if ( 1 > $blog_id ) {
788 | return $value;
789 | }
790 |
791 | if ( is_multisite() && 'pull' === $this->state_data['intent'] &&
792 | ! empty( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['basedir'] )
793 | ) {
794 | $file_given = $upload_dir . $short_file_path;
795 | $file_munged = trailingslashit( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['basedir'] ) . substr( $short_file_path, strlen( $this->state_data['site_details']['local']['subsites_info'][ $blog_id ]['uploads']['short_basedir'] ) );
796 |
797 | if ( $file_given !== $file_munged ) {
798 | $value = true;
799 | }
800 | }
801 |
802 | return $value;
803 | }
804 |
805 | /**
806 | * Handler for "wpmdbmf_include_subsite" filter to disallow subsite's media to be migrated if not selected.
807 | *
808 | * @param bool $value
809 | * @param int $blog_id
810 | * @param WPMDBPro_Media_Files_Base $wpmdbmf
811 | *
812 | * @return bool
813 | */
814 | public function include_subsite( $value, $blog_id, $wpmdbmf ) {
815 | $selected_blog_id = $this->selected_subsite( $wpmdbmf );
816 |
817 | if ( 1 > $selected_blog_id ) {
818 | return $value;
819 | }
820 |
821 | if ( $blog_id !== $selected_blog_id ) {
822 | $value = false;
823 | }
824 |
825 | return $value;
826 | }
827 |
828 | /**
829 | * Maybe change options keys to be preserved.
830 | *
831 | * @param array $preserved_options
832 | * @param string $intent
833 | *
834 | * @return array
835 | */
836 | public function filter_preserved_options( $preserved_options, $intent = '' ) {
837 | $blog_id = $this->selected_subsite();
838 |
839 | if ( 1 > $blog_id || 'push' !== $intent ) {
840 | return $preserved_options;
841 | }
842 |
843 | $keep_active_plugins = $this->profile_value( 'keep_active_plugins' );
844 |
845 | if ( empty( $keep_active_plugins ) ) {
846 | $preserved_options[] = 'active_plugins';
847 | }
848 |
849 | return $preserved_options;
850 | }
851 |
852 | /**
853 | * Maybe preserve the WPMDB plugins if they aren't already preserved.
854 | *
855 | * @param array $preserved_options_data
856 | * @param string $intent
857 | *
858 | * @return array
859 | */
860 | public function filter_preserved_options_data( $preserved_options_data, $intent = '' ) {
861 | $blog_id = $this->selected_subsite();
862 |
863 | $keep_active_plugins = $this->profile_value( 'keep_active_plugins' );
864 |
865 | if ( 1 > $blog_id || 'push' !== $intent || ! empty( $keep_active_plugins ) ) {
866 | return $preserved_options_data;
867 | }
868 |
869 | if ( ! empty( $preserved_options_data ) ) {
870 | foreach ( $preserved_options_data as $table => $data ) {
871 | foreach ( $data as $key => $option ) {
872 | if ( 'active_plugins' === $option['option_name'] ) {
873 | global $wpdb;
874 |
875 | $table_name = esc_sql( $table );
876 | $option_value = unserialize( $option['option_value'] );
877 | $migrated_plugins = array();
878 | $wpmdb_plugins = array();
879 |
880 | if ( $result = $wpdb->get_var( "SELECT option_value FROM $table_name WHERE option_name = 'active_plugins'" ) ) {
881 | $unserialized = unserialize( $result );
882 | if ( is_array( $unserialized ) ) {
883 | $migrated_plugins = $unserialized;
884 | }
885 | }
886 |
887 | foreach ( $option_value as $plugin_key => $plugin ) {
888 | if ( 0 === strpos( $plugin, 'wp-migrate-db' ) ) {
889 | $wpmdb_plugins[] = $plugin;
890 | }
891 | }
892 |
893 | $merged_plugins = array_unique( array_merge( $wpmdb_plugins, $migrated_plugins ) );
894 | $option['option_value'] = serialize( $merged_plugins );
895 | $preserved_options_data[ $table ][ $key ] = $option;
896 | break;
897 | }
898 | }
899 | }
900 | }
901 |
902 | return $preserved_options_data;
903 | }
904 |
905 | /**
906 | * Append more queries to be run at finalize_migration.
907 | *
908 | * @param array $queries
909 | *
910 | * @return array
911 | */
912 | public function filter_get_alter_queries( $queries ) {
913 | $blog_id = $this->selected_subsite();
914 |
915 | if ( 1 > $blog_id ) {
916 | return $queries;
917 | }
918 |
919 | if ( is_multisite() && 'pull' === $this->state_data['intent'] && ! empty( $this->state_data['tables'] ) ) {
920 | global $wpdb;
921 |
922 | $tables = explode( ',', $this->state_data['tables'] );
923 |
924 | $target_users_table = null;
925 | $source_users_table = null;
926 | $target_usermeta_table = null;
927 | $source_usermeta_table = null;
928 | $posts_imported = false;
929 | $target_posts_table = null;
930 | $comments_imported = false;
931 | $target_comments_table = null;
932 | foreach ( $tables as $table ) {
933 | if ( empty( $source_users_table ) && $this->wpmdbpro->table_is( 'users', $table ) ) {
934 | $target_users_table = $table;
935 | $source_users_table = $this->filter_finalize_target_table_name( $table, $this->state_data['intent'], $this->state_data['site_details'] );
936 | continue;
937 | }
938 | if ( empty( $source_usermeta_table ) && $this->wpmdbpro->table_is( 'usermeta', $table ) ) {
939 | $target_usermeta_table = $table;
940 | $source_usermeta_table = $this->filter_finalize_target_table_name( $table, $this->state_data['intent'], $this->state_data['site_details'] );
941 | continue;
942 | }
943 | if ( ! $posts_imported && $this->wpmdbpro->table_is( 'posts', $table ) ) {
944 | $posts_imported = true;
945 | $target_posts_table = $this->filter_finalize_target_table_name( $table, $this->state_data['intent'], $this->state_data['site_details'] );
946 | continue;
947 | }
948 | if ( ! $comments_imported && $this->wpmdbpro->table_is( 'comments', $table ) ) {
949 | $comments_imported = true;
950 | $target_comments_table = $this->filter_finalize_target_table_name( $table, $this->state_data['intent'], $this->state_data['site_details'] );
951 | continue;
952 | }
953 | }
954 |
955 | // Find users that already exist and update their content to adopt existing user id and remove from import.
956 | if ( ! empty( $source_users_table ) ) {
957 | $updated_user_ids = array();
958 | $temp_prefix = $this->state_data['temp_prefix'];
959 | $temp_source_users_table = $temp_prefix . $source_users_table;
960 |
961 | $sql = "
962 | SELECT source.id AS source_id, target.id AS target_id FROM `{$temp_source_users_table}` AS source, `{$target_users_table}` AS target
963 | WHERE target.user_login = source.user_login
964 | AND target.user_email = source.user_email
965 | ";
966 |
967 | $user_ids_to_update = $wpdb->get_results( $sql, ARRAY_A );
968 |
969 | if ( ! empty( $user_ids_to_update ) ) {
970 | foreach ( $user_ids_to_update as $user_ids ) {
971 | $blogs_of_user = get_blogs_of_user( $user_ids['target_id'] );
972 |
973 | if ( empty( $blogs_of_user ) || array_key_exists( $blog_id, $blogs_of_user ) ) {
974 | // Only update content ownership if user id has changed.
975 | if ( $user_ids['source_id'] !== $user_ids['target_id'] ) {
976 | if ( $posts_imported ) {
977 | $queries[]['query'] = "
978 | UPDATE `{$target_posts_table}`
979 | SET post_author = {$user_ids['target_id']}
980 | WHERE post_author = {$user_ids['source_id']}
981 | ;\n
982 | ";
983 | }
984 |
985 | if ( $comments_imported ) {
986 | $queries[]['query'] = "
987 | UPDATE `{$target_comments_table}`
988 | SET user_id = {$user_ids['target_id']}
989 | WHERE user_id = {$user_ids['source_id']}
990 | ;\n
991 | ";
992 | }
993 | }
994 |
995 | // Log user for exclusion from import.
996 | $updated_user_ids[] = $user_ids['source_id'];
997 | }
998 | }
999 | }
1000 |
1001 | $queries[]['query'] = "ALTER TABLE `{$target_users_table}` ADD COLUMN wpmdb_user_id BIGINT(20) UNSIGNED;\n";
1002 |
1003 | $where = '';
1004 | if ( ! empty( $updated_user_ids ) ) {
1005 | $where = 'WHERE u2.id NOT IN (' . join( ',', $updated_user_ids ) . ')';
1006 | }
1007 | $queries[]['query'] = "INSERT INTO `{$target_users_table}` (user_login, user_pass, user_nicename, user_email, user_url, user_registered, user_activation_key, user_status, display_name, wpmdb_user_id)
1008 | SELECT u2.user_login, u2.user_pass, u2.user_nicename, u2.user_email, u2.user_url, u2.user_registered, u2.user_activation_key, u2.user_status, u2.display_name, u2.id
1009 | FROM `{$source_users_table}` AS u2
1010 | {$where};\n";
1011 |
1012 | if ( ! empty( $source_usermeta_table ) ) {
1013 | $queries[]['query'] = "INSERT INTO `{$target_usermeta_table}` (user_id, meta_key, meta_value)
1014 | SELECT u.id, m2.meta_key, m2.meta_value
1015 | FROM `{$source_usermeta_table}` AS m2
1016 | JOIN `{$target_users_table}` AS u ON m2.user_id = u.wpmdb_user_id;\n";
1017 | }
1018 |
1019 | if ( $posts_imported ) {
1020 | $queries[]['query'] = "
1021 | UPDATE `{$target_posts_table}` AS p, `{$target_users_table}` AS u
1022 | SET p.post_author = u.id
1023 | WHERE p.post_author = u.wpmdb_user_id
1024 | ;\n";
1025 | }
1026 |
1027 | if ( $comments_imported ) {
1028 | $queries[]['query'] = "
1029 | UPDATE `{$target_comments_table}` AS c, `{$target_users_table}` AS u
1030 | SET c.user_id = u.id
1031 | WHERE c.user_id = u.wpmdb_user_id
1032 | ;\n";
1033 | }
1034 | $queries[]['query'] = "DROP TABLE `{$source_users_table}`;\n";
1035 |
1036 | $queries[]['query'] = "ALTER TABLE `{$target_users_table}` DROP COLUMN wpmdb_user_id;\n";
1037 | }
1038 |
1039 | // Cleanup imported usermeta table, whether used by above user related queries or not.
1040 | // TODO: Maybe support updating usermeta without imported users table?
1041 | if ( ! empty( $source_usermeta_table ) ) {
1042 | $queries[]['query'] = "DROP TABLE `{$source_usermeta_table}`;\n";
1043 | }
1044 | }
1045 |
1046 | return $queries;
1047 | }
1048 |
1049 | /**
1050 | * Does the passed subsite (ID) exist?
1051 | *
1052 | * @param int $blog_id
1053 | *
1054 | * @return bool
1055 | */
1056 | public function subsite_exists( $blog_id ) {
1057 | if ( ! is_multisite() ) {
1058 | return false;
1059 | }
1060 |
1061 | $blogs = wp_get_sites( array( 'limit' => 0 ) );
1062 |
1063 | if ( empty( $blogs ) ) {
1064 | return false;
1065 | }
1066 |
1067 | foreach ( $blogs as $blog ) {
1068 | if ( ! empty( $blog['blog_id'] ) && $blog_id == $blog['blog_id'] ) {
1069 | return true;
1070 | }
1071 | }
1072 |
1073 | return false;
1074 | }
1075 | }
1076 |
--------------------------------------------------------------------------------
/languages/wp-migrate-db-pro-multisite-tools-en.pot:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: wp-migrate-db-pro-multisite-tools\n"
10 | "Report-Msgid-Bugs-To: nom@deliciousbrains.com\n"
11 | "POT-Creation-Date: 2016-07-11 18:30-0700\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=CHARSET\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: class/cli/wpmdbpro-multisite-tools-cli.php:49
21 | msgid ""
22 | "The installation must be a Multisite network to make use of the subsite "
23 | "option"
24 | msgstr ""
25 |
26 | #: class/cli/wpmdbpro-multisite-tools-cli.php:52
27 | #: class/cli/wpmdbpro-multisite-tools-cli.php:57
28 | msgid ""
29 | "A valid Blog ID or Subsite URL must be supplied to make use of the subsite "
30 | "option"
31 | msgstr ""
32 |
33 | #: class/cli/wpmdbpro-multisite-tools-cli.php:68
34 | msgid "A new table name prefix may only be specified for subsite exports."
35 | msgstr ""
36 |
37 | #: class/cli/wpmdbpro-multisite-tools-cli.php:71
38 | msgid "A valid prefix must be supplied to make use of the prefix option"
39 | msgstr ""
40 |
41 | #: class/wpmdbpro-multisite-tools.php:234
42 | msgid "Migration failed"
43 | msgstr ""
44 |
45 | #: class/wpmdbpro-multisite-tools.php:235
46 | msgid "Please select a subsite."
47 | msgstr ""
48 |
49 | #: class/wpmdbpro-multisite-tools.php:236
50 | msgid "Please enter a new table prefix."
51 | msgstr ""
52 |
53 | #: class/wpmdbpro-multisite-tools.php:237
54 | msgid ""
55 | "Please only enter letters, numbers or underscores for the new table prefix."
56 | msgstr ""
57 |
58 | #: class/wpmdbpro-multisite-tools.php:238
59 | msgid "Export a subsite as a single site install"
60 | msgstr ""
61 |
62 | #: class/wpmdbpro-multisite-tools.php:239
63 | msgid "Pull into a specific subsite"
64 | msgstr ""
65 |
66 | #: class/wpmdbpro-multisite-tools.php:240
67 | msgid "Push a specific subsite"
68 | msgstr ""
69 |
70 | #: template/migrate.php:28
71 | msgid "Select a subsite"
72 | msgstr ""
73 |
74 | #: template/migrate.php:46
75 | msgid "New Table Name Prefix"
76 | msgstr ""
77 |
78 | #: template/migrate.php:48
79 | msgid "New Prefix"
80 | msgstr ""
81 |
82 | #: template/migrate.php:55
83 | msgid "Addon Missing"
84 | msgstr ""
85 |
86 | #: template/migrate.php:55
87 | msgid ""
88 | "The Multisite Tools addon is inactive on the remote site. "
89 | "Please install and activate it to enable subsite migrations."
90 | msgstr ""
91 |
92 | #: template/migrate.php:59
93 | msgid "Version Mismatch"
94 | msgstr ""
95 |
96 | #: template/migrate.php:59
97 | #, php-format
98 | msgid ""
99 | "We have detected you have version "
100 | "of WP Migrate DB Pro Multisite Tools at but are using %1$s here. Please go to the Plugins page on both installs and check for updates."
103 | msgstr ""
104 |
105 | #: template/migrate.php:63
106 | msgid "Different Table Prefixes"
107 | msgstr ""
108 |
109 | #: template/migrate.php:63
110 | #, php-format
111 | msgid ""
112 | "We have detected you have table prefix \""
113 | "span>\" at but have \"%1$s\" "
114 | "here. Multisite Tools currently only supports migrating subsites between "
115 | "sites with the same base table prefix."
116 | msgstr ""
117 |
--------------------------------------------------------------------------------
/template/migrate.php:
--------------------------------------------------------------------------------
1 | is_valid_licence() ) {
3 | global $loaded_profile, $wpdb;
4 | $table_prefix = $wpdb->base_prefix;
5 |
6 | if ( isset( $loaded_profile['multisite_subsite_export'] ) ) {
7 | $loaded_profile['mst_select_subsite'] = $loaded_profile['multisite_subsite_export'];
8 | }
9 |
10 | if ( isset( $loaded_profile['select_subsite'] ) ) {
11 | $loaded_profile['mst_selected_subsite'] = $loaded_profile['select_subsite'];
12 | }
13 | ?>
14 |
15 |
16 |
22 |
23 |
24 |
43 |
44 |
45 |
51 |
52 |
53 |
54 |
55 | — remote site. Please install and activate it to enable subsite migrations.', 'wp-migrate-db-pro-multisite-tools' ); ?>
56 |
57 |
58 |
59 | — of WP Migrate DB Pro Multisite Tools at but are using %1$s here. Please go to the Plugins page on both installs and check for updates.', 'wp-migrate-db-pro-multisite-tools' ), $GLOBALS['wpmdb_meta'][ $this->plugin_slug ]['version'], network_admin_url( 'plugins.php' ) ); ?>
60 |
61 |
62 |
63 | — " at but have "%1$s" here. Multisite Tools currently only supports migrating subsites between sites with the same base table prefix.', 'wp-migrate-db-pro-multisite-tools' ), $table_prefix ); ?>
64 |