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
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);
38 // Disable PHP timeout. This will not work in safe mode.
39 @ini_set
('max_execution_time', '0');
40 if (ob_get_level() === 0) {
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\VersionService
;
57 $GLOBALS["enable_auditlog"] = 0;
60 $sqldir = "$webserver_root/sql";
61 $dh = opendir($sqldir);
63 die("Cannot read $sqldir");
66 while (false !== ($sfname = readdir($dh))) {
67 if ($sfname[0] === '.') {
71 if (preg_match('/^(\d+)_(\d+)_(\d+)-to-\d+_\d+_\d+_upgrade.sql$/', $sfname, $matches)) {
72 $version = $matches[1] . '.' . $matches[2] . '.' . $matches[3];
73 $versions[$version] = $sfname;
80 $res2 = sqlStatement("select * from lang_languages where lang_description = ?", array($GLOBALS['language_default']));
81 for ($iter = 0; $row = sqlFetchArray($res2); $iter++
) {
82 $result2[$iter] = $row;
85 if (count($result2) == 1) {
86 $defaultLangID = $result2[0]["lang_id"];
87 $defaultLangName = $result2[0]["lang_description"];
88 $direction = (int)$result2[0]["lang_is_rtl"] === 1 ?
'rtl' : 'ltr';
90 //default to english if any problems
92 $defaultLangName = "English";
95 $_SESSION['language_choice'] = $defaultLangID;
96 $_SESSION['language_direction'] = $direction;
97 CsrfUtils
::setupCsrfKey();
98 session_write_close();
100 header('Content-type: text/html; charset=utf-8');
103 <!-- @todo Adding DOCTYPE html breaks BS width
/height percentages
. Why?
-->
106 <title
>OpenEMR Database Upgrade
</title
>
107 <?php Header
::setupHeader(); ?
>
108 <link rel
="shortcut icon" href
="public/images/favicon.ico" />
111 let processProgress
= 0;
113 let serverPaused
= 0;
114 // recursive long polling where ending is based
115 // on global doPoll true or false.
116 // added a forcePollOff parameter to avoid polling from staying on indefinitely when updating from patch.sql
117 async
function serverStatus(version
= '', start
= 0, forcePollOff
= 0) {
119 let endMsg
= "<li class='text-success bg-light'>" +
120 <?php
echo xlj("End watching server processes for upgrade version"); ?
> +
" " + currentVersion +
"</li>";
122 currentVersion
= version
;
123 updateMsg
= "<li class='text-light bg-success'>" +
124 <?php
echo xlj("Start watching server processes for upgrade version"); ?
> +
" " + version +
"</li>";
128 let url
= "library/ajax/sql_server_status.php?poll=" +
encodeURIComponent(currentVersion
);
129 let data
= new FormData
;
130 data
.append("csrf_token_form", <?php
echo js_escape(CsrfUtils
::collectCsrfToken('sqlupgrade')); ?
>);
131 data
.append("poll", currentVersion
);
133 let response
= await
fetch(url
, {
138 if (response
.status
=== 502) {
139 progressStatus("<li class='text-light bg-danger'> ERROR: Restarting. " + response
.statusText
) +
"</li>";
140 // connection timeout, just reconnect
142 await
serverStatus();
144 } else if (response
.status
!== 200) {
146 progressStatus("<li class='text-light bg-danger'> ERROR: " + response
.statusText
) +
"</li>";
147 // reconnect in one second
149 await
serverStatus();
153 let status
= await response
.text();
154 if (status
=== 'Internal Server Error') {
155 let errorMsg
= "<li class='text-light bg-danger'>" +
156 <?php
echo xlj("Stopping activity status checks. Internal Server Error"); ?
> +
"</li>";
157 progressStatus(errorMsg
);
161 if (status
=== 'Authentication Error') {
162 let errorMsg
= "<li class='text-light bg-danger'>" +
163 <?php
echo xlj("Stopping status checks. Csrf Error. No harm to upgrade and will continue."); ?
> +
"</li>";
164 progressStatus(errorMsg
);
169 progressStatus(updateMsg
);
174 if (forcePollOff
=== 1) {
177 // display to screen div
179 progressStatus(status
);
184 await
serverStatus();
186 progressStatus(endMsg
);
191 * Focus scrolls to bottom of view
193 function doScrolls() {
194 let serverStatus
= document
.getElementById("serverStatus");
195 let isServerBottom
= serverStatus
.scrollHeight
- serverStatus
.clientHeight
<= serverStatus
.scrollTop +
1;
196 let processDetails
= document
.getElementById("processDetails");
197 let isDetailsBottom
= processDetails
.scrollHeight
- processDetails
.clientHeight
<= processDetails
.scrollTop +
1;
199 if(!isServerBottom
) {
200 serverStatus
.scrollTop
= serverStatus
.scrollHeight
- serverStatus
.clientHeight
;
201 }if(!isDetailsBottom
) {
202 processDetails
.scrollTop
= processDetails
.scrollHeight
- processDetails
.clientHeight
;
206 function progressStatus(msg
= '') {
207 let eventList
= document
.getElementById('status-message');
208 let progressEl
= document
.getElementById('progress');
210 if (currentVersion
== "UUID") {
211 if (processProgress
< 30) {
213 } else if (processProgress
< 40) {
214 if (Math
.random() > 0.9) {
217 } else if (processProgress
< 50) {
218 if (Math
.random() > 0.95) {
221 } else if (processProgress
< 60) {
222 if (Math
.random() > 0.97) {
225 } else if (processProgress
< 70) {
226 if (Math
.random() > 0.98) {
229 } else if (processProgress
< 80) {
230 if (Math
.random() > 0.99) {
233 } else if (processProgress
< 96) {
234 if (Math
.random() > 0.999) {
237 } else if (processProgress
< 99) {
238 if (Math
.random() > 0.9999) {
242 progressEl
.style
.width
= processProgress +
"%";
243 progressEl
.innerHTML
= processProgress +
"%" +
" UUID Update";
245 progressEl
.style
.width
= processProgress +
"%";
246 progressEl
.innerHTML
= processProgress +
"%" +
" v" + currentVersion
;
249 eventList
.innerHTML +
= msg
;
254 function setWarnings(othis
) {
255 if (othis
.value
< '5.0.0') {
256 document
.querySelector('.version-warning').classList
.remove("d-none");
258 document
.querySelector('.version-warning').classList
.add("d-none");
262 function pausePoll(othis
) {
263 if (serverPaused
=== 0 && doPoll
=== 1) {
264 let alertMsg
= "<li class='text-dark bg-warning'>" +
265 <?php
echo xlj("Paused status checks."); ?
> +
"</li>";
266 progressStatus(alertMsg
);
269 document
.querySelector('.pause-server').classList
.remove("btn-success");
270 document
.querySelector('.pause-server').classList
.add("btn-warning");
271 } else if (serverPaused
=== 1) {
272 let alertMsg
= "<li class='text-dark bg-success'>" +
273 <?php
echo xlj("Resuming status checks."); ?
> +
"</li>";
274 progressStatus(alertMsg
);
278 document
.querySelector('.pause-server').classList
.remove("btn-warning");
279 document
.querySelector('.pause-server').classList
.add("btn-success");
285 <div
class="container my-3">
288 <h2
><?php
echo xlt("OpenEMR Database Upgrade"); ?
></h2
>
293 <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 <form
class="form-inline" method
='post' action
='sql_upgrade.php'>
298 <div
class="form-group mb-1">
299 <label
><?php
echo xlt("Please select the prior release you are converting from"); ?
>:</label
>
300 <select
class='mx-3 form-control' name
='form_old_version' onchange
="setWarnings(this)">
302 $cnt_versions = count($versions);
303 foreach ($versions as $version => $filename) {
305 echo " <option value='$version'";
306 // Defaulting to most recent version or last version in list.
307 if ($version == ($_POST['form_old_version'] ??
'')) {
309 } elseif ($cnt_versions === 0 && !($_POST['form_old_version'] ??
'')) {
312 echo ">$version</option>\n";
317 <span
class="alert alert-warning text-danger version-warning d-none">
318 <?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)"); ?
>.
320 <button type
='submit' class='btn btn-primary btn-transmit' name
='form_submit' value
='Upgrade Database'>
321 <?php
echo xlt("Upgrade Database"); ?
>
323 <div
class="btn-group">
326 <!-- server status card
-->
327 <div
class="card card-header">
328 <span
class="btn-group">
329 <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
>
330 <a
class="btn btn-primary w-100" data
-toggle
="collapse" href
="#serverStatus">
331 <?php
echo xlt("Server Status"); ?
><i
class="fa fa-angle-down rotate-icon float-right"></i
>
335 <div id
="serverStatus" class="card card-body pb-2 h-25 overflow-auto collapse show">
336 <div
class="bg-light text-dark">
337 <ul id
="status-message"></ul
>
341 <!-- collapse place holder
for upgrade processing on submit
. -->
342 <div
class="card card-header">
343 <a
class="btn btn-primary" data
-toggle
="collapse" href
="#processDetails">
344 <?php
echo xlt("Processing Details"); ?
><i
class="fas fa-angle-down rotate-icon float-right"></i
>
346 <div id
="progress-div" class="bg-secondary float-left">
347 <div id
="progress" class="mt-1 progress-bar bg-success" style
="height:1.125rem;width:0;"></div
>
350 <div id
='processDetails' class='card card-body pb-2 h-50 overflow-auto collapse show'>
351 <div
class='bg-light text-dark'>
352 <?php
if (!empty($_POST['form_submit'])) {
353 $form_old_version = $_POST['form_old_version'];
355 foreach ($versions as $version => $filename) {
356 if (strcmp($version, $form_old_version) < 0) {
359 // set polling version and start
360 flush_echo("<script>serverStatus(" . js_escape($version) . ", 1);</script>");
361 upgradeFromSqlFile($filename);
363 sleep(2); // fixes odd bug, where if the sql upgrade goes to fast, then the polling does not stop
364 flush_echo("<script>processProgress = 100;doPoll = 0;</script>");
367 if (!empty($GLOBALS['ippf_specific'])) {
368 // Upgrade custom stuff for IPPF.
369 upgradeFromSqlFile('ippf_upgrade.sql');
372 if ((!empty($v_realpatch)) && ($v_realpatch != "") && ($v_realpatch > 0)) {
373 // This release contains a patch file, so process it.
374 echo "<script>serverStatus('Patch', 0, 1);</script>";
375 upgradeFromSqlFile('patch.sql');
379 echo "<br /><p class='text-success'>Updating UUIDs (this could take some time)<br />\n";
380 flush_echo("<script>processProgress = 10; serverStatus('UUID', 1);</script>");
381 $updateUuidLog = UuidRegistry
::populateAllMissingUuids();
382 if (!empty($updateUuidLog)) {
383 echo "Updated UUIDs: " . text($updateUuidLog) . "</p><br />\n";
385 echo "Did not need to update or add any new UUIDs</p><br />\n";
387 sleep(2); // fixes odd bug, where if process goes to fast, then the polling does not stop
388 flush_echo("<script>processProgress = 100;doPoll = 0;</script>");
390 echo "<p class='text-success'>" . xlt("Updating global configuration defaults") . "..." . "</p><br />\n";
391 $skipGlobalEvent = true; //use in globals.inc.php script to skip event stuff
392 require_once("library/globals.inc.php");
393 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
394 foreach ($grparr as $fldid => $fldarr) {
395 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
396 if (is_array($fldtype) ||
(substr($fldtype, 0, 2) !== 'm_')) {
397 $row = sqlQuery("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
398 if (empty($row['count'])) {
399 sqlStatement("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
400 "VALUES ( '$fldid', '0', '$flddef' )");
406 echo "<p class='text-success'>" . xlt("Updating Access Controls") . "..." . "</p><br />\n";
407 require("acl_upgrade.php");
410 $versionService = new VersionService();
411 $currentVersion = $versionService->fetch();
412 $desiredVersion = $currentVersion;
413 $desiredVersion['v_database'] = $v_database;
414 $desiredVersion['v_tag'] = $v_tag;
415 $desiredVersion['v_realpatch'] = $v_realpatch;
416 $desiredVersion['v_patch'] = $v_patch;
417 $desiredVersion['v_minor'] = $v_minor;
418 $desiredVersion['v_major'] = $v_major;
420 $canRealPatchBeApplied = $versionService->canRealPatchBeApplied($desiredVersion);
421 $line = "Updating version indicators";
423 if ($canRealPatchBeApplied) {
424 $line = $line . ". " . xlt("Patch was also installed, updating version patch indicator");
427 echo "<p class='text-success'>" . $line . "...</p><br />\n";
428 $versionService->update($desiredVersion);
430 echo "<p><p class='text-success'>" . xlt("Database and Access Control upgrade finished.") . "</p></p>\n";
431 echo "</div></body></html>\n";