PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.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 6.2.1, at pages/page-mainwp-install-bulk.php

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