PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 5.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v5.2.1
6.2.3 6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 All 156 releases
mainwp / pages / page-mainwp-install-bulk.php

page-mainwp-install-bulk.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 5.2.1, at pages/page-mainwp-install-bulk.php

810 lines 36.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This file handles all of the Bulk Installation Methods
4 * for Plugins & Themes.
5 *
6 * @package MainWP/Dashboard
7 */
8
9 namespace MainWP\Dashboard;
10
11 /**
12 * Class MainWP_Install_Bulk
13 *
14 * @used-by MainWP_Plugins::InstallPlugins
15 * @used-by MainWP_Themes::InstallThemes
16 */
17 class MainWP_Install_Bulk { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
18
19 /**
20 * Get Class Name
21 *
22 * @return string __CLASS__
23 *
24 * @uses static::init()
25 */
26 public static function get_class_name() {
27 return __CLASS__;
28 }
29
30 /**
31 * Method init()
32 *
33 * Instantiate the main page
34 *
35 * Has to be called in System constructor,
36 * adds handling for the main page.
37 */
38 public static function init() {
39 add_action( 'admin_init', array( static::get_class_name(), 'admin_init' ) );
40 }
41
42 /**
43 * Method admin_init()
44 *
45 * Handles the uploading of a file.
46 *
47 * @uses \MainWP\Dashboard\MainWP_QQ2_File_Uploader
48 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_mainwp_specific_dir()
49 */
50 public static function admin_init() {
51 if ( isset( $_REQUEST['mainwp_do'] ) && isset( $_REQUEST['qq_nonce'] ) && 'MainWP_Install_Bulk-uploadfile' === $_REQUEST['mainwp_do'] ) { // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
52 do_action( 'mainwp_secure_request', 'qq_nonce', 'qq_nonce' );
53 $allowedExtensions = array( 'zip' ); // Only zip allowed.
54 // max file size in bytes.
55 $sizeLimit = 2 * 1024 * 1024; // 2MB = max allowed.
56
57 $uploader = new MainWP_QQ2_File_Uploader( $allowedExtensions, $sizeLimit );
58 $path = MainWP_System_Utility::get_mainwp_specific_dir( 'bulk' );
59
60 $result = $uploader->handle_upload( $path, true );
61 // to pass data through iframe you will need to encode all html tags.
62 die( htmlspecialchars( wp_json_encode( $result ), ENT_NOQUOTES ) ); // phpcs:ignore WordPress.Security.EscapeOutput
63 }
64 }
65
66
67 /**
68 * Method render_upload()
69 *
70 * Renders the upload sub part.
71 *
72 * @param string $type Plugin|Theme Type of upload.
73 */
74 public static function render_upload( $type ) {
75 $title = ( 'plugin' === $type ) ? 'Plugins' : 'Themes';
76 $favorites_enabled = is_plugin_active( 'mainwp-favorites-extension/mainwp-favorites-extension.php' );
77 $cls = $favorites_enabled ? 'favorites-extension-enabled ' : '';
78 $cls .= ( 'plugin' === $type ) ? 'qq-upload-plugins' : '';
79
80 $disabled_upload = false;
81 $disabled_functions = ini_get( 'disable_functions' );
82 if ( ! empty( $disabled_functions ) ) {
83 $disabled_functions_array = explode( ',', $disabled_functions );
84 if ( is_array( $disabled_functions_array ) && in_array( 'tmpfile', $disabled_functions_array ) ) {
85 $disabled_upload = true;
86 }
87 }
88
89 if ( $disabled_upload ) {
90 ?>
91 <div class="ui red message">
92 <div><?php esc_html_e( 'MainWP has detected that the tmpfile() PHP function is disabled on your server. This function is essential for uploading and installing zip files for plugins and themes. Please enable the tmpfile() function on your server to proceed. Contact your hosting provider for assistance if needed.', 'mainwp' ); ?></div>
93 </div>
94
95 <div class="ui secondary center aligned padded segment">
96 <h2 class="ui icon header">
97 <i class="file archive outline icon" style="color: #ddd"></i>
98 <div class="content" style="color: #ddd">
99 <?php esc_html_e( 'Upload .zip File', 'mainwp' ); ?>
100 <div class="sub header" style="color: #ddd"><?php esc_html_e( 'If you have', 'mainwp' ); ?> <?php echo esc_html( strtolower( $title ) ); ?> <?php esc_html_e( 'in a .zip format, you may install it by uploading it here.', 'mainwp' ); ?></div>
101 </div>
102 </h2>
103
104 <div class="ui hidden divider"></div>
105
106 <div class="ui center aligned segment">
107 <div class="ui labeled icon massive green button disabled">
108 <i class="upload icon"></i>
109 <?php esc_html_e( 'Upload Now', 'mainwp' ); ?>
110 </div>
111 </div>
112
113 </div>
114 <?php
115 } else {
116 ?>
117 <div class="ui secondary center aligned padded segment">
118 <h2 class="ui icon header">
119 <i class="file archive outline icon"></i>
120 <div class="content">
121 <?php esc_html_e( 'Upload .zip File', 'mainwp' ); ?>
122 <div class="sub header"><?php esc_html_e( 'If you have', 'mainwp' ); ?> <?php echo esc_html( strtolower( $title ) ); ?> <?php esc_html_e( 'in a .zip format, you may install it by uploading it here.', 'mainwp' ); ?></div>
123 </div>
124 </h2>
125
126 <div class="ui hidden divider"></div>
127
128 <div id="mainwp-file-uploader" class="<?php echo esc_attr( $cls ); ?>" >
129 <noscript>
130 <div class="ui message red"><?php esc_html_e( 'Please enable JavaScript to use file uploader.', 'mainwp' ); ?></div>
131 </noscript>
132
133 <div class="dropzone" id="mainwp-dropzone-upload" >
134 <div class="dz-clickable-area">
135 <div class="ui labeled icon massive green button qq-upload-button" style="position: relative; overflow: hidden; direction: ltr;">
136 <i class="upload icon"></i> <?php esc_html_e( 'Upload Now', 'mainwp' ); ?>
137 <div style="position: absolute; right: 0px; top: 0px; font-family: Arial; font-size: 118px; margin: 0px; padding: 0px; cursor: pointer; opacity: 0;"></div>
138 </div>
139 <div class="qq-upload-drop-area" id="qq-upload-drop-area">
140 <div class="dz-message">
141 <?php esc_html_e( 'Drop files here to upload', 'mainwp' ); ?>
142 </div>
143 </div>
144 </div>
145 <div class="ui hidden divider"></div>
146 </div>
147
148 </div>
149
150 <div id="mainwp-dropzone-preview-tpl" style="display: none">
151 <div class="item file-uploaded-item" style="padding:0!important;">
152 <div class="ui grid" style="margin:0!important;">
153 <div class="four column row">
154 <div class="left aligned middle aligned column dz-filename"><span class="qq-upload-file" filename="" data-dz-name=""></span></div>
155 <div class="middle aligned column">
156 <span class="qq-upload-percent"></span>
157 <span class="qq-upload-size dz-size" data-dz-size=""></span>
158 </div>
159 <div class="middle aligned column ">
160 <div class="dz-error-message"><span data-dz-errormessage=""></span></div>
161 <span class="qq-upload-processing">
162 <i class="notched circle loading icon"></i> Uploading...
163 </span>
164 </div>
165 <div class="right aligned middle aligned column">
166 <a class="ui mini button basic red qq-upload-cancel" href="#">Cancel Upload</a>
167 <?php echo $favorites_enabled ? '<span class="qq-upload-add-to-favorites" style="display:none;"><a class="ui mini button basic" href="#">Add to Favorites</a></span>' : ''; ?>
168 <a class="ui mini button basic red qq-upload-cancel-install" style="display:none;" href="#">Remove Item</a>
169 </div>
170 </div>
171 </div>
172 </div>
173 </div>
174 <?php
175
176 $chunksize_upload = apply_filters( 'mainwp_file_uploader_chunk_size', 1000000 ); // to support custom chunk size upload.
177 if ( empty( $chunksize_upload ) ) {
178 $chunksize_upload = 1000000; // bytes.
179 }
180
181 ?>
182 <script type="text/javascript">
183 document.addEventListener("DOMContentLoaded", (event) => {
184 Dropzone.autoDiscover = false;
185 let dropzone = new Dropzone('#mainwp-dropzone-upload', {
186 url: function(){
187 return 'admin.php?page=<?php echo isset( $_GET['page'] ) ? esc_js( sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified. ?>';
188 },
189 paramName: 'qqfile', // file input name.
190 autoProcessQueue : true,
191 clickable: '.dz-clickable-area',
192 previewTemplate: document.querySelector('#mainwp-dropzone-preview-tpl').innerHTML,
193 parallelUploads: 3,
194 chunking: true,
195 chunkSize: <?php echo intval( $chunksize_upload ); ?>,
196 uploadMultiple: false, // 1: to compatible with server side process.
197 acceptedFiles: '.zip',
198 createImageThumbnails: 0,
199 maxFilesize: 150,
200 filesizeBase: 1000,
201 init: function() {
202 let self = this;
203 this.on("addedfile", file => {
204 jQuery(file.previewElement).find('.qq-upload-cancel').on('click',function(){
205 self.removeFile(file); // Remove the specific file from Dropzone
206 return false;
207 });
208 jQuery(file.previewElement).find('.qq-upload-file').attr('filename', file.name );
209 });
210
211 this.on("error", function(file, errorMessage) {
212 jQuery(file.previewElement).find('.qq-upload-processing').hide();
213 });
214 this.on("sending", function(file, xhr, formData) {
215 // Add custom parameters to the FormData object
216 formData.append("mainwp_do", "MainWP_Install_Bulk-uploadfile");
217 formData.append("qq_nonce", "<?php echo esc_js( wp_create_nonce( 'qq_nonce' ) ); ?>");
218 });
219 this.on("uploadprogress", function( file, progress, bytesSent ) {
220 progress = parseInt(progress);
221 jQuery(file.previewElement).find('.qq-upload-percent').html( progress + '% from ');
222 });
223 this.on("success", function( file, result ) {
224 let obj = false;
225 try{
226 obj =JSON.parse(result);
227 } catch( err ){
228 //error parse response.
229 obj = false;
230 }
231
232 file.previewElement.classList.add('qq-upload-completed');
233 jQuery(file.previewElement).find('.qq-upload-cancel-install').show();
234 jQuery(file.previewElement).find('.qq-upload-cancel-install').on('click',function(){
235 self.removeFile(file); // Remove the specific file from Dropzone
236 return false;
237 });
238
239 let unerror = true;
240
241 if( obj ){
242 if(obj.success){
243 file.previewElement.classList.add('qq-upload-success');
244 jQuery(file.previewElement).find('.qq-upload-processing').html('Upload completed.');
245 unerror = false;
246 } else if(obj.error){
247 jQuery(file.previewElement).find('.qq-upload-processing').html('<span data-tooltip="' + obj.error + '" data-inverted="" data-position="top left"><i class="red times icon"></i></span>' );
248 unerror = false;
249 }
250 }
251
252 if(unerror){
253 jQuery(file.previewElement).find('.qq-upload-processing').html('<span data-tooltip="Upload failed. Please try again." data-inverted="" data-position="top left"><i class="red times icon"></i></span>' );
254 }
255 });
256 }
257 });
258 });
259 (function(){
260
261 let dropzoneArea = jQuery('.qq-upload-drop-area');
262 let dropzoneId = 'qq-upload-drop-area';
263
264 window.addEventListener("dragover", function(e) {
265 if (e.target.id != dropzoneId) {
266 e.preventDefault();
267 e.dataTransfer.effectAllowed = "none";
268 e.dataTransfer.dropEffect = "none";
269 }
270 }, false);
271
272 window.addEventListener("dragenter", function(e) {
273 if (e.target.id != dropzoneId) {
274 e.preventDefault();
275 e.dataTransfer.effectAllowed = "none";
276 e.dataTransfer.dropEffect = "none";
277 }
278 }, false);
279
280 window.addEventListener("drop", function(e) {
281 if (e.target.id != dropzoneId) {
282 e.preventDefault();
283 e.dataTransfer.effectAllowed = "none";
284 e.dataTransfer.dropEffect = "none";
285 }
286 });
287
288 jQuery('.dz-clickable-area').on("dragenter", function(event){
289 event.preventDefault();
290 dropzoneArea.show();
291 }).on("dragover", function(event){
292 event.preventDefault();
293 dropzoneArea.show();
294 }).on("drop", function(event){
295 event.preventDefault();
296 dropzoneArea.hide();
297 });
298 })();
299 </script>
300 </div>
301 <?php
302 }
303 }
304
305 /**
306 * Method prepare_install()
307 *
308 * Prepare for the installation.
309 *
310 * Grab all the necessary data to make the upload and prepare json response.
311 *
312 * @uses \MainWP\Dashboard\MainWP_DB::query()
313 * @uses \MainWP\Dashboard\MainWP_DB::get_website_by_id()
314 * @uses \MainWP\Dashboard\MainWP_DB::get_sql_websites_by_group_id()
315 * @uses \MainWP\Dashboard\MainWP_DB::fetch_object()
316 * @uses \MainWP\Dashboard\MainWP_DB::free_result()
317 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_mainwp_dir()
318 * @uses \MainWP\Dashboard\MainWP_Utility::ctype_digit()
319 * @uses \MainWP\Dashboard\MainWP_Utility::map_site()
320 */
321 public static function prepare_install() { // phpcs:ignore -- NOSONAR -Current complexity is the only way to achieve desired results, pull request solutions appreciated.
322 include_once ABSPATH . '/wp-admin/includes/plugin-install.php'; // NOSONAR - WP compatible.
323 // phpcs:disable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
324
325 $type = 'theme';
326 if ( isset( $_POST['type'] ) && 'plugin' === $_POST['type'] ) {
327 $type = 'plugin';
328 }
329 if ( ! isset( $_POST['url'] ) ) {
330 $api = MainWP_System_Utility::get_plugin_theme_info(
331 $type,
332 array(
333 'slug' => isset( $_POST['slug'] ) ? wp_unslash( $_POST['slug'] ) : '',
334 'fields' => array( 'sections' => false ),
335 )
336 ); // Save on a bit of bandwidth.
337
338 $url = $api->download_link;
339 $url = apply_filters( 'mainwp_prepare_install_download_url', $url, $_POST );
340 } else {
341 $url = isset( $_POST['url'] ) ? wp_unslash( $_POST['url'] ) : '';
342
343 $mwpDir = MainWP_System_Utility::get_mainwp_dir();
344 $mwpUrl = $mwpDir[1];
345 if ( stristr( $url, $mwpUrl ) ) {
346 $fullFile = $mwpDir[0] . str_replace( $mwpUrl, '', $url );
347 $url = admin_url( '?sig=' . MainWP_System_Utility::get_download_sig( $fullFile ) . '&mwpdl=' . rawurlencode( str_replace( $mwpDir[0], '', $fullFile ) ) );
348 }
349 }
350
351 $output = array();
352 $output['url'] = $url;
353 $output['slug'] = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : '';
354 $output['name'] = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
355 $output['sites'] = array();
356
357 static::get_selected_sites( $output );
358
359 /**
360 * Filter: mainwp_bulk_prepare_install_result
361 *
362 * Fires after plugin/theme prepare install.
363 *
364 * @since 4.6
365 */
366 $output = apply_filters( 'mainwp_bulk_prepare_install_result', $output, $type );
367 mainwp_send_json_output( $output );
368 }
369
370
371 /**
372 * Method addition_post_data()
373 *
374 * Grab Post addition data.
375 *
376 * @param array $post_data Data for post.
377 *
378 * @return mixed $post_data Bulk post addition data.
379 */
380 public static function addition_post_data( &$post_data = array() ) {
381
382 /**
383 * Clean and Lock extension options
384 *
385 * Adds additional options related to Clean and Lock options in order to avoid conflicts when HTTP Basic auth is set.
386 *
387 * @since Unknown
388 */
389 $clear_and_lock_opts = apply_filters( 'mainwp_clear_and_lock_options', array() );
390 $mwpdl = isset( $post_data['url'] ) && false !== strpos( $post_data['url'], 'mwpdl' ) && false !== strpos( $post_data['url'], 'sig' );
391 if ( $mwpdl && is_array( $clear_and_lock_opts ) && isset( $clear_and_lock_opts['wpadmin_user'] ) && ! empty( $clear_and_lock_opts['wpadmin_user'] ) && isset( $clear_and_lock_opts['wpadmin_passwd'] ) && ! empty( $clear_and_lock_opts['wpadmin_passwd'] ) ) {
392 $post_data['wpadmin_user'] = $clear_and_lock_opts['wpadmin_user'];
393 $post_data['wpadmin_passwd'] = $clear_and_lock_opts['wpadmin_passwd'];
394 }
395 return $post_data;
396 }
397
398 /**
399 * Perform Install.
400 *
401 * @uses \MainWP\Dashboard\MainWP_Connect::fetch_url_authed()
402 * @uses \MainWP\Dashboard\MainWP_DB::get_website_by_id()
403 * @uses \MainWP\Dashboard\MainWP_Utility::end_session()
404 */
405 public static function perform_install() {
406 MainWP_Utility::end_session();
407
408 // phpcs:disable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
409 // Fetch info.
410 $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
411 $post_data = array(
412 'type' => $type,
413 );
414 if ( isset( $_POST['activatePlugin'] ) && 'true' === $_POST['activatePlugin'] ) {
415 $post_data['activatePlugin'] = 'yes';
416 }
417 if ( isset( $_POST['overwrite'] ) && 'true' === $_POST['overwrite'] ) {
418 $post_data['overwrite'] = true;
419 }
420
421 /**
422 * Addition Post Data.
423 *
424 * @param $post_data The post data.
425 * @deprecated From.
426 * @since 3.5.6.
427 */
428 static::addition_post_data( $post_data );
429
430 $site_id = isset( $_POST['siteId'] ) ? intval( $_POST['siteId'] ) : 0;
431 $website = MainWP_DB::instance()->get_website_by_id( $site_id );
432
433 // to support demo data.
434 if ( MainWP_Demo_Handle::get_instance()->is_demo_website( $website ) ) {
435 return MainWP_Demo_Handle::get_instance()->handle_action_demo( $website, 'perform_install' );
436 }
437
438 $websites = array( $website );
439
440 /**
441 * Perform insatallation additional data
442 *
443 * Adds support for additional data such as HTTP User and HTTP Password.
444 *
445 * @param array $post_data Array containg the post data.
446 *
447 * @since Unknown
448 */
449 $post_data = apply_filters( 'mainwp_perform_install_data', $post_data );
450
451 $post_data['url'] = isset( $_POST['url'] ) ? wp_json_encode( wp_unslash( $_POST['url'] ) ) : '';
452
453 $output = new \stdClass();
454 $output->ok = array();
455 $output->errors = array();
456 $output->results = array();
457 $output->other_data = array();
458
459 // phpcs:enable
460 /**
461 * Action: mainwp_before_plugin_theme_install
462 *
463 * Fires before plugin/theme install.
464 *
465 * @since 4.1
466 */
467 do_action( 'mainwp_before_plugin_theme_install', $post_data, $websites );
468
469 MainWP_Connect::fetch_urls_authed(
470 $websites,
471 'installplugintheme',
472 $post_data,
473 array(
474 static::get_class_name(),
475 'install_plugin_theme_handler',
476 ),
477 $output,
478 null,
479 array( 'upgrade' => true )
480 );
481
482 $output_obj = $output;
483
484 mainwp_get_actions_handler_instance()->do_action_mainwp_install_actions( $websites, 'install', $output_obj, $type, $post_data );
485
486 /**
487 * Action: mainwp_after_plugin_theme_install
488 *
489 * Fires after plugin/theme install.
490 *
491 * @since 4.1
492 */
493 do_action( 'mainwp_after_plugin_theme_install', $output, $post_data, $websites );
494
495 wp_send_json( $output );
496 }
497
498 /**
499 * Method prepare_upload()
500 *
501 * Prepare the upload.
502 *
503 * @uses \MainWP\Dashboard\MainWP_DB::query()
504 * @uses \MainWP\Dashboard\MainWP_DB::get_website_by_id()
505 * @uses \MainWP\Dashboard\MainWP_DB::fetch_object()
506 * @uses \MainWP\Dashboard\MainWP_DB::free_result()
507 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_download_url()
508 * @uses \MainWP\Dashboard\MainWP_Utility::ctype_digit()
509 * @uses \MainWP\Dashboard\MainWP_Utility::map_site()
510 */
511 public static function prepare_upload() { // phpcs:ignore -- NOSONAR - comlex function. Current complexity is the only way to achieve desired results, pull request solutions appreciated.
512 include_once ABSPATH . '/wp-admin/includes/plugin-install.php'; // NOSONAR - WP compatible.
513
514 $output = array();
515 $output['sites'] = array();
516
517 static::get_selected_sites( $output );
518
519 // phpcs:disable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
520 $output['urls'] = array();
521 $output['files'] = array();
522 $files = isset( $_POST['files'] ) && is_array( $_POST['files'] ) ? wp_unslash( $_POST['files'] ) : array();
523 foreach ( $files as $file ) {
524 $output['urls'][] = MainWP_System_Utility::get_download_url( 'bulk', $file );
525 $output['files'][] = esc_html( $file );
526 }
527 $output['urls'] = implode( '||', $output['urls'] );
528 // phpcs:enable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
529
530 /**
531 * Prepare upload
532 *
533 * Prepares upload URLs for the bulk install process.
534 *
535 * @since Unknown
536 */
537 $output['urls'] = apply_filters( 'mainwp_installbulk_prepareupload', $output['urls'] );
538
539 wp_send_json( $output );
540 }
541
542 /**
543 * Method prepare_upload()
544 *
545 * @param array $output selected sites output.
546 */
547 public static function get_selected_sites( &$output ) { //phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR - complexity.
548
549 $data_fields = array(
550 'id',
551 'url',
552 'name',
553 'sync_errors',
554 );
555
556 // phpcs:disable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
557 if ( isset( $_POST['selected_by'] ) && 'site' === $_POST['selected_by'] ) {
558 $selected_sites = isset( $_POST['selected_sites'] ) && is_array( $_POST['selected_sites'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['selected_sites'] ) ) : array();
559 // Get sites.
560 foreach ( $selected_sites as $enc_id ) {
561 $websiteid = $enc_id;
562 if ( MainWP_Utility::ctype_digit( $websiteid ) ) {
563 $website = MainWP_DB::instance()->get_website_by_id( $websiteid );
564 $output['sites'][ $website->id ] = MainWP_Utility::map_site(
565 $website,
566 array(
567 'id',
568 'url',
569 'name',
570 )
571 );
572 }
573 }
574 } elseif ( isset( $_POST['selected_by'] ) && 'client' === $_POST['selected_by'] ) {
575 $selected_clients = isset( $_POST['selected_clients'] ) && is_array( $_POST['selected_clients'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['selected_clients'] ) ) : array();
576 $websites = MainWP_DB_Client::instance()->get_websites_by_client_ids(
577 $selected_clients,
578 array(
579 'select_data' => $data_fields,
580 )
581 );
582 // Get sites.
583 if ( $websites ) {
584 foreach ( $websites as $website ) {
585 if ( '' !== $website->sync_errors || MainWP_System_Utility::is_suspended_site( $website ) ) {
586 continue;
587 }
588 $output['sites'][ $website->id ] = MainWP_Utility::map_site(
589 $website,
590 array(
591 'id',
592 'url',
593 'name',
594 )
595 );
596 }
597 }
598 } else {
599 $selected_groups = ( isset( $_POST['selected_groups'] ) && is_array( $_POST['selected_groups'] ) ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['selected_groups'] ) ) : array();
600 // Get sites from group.
601 foreach ( $selected_groups as $enc_id ) {
602 $groupid = $enc_id;
603 if ( MainWP_Utility::ctype_digit( $groupid ) ) {
604 $websites = MainWP_DB::instance()->query( MainWP_DB::instance()->get_sql_websites_by_group_id( $groupid ) );
605 while ( $websites && ( $website = MainWP_DB::fetch_object( $websites ) ) ) {
606 if ( '' !== $website->sync_errors || MainWP_System_Utility::is_suspended_site( $website ) ) {
607 continue;
608 }
609 $output['sites'][ $website->id ] = MainWP_Utility::map_site(
610 $website,
611 array(
612 'id',
613 'url',
614 'name',
615 )
616 );
617 }
618 MainWP_DB::free_result( $websites );
619 }
620 }
621 }
622 // phpcs:enable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
623 }
624
625 /**
626 * Method perform_upload()
627 *
628 * Perform the upload.
629 *
630 * @uses \MainWP\Dashboard\MainWP_Connect::fetch_url_authed()
631 * @uses \MainWP\Dashboard\MainWP_DB::get_website_by_id()
632 * @uses \MainWP\Dashboard\MainWP_Utility::end_session()
633 */
634 public static function perform_upload() {
635 MainWP_Utility::end_session();
636 // phpcs:disable WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
637 $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
638 // Fetch info.
639 $post_data = array(
640 'type' => $type,
641 );
642 if ( isset( $_POST['activatePlugin'] ) && 'true' === $_POST['activatePlugin'] ) {
643 $post_data['activatePlugin'] = 'yes';
644 }
645 if ( isset( $_POST['overwrite'] ) && 'true' === $_POST['overwrite'] ) {
646 $post_data['overwrite'] = true;
647 }
648
649 // deprecated from 3.5.6.
650 static::addition_post_data( $post_data );
651
652 /** This filter is documented in pages/page-mainwp-install-bulk.php */
653 $post_data = apply_filters( 'mainwp_perform_install_data', $post_data );
654
655 $urls = isset( $_POST['urls'] ) ? esc_url_raw( wp_unslash( $_POST['urls'] ) ) : '';
656 $post_data['url'] = wp_json_encode( explode( '||', $urls ) );
657 $site_id = isset( $_POST['siteId'] ) ? intval( $_POST['siteId'] ) : 0;
658 $website = MainWP_DB::instance()->get_website_by_id( $site_id );
659
660 // phpcs:enable
661
662 // to support demo data.
663 if ( MainWP_Demo_Handle::get_instance()->is_demo_website( $website ) ) {
664 return MainWP_Demo_Handle::get_instance()->handle_action_demo( $website, 'perform_upload' );
665 }
666
667 $output = new \stdClass();
668 $output->ok = array();
669 $output->errors = array();
670 $output->results = array();
671 $output->other_data = array();
672 $websites = array( $website );
673
674 /**
675 * Action: mainwp_before_plugin_theme_install
676 *
677 * Fires before plugin/theme install.
678 *
679 * @since 4.1
680 */
681 do_action( 'mainwp_before_plugin_theme_install', $post_data, $websites );
682
683 MainWP_Connect::fetch_urls_authed(
684 $websites,
685 'installplugintheme',
686 $post_data,
687 array(
688 static::get_class_name(),
689 'install_plugin_theme_handler',
690 ),
691 $output,
692 null,
693 array( 'upgrade' => true )
694 );
695 $output_obj = $output;
696 mainwp_get_actions_handler_instance()->do_action_mainwp_install_actions( $websites, 'install', $output_obj, $type, $post_data, true );
697
698 /**
699 * Action: mainwp_after_plugin_theme_install
700 *
701 * Fires after plugin/theme install.
702 *
703 * @since 4.1
704 */
705 do_action( 'mainwp_after_plugin_theme_install', $output, $post_data, $websites, $type );
706
707 /**
708 * Filter: mainwp_bulk_upload_install_result
709 *
710 * Fires after plugin/theme install.
711 *
712 * @since 4.6
713 */
714 $output = apply_filters( 'mainwp_bulk_upload_install_result', $output, $type, $post_data, $websites );
715
716 wp_send_json( $output );
717 }
718
719 /**
720 * Clean the upload
721 *
722 * Do file structure maintenance and tmp file removals.
723 *
724 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_wp_file_system()
725 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_mainwp_specific_dir()
726 */
727 public static function clean_upload() {
728 MainWP_System_Utility::get_wp_file_system();
729
730 /**
731 * WordPress files system object.
732 *
733 * @global object
734 */
735 global $wp_filesystem;
736
737 $path = MainWP_System_Utility::get_mainwp_specific_dir( 'bulk' );
738 if ( $wp_filesystem->exists( $path ) ) {
739 $dh = opendir( $path );
740 if ( $dh ) {
741 while ( false !== ( $file = readdir( $dh ) ) ) {
742 if ( '.' !== $file && '..' !== $file ) {
743 $wp_filesystem->delete( $path . $file );
744 }
745 }
746 closedir( $dh );
747 }
748 }
749
750 die( wp_json_encode( array( 'ok' => true ) ) );
751 }
752
753 /**
754 * Plugin & Theme upload handler.
755 *
756 * @param mixed $data Processing data.
757 * @param object $website The website object.
758 * @param mixed $output Function output.
759 * @param mixed $post_data Post data.
760 *
761 * @return mixed $output->ok[ $website->id ] = array( $website->name )|Error,
762 * Already installed,
763 * Undefined error! Please reinstall the MainWP Child plugin on the child site,
764 * Error while installing.
765 *
766 * @uses \MainWP\Dashboard\MainWP_System_Utility::get_child_response()
767 */
768 public static function install_plugin_theme_handler( $data, $website, &$output, $post_data = array() ) { // phpcs:ignore -- NOSONAR - complex.
769 if ( MainWP_Demo_Handle::get_instance()->is_demo_website( $website ) ) {
770 return;
771 }
772 if ( preg_match( '/<mainwp>(.*)<\/mainwp>/', $data, $results ) > 0 ) {
773
774 $result = $results[1];
775 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- base64_encode used for http encoding compatible.
776 $information = MainWP_System_Utility::get_child_response( base64_decode( $result ) );
777 if ( is_array( $information ) && isset( $information['other_data']['install_items'] ) ) {
778 $output->other_data[ $website->id ] = $information['other_data']; // content: install_items themes/plugins.
779 }
780
781 if ( isset( $information['installation'] ) && 'SUCCESS' === $information['installation'] ) {
782 $output->ok[ $website->id ] = array( $website->name );
783 $output->results[ $website->id ] = isset( $information['install_results'] ) ? $information['install_results'] : array();
784 } elseif ( isset( $information['error'] ) ) {
785 $error = esc_html( $information['error'] );
786 if ( isset( $information['error_code'] ) && 'folder_exists' === $information['error_code'] ) {
787 $error = esc_html__( 'Already installed', 'mainwp' );
788 }
789
790 if ( 'not found' === strtolower( $error ) && is_array( $post_data ) && isset( $post_data['type'] ) ) {
791 if ( 'plugin' === $post_data['type'] ) {
792 $error = esc_html__( 'Plugin file not found. Make sure security plugins or server-side security rules are not blocking requests from your child sites.', 'mainwp' );
793 } elseif ( 'theme' === $post_data['type'] ) {
794 $error = esc_html__( 'Theme file not found. Make sure security plugins or server-side security rules are not blocking requests from your child sites.', 'mainwp' );
795 }
796 }
797
798 $output->errors[ $website->id ] = array( $website->name, $error );
799 } else {
800 $output->errors[ $website->id ] = array(
801 $website->name,
802 __( 'Undefined error! Please reinstall the MainWP Child plugin on the child site', 'mainwp' ),
803 );
804 }
805 } else {
806 $output->errors[ $website->id ] = array( $website->name, 'Error while installing' );
807 }
808 }
809 }
810