PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.1.5
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.1.5
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 5.2.1 5.2.2 5.3 All 153 releases
mainwp / class / class-mainwp-install.php

class-mainwp-install.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.1.5, at class/class-mainwp-install.php

741 lines 29.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Install
4 *
5 * This file handles install MainWP DB.
6 *
7 * @package MainWP/Dashboard
8 */
9
10 namespace MainWP\Dashboard;
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17
18 /**
19 * Class MainWP_Install
20 *
21 * @package MainWP\Dashboard
22 *
23 * @uses \MainWP\Dashboard\MainWP_DB_Base
24 */
25 class MainWP_Install extends MainWP_DB_Base { // phpcs:ignore Generic.Classes.OpeningBraceSameLine.ContentAfterBrace -- NOSONAR.
26
27 // phpcs:disable WordPress.DB.RestrictedFunctions, WordPress.DB.PreparedSQL.NotPrepared -- unprepared SQL ok, accessing the database directly to custom database functions.
28
29 /**
30 * Private variable to hold the database version info.
31 *
32 * @var string DB version info.
33 */
34 protected $mainwp_db_version = '9.0.2.3'; // NOSONAR - no IP. Bumped for MWP-1566 sibling-dir chmod migration (MWP-1558 follow-up). Original 9.0.2.0 bump for MWP-1557/1558.
35
36 /**
37 * Protected variable to hold the database option name.
38 *
39 * @var string DB version info.
40 */
41 protected $option_db_key = 'mainwp_db_version';
42
43 /**
44 * Private static variable to hold the single instance of the class.
45 *
46 * @static
47 *
48 * @var mixed Default null
49 */
50 private static $instance = null;
51
52 /**
53 * Method instance()
54 *
55 * Return public static instance.
56 *
57 * @static
58 * @return MainWP_DB
59 */
60 public static function instance() {
61 if ( null === static::$instance ) {
62 static::$instance = new self();
63 }
64
65 static::$instance->test_connection();
66
67 return static::$instance;
68 }
69
70 /**
71 * Method install()
72 *
73 * Installs the new DB.
74 *
75 * @return void
76 *
77 * @uses \MainWP\Dashboard\MainWP_Utility::update_option()
78 */
79 public function install() { // phpcs:ignore -- NOSONAR - complex function. Current complexity is the only way to achieve desired results, pull request solutions appreciated.
80 // get_site_option is multisite aware!
81 $currentVersion = get_site_option( $this->option_db_key );
82
83 if ( empty( $currentVersion ) ) {
84 update_option( 'mainwp_run_quick_setup', 'yes' );
85 MainWP_Utility::update_option( 'mainwp_enableLegacyBackupFeature', 0 );
86 } elseif ( false === get_option( 'mainwp_enableLegacyBackupFeature' ) ) {
87 MainWP_Utility::update_option( 'mainwp_enableLegacyBackupFeature', 1 );
88 }
89
90 if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.8', '<' ) ) {
91 MainWP_Utility::update_option( 'mainwp_selected_theme', 'default' );
92 }
93
94 $wp_table = esc_sql( $this->table_name( 'wp' ) );
95 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
96 $rslt = static::instance()->query( "SHOW TABLES LIKE '{$wp_table}'" );
97 if ( empty( static::num_rows( $rslt ) ) ) {
98 $currentVersion = false;
99 }
100
101 if ( $currentVersion === $this->mainwp_db_version ) {
102 return;
103 }
104
105 $this->pre_update_tables();
106
107 $charset_collate = $this->wpdb->get_charset_collate();
108
109 $sql = array();
110 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp' ) . " (
111 id int(11) NOT NULL auto_increment,
112 userid int(11) NOT NULL,
113 adminname text NOT NULL,
114 name text NOT NULL,
115 url text NOT NULL,
116 pubkey text NOT NULL,
117 privkey text NOT NULL,
118 siteurl text NOT NULL,
119 ga_id text NOT NULL,
120 gas_id int(11) NOT NULL,
121 offline_checks_last int(11) NOT NULL,
122 offline_check_result int(11) NOT NULL,
123 http_response_code int(11) NOT NULL DEFAULT 0,
124 http_code_noticed tinyint(1) NOT NULL DEFAULT 1,
125 disable_health_check tinyint(1) NOT NULL DEFAULT 0,
126 health_threshold int(11) NOT NULL DEFAULT 0,
127 note text NOT NULL,
128 statsUpdate int(11) NOT NULL,
129 directories longtext NOT NULL,
130 plugin_upgrades longtext NOT NULL,
131 theme_upgrades longtext NOT NULL,
132 translation_upgrades longtext NOT NULL,
133 premium_upgrades longtext NOT NULL,
134 securityIssues longtext NOT NULL,
135 themes longtext NOT NULL,
136 ignored_themes longtext NOT NULL,
137 plugins longtext NOT NULL,
138 ignored_plugins longtext NOT NULL,
139 users longtext NOT NULL,
140 categories longtext NOT NULL,
141 pluginDir text NOT NULL,
142 automatic_update tinyint(1) NOT NULL,
143 backup_before_upgrade tinyint(1) NOT NULL DEFAULT 2,
144 mainwpdir tinyint(1) NOT NULL,
145 loadFilesBeforeZip tinyint(1) NOT NULL DEFAULT 1,
146 is_ignoreCoreUpdates tinyint(1) NOT NULL DEFAULT 0,
147 is_ignorePluginUpdates tinyint(1) NOT NULL DEFAULT 0,
148 is_ignoreThemeUpdates tinyint(1) NOT NULL DEFAULT 0,
149 verify_certificate tinyint(1) NOT NULL DEFAULT 1,
150 force_use_ipv4 tinyint(1) NOT NULL DEFAULT 0,
151 ssl_version tinyint(1) NOT NULL DEFAULT 0,
152 ip text NOT NULL DEFAULT '',
153 uniqueId text NOT NULL,
154 maximumFileDescriptorsOverride tinyint(1) NOT NULL DEFAULT 0,
155 maximumFileDescriptorsAuto tinyint(1) NOT NULL DEFAULT 1,
156 maximumFileDescriptors int(11) NOT NULL DEFAULT 150,
157 primary_backup_method varchar(64) NOT NULL DEFAULT '',
158 http_user text NOT NULL DEFAULT '',
159 http_pass text NOT NULL DEFAULT '',
160 wpe tinyint(1) NOT NULL,
161 is_staging tinyint(1) NOT NULL DEFAULT 0,
162 client_id int(11) NOT NULL DEFAULT 0,
163 `suspended` tinyint(1) NOT NULL DEFAULT 0,
164 KEY idx_wp_staging_name_id (is_staging, name(191), id),
165 KEY idx_userid (userid),
166 KEY idx_client_id (client_id),
167 KEY idx_url (url(191))";
168
169 if ( empty( $currentVersion ) ) {
170 $tbl .= ',
171 PRIMARY KEY (id) ';
172 }
173 $tbl .= ') ' . $charset_collate;
174 $sql[] = $tbl;
175
176 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_sync' ) . " (
177 sync_id int(11) NOT NULL auto_increment,
178 wpid int(11) NOT NULL,
179 version text NOT NULL DEFAULT '',
180 sync_errors longtext NOT NULL DEFAULT '',
181 uptodate longtext NOT NULL DEFAULT '',
182 dtsAutomaticSync int(11) NOT NULL DEFAULT 0,
183 dtsAutomaticSyncStart int(11) NOT NULL DEFAULT 0,
184 dtsSync int(11) NOT NULL DEFAULT 0,
185 dtsSyncStart int(11) NOT NULL DEFAULT 0,
186 totalsize int(11) NOT NULL DEFAULT 0,
187 dbsize int(11) NOT NULL DEFAULT 0,
188 extauth text NOT NULL DEFAULT '',
189 last_post_gmt int(11) NOT NULL DEFAULT 0,
190 health_value int(11) NOT NULL DEFAULT 0,
191 health_status tinyint(1) NOT NULL DEFAULT 0,
192 health_site_noticed tinyint(1) NOT NULL DEFAULT 1,
193 KEY idx_wpid (wpid)";
194
195 if ( empty( $currentVersion ) ) {
196 $tbl .= ',
197 PRIMARY KEY (sync_id)';
198 }
199
200 $tbl .= ') ' . $charset_collate;
201
202 $sql[] = $tbl;
203
204 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_options' ) . " (
205 opt_id int(11) NOT NULL auto_increment,
206 wpid int(11) NOT NULL,
207 name text NOT NULL DEFAULT '',
208 value longtext NOT NULL DEFAULT '',
209 KEY idx_options_wpid_name (wpid, name(191)),
210 KEY idx_wpid (wpid)";
211
212 if ( empty( $currentVersion ) ) {
213 $tbl .= ',
214 PRIMARY KEY (opt_id)';
215 }
216 $tbl .= ') ' . $charset_collate;
217 $sql[] = $tbl;
218
219 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_settings_backup' ) . ' (
220 set_id int(11) NOT NULL auto_increment,
221 wpid int(11) NOT NULL,
222 archiveFormat text NOT NULL,
223 KEY idx_wpid (wpid)';
224
225 if ( empty( $currentVersion ) ) {
226 $tbl .= ',
227 PRIMARY KEY (set_id)';
228 }
229 $tbl .= ') ' . $charset_collate;
230 $sql[] = $tbl;
231
232 $tbl = 'CREATE TABLE ' . $this->table_name( 'users' ) . " (
233 userid int(11) NOT NULL,
234 user_email text NOT NULL DEFAULT '',
235 ignored_plugins longtext NOT NULL DEFAULT '',
236 trusted_plugins longtext NOT NULL DEFAULT '',
237 trusted_plugins_notes longtext NOT NULL DEFAULT '',
238 ignored_themes longtext NOT NULL DEFAULT '',
239 ignored_wp_upgrades longtext NOT NULL DEFAULT '',
240 trusted_themes longtext NOT NULL DEFAULT '',
241 trusted_themes_notes longtext NOT NULL DEFAULT '',
242 site_view tinyint(1) NOT NULL DEFAULT '0',
243 pluginDir text NOT NULL DEFAULT '',
244 dismissed_plugins longtext NOT NULL DEFAULT '',
245 dismissed_themes longtext NOT NULL DEFAULT ''";
246 if ( empty( $currentVersion ) ) {
247 $tbl .= ',
248 PRIMARY KEY (userid) ';
249 }
250 $tbl .= ') ' . $charset_collate;
251 $sql[] = $tbl;
252
253 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_status' ) . ' (
254 statusid bigint(20) unsigned NOT NULL auto_increment,
255 wpid int(11) NOT NULL,
256 http_code smallint NOT NULL DEFAULT 0,
257 status tinyint(1) NOT NULL DEFAULT 0,
258 event_timestamp int(11) NOT NULL,
259 duration int(11) NOT NULL DEFAULT 0';
260 if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.31', '<=' ) ) {
261 $tbl .= ',
262 PRIMARY KEY (statusid) ';
263 }
264 $tbl .= ') ' . $charset_collate;
265 $sql[] = $tbl;
266
267 $tbl = 'CREATE TABLE ' . $this->table_name( 'group' ) . ' (
268 id int(11) NOT NULL auto_increment,
269 userid int(11) NOT NULL,
270 name text NOT NULL,
271 color varchar(32) NOT NULL DEFAULT ""';
272 if ( empty( $currentVersion ) ) {
273 $tbl .= ',
274 PRIMARY KEY (id) ';
275 }
276 $tbl .= ') ' . $charset_collate;
277 $sql[] = $tbl;
278
279 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_group' ) . ' (
280 wp_group_id int(11) NOT NULL auto_increment,
281 wpid int(11) NOT NULL,
282 groupid int(11) NOT NULL,
283 KEY idx_wpid (wpid),
284 KEY idx_groupid (groupid)';
285 if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.57', '<=' ) ) {
286 $tbl .= ',
287 PRIMARY KEY (wp_group_id) ';
288 }
289 $tbl .= ') ' . $charset_collate;
290 $sql[] = $tbl;
291
292 $tbl = 'CREATE TABLE ' . $this->table_name( 'lookup_item_objects' ) . ' (
293 lookup_id bigint(20) unsigned NOT NULL auto_increment,
294 item_id bigint(20) unsigned NOT NULL,
295 item_name varchar(32) NOT NULL,
296 object_id bigint(20) unsigned NOT NULL,
297 object_name varchar(32) NOT NULL,
298 KEY item_id (item_id),
299 KEY object_id (object_id)';
300
301 if ( empty( $currentVersion ) || version_compare( $currentVersion, '9.0.0.5', '<' ) ) { // NOSONAR - none IP.
302 $tbl .= ',
303 PRIMARY KEY (lookup_id) ';
304 }
305 $tbl .= ') ' . $charset_collate . ';';
306 $sql[] = $tbl;
307
308 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_backup_progress' ) . " (
309 task_id int(11) NOT NULL,
310 wp_id int(11) NOT NULL,
311 dtsFetched int(11) NOT NULL DEFAULT 0,
312 fetchResult text NOT NULL DEFAULT '',
313 downloadedDB text NOT NULL DEFAULT '',
314 downloadedFULL text NOT NULL DEFAULT '',
315 downloadedDBComplete tinyint(1) NOT NULL DEFAULT 0,
316 downloadedFULLComplete tinyint(1) NOT NULL DEFAULT 0,
317 removedFiles tinyint(1) NOT NULL DEFAULT 0,
318 attempts int(11) NOT NULL DEFAULT 0,
319 last_error text NOT NULL DEFAULT '',
320 pid int(11) NOT NULL DEFAULT 0,
321 KEY idx_task_id (task_id)";
322 if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.53', '<=' ) ) {
323 $tbl .= ',
324 UNIQUE (task_id)';
325 }
326 $tbl .= ') ' . $charset_collate;
327 $sql[] = $tbl;
328
329 $tbl = 'CREATE TABLE ' . $this->table_name( 'wp_backup' ) . ' (
330 id int(11) NOT NULL auto_increment,
331 userid int(11) NOT NULL,
332 name text NOT NULL,
333 schedule text NOT NULL,
334 type text NOT NULL,
335 exclude text NOT NULL,
336 sites text NOT NULL,
337 `groups` text NOT NULL,
338 last int(11) NOT NULL,
339 last_run int(11) NOT NULL,
340 lastStartNotificationSent int(11) NOT NULL DEFAULT 0,
341 last_run_manually int(11) NOT NULL,
342 completed_sites text NOT NULL,
343 completed int(11) NOT NULL,
344 backup_errors text NOT NULL,
345 subfolder text NOT NULL,
346 filename text NOT NULL,
347 paused tinyint(1) NOT NULL,
348 template tinyint(1) DEFAULT 0,
349 excludebackup tinyint(1) DEFAULT 0,
350 excludecache tinyint(1) DEFAULT 0,
351 excludenonwp tinyint(1) DEFAULT 0,
352 excludezip tinyint(1) DEFAULT 0,
353 archiveFormat text NOT NULL,
354 loadFilesBeforeZip tinyint(1) NOT NULL DEFAULT 1,
355 maximumFileDescriptorsOverride tinyint(1) NOT NULL DEFAULT 0,
356 maximumFileDescriptorsAuto tinyint(1) NOT NULL DEFAULT 1,
357 maximumFileDescriptors int(11) NOT NULL DEFAULT 150';
358 if ( empty( $currentVersion ) ) {
359 $tbl .= ',
360 PRIMARY KEY (id) ';
361 }
362 $tbl .= ') ' . $charset_collate;
363 $sql[] = $tbl;
364
365 $tbl = 'CREATE TABLE ' . $this->table_name( 'api_keys' ) . ' (
366 key_id bigint(20) unsigned NOT NULL auto_increment,
367 user_id bigint(20) unsigned NOT NULL,
368 description varchar(200) NULL,
369 permissions varchar(10) NOT NULL,
370 consumer_key char(64) NOT NULL,
371 consumer_secret varchar(255) NOT NULL,
372 nonces longtext NULL,
373 truncated_key char(7) NOT NULL,
374 key_pass char(64) NOT NULL DEFAULT "",
375 key_type tinyint(1) NOT NULL DEFAULT 0,
376 `enabled` tinyint(1) DEFAULT 0,
377 last_access datetime NULL default null,
378 KEY consumer_key (consumer_key)';
379 if ( empty( $currentVersion ) || version_compare( $currentVersion, '9.0.0.9', '<=' ) ) { // NOSONAR - none IP.
380 $tbl .= ',
381 PRIMARY KEY (key_id) ';
382 }
383 $tbl .= ') ' . $charset_collate . ';';
384 $sql[] = $tbl;
385
386 $tbl = 'CREATE TABLE ' . $this->table_name( 'action_log' ) . " (
387 id int(11) NOT NULL auto_increment,
388 log_content mediumtext NOT NULL DEFAULT '',
389 log_type tinyint(1) DEFAULT 0,
390 log_color tinyint(1) DEFAULT 0,
391 log_user varchar(128) NOT NULL DEFAULT '',
392 log_timestamp int(11) NOT NULL DEFAULT 0";
393 if ( empty( $currentVersion ) || version_compare( $currentVersion, '8.50', '<=' ) ) {
394 $tbl .= ',
395 PRIMARY KEY (id) ';
396 }
397 $tbl .= ') ' . $charset_collate . ';';
398 $sql[] = $tbl;
399
400 $tbl = 'CREATE TABLE ' . $this->table_name( 'request_log' ) . " (
401 id int(11) NOT NULL auto_increment,
402 wpid int(11) NOT NULL,
403 ip text NOT NULL DEFAULT '',
404 subnet text NOT NULL DEFAULT '',
405 micro_timestamp_stop DECIMAL( 12, 2 ) NOT NULL DEFAULT 0,
406 micro_timestamp_start DECIMAL( 12, 2 ) NOT NULL DEFAULT 0";
407 if ( empty( $currentVersion ) || version_compare( $currentVersion, '5.7', '<=' ) ) {
408 $tbl .= ',
409 PRIMARY KEY (id) ';
410 }
411 $tbl .= ') ' . $charset_collate . ';';
412 $sql[] = $tbl;
413
414 $tbl = 'CREATE TABLE ' . $this->table_name( 'schedule_processes' ) . " (
415 process_id int(11) NOT NULL auto_increment,
416 item_id int(11) NOT NULL,
417 `type` varchar(32) NOT NULL,
418 `process_slug` varchar(64) NOT NULL,
419 `status` varchar(32) NOT NULL DEFAULT '',
420 dts_process_start int(11) NOT NULL DEFAULT 0,
421 dts_process_init_time int(11) NOT NULL DEFAULT 0,
422 dts_process_stop int(11) NOT NULL DEFAULT 0";
423
424 if ( empty( $currentVersion ) || version_compare( $currentVersion, '9.0.0.45', '<' ) ) { //phpcs:ignore -- NOSONAR - no ip.
425 $tbl .= ',
426 PRIMARY KEY (process_id) ';
427 }
428
429 $tbl .= ') ' . $charset_collate . ';';
430 $sql[] = $tbl;
431
432 // End of tables.
433
434 $sql = apply_filters( 'mainwp_db_install_tables', $sql, $currentVersion, $charset_collate );
435
436 MainWP_DB_Uptime_Monitoring::instance()->get_db_schema( $sql, $currentVersion );
437
438 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // NOSONAR - WP compatible.
439
440 global $wpdb;
441
442 if ( MainWP_Utility::instance()->is_disabled_functions( 'error_log' ) || ! function_exists( '\error_log' ) ) {
443 error_reporting(0); // phpcs:ignore -- try to disabled the error_log somewhere in WP.
444 }
445
446 $suppress = $wpdb->suppress_errors();
447 foreach ( $sql as $query ) {
448 dbDelta( $query );
449 }
450 $wpdb->suppress_errors( $suppress );
451
452 $this->post_update();
453
454 do_action( 'mainwp_db_after_update', $currentVersion, $this->mainwp_db_version ); // new version: $this->mainwp_db_version.
455
456 if ( ! is_multisite() ) {
457 MainWP_Utility::update_option( $this->option_db_key, $this->mainwp_db_version );
458 } else {
459 update_site_option( $this->option_db_key, $this->mainwp_db_version );
460 }
461 }
462
463 /**
464 * Returns the database version.
465 *
466 * @return string
467 */
468 public function get_db_version() {
469 return get_site_option( $this->option_db_key );
470 }
471
472 /**
473 * Method post_update()
474 *
475 * Update MainWP DB.
476 *
477 * @return void
478 */
479 public function post_update() { // phpcs:ignore -- NOSONAR - complex.
480
481 // get_site_option is multisite aware!
482 $currentVersion = get_site_option( $this->option_db_key );
483
484 if ( false === $currentVersion ) {
485 return;
486 }
487
488 $suppress = $this->wpdb->suppress_errors();
489
490 $this->post_update_81( $currentVersion );
491
492 if ( version_compare( $currentVersion, '8.984', '<' ) ) {
493 $sslColumns = array(
494 'nossl',
495 'nosslkey',
496 );
497 $wp_table = esc_sql( $this->table_name( 'wp' ) );
498 foreach ( $sslColumns as $col ) {
499 $col = esc_sql( $col );
500 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
501 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$col}" );
502 }
503 }
504
505 // delete old columns.
506 if ( version_compare( $currentVersion, '8.17', '<' ) ) {
507 $rankColumns = array(
508 'pagerank',
509 'indexed',
510 'alexia',
511 'pagerank_old',
512 'indexed_old',
513 'alexia_old',
514 'last_db_backup_size',
515 );
516
517 foreach ( $rankColumns as $rankColumn ) {
518 $rankColumn = esc_sql( $rankColumn );
519 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
520 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$rankColumn}" );
521 }
522
523 $syncColumns = array( 'uptodate' );
524 $wp_sync_table = esc_sql( $this->table_name( 'wp_sync' ) );
525 foreach ( $syncColumns as $column ) {
526 $column = esc_sql( $column );
527 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
528 $this->wpdb->query( "ALTER TABLE {$wp_sync_table} DROP COLUMN {$column}" );
529 }
530 }
531
532 // delete old columns.
533 if ( version_compare( $currentVersion, '8.35', '<' ) ) {
534 $delColumns = array( 'offline_checks' );
535 foreach ( $delColumns as $column ) {
536 $column = esc_sql( $column );
537 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
538 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$column}" );
539 }
540 $delColumns = array( 'heatMap' );
541 $users_table = esc_sql( $this->table_name( 'users' ) );
542 foreach ( $delColumns as $column ) {
543 $column = esc_sql( $column );
544 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
545 $this->wpdb->query( "ALTER TABLE {$users_table} DROP COLUMN {$column}" );
546 }
547 }
548
549 // delete columns.
550 if ( version_compare( $currentVersion, '8.42', '<' ) ) {
551 $delColumns = array( 'offlineChecksOnlineNotification' );
552 foreach ( $delColumns as $column ) {
553 $column = esc_sql( $column );
554 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
555 $this->wpdb->query( "ALTER TABLE {$users_table} DROP COLUMN {$column}" );
556 }
557 }
558
559 // fix missing PRIMARY keys.
560 if ( version_compare( $currentVersion, '8.53', '<=' ) ) {
561 $wp_options_table = esc_sql( $this->table_name( 'wp_options' ) );
562 $wp_settings_backup_table = esc_sql( $this->table_name( 'wp_settings_backup' ) );
563 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
564 $this->wpdb->query( "ALTER TABLE {$wp_options_table} ADD opt_id int NOT NULL AUTO_INCREMENT PRIMARY KEY" );
565 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
566 $this->wpdb->query( "ALTER TABLE {$wp_settings_backup_table} ADD set_id int NOT NULL AUTO_INCREMENT PRIMARY KEY" );
567 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql.
568 $this->wpdb->query( "ALTER TABLE {$wp_sync_table} ADD sync_id int NOT NULL AUTO_INCREMENT PRIMARY KEY" );
569 }
570
571 $this->update_optimize_indexes_55( $currentVersion );
572
573 $this->wpdb->suppress_errors( $suppress );
574 MainWP_DB_Client::instance()->check_to_updates_reports_data_861( $currentVersion );
575 }
576
577 /**
578 * Handle optimize tables indexes.
579 *
580 * @param string $current_ver Current DB version.
581 *
582 * @return void
583 */
584 public function update_optimize_indexes_55( $current_ver ) {
585 if ( ! empty( $current_ver ) && version_compare( $current_ver, '9.0.1.1', '<' ) ) { // NOSONAR - no ip.
586 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_wp_staging_name_id (is_staging, name(191), id)' ); //phpcs:ignore -- ok.
587 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp_options' ) . ' ADD INDEX KEY idx_options_wpid_name (wpid, name(191))' ); //phpcs:ignore -- ok.
588 }
589 if ( ! empty( $current_ver ) && version_compare( $current_ver, '9.0.1.3', '<' ) ) { // NOSONAR - no ip.
590 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_userid (userid)' ); //phpcs:ignore -- ok.
591 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_client_id (client_id)' ); //phpcs:ignore -- ok.
592 $this->wpdb->query( 'ALTER TABLE ' . $this->table_name( 'wp' ) . ' ADD INDEX idx_url (url(191))' ); //phpcs:ignore -- ok.
593 }
594 // MWP-1540: widen consumer_secret so wp_hash_password output (variable length,
595 // typically 34 chars for $P$ but up to ~150 for argon2) fits without truncation,
596 // and drop the unused KEY consumer_secret index (lookups are by HMAC'd consumer_key,
597 // never by consumer_secret). dbDelta does not reliably MODIFY existing column types
598 // or DROP indexes, so both changes are applied explicitly here.
599 if ( ! empty( $current_ver ) && version_compare( $current_ver, '9.0.1.5', '<' ) ) { // NOSONAR - no ip.
600 $api_keys_table = $this->table_name( 'api_keys' );
601 // Drop the unused index FIRST so the column type change is unambiguous.
602 $existing_indexes = $this->wpdb->get_col( "SHOW INDEX FROM {$api_keys_table} WHERE Key_name = 'consumer_secret'", 2 ); // phpcs:ignore -- table name is internal.
603 if ( ! empty( $existing_indexes ) ) {
604 $this->wpdb->query( "ALTER TABLE {$api_keys_table} DROP INDEX consumer_secret" ); // phpcs:ignore -- table name is internal.
605 }
606 $this->wpdb->query( "ALTER TABLE {$api_keys_table} MODIFY COLUMN consumer_secret varchar(255) NOT NULL" ); // phpcs:ignore -- table name is internal.
607
608 // Confirm the MODIFY actually took effect before we trust this migration.
609 // WP 6.5+ produces bcrypt hashes around 60 characters, so a silent failure
610 // would leave the column at char(43) and truncate every freshly hashed
611 // secret on insert, quietly breaking auth on any newly created key. On a
612 // width mismatch we set a flag option that the dashboard surfaces as an
613 // admin notice; mainwp_notice_wp_mail_failed in class-mainwp-system.php
614 // uses the same shape.
615 $col_def = $this->wpdb->get_row( "SHOW COLUMNS FROM {$api_keys_table} LIKE 'consumer_secret'" ); // phpcs:ignore -- table name is internal.
616 if ( empty( $col_def ) || false === stripos( (string) $col_def->Type, 'varchar(255)' ) ) {
617 update_option( 'mainwp_notice_consumer_secret_migration_failed', current_time( 'mysql' ) );
618 } else {
619 delete_option( 'mainwp_notice_consumer_secret_migration_failed' );
620 }
621 }
622 }
623
624 /**
625 * Method pre_update_tables()
626 *
627 * Handle pre update tables.
628 *
629 * @return void
630 */
631 public function pre_update_tables() {
632 // get_site_option is multisite aware!
633 $currentVersion = get_site_option( $this->option_db_key );
634
635 if ( false === $currentVersion ) {
636 return;
637 }
638
639 $suppress = $this->wpdb->suppress_errors();
640
641 if ( version_compare( $currentVersion, '8.98', '<=' ) ) {
642 $wp_table = esc_sql( $this->table_name( 'wp' ) );
643 $existing_columns = $this->wpdb->get_col( "SHOW COLUMNS FROM {$wp_table}", 0 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL introspection; table name is a hardcoded internal identifier escaped via esc_sql(), no user input involved.
644 foreach ( array( 'backups', 'note_lastupdate', 'pages' ) as $column ) {
645 if ( in_array( $column, $existing_columns, true ) ) {
646 $this->wpdb->query( 'ALTER TABLE ' . $wp_table . ' DROP COLUMN ' . esc_sql( $column ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- DDL statement; table and column names are hardcoded internal identifiers escaped via esc_sql(), no user input involved.
647 }
648 }
649 }
650
651 $this->wpdb->suppress_errors( $suppress );
652 }
653
654 /**
655 * Method post_update_81()
656 *
657 * Update MainWP DB for version 8.1.
658 *
659 * @param string $current_version Current version DB.
660 *
661 * @return void
662 */
663 public function post_update_81( $current_version ) { //phpcs:ignore -- NOSONAR - complex.
664
665 if ( version_compare( $current_version, '8.1', '<' ) ) {
666
667 // We can't split up here!
668 $wpSyncColumns = array(
669 'version',
670 'totalsize',
671 'dbsize',
672 'extauth',
673 'last_post_gmt',
674 'sync_errors',
675 'dtsSync',
676 'dtsSyncStart',
677 'dtsAutomaticSync',
678 'dtsAutomaticSyncStart',
679 );
680 $wp_table = esc_sql( $this->table_name( 'wp' ) );
681 $wp_sync_table = esc_sql( $this->table_name( 'wp_sync' ) );
682 foreach ( $wpSyncColumns as $wpSyncColumn ) {
683 $wpSyncColumn = esc_sql( $wpSyncColumn );
684 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
685 $rslts = $this->wpdb->get_results( "SELECT id,{$wpSyncColumn} FROM {$wp_table}", ARRAY_A );
686 if ( empty( $rslts ) ) {
687 continue;
688 }
689
690 foreach ( $rslts as $rslt ) {
691 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name escaped via esc_sql and query uses proper prepare.
692 $exists = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT wpid FROM {$wp_sync_table} WHERE wpid = %d", $rslt['id'] ), ARRAY_A );
693 if ( empty( $exists ) ) {
694 $this->wpdb->insert(
695 $this->table_name( 'wp_sync' ),
696 array(
697 'wpid' => $rslt['id'],
698 $wpSyncColumn => $rslt[ $wpSyncColumn ],
699 )
700 );
701 } else {
702 $this->wpdb->update( $this->table_name( 'wp_sync' ), array( $wpSyncColumn => $rslt[ $wpSyncColumn ] ), array( 'wpid' => $rslt['id'] ) );
703 }
704 }
705
706 $suppress = $this->wpdb->suppress_errors();
707 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
708 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$wpSyncColumn}" );
709 $this->wpdb->suppress_errors( $suppress );
710 }
711
712 $optionColumns = array(
713 'last_wp_upgrades',
714 'last_plugin_upgrades',
715 'last_theme_upgrades',
716 'wp_upgrades',
717 'recent_comments',
718 'recent_posts',
719 'recent_pages',
720 );
721 foreach ( $optionColumns as $optionColumn ) {
722 $optionColumn = esc_sql( $optionColumn );
723 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
724 $rslts = $this->wpdb->get_results( "SELECT id,{$optionColumn} FROM {$wp_table}", ARRAY_A );
725 if ( empty( $rslts ) ) {
726 continue;
727 }
728
729 foreach ( $rslts as $rslt ) {
730 static::update_website_option( (object) $rslt, $optionColumn, $rslt[ $optionColumn ] );
731 }
732
733 $suppress = $this->wpdb->suppress_errors();
734 // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Column name escaped via esc_sql, table name escaped separately.
735 $this->wpdb->query( "ALTER TABLE {$wp_table} DROP COLUMN {$optionColumn}" );
736 $this->wpdb->suppress_errors( $suppress );
737 }
738 }
739 }
740 }
741