completed g7 for mu3 (#5044)
[openemr.git] / sql_upgrade.php
blob11233f67142c5cccebb5f9e46c13d96b84b98dc8
1 <?php
3 /* sql_upgrade.php
5 * This may be run after an upgraded OpenEMR has been installed.
6 * It's purpose is to upgrade the MySQL OpenEMR database as needed
7 * for the new release.
9 * @package OpenEMR
10 * @author Rod Roark <rod@sunsetsystems.com>
11 * @author Brady Miller <brady.g.miller@gmail.com>
12 * @copyright Copyright (c) 2008-2010 Rod Roark <rod@sunsetsystems.com>
13 * @copyright Copyright (c) 2011-2021 Brady Miller <brady.g.miller@gmail.com>
14 * @link https://github.com/openemr/openemr/tree/master
15 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
18 /* @TODO add language selection. needs RTL testing */
20 $GLOBALS['ongoing_sql_upgrade'] = true;
22 if (php_sapi_name() === 'cli') {
23 // setting for when running as command line script
24 // need this for output to be readable when running as command line
25 $GLOBALS['force_simple_sql_upgrade'] = true;
28 // Checks if the server's PHP version is compatible with OpenEMR:
29 require_once(__DIR__ . "/src/Common/Compatibility/Checker.php");
30 $response = OpenEMR\Common\Compatibility\Checker::checkPhpVersion();
31 if ($response !== true) {
32 die(htmlspecialchars($response));
35 @ini_set('zlib.output_compression', 0);
36 @ini_set('implicit_flush', 1);
37 @ob_end_clean();
38 // Disable PHP timeout. This will not work in safe mode.
39 @ini_set('max_execution_time', '0');
40 if (ob_get_level() === 0) {
41 ob_start();
44 $ignoreAuth = true; // no login required
45 $sessionAllowWrite = true;
46 $GLOBALS['connection_pooling_off'] = true; // force off database connection pooling
48 require_once('interface/globals.php');
49 require_once('library/sql_upgrade_fx.php');
51 use OpenEMR\Common\Csrf\CsrfUtils;
52 use OpenEMR\Common\Uuid\UuidRegistry;
53 use OpenEMR\Core\Header;
54 use OpenEMR\Services\Utils\SQLUpgradeService;
55 use OpenEMR\Services\VersionService;
57 // Force logging off
58 $GLOBALS["enable_auditlog"] = 0;
60 $versions = array();
61 $sqldir = "$webserver_root/sql";
62 $dh = opendir($sqldir);
63 if (!$dh) {
64 die("Cannot read $sqldir");
67 while (false !== ($sfname = readdir($dh))) {
68 if ($sfname[0] === '.') {
69 continue;
72 if (preg_match('/^(\d+)_(\d+)_(\d+)-to-\d+_\d+_\d+_upgrade.sql$/', $sfname, $matches)) {
73 $version = $matches[1] . '.' . $matches[2] . '.' . $matches[3];
74 $versions[$version] = $sfname;
78 closedir($dh);
79 ksort($versions);
81 $res2 = sqlStatement("select * from lang_languages where lang_description = ?", array($GLOBALS['language_default']));
82 for ($iter = 0; $row = sqlFetchArray($res2); $iter++) {
83 $result2[$iter] = $row;
86 if (count($result2) == 1) {
87 $defaultLangID = $result2[0]["lang_id"];
88 $defaultLangName = $result2[0]["lang_description"];
89 $direction = (int)$result2[0]["lang_is_rtl"] === 1 ? 'rtl' : 'ltr';
90 } else {
91 //default to english if any problems
92 $defaultLangID = 1;
93 $defaultLangName = "English";
96 $_SESSION['language_choice'] = $defaultLangID;
97 $_SESSION['language_direction'] = $direction;
98 CsrfUtils::setupCsrfKey();
99 session_write_close();
101 $sqlUpgradeService = new SQLUpgradeService();
103 header('Content-type: text/html; charset=utf-8');
106 <!-- @todo Adding DOCTYPE html breaks BS width/height percentages. Why? -->
107 <html>
108 <head>
109 <title>OpenEMR Database Upgrade</title>
110 <?php Header::setupHeader(); ?>
111 <link rel="shortcut icon" href="public/images/favicon.ico" />
112 <script>
113 let currentVersion;
114 let processProgress = 0;
115 let doPoll = 0;
116 let serverPaused = 0;
117 // recursive long polling where ending is based
118 // on global doPoll true or false.
119 // added a forcePollOff parameter to avoid polling from staying on indefinitely when updating from patch.sql
120 async function serverStatus(version = '', start = 0, forcePollOff = 0) {
121 let updateMsg = "";
122 let endMsg = "<li class='text-success bg-light'>" +
123 <?php echo xlj("End watching server processes for upgrade version"); ?> + " " + currentVersion + "</li>";
124 if (version) {
125 currentVersion = version;
126 updateMsg = "<li class='text-light bg-success'>" +
127 <?php echo xlj("Start watching server processes for upgrade version"); ?> + " " + version + "</li>";
130 // start polling
131 let url = "library/ajax/sql_server_status.php?poll=" + encodeURIComponent(currentVersion);
132 let data = new FormData;
133 data.append("csrf_token_form", <?php echo js_escape(CsrfUtils::collectCsrfToken('sqlupgrade')); ?>);
134 data.append("poll", currentVersion);
136 let response = await fetch(url, {
137 method: 'post',
138 body: data
141 if (response.status === 502) {
142 progressStatus("<li class='text-light bg-danger'> ERROR: Restarting. " + response.statusText) + "</li>";
143 // connection timeout, just reconnect
144 if (doPoll) {
145 await serverStatus();
147 } else if (response.status !== 200) {
148 // show error
149 progressStatus("<li class='text-light bg-danger'> ERROR: " + response.statusText) + "</li>";
150 // reconnect in one second
151 if (doPoll) {
152 await serverStatus();
154 } else {
155 // await status
156 let status = await response.text();
157 if (status === 'Internal Server Error') {
158 let errorMsg = "<li class='text-light bg-danger'>" +
159 <?php echo xlj("Stopping activity status checks. Internal Server Error"); ?> +"</li>";
160 progressStatus(errorMsg);
161 // end polling
162 doPoll = 0;
164 if (status === 'Authentication Error') {
165 let errorMsg = "<li class='text-light bg-danger'>" +
166 <?php echo xlj("Stopping status checks. Csrf Error. No harm to upgrade and will continue."); ?> +"</li>";
167 progressStatus(errorMsg);
168 // end polling
169 doPoll = 0;
171 if (version) {
172 progressStatus(updateMsg);
174 if (start === 1) {
175 doPoll = 1;
177 if (forcePollOff === 1) {
178 doPoll = 0;
180 // display to screen div
181 if (status > "") {
182 progressStatus(status);
185 // and so forth.
186 if (doPoll) {
187 await serverStatus();
188 } else {
189 progressStatus(endMsg);
194 * Focus scrolls to bottom of view
195 * */
196 function doScrolls() {
197 let serverStatus = document.getElementById("serverStatus");
198 let isServerBottom = serverStatus.scrollHeight - serverStatus.clientHeight <= serverStatus.scrollTop + 1;
199 let processDetails = document.getElementById("processDetails");
200 let isDetailsBottom = processDetails.scrollHeight - processDetails.clientHeight <= processDetails.scrollTop + 1;
202 if(!isServerBottom) {
203 serverStatus.scrollTop = serverStatus.scrollHeight - serverStatus.clientHeight;
204 }if(!isDetailsBottom) {
205 processDetails.scrollTop = processDetails.scrollHeight - processDetails.clientHeight;
209 function progressStatus(msg = '') {
210 let eventList = document.getElementById('status-message');
211 let progressEl = document.getElementById('progress');
213 if (currentVersion == "UUID") {
214 if (processProgress < 30) {
215 processProgress++;
216 } else if (processProgress < 40) {
217 if (Math.random() > 0.9) {
218 processProgress++;
220 } else if (processProgress < 50) {
221 if (Math.random() > 0.95) {
222 processProgress++;
224 } else if (processProgress < 60) {
225 if (Math.random() > 0.97) {
226 processProgress++;
228 } else if (processProgress < 70) {
229 if (Math.random() > 0.98) {
230 processProgress++;
232 } else if (processProgress < 80) {
233 if (Math.random() > 0.99) {
234 processProgress++;
236 } else if (processProgress < 96) {
237 if (Math.random() > 0.999) {
238 processProgress++;
240 } else if (processProgress < 99) {
241 if (Math.random() > 0.9999) {
242 processProgress++;
245 progressEl.style.width = processProgress + "%";
246 progressEl.innerHTML = processProgress + "%" + " UUID Update";
247 } else {
248 progressEl.style.width = processProgress + "%";
249 progressEl.innerHTML = processProgress + "%" + " v" + currentVersion;
251 if (msg) {
252 eventList.innerHTML += msg;
253 doScrolls();
257 function setWarnings(othis) {
258 if (othis.value < '5.0.0') {
259 document.querySelector('.version-warning').classList.remove("d-none");
260 } else {
261 document.querySelector('.version-warning').classList.add("d-none");
265 function pausePoll(othis) {
266 if (serverPaused === 0 && doPoll === 1) {
267 let alertMsg = "<li class='text-dark bg-warning'>" +
268 <?php echo xlj("Paused status checks."); ?> +"</li>";
269 progressStatus(alertMsg);
270 serverPaused = 1;
271 doPoll = 0;
272 document.querySelector('.pause-server').classList.remove("btn-success");
273 document.querySelector('.pause-server').classList.add("btn-warning");
274 } else if (serverPaused === 1) {
275 let alertMsg = "<li class='text-dark bg-success'>" +
276 <?php echo xlj("Resuming status checks."); ?> +"</li>";
277 progressStatus(alertMsg);
278 serverPaused = 0;
279 doPoll = 1;
280 serverStatus('', 1);
281 document.querySelector('.pause-server').classList.remove("btn-warning");
282 document.querySelector('.pause-server').classList.add("btn-success");
285 </script>
286 </head>
287 <body>
288 <div class="container my-3">
289 <div class="row">
290 <div class="col-12">
291 <h2><?php echo xlt("OpenEMR Database Upgrade"); ?></h2>
292 </div>
293 </div>
294 <div class="row">
295 <div class="col-12">
296 <p><?php echo xlt("If you are unsure or were using a development version between two releases, then choose the older of possible releases."); ?></p>
297 </div>
298 </div>
299 <div>
300 <form class="form-inline" method='post' action='sql_upgrade.php'>
301 <div class="form-group mb-1">
302 <label><?php echo xlt("Please select the prior release you are converting from"); ?>:</label>
303 <select class='mx-3 form-control' name='form_old_version' onchange="setWarnings(this)">
304 <?php
305 $cnt_versions = count($versions);
306 foreach ($versions as $version => $filename) {
307 --$cnt_versions;
308 echo " <option value='$version'";
309 // Defaulting to most recent version or last version in list.
310 if ($version == ($_POST['form_old_version'] ?? '')) {
311 echo " selected";
312 } elseif ($cnt_versions === 0 && !($_POST['form_old_version'] ?? '')) {
313 echo " selected";
315 echo ">$version</option>\n";
318 </select>
319 </div>
320 <span class="alert alert-warning text-danger version-warning d-none">
321 <?php echo xlt("If you are upgrading from a version below 5.0.0 to version 5.0.0 or greater, do note that this upgrade can take anywhere from several minutes to several hours (you will only see a whitescreen until it is complete; do not stop the script before it is complete or you risk corrupting your data)"); ?>.
322 </span>
323 <button type='submit' class='btn btn-primary btn-transmit' name='form_submit' value='Upgrade Database'>
324 <?php echo xlt("Upgrade Database"); ?>
325 </button>
326 <div class="btn-group">
327 </div>
328 </form>
329 <!-- server status card -->
330 <div class="card card-header">
331 <span class="btn-group">
332 <a class="btn btn-success pause-server fa fa-pause float-left" onclick="pausePoll(this)" title="<?php echo xla("Click to start or end sql server activity checks."); ?>"></a>
333 <a class="btn btn-primary w-100" data-toggle="collapse" href="#serverStatus">
334 <?php echo xlt("Server Status"); ?><i class="fa fa-angle-down rotate-icon float-right"></i>
335 </a>
336 </span>
337 </div>
338 <div id="serverStatus" class="card card-body pb-2 h-25 overflow-auto collapse show">
339 <div class="bg-light text-dark">
340 <ul id="status-message"></ul>
341 </div>
342 </div>
343 </div>
344 <!-- collapse place holder for upgrade processing on submit. -->
345 <div class="card card-header">
346 <a class="btn btn-primary" data-toggle="collapse" href="#processDetails">
347 <?php echo xlt("Processing Details"); ?><i class="fas fa-angle-down rotate-icon float-right"></i>
348 </a>
349 <div id="progress-div" class="bg-secondary float-left">
350 <div id="progress" class="mt-1 progress-bar bg-success" style="height:1.125rem;width:0;"></div>
351 </div>
352 </div>
353 <div id='processDetails' class='card card-body pb-2 h-50 overflow-auto collapse show'>
354 <div class='bg-light text-dark'>
355 <?php if (!empty($_POST['form_submit'])) {
356 $form_old_version = $_POST['form_old_version'];
358 foreach ($versions as $version => $filename) {
359 if (strcmp($version, $form_old_version) < 0) {
360 continue;
362 // set polling version and start
363 $sqlUpgradeService->flush_echo("<script>serverStatus(" . js_escape($version) . ", 1);</script>");
364 $sqlUpgradeService->upgradeFromSqlFile($filename);
365 // end polling
366 sleep(2); // fixes odd bug, where if the sql upgrade goes to fast, then the polling does not stop
367 $sqlUpgradeService->flush_echo("<script>processProgress = 100;doPoll = 0;</script>");
370 if (!empty($GLOBALS['ippf_specific'])) {
371 // Upgrade custom stuff for IPPF.
372 $sqlUpgradeService->upgradeFromSqlFile('ippf_upgrade.sql');
375 if ((!empty($v_realpatch)) && ($v_realpatch != "") && ($v_realpatch > 0)) {
376 // This release contains a patch file, so process it.
377 echo "<script>serverStatus('Patch', 0, 1);</script>";
378 $sqlUpgradeService->upgradeFromSqlFile('patch.sql');
380 flush();
382 echo "<br /><p class='text-success'>Updating UUIDs (this could take some time)<br />\n";
383 $sqlUpgradeService->flush_echo("<script>processProgress = 10; serverStatus('UUID', 1);</script>");
384 $updateUuidLog = UuidRegistry::populateAllMissingUuids();
385 if (!empty($updateUuidLog)) {
386 echo "Updated UUIDs: " . text($updateUuidLog) . "</p><br />\n";
387 } else {
388 echo "Did not need to update or add any new UUIDs</p><br />\n";
390 sleep(2); // fixes odd bug, where if process goes to fast, then the polling does not stop
391 $sqlUpgradeService->flush_echo("<script>processProgress = 100;doPoll = 0;</script>");
393 echo "<p class='text-success'>" . xlt("Updating global configuration defaults") . "..." . "</p><br />\n";
394 $skipGlobalEvent = true; //use in globals.inc.php script to skip event stuff
395 require_once("library/globals.inc.php");
396 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
397 foreach ($grparr as $fldid => $fldarr) {
398 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
399 if (is_array($fldtype) || (substr($fldtype, 0, 2) !== 'm_')) {
400 $row = sqlQuery("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
401 if (empty($row['count'])) {
402 sqlStatement("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
403 "VALUES ( '$fldid', '0', '$flddef' )");
409 echo "<p class='text-success'>" . xlt("Updating Access Controls") . "..." . "</p><br />\n";
410 require("acl_upgrade.php");
411 echo "<br />\n";
413 $versionService = new VersionService();
414 $currentVersion = $versionService->fetch();
415 $desiredVersion = $currentVersion;
416 $desiredVersion['v_database'] = $v_database;
417 $desiredVersion['v_tag'] = $v_tag;
418 $desiredVersion['v_realpatch'] = $v_realpatch;
419 $desiredVersion['v_patch'] = $v_patch;
420 $desiredVersion['v_minor'] = $v_minor;
421 $desiredVersion['v_major'] = $v_major;
423 $canRealPatchBeApplied = $versionService->canRealPatchBeApplied($desiredVersion);
424 $line = "Updating version indicators";
426 if ($canRealPatchBeApplied) {
427 $line = $line . ". " . xlt("Patch was also installed, updating version patch indicator");
430 echo "<p class='text-success'>" . $line . "...</p><br />\n";
431 $versionService->update($desiredVersion);
433 echo "<p><p class='text-success'>" . xlt("Database and Access Control upgrade finished.") . "</p></p>\n";
434 echo "</div></body></html>\n";
435 exit();
438 </div>
439 </div>
440 </div>
441 </body>
442 </html>