/
upgrade_v3.php
2196 lines (1825 loc) · 86.9 KB
/
upgrade_v3.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/*
* @copyright 2020 Mautic Contributors. All rights reserved
* @author Mautic
*
* @link http://mautic.org
*
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*
* NOTE: This upgrade script is specifically for upgrading Mautic 2.16.3+ to Mautic 3.0.0 (or later patch releases). It can only be started in standalone mode!
*/
@ini_set('display_errors', 'Off');
if (stristr(PHP_OS, 'WIN')) {
define('IS_WINDOWS', true);
}
define('IN_CLI', php_sapi_name() === 'cli');
// Enable PHP errors only in CLI
if (IN_CLI === true) {
@ini_set('display_errors', 'STDOUT');
}
// Try to set max_execution_time to 240 if it's lower currently. If this fails we'll throw an error later in the preUpgradeChecks.
$minExecutionTime = 240;
if (defined('IS_WINDOWS')) {
// Cache creation is slower on Windows; set an even higher minExecutionTime.
$minExecutionTime = 600;
}
$maxExecutionTime = (int) ini_get('max_execution_time');
if ($maxExecutionTime > 0 && $maxExecutionTime < $minExecutionTime) {
@ini_set('max_execution_time', $minExecutionTime);
}
// Try to set the memory_limit to 256M if it's lower currently. If this fails we'll throw an error later in the preUpgradeChecks.
$minMemoryLimit = '256M';
if (defined('IS_WINDOWS')) {
// In our testing, during CLI upgrades, 256M was not enough on Windows
$minMemoryLimit = '512M';
}
$minMemoryLimitBytes = returnBytes($minMemoryLimit);
$memoryLimit = ini_get('memory_limit');
$memoryLimitBytes = returnBytes($memoryLimit);
if ($memoryLimitBytes > 0 && $memoryLimitBytes < $minMemoryLimitBytes) {
@ini_set('memory_limit', $minMemoryLimit);
}
date_default_timezone_set('UTC');
define('MAUTIC_MINIMUM_PHP', '7.2.21');
define('MAUTIC_MAXIMUM_PHP', '7.3.999');
// We can only run this script in standalone mode, either in the browser or in CLI, due to extensive backwards incompatbile changes.
$standalone = 1;
$task = getVar('task');
define('MAUTIC_ROOT', __DIR__);
define('MAUTIC_APP_ROOT', MAUTIC_ROOT . '/app');
define('MAUTIC_UPGRADE_FOLDER_NAME', 'mautic-3-temp-files');
define('MAUTIC_UPGRADE_ROOT', MAUTIC_ROOT . DIRECTORY_SEPARATOR . MAUTIC_UPGRADE_FOLDER_NAME);
// This value always needs to contain mautic-2-backup-files as we replace that with a unique hashed name later on!
define('MAUTIC_BACKUP_FOLDER_ROOT', MAUTIC_ROOT . DIRECTORY_SEPARATOR . 'mautic-2-backup-files');
define('MAUTIC_POST_UPGRADE_BACKUP_FOLDER_ROOT', MAUTIC_ROOT . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'mautic-2-backup-files');
define('POST_UPGRADE_BACKUP_FILES_REMOVAL_PENDING_FILE', MAUTIC_ROOT . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'm3_upgrade_backup_files_not_removed_yet.txt');
// Get local parameters
$localParameters = get_local_config();
if (file_exists(POST_UPGRADE_BACKUP_FILES_REMOVAL_PENDING_FILE)) {
if (IN_CLI) {
echo "Mautic 3 upgrade complete. Do you want to remove the backup files? We recommend first checking in a browser whether Mautic 3 works as expected. Type \"yes\" to remove: ";
$handle = fopen("php://stdin", "r");
$line = fgets($handle);
if (trim($line) != "yes") {
echo "Ok, we won't do anything for now. "
. "Please note that upgrade_v3.php will remain (publicly) available until you choose to remove the backup files using this script! "
. "Run this script again to be prompted to delete backup files.\n";
exit;
}
fclose($handle);
echo "\n";
echo "Thank you, continuing...\n";
removeBackupFilesAndExit();
} else {
if (!isset($_GET['confirmDeleteBackup'])) {
html_body(
"<p>Mautic 3 upgrade complete. Do you want to remove the backup files? We recommend first checking in a browser whether Mautic 3 works as expected.</p>"
. "<p>Please note that upgrade_v3.php will remain (publicly) available until you choose to remove the backup files using this script! "
. "Run this script again to be prompted to delete backup files.</p>
<a class=\"btn btn-primary\" href=\"?confirmDeleteBackup\">Click here to remove the backup files and continue to Mautic</a>",
true
);
exit;
} else {
removeBackupFilesAndExit();
}
}
// Additional exit, just to be sure
exit;
}
if (!file_exists(MAUTIC_UPGRADE_ROOT)) {
mkdir(MAUTIC_UPGRADE_ROOT);
}
// This value always needs to contain upgrade_log.txt as we replace that with a unique hashed name later on!
define('UPGRADE_LOG_FILE', MAUTIC_ROOT . DIRECTORY_SEPARATOR . 'upgrade_log.txt');
define('POST_UPGRADE_LOG_FILE', MAUTIC_ROOT . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'upgrade_log.txt');
if (!file_exists(UPGRADE_LOG_FILE)) {
file_put_contents(UPGRADE_LOG_FILE, '');
}
if (isset($localParameters['cache_path'])) {
$cacheDir = str_replace('%kernel.root_dir%', MAUTIC_APP_ROOT, $localParameters['cache_path'] . '/prod');
} else {
$cacheDir = MAUTIC_APP_ROOT . '/cache/prod';
}
define('MAUTIC_CACHE_DIR', $cacheDir);
// Data we fetch from a special JSON file to control upgrade behavior, like e.g. the download URL.
$data = make_request('https://updates.mautic.org/upgrade-configs/m2-to-m3.json', 'GET');
$updateData = json_decode($data, true);
/**
* Run pre-upgrade checks. Returns array with keys "warnings" (dismissable) and "errors" (block upgrading)
*
* ==== PRE-UPGRADE CHECKS ====
*
* To ensure a smooth upgrade to 3.0, we check a few things beforehand:
* - PHP version >= 7.2.21 and <= 7.3999
* - Current database driver = pdo_mysql or mysqli (get from existing Mautic config file)
* - MySQL version > 5.7.14 or MariaDB version > 10.1
* - Mautic version > 2.16.3 (this version adds support for upgrading to 3.0)
* - Check if Mautic's root folder is writable by creating + deleting a dummy folder
* - Ensure PHP's max_execution_time is at least 240 (4 mins).
* - Check if Mautic's upgrade kill switch is enabled (the Product team can activate the kill switch if many users fail upgrading)
*
* @return array
*/
function runPreUpgradeChecks()
{
global $updateData;
global $localParameters;
global $minExecutionTime;
global $minMemoryLimit;
global $minMemoryLimitBytes;
// Errors prevent a user from updating.
$preUpgradeErrors = [];
// Warnings can be dismissed
$preUpgradeWarnings = [];
// Are we running the minimum version?
if (version_compare(PHP_VERSION, MAUTIC_MINIMUM_PHP, 'lt')) {
$preUpgradeErrors[] = 'Your server does not meet the minimum PHP requirements. Mautic requires PHP version ' . MAUTIC_MINIMUM_PHP . ' while your server has ' . PHP_VERSION . '. Please contact your host to update your PHP installation.' . "\n";
}
// Are we running a version newer than what Mautic supports?
if (version_compare(PHP_VERSION, MAUTIC_MAXIMUM_PHP, 'gt')) {
$preUpgradeErrors[] = 'Mautic does not support PHP version ' . PHP_VERSION . ' at this time. To use Mautic, you will need to downgrade to an earlier version.' . "\n";
}
// Check database connection and database version
if (!in_array($localParameters['db_driver'], ['pdo_mysql', 'mysqli'])) {
$preUpgradeErrors[] = 'Your database driver is not pdo_mysql or mysqli, which are the only drivers that Mautic supports. Please change your database driver (config/local.php)!';
}
$mysqli = new mysqli($localParameters['db_host'], $localParameters['db_user'], $localParameters['db_password']);
if (mysqli_connect_errno()) {
$preUpgradeErrors[] = 'Could not connect to your database. Please try again or fix your Mautic settings.';
} else {
$dbVersion = $mysqli->server_version;
if (!(($dbVersion >= 50714 && $dbVersion < 100000) || ($dbVersion >= 100100))) {
$preUpgradeErrors[] = 'Your MySQL/MariaDB version is not supported. You need at least MySQL 5.7.14 or MariaDB 10.1 in order to run Mautic 3.';
}
$mysqli->close();
}
// Check if there is a custom configuration for api_rate_limiter_cache in the local.php file (see https://github.com/mautic/mautic/blob/3.x/UPGRADE-3.0.md#configuration)
if (
!empty($localParameters['api_rate_limiter_cache'])
&& !empty($localParameters['api_rate_limiter_cache']['type'])
&& $localParameters['api_rate_limiter_cache']['type'] !== 'file_system'
) {
$preUpgradeWarnings[] = 'You seem to have a custom configuration for the api_rate_limiter_cache setting in local.php. '
. 'This is an advanced feature and we can only support the default file_system type during migration. '
. 'WE WILL UPDATE YOUR CUSTOM CONFIGURATION TO THE DEFAULT file_system ADAPTER TO PREVENT CONFLICTS. '
. 'If you want to keep your custom configuration, you can manually update it after the migration. '
. 'Please check https://github.com/mautic/mautic/blob/3.x/UPGRADE-3.0.md#configuration for details.';
}
// Check if Mautic's root folder is writable
if (!is_writable(MAUTIC_ROOT)) {
$preUpgradeErrors[] = 'Mautic\'s root directory is not writable. We need write access in order to update application files.';
} else {
$folderPermissionError = 'We tried creating and deleting a dummy folder for testing permissions on Mautic\'s root directory, but failed. Please make sure that your webserver has write access on Mautic\'s root folder.';
$dummyFolder = MAUTIC_ROOT . '/upgrade-test-folder-permissions';
if (!mkdir($dummyFolder)) {
$preUpgradeErrors[] = $folderPermissionError;
}
if (!rmdir($dummyFolder)) {
$preUpgradeErrors[] = $folderPermissionError;
}
}
// Check if there are items in the spool/default folder
$spoolFolder = str_replace(
'%kernel.root_dir%',
MAUTIC_APP_ROOT,
$localParameters['mailer_spool_path']
);
$spoolFolder .= DIRECTORY_SEPARATOR . 'default';
if (file_exists($spoolFolder)) {
$data = scandir($spoolFolder);
// We always have . and .. in the array, so only raise a warning if the count > 2
if (count($data) > 2) {
$preUpgradeWarnings[] = 'It looks like there are items in your spool/default foler, which might mean that there are still emails pending to be sent. If you have access to the command line, run "php /path/to/mautic/app/console mautic:email:process --env=prod" to send them before proceeding.';
}
}
// Check free disk space.
$freeDiskSpace = disk_free_space(MAUTIC_ROOT);
$mauticRootFolderSize = 0;
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(MAUTIC_ROOT)) as $file) {
$mauticRootFolderSize += $file->getSize();
}
if (empty($freeDiskSpace) || $mauticRootFolderSize === 0) {
$preUpgradeWarnings[] = 'We cannot seem to check your free disk space or current Mautic installation folder size. Please ensure your free disk space is at least 2x of your current Mautic installation, just to be sure.';
} else {
if (($mauticRootFolderSize * 2) > $freeDiskSpace) {
$preUpgradeWarnings[] = 'You don\'t seem to have enough disk space for the upgrade. Just to be sure, we\'d like to have 2x the size of the current Mautic installation. Current free disk space is ' . $freeDiskSpace . ' bytes, Mautic installation size is ' . $mauticRootFolderSize . ' bytes.';
}
}
// Check Mautic version
if (file_exists(MAUTIC_APP_ROOT . '/release_metadata.json')) {
$preUpgradeErrors[] = 'You already seem to be running Mautic 3.0.0 or newer, so this upgrade script is not relevant to you anymore. Aborting.';
} elseif (file_exists(MAUTIC_APP_ROOT . '/version.txt')) {
// Check if we have the required Mautic version 2.16.3 prior to upgrading.
$version = file_get_contents(MAUTIC_APP_ROOT . '/version.txt');
$version = str_replace("\n", "", $version);
if (!version_compare($version, '2.16.2', '>')) {
$preUpgradeErrors[] = 'You need to have at least Mautic 2.16.3 installed, which supports upgrading to 3.0. Please update to 2.16.3 first.';
}
} else {
$preUpgradeErrors[] = 'We can\'t seem to detect your current Mautic version. Make sure you have a version.txt file in your app folder.';
}
// Check PHP's max_execution_time
$maxExecutionTime = ini_get('max_execution_time');
if ($maxExecutionTime > 0 && $maxExecutionTime < $minExecutionTime) {
$preUpgradeErrors[] = 'PHP max_execution_time needs to be at least ' . $minExecutionTime . ' seconds (' . round($minExecutionTime / 60, 2) . ' minutes) to allow for a successful upgrade (current value is ' .$maxExecutionTime . ' seconds). We tried setting it to this value but weren\'t able to do so. Please contact your host to set this value to ' . $minExecutionTime . ' seconds or higher.';
}
// Check if mysqldump is available on the system for creating a DB backup.
if (!function_exists('exec')) {
$preUpgradeWarnings[] = 'We can\'t make a database backup for you due to restrictions on your system. Only continue if you have your own database backup available!';
} else {
if (is_mysqldump_available() === false) {
$preUpgradeWarnings[] = 'We can\'t work with the database backup mechanism called "mysqldump" on your system. Only continue if you have your own database backup available!';
}
}
if (empty($updateData['mautic3downloadUrl'])) {
$preUpgradeErrors[] = 'There\'s no upgrade package available for download. This might indicate that the Mautic team is working on releasing a new version.
Please see our <a href="' . $updateData['statusPageUrl'] . '" target="_blank">status page</a> for more details.';
}
// If our kill switch is activated, show a clear warning to users prior to upgrading.
if ($updateData['killSwitchActivated'] === true) {
$preUpgradeWarnings[] = '<strong>WARNING</strong>: It looks like many Mautic users are having trouble upgrading to this new version,
please proceed with caution! For all details and to see if you\'re affected, <strong>please see our <a href="' . $updateData['statusPageUrl'] . '" target="_blank">status page</a></strong>.';
}
// Check if there are custom plugins installed
list($areCustomPluginsInstalled, $customPluginsString) = are_custom_plugins_installed();
if ($areCustomPluginsInstalled === true) {
$preUpgradeWarnings[] = 'It looks like you have one or more custom plugins installed (' . $customPluginsString . '). '
. 'Please make sure they are compatible with Mautic 3, otherwise temporarily disable them! '
. 'Incompatible plugins will very likely cause errors in the upgrade process.';
}
// Get the amount of available database migrations
list($success, $data) = get_available_migrations();
if ($success === false) {
$preUpgradeWarnings[] = 'We couldn\'t reliably detect the amount of available database migrations. Please proceed with caution!';
} else {
if (!isset($data[2]) || !isset($data[2][0])) {
$preUpgradeWarnings[] = 'We couldn\'t reliably detect the amount of available database migrations. Please proceed with caution!';
}
}
// Cache creation is a lot slower on Windows; inform the user about this.
if (defined('IS_WINDOWS')) {
$preUpgradeWarnings[] = 'It looks like you\'re running Mautic on Windows. '
. 'The "creating cache" step is very slow on this platform and it might look like it hangs; please be patient in this case.';
}
// Check PHP's memory_limit
$memoryLimit = ini_get('memory_limit');
$memoryLimitBytes = returnBytes($memoryLimit);
if ($memoryLimitBytes > 0 && $memoryLimitBytes < $minMemoryLimitBytes) {
$preUpgradeErrors[] = 'PHP memory_limit needs to be at least ' . $minMemoryLimit . ' to allow for a successful upgrade (current value is ' . $memoryLimit . '). We tried setting it to this value but weren\'t able to do so. Please contact your host to set this value to ' . $minMemoryLimit . ' or higher.';
}
return [
'warnings' => $preUpgradeWarnings,
'errors' => $preUpgradeErrors
];
}
// Web request upgrade
if (!IN_CLI) {
$request = explode('?', $_SERVER['REQUEST_URI'])[0];
$url = "//{$_SERVER['HTTP_HOST']}{$request}";
$query = '';
$maxCount = (!empty($standalone)) ? 25 : 5;
$apiTask = !empty($_GET['apiTask']) ? $_GET['apiTask'] : '';
switch ($apiTask) {
case '':
// If no specific API task is given, we render our HTML.
logUpdateStart();
html_body('');
exit;
case 'preUpgradeChecks':
writeToLog('Running pre-upgrade checks...');
$preUpgradeCheckResults = runPreUpgradeChecks();
$preUpgradeCheckErrors = $preUpgradeCheckResults['errors'];
$preUpgradeCheckWarnings = $preUpgradeCheckResults['warnings'];
$html = "";
$generalRemarks = "<strong>IMPORTANT: you will need to update your cron jobs from app/console to bin/console after the upgrade.<br>
You can already change them now if you want. For instructions, please read our <a target=\"_blank\" href=\"http://mau.tc/m3-upgrade-getting-started\">Mautic 3: Getting Started guide</a>.<br><br>
<u>It's strongly recommended to have a backup before you start upgrading!</u></strong><br><br>";
if (count($preUpgradeCheckErrors) > 0) {
$logData = "One or more errors occurred in the pre-upgrade checks:";
$html .= '<h3>Whoops! You\'re not ready for Mautic 3 (yet)</h3>
<p>The following <strong style="color: red">errors</strong> occurred while checking system compatibility:</p>
<ul style="text-align: left">';
foreach ($preUpgradeCheckErrors as $error) {
$logData .= "\n- " . $error;
$html .= '<li>' . $error . '</li>';
}
$html .= '</ul>';
$logData .= "\n";
writeToLog($logData);
}
if (count($preUpgradeCheckWarnings) > 0) {
$logData = "One or more warnings occurred in the pre-upgrade checks:";
$html .= '<p>The following <strong style="color: orange">warnings</strong> occurred while checking system compatibility:</p><ul style="text-align: left">';
foreach ($preUpgradeCheckWarnings as $warning) {
$logData .= "\n- " . $warning;
$html .= '<li>' . $warning . '</li>';
}
$html .= '</ul>';
$logData .= "\n";
writeToLog($logData);
}
if (count($preUpgradeCheckErrors) === 0 && count($preUpgradeCheckWarnings) > 0) {
// The checkbox doesn't do anything, but is just there to make users aware that they are doing risky things.
$html .= $generalRemarks . '
<div style="text-align: left">
<input type="checkbox" id="forceUpgradeStart" /> <label for="forceUpgradeStart">Yes, I am aware of the warnings above and still want to proceed with the upgrade.</label>
</div><br />
<button class="btn btn-primary" id="startUpgradeButton">Start the upgrade</button>';
}
if (count($preUpgradeCheckErrors) === 0 && count($preUpgradeCheckWarnings) === 0) {
writeToLog("All pre-upgrade checks passed successfully.");
$html .= "<h3>Ready to upgrade ✅</h3>
<br />Your system is compatible with Mautic 3!<br>Do not refresh or stop the process. This may take several minutes.<br><br>
" . $generalRemarks . "
<button class=\"btn btn-primary\" id=\"startUpgradeButton\">Start the upgrade</button>";
}
outputJSON('success', $html);
case 'startUpgrade':
writeToLog("Starting the upgrade...");
sendUpgradeStats('started');
outputJSON('success', 'OK', 'backupDatabase', 'Backing up the database if we can...');
case 'backupDatabase':
// Only do the backup if mysqldump is available, otherwise skip this step.
if (is_mysqldump_available() === true) {
writeToLog("Running database backup...");
list($success, $message) = backup_database();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_DATABASE_BACKUP_FAILED",
"Database backup failed. Your Mautic 2 installation is intact, so you can safely restart the upgrade. Error from mysqldump: " . $message
);
}
writeToLog("Successfully backed up database.");
} else {
writeToLog("Skipping database backup due to mysqldump not being available.");
}
outputJSON('success', 'OK', 'applyV2Migrations', 'Applying Mautic 2 database migrations to ensure all migrations are in place prior to upgrade...');
case 'applyV2Migrations':
writeToLog("Getting the amount of available Mautic 2 database migrations...");
// Get the amount of available database migrations
list($success, $data) = get_available_migrations();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MAUTIC_2_MIGRATIONS_IDENTIFICATION_FAILED",
'We couldn\'t reliably detect the amount of available database migrations. Please try again by refreshing this page.'
);
} else {
if (isset($data[2]) && isset($data[2][0])) {
$availableMigrations = intval($data[2][0]);
if ($availableMigrations > 0) {
writeToLog("Applying Mautic 2 database migrations, " . $availableMigrations . " to go...");
// Apply migrations on the 2.x branch just so we're sure that we have all migrations in place.
list($success, $output) = apply_single_migration();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MAUTIC_2_MIGRATIONS_FAILED",
"Oh no! While preparing the upgrade, the so-called 'database migrations' for Mautic 2 have failed. "
. "Command output: " . $output
);
};
$availableMigrations--;
if ($availableMigrations > 0) {
// There are still more than 0 available migrations left, so we need to re-run this script...
outputJSON(
'success',
'OK',
'applyV2Migrations',
'Applying Mautic 2 database migrations to ensure all migrations are in place prior to upgrade... ' . $availableMigrations . ' to go...'
);
} else {
writeToLog("All Mautic 2 migrations applied successfully.");
outputJSON('success', 'OK', 'fetchUpdates', 'Downloading Mautic 3 upgrade package...');
}
} else {
writeToLog("No available database migrations found. On to the next step...");
outputJSON('success', 'OK', 'fetchUpdates', 'Downloading Mautic 3 upgrade package...');
}
} else {
throwErrorAndWriteToLog(
"ERR_MAUTIC_2_MIGRATIONS_IDENTIFICATION_FAILED",
'We couldn\'t reliably detect the amount of available database migrations. Please try again by refreshing this page.'
);
}
}
break;
case 'fetchUpdates':
writeToLog("Downloading Mautic 3 upgrade package...");
list($success, $message) = fetch_updates();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_DOWNLOAD_UPGRADE_PACKAGE_FAILED",
"Downloading the Mautic 3 upgrade package has failed: " . $message
);
}
writeToLog("Successfully downloaded Mautic 3 upgrade package.");
outputJSON('success', 'OK', 'extractUpdate', 'Extracting Mautic 3 files...');
case 'extractUpdate':
writeToLog("Extracting Mautic 3 files...");
list($success, $message) = extract_package();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_EXTRACT_UPGRADE_PACKAGE_FAILED",
"Error while extracting Mautic 3 files: " . $message
);
}
writeToLog("Mautic 3 files extracted successfully.");
outputJSON(
'success',
'OK',
'moveMautic2and3Files',
'Moving Mautic 2 files to mautic-2-backup-files folder, then moving Mautic 3 files from mautic-3-temp-files to the root folder...'
);
break;
case 'moveMautic2and3Files':
/**
* Move current Mautic 2 files into a temporary directory called "mautic-2-backup-files",
* then move the Mautic 3 files from "mautic-3-temp-files" to the root directory.
*/
writeToLog("Moving Mautic 2 files to mautic-2-backup-files folder, then moving Mautic 3 files from mautic-3-temp-files to the root folder...");
list($success, $message) = replace_mautic_2_with_mautic_3();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_MOVE_MAUTIC_2_AND_3_FILES",
"Error while moving Mautic 2 or 3 files: " . $message
);
}
writeToLog("Successfully moved Mautic 3 files into place!");
outputJSON(
'success',
'OK',
'updateLocalConfig',
'Updating config/local.php with new configuration parameters...'
);
case 'updateLocalConfig':
// Update config/local.php with updated keys.
writeToLog("Updating config/local.php with new configuration parameters...");
list($success, $message) = update_local_config();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_UPDATE_LOCAL_CONFIG",
"Failed updating your configuration in config/local.php: " . $message
);
}
writeToLog("Successfully updated config/local.php.");
outputJSON('success', 'OK', 'applyMigrations', 'Applying Mautic 3 database migrations... This might take a while!');
case 'applyMigrations':
writeToLog("Getting the amount of available Mautic 3 database migrations... This might take a while!");
// Get the amount of available database migrations
list($success, $data) = get_available_migrations();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MAUTIC_3_MIGRATIONS_IDENTIFICATION_FAILED",
'We couldn\'t reliably detect the amount of available database migrations. Please try again by refreshing this page.'
);
} else {
if (isset($data[2]) && isset($data[2][0])) {
$availableMigrations = intval($data[2][0]);
if ($availableMigrations > 0) {
writeToLog("Applying Mautic 3 database migrations, " . $availableMigrations . " to go...");
// Apply migrations one by one.
list($success, $output) = apply_single_migration();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MAUTIC_3_MIGRATIONS_FAILED",
"Oops! We couldn't run the so-called 'database migrations' for Mautic 3. These are crucial for the upgrade to finish, so we can't proceed. "
. "Command output: " . $output
);
};
$availableMigrations--;
if ($availableMigrations > 0) {
// There are still more than 0 available migrations left, so we need to re-run this script...
outputJSON(
'success',
'OK',
'applyMigrations',
'Applying Mautic 3 database migrations... ' . $availableMigrations . ' to go...'
);
} else {
writeToLog("Successfully applied Mautic 3 database migrations");
outputJSON('success', 'OK', 'restoreUserData', 'Restoring user data (plugins/themes/media) from Mautic 2 installation...');
}
} else {
writeToLog("No available database migrations found. On to the next step...");
outputJSON('success', 'OK', 'restoreUserData', 'Restoring user data (plugins/themes/media) from Mautic 2 installation...');
}
} else {
throwErrorAndWriteToLog(
"ERR_MAUTIC_3_MIGRATIONS_IDENTIFICATION_FAILED",
'We couldn\'t reliably detect the amount of available database migrations. Please try again by refreshing this page.'
);
}
}
break;
case 'restoreUserData':
writeToLog("Restoring user data (plugins/themes/media) from Mautic 2 installation...");
// Restore user data like plugins/themes/media from the original Mautic 2 installation to the "fresh" M3 installation
list($success, $message) = restore_user_data();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_RESTORE_USER_DATA_FAILED",
"Failed to restore user data from Mautic 2 installation: " . $message
);
}
writeToLog("Successfully restored user data from Mautic 2 installation.");
outputJSON('success', 'OK', 'cleanupFiles', 'Cleaning up after ourselves...');
case 'cleanupFiles':
// Cleanup some of our installation files that we no longer need.
writeToLog("Cleanup upgrade files...");
if (cleanup_files() === false) {
throwErrorAndWriteToLog(
"ERR_CLEANUP_FILES",
"Oops! We tried cleaning up after ourselves, but it didn\'t work as expected. You should be able to start using Mautic 3 now, though."
);
}
writeToLog("Successfully cleaned up upgrade files.");
outputJSON('success', 'OK', 'buildCache', 'Preparing Mautic 3 cache...');
case 'buildCache':
writeToLog("Building cache for Mautic 3...");
// Build fresh cache for M3.
list($success, $output) = build_cache();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_BUILD_M3_CACHE",
"Failed to build cache for Mautic 3. "
. "All your data has been successfully migrated to Mautic 3, "
. "but this error very likely needs to be fixed before you can start using Mautic 3. "
. "<br><br>Command output: " . $output
);
};
writeToLog("Successfully created cache for Mautic 3");
outputJSON('success', 'OK', 'finished', 'Finishing up...');
break;
case 'finished':
sendUpgradeStats('succeeded');
writeToLog("We're done! Ready to use Mautic 3 :)");
final_cleanup_files();
outputJSON(
'success',
"<h3>We're done! ✅</h3>
<p><strong>You're ready to use Mautic 3!</strong> Don't forget to <a target=\"_blank\" href=\"http://mau.tc/m3-upgrade-getting-started\">update your cron jobs</a> if you haven't done so already.</p>
<p>One last thing: during the upgrade, we created several backup files. Please remove them as soon as possible if you are sure Mautic works as expected. "
. "You can do that by clicking \"Open Mautic 3 without removing backup files\" below, and if everything works, come back here to remove the backup files.</p><br>
<a href=\"" . $localParameters['site_url'] . "\" class=\"btn btn-primary\" target=\"_blank\">Open Mautic 3 without removing backup files</a><br><br>
<a href=\"?confirmDeleteBackup\" class=\"btn btn-primary\">Remove backup files and open Mautic 3</a>"
);
default:
outputJSON('error', 'unknown apiTask given');
}
} else {
// CLI upgrade
// We create this file when we've moved M3 files into place. Users have to restart the script then for Symfony commands to finish successfully.
$m3_phase_2_file = MAUTIC_ROOT . DIRECTORY_SEPARATOR . 'm3_upgrade_pending_phase_2.txt';
if (!file_exists($m3_phase_2_file)) {
echo "Welcome to the Mautic 3 upgrade script! Before we start, we'll run some pre-upgrade checks to make sure your system is compatible.\n";
echo "IMPORTANT: you will need to update your cron jobs from app/console/* to bin/console/* after the upgrade. You can already change them now if you want.\n";
echo "Please type 'yes' if you're ready to start: ";
$handle = fopen("php://stdin", "r");
$line = fgets($handle);
if (trim($line) != 'yes') {
echo "Alright, we'll abort the upgrade now.\n";
exit;
}
fclose($handle);
echo "\n";
echo "Thank you, continuing...\n";
logUpdateStart();
writeToLog("Doing pre-upgrade checks...");
$preUpgradeCheckResults = runPreUpgradeChecks();
$preUpgradeCheckErrors = $preUpgradeCheckResults['errors'];
$preUpgradeCheckWarnings = $preUpgradeCheckResults['warnings'];
if (count($preUpgradeCheckErrors) > 0) {
writeToLog("One or more errors occurred during pre-upgrade checks: \n- " . implode("\n- ", $preUpgradeCheckErrors));
exit;
}
if (count($preUpgradeCheckWarnings) > 0) {
writeToLog("One or more warnings occurred during pre-upgrade checks, please run this script with the --ignore-warnings flag to continue: \n- " . implode("\n- ", $preUpgradeCheckWarnings));
$val = getopt('i', ['ignore-warnings']);
if (empty($val)) {
exit;
}
}
writeToLog("Finished pre-upgrade checks.");
writeToLog("Starting upgrade...");
sendUpgradeStats('started');
if (is_mysqldump_available() === true) {
writeToLog("Backing up your database...");
list($success, $message) = backup_database();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_DATABASE_BACKUP_FAILED",
"Database backup failed. Your Mautic 2 installation is intact, so you can safely restart the upgrade. Error from mysqldump: " . $message
);
} else {
writeToLog("Database backup successfully written to your Mautic root folder.");
}
} else {
writeToLog("Skipping database backup because we can't find mysqldump on your system...");
}
// Run Mautic 2 migrations
checkAndRunMigrationsCLI(2);
writeToLog("Downloading Mautic 3...");
list($success, $message) = fetch_updates();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_DOWNLOAD_UPGRADE_PACKAGE_FAILED",
"Downloading the Mautic 3 upgrade package has failed: " . $message
);
}
writeToLog("Extracting the update package...");
list($success, $message) = extract_package();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_EXTRACT_UPGRADE_PACKAGE_FAILED",
"Error while extracting Mautic 3 files: " . $message
);
}
writeToLog("Extracting done!");
writeToLog("Preparing for phase 2 of the upgrade...");
$result = file_put_contents($m3_phase_2_file, 'READY FOR PHASE 2, RUN php upgrade_v3.php');
if ($result === false) {
sendUpgradeStats('failed');
writeToLog("IMPORTANT: We couldn't prepare for Phase 2 of the upgrade, so we need your help. "
. "In the same folder where upgrade_v3.php is located, create a file called m3_upgrade_pending_phase_2.txt (no contents needed), then run this script again.");
exit;
}
writeToLog("IMPORTANT: NOT DONE YET! Due to the large amount of changes in Composer dependencies, we now need to restart the script to continue. "
. "We've saved your state, so we'll continue where we left off.");
writeToLog("PLEASE RUN php upgrade_v3.php AGAIN TO START PHASE 2 OF THE UPGRADE!");
exit;
}
writeToLog("Welcome to Phase 2 of the Mautic 3 upgrade! We'll continue where we left off.");
writeToLog("Moving Mautic 2 files into mautic-2-backup and moving the Mautic 3 files in place, this might take a while... DO NOT ABORT THE SCRIPT!!!");
/**
* Move current Mautic 2 files into a temporary directory called "mautic-2-backup-files",
* then move the Mautic 3 files from "mautic-3-temp-files" to the root directory.
*/
list($success, $message) = replace_mautic_2_with_mautic_3();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MOVE_MAUTIC_2_AND_3_FILES",
"Error while moving Mautic 2 or 3 files: " . $message
);
}
writeToLog("Done!\n");
writeToLog("Updating your config/local.php with new settings that were changed/introduced in Mautic 3...");
list($success, $message) = update_local_config();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_UPDATE_LOCAL_CONFIG",
"Failed updating your configuration in config/local.php: " . $message
);
}
writeToLog("Done! Your config file has been updated.");
// Run Mautic 3 migrations
checkAndRunMigrationsCLI(3);
writeToLog("Restoring your user data like custom plugins/themes/media from the Mautic 2 installation. This might take a while... DO NOT ABORT THE SCRIPT!!!");
list($success, $message) = restore_user_data();
if (!$success) {
throwErrorAndWriteToLog(
"ERR_RESTORE_USER_DATA_FAILED",
"Failed to restore user data from Mautic 2 installation: " . $message
);
}
writeToLog("Done! Your user data has been restored. Your Mautic 3 installation is ready. Just one more thing:");
writeToLog("Cleaning up installation files that we no longer need...");
if (cleanup_files() === false) {
throwErrorAndWriteToLog(
"ERR_CLEANUP_FILES",
"Oops! We tried cleaning up after ourselves, but it didn\'t work as expected. You should be able to start using Mautic 3 now, though."
);
}
// We only use the Phase 2 file in the CLI version of the upgrade, so we'll delete it here...
if (file_exists($m3_phase_2_file)) {
unlink($m3_phase_2_file);
}
writeToLog("Cleaned up successfully!");
writeToLog("Building cache for Mautic 3...");
list($success, $output) = build_cache();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_BUILD_M3_CACHE",
"Failed to build cache for Mautic 3. "
. "All your data has been successfully migrated to Mautic 3, "
. "but this error very likely needs to be fixed before you can start using Mautic 3. "
. "\n\nCommand output: " . $output
);
};
writeToLog("Done! Cache has been built.");
sendUpgradeStats('succeeded');
writeToLog("We're done! Enjoy using Mautic 3 :)\nDon't forget to update your cron jobs!");
final_cleanup_files();
writeToLog("Do you want to remove the backup files? We recommend first checking in a browser whether Mautic 3 works as expected. Type \"yes\" to remove: ");
$handle = fopen("php://stdin", "r");
$line = fgets($handle);
if (trim($line) != "yes") {
writeToLog(
"Ok, we won't do anything for now. "
. "Please note that upgrade_v3.php will remain (publicly) available until you choose to remove the backup files using this script! "
. "Run this script again to be prompted to delete backup files."
);
exit;
}
fclose($handle);
writeToLog("\nThank you, continuing...");
removeBackupFilesAndExit();
// Exit just to be sure.
exit;
}
/**
* Check if there are any migrations available and run them.
*
* @return void
*/
function checkAndRunMigrationsCLI($mauticMajorVersion, $ignoreFirstLog = false)
{
if ($ignoreFirstLog === false) {
writeToLog("Getting the amount of available Mautic $mauticMajorVersion database migrations... This might take a while!");
}
// Get the amount of available database migrations
list($success, $data) = get_available_migrations();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MAUTIC_" . $mauticMajorVersion . "_MIGRATIONS_IDENTIFICATION_FAILED",
'We couldn\'t reliably detect the amount of available database migrations.'
);
} else {
if (isset($data[2]) && isset($data[2][0])) {
$availableMigrations = intval($data[2][0]);
if ($availableMigrations > 0) {
writeToLog("Applying Mautic $mauticMajorVersion database migrations, " . $availableMigrations . " migrations in total... This might take a while!");
// Apply all migrations.
list($success, $output) = apply_migrations();
if ($success === false) {
throwErrorAndWriteToLog(
"ERR_MAUTIC_" . $mauticMajorVersion . "_MIGRATIONS_FAILED",
"Something went wrong while applying Mautic " . $mauticMajorVersion . " migrations. "
. "Please try to run app/console doctrine:migrations:migrate --no-interaction --env=prod --no-debug, to troubleshoot further. "
. "Then run this script again."
. "\n\nCommand output: " . $output
);
};
writeToLog("All Mautic $mauticMajorVersion migrations applied successfully.");
} else {
writeToLog("No available database migrations found. On to the next step...");
}
} else {
throwErrorAndWriteToLog(
"ERR_MAUTIC_" . $mauticMajorVersion . "_MIGRATIONS_IDENTIFICATION_FAILED",
'We couldn\'t reliably detect the amount of available database migrations.'
);
}
}
}
/**
* Get local parameters.
*
* @return mixed
*/
function get_local_config()
{
static $parameters;
if (null === $parameters) {
// Used in paths.php
$root = MAUTIC_APP_ROOT;
/** @var array $paths */
include MAUTIC_APP_ROOT . '/config/paths.php';
// Include local config to get cache_path
$localConfig = str_replace('%kernel.root_dir%', MAUTIC_APP_ROOT, $paths['local_config']);
/** @var array $parameters */
include $localConfig;
$localParameters = $parameters;
//check for parameter overrides
if (file_exists(MAUTIC_APP_ROOT . '/config/parameters_local.php')) {
/** @var $parameters */
include MAUTIC_APP_ROOT . '/config/parameters_local.php';
$localParameters = array_merge($localParameters, $parameters);
}
foreach ($localParameters as $k => &$v) {
if (!empty($v) && is_string($v) && preg_match('/getenv\((.*?)\)/', $v, $match)) {
$v = (string) getenv($match[1]);
}
}
$parameters = $localParameters;
}
return $parameters;
}
/**
* Throw an error. In the CLI, we echo it, in the UI we output it in the browser.
* This function also automatically generates a link to Mautic's documentation.
*