support readable output when run sql upgrade as commandline and a quick sql upgrade...
[openemr.git] / sql_upgrade.php
blob688ce7baa7bc42db5689127ecc951b87da6a51ac
1 <?php
3 // Copyright (C) 2008-2010 Rod Roark <rod@sunsetsystems.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This may be run after an upgraded OpenEMR has been installed.
11 // Its purpose is to upgrade the MySQL OpenEMR database as needed
12 // for the new release.
14 /* @TODO add language selection. needs RTL testing */
16 if (php_sapi_name() === 'cli') {
17 // setting for when running as command line script
18 // need this for output to be readable when running as command line
19 $GLOBALS['force_simple_sql_upgrade'] = true;
22 // Checks if the server's PHP version is compatible with OpenEMR:
23 require_once(__DIR__ . "/src/Common/Compatibility/Checker.php");
24 $response = OpenEMR\Common\Compatibility\Checker::checkPhpVersion();
25 if ($response !== true) {
26 die(htmlspecialchars($response));
29 @ini_set('zlib.output_compression', 0);
30 @ini_set('implicit_flush', 1);
31 @ob_end_clean();
32 // Disable PHP timeout. This will not work in safe mode.
33 @ini_set('max_execution_time', '0');
34 if (ob_get_level() === 0) {
35 ob_start();
38 $ignoreAuth = true; // no login required
39 $sessionAllowWrite = true;
40 $GLOBALS['connection_pooling_off'] = true; // force off database connection pooling
42 require_once('interface/globals.php');
43 require_once('library/sql_upgrade_fx.php');
45 use OpenEMR\Common\Csrf\CsrfUtils;
46 use OpenEMR\Core\Header;
47 use OpenEMR\Services\VersionService;
49 $versionService = new VersionService();
51 // Fetching current version because it was updated by the sql_upgrade_fx
52 // script and this script will further modify it.
53 $currentVersion = $versionService->fetch();
55 $desiredVersion = $currentVersion;
56 $desiredVersion->setDatabase($v_database);
57 $desiredVersion->setTag($v_tag);
58 $desiredVersion->setRealPatch($v_realpatch);
59 $desiredVersion->setPatch($v_patch);
60 $desiredVersion->setMinor($v_minor);
61 $desiredVersion->setMajor($v_major);
63 // Force logging off
64 $GLOBALS["enable_auditlog"] = 0;
66 $versions = array();
67 $sqldir = "$webserver_root/sql";
68 $dh = opendir($sqldir);
69 if (!$dh) {
70 die("Cannot read $sqldir");
73 while (false !== ($sfname = readdir($dh))) {
74 if ($sfname[0] === '.') {
75 continue;
78 if (preg_match('/^(\d+)_(\d+)_(\d+)-to-\d+_\d+_\d+_upgrade.sql$/', $sfname, $matches)) {
79 $version = $matches[1] . '.' . $matches[2] . '.' . $matches[3];
80 $versions[$version] = $sfname;
84 closedir($dh);
85 ksort($versions);
87 $res2 = sqlStatement("select * from lang_languages where lang_description = ?", array($GLOBALS['language_default']));
88 for ($iter = 0; $row = sqlFetchArray($res2); $iter++) {
89 $result2[$iter] = $row;
92 if (count($result2) == 1) {
93 $defaultLangID = $result2[0]["lang_id"];
94 $defaultLangName = $result2[0]["lang_description"];
95 $direction = (int)$result2[0]["lang_is_rtl"] === 1 ? 'rtl' : 'ltr';
96 } else {
97 //default to english if any problems
98 $defaultLangID = 1;
99 $defaultLangName = "English";
102 $_SESSION['language_choice'] = $defaultLangID;
103 $_SESSION['language_direction'] = $direction;
104 CsrfUtils::setupCsrfKey();
105 session_write_close();
107 header('Content-type: text/html; charset=utf-8');
110 <!-- @todo Adding DOCTYPE html breaks BS width/height percentages. Why? -->
111 <html>
112 <head>
113 <title>OpenEMR Database Upgrade</title>
114 <?php Header::setupHeader(); ?>
115 <link rel="shortcut icon" href="public/images/favicon.ico" />
116 <script>
117 let currentVersion;
118 let processProgress = 0;
119 let doPoll = 0;
120 let serverPaused = 0;
121 // recursive long polling where ending is based
122 // on global doPoll true or false.
123 async function serverStatus(version = '', start = 0) {
124 let updateMsg = "";
125 let endMsg = "<li class='text-success bg-light'>" +
126 <?php echo xlj("End watching server processes for upgrade version"); ?> + " " + currentVersion + "</li>";
127 if (version) {
128 currentVersion = version;
129 updateMsg = "<li class='text-light bg-success'>" +
130 <?php echo xlj("Start watching server processes for upgrade version"); ?> + " " + version + "</li>";
133 // start polling
134 let url = "library/ajax/sql_server_status.php?poll=" + encodeURIComponent(currentVersion);
135 let data = new FormData;
136 data.append("csrf_token_form", <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>);
137 data.append("poll", currentVersion);
139 let response = await fetch(url, {
140 method: 'post',
141 body: data
144 if (response.status === 502) {
145 progressStatus("<li class='text-light bg-danger'> ERROR: Restarting. " + response.statusText) + "</li>";
146 // connection timeout, just reconnect
147 if (doPoll) {
148 await serverStatus();
150 } else if (response.status !== 200) {
151 // show error
152 progressStatus("<li class='text-light bg-danger'> ERROR: " + response.statusText) + "</li>";
153 // reconnect in one second
154 if (doPoll) {
155 await serverStatus();
157 } else {
158 // await status
159 let status = await response.text();
160 if (status === 'Internal Server Error') {
161 let errorMsg = "<li class='text-light bg-danger'>" +
162 <?php echo xlj("Stopping activity status checks. Internal Server Error"); ?> +"</li>";
163 progressStatus(errorMsg);
164 // end polling
165 doPoll = 0;
167 if (status === 'Authentication Error') {
168 let errorMsg = "<li class='text-light bg-danger'>" +
169 <?php echo xlj("Stopping status checks. Csrf Error. No harm to upgrade and will continue."); ?> +"</li>";
170 progressStatus(errorMsg);
171 // end polling
172 doPoll = 0;
174 if (version) {
175 progressStatus(updateMsg);
177 if (start === 1) {
178 doPoll = 1;
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 progressEl.style.width = processProgress + "%";
214 progressEl.innerHTML = processProgress + "%" + " v" + currentVersion;
215 if (msg) {
216 eventList.innerHTML += msg;
217 doScrolls();
221 function setWarnings(othis) {
222 if (othis.value < '5.0.0') {
223 document.querySelector('.version-warning').classList.remove("d-none");
224 } else {
225 document.querySelector('.version-warning').classList.add("d-none");
229 function pausePoll(othis) {
230 if (serverPaused === 0 && doPoll === 1) {
231 let alertMsg = "<li class='text-dark bg-warning'>" +
232 <?php echo xlj("Paused status checks."); ?> +"</li>";
233 progressStatus(alertMsg);
234 serverPaused = 1;
235 doPoll = 0;
236 document.querySelector('.pause-server').classList.remove("btn-success");
237 document.querySelector('.pause-server').classList.add("btn-warning");
238 } else if (serverPaused === 1) {
239 let alertMsg = "<li class='text-dark bg-success'>" +
240 <?php echo xlj("Resuming status checks."); ?> +"</li>";
241 progressStatus(alertMsg);
242 serverPaused = 0;
243 doPoll = 1;
244 serverStatus('', 1);
245 document.querySelector('.pause-server').classList.remove("btn-warning");
246 document.querySelector('.pause-server').classList.add("btn-success");
249 </script>
250 </head>
251 <body>
252 <div class="container my-3">
253 <div class="row">
254 <div class="col-12">
255 <h2><?php echo xlt("OpenEMR Database Upgrade"); ?></h2>
256 </div>
257 </div>
258 <div>
259 <form class="form-inline" method='post' action='sql_upgrade.php'>
260 <div class="form-group mb-1">
261 <label><?php echo xlt("Please select the prior release you are converting from"); ?>:</label>
262 <select class='mx-3 form-control' name='form_old_version' onchange="setWarnings(this)">
263 <?php
264 $cnt_versions = count($versions);
265 foreach ($versions as $version => $filename) {
266 --$cnt_versions;
267 echo " <option value='$version'";
268 // Defaulting to most recent version or last version in list.
269 if ($cnt_versions === 0) {
270 echo " selected";
272 echo ">$version</option>\n";
275 </select>
276 <span><?php echo xlt("If you are unsure or were using a development version between two releases, then choose the older of possible releases"); ?>.</span>
277 </div>
278 <span class="alert alert-warning text-danger version-warning d-none">
279 <?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)"); ?>.
280 </span>
281 <button type='submit' class='btn btn-primary btn-transmit' name='form_submit' value='Upgrade Database'>
282 <?php echo xlt("Upgrade Database"); ?>
283 </button>
284 <div class="btn-group">
285 </div>
286 </form>
287 <!-- server status card -->
288 <div class="card card-header">
289 <span class="btn-group">
290 <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>
291 <a class="btn btn-primary w-100" data-toggle="collapse" href="#serverStatus">
292 <?php echo xlt("Server Status"); ?><i class="fa fa-angle-down rotate-icon float-right"></i>
293 </a>
294 </span>
295 </div>
296 <div id="serverStatus" class="card card-body pb-2 h-25 overflow-auto collapse show">
297 <div class="bg-light text-dark">
298 <ul id="status-message"></ul>
299 </div>
300 </div>
301 </div>
302 <!-- collapse place holder for upgrade processing on submit. -->
303 <div class="card card-header">
304 <a class="btn btn-primary" data-toggle="collapse" href="#processDetails">
305 <?php echo xlt("Processing Details"); ?><i class="fas fa-angle-down rotate-icon float-right"></i>
306 </a>
307 <div id="progress-div" class="bg-secondary float-left">
308 <div id="progress" class="mt-1 progress-bar bg-success" style="height:1.125rem;width:0;"></div>
309 </div>
310 </div>
311 <div id='processDetails' class='card card-body pb-2 h-50 overflow-auto collapse show'>
312 <div class='bg-light text-dark'>
313 <?php if (!empty($_POST['form_submit'])) {
314 $form_old_version = $_POST['form_old_version'];
316 foreach ($versions as $version => $filename) {
317 if (strcmp($version, $form_old_version) < 0) {
318 continue;
320 // set polling version and start
321 flush_echo("<script>serverStatus(" . js_escape($version) . ", 1);</script>");
322 upgradeFromSqlFile($filename);
323 // end polling
324 flush_echo("<script>processProgress = 100;doPoll = 0;</script>");
327 if (!empty($GLOBALS['ippf_specific'])) {
328 // Upgrade custom stuff for IPPF.
329 upgradeFromSqlFile('ippf_upgrade.sql');
332 if ((!empty($v_realpatch)) && ($v_realpatch != "") && ($v_realpatch > 0)) {
333 // This release contains a patch file, so process it.
334 echo "<script>serverStatus('Patch');</script>";
335 upgradeFromSqlFile('patch.sql');
337 flush();
339 echo "<p class='text-success'>" . xlt("Updating global configuration defaults") . "..." . "</p><br />\n";
340 $skipGlobalEvent = true; //use in globals.inc.php script to skip event stuff
341 require_once("library/globals.inc.php");
342 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
343 foreach ($grparr as $fldid => $fldarr) {
344 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
345 if (is_array($fldtype) || (substr($fldtype, 0, 2) !== 'm_')) {
346 $row = sqlQuery("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
347 if (empty($row['count'])) {
348 sqlStatement("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
349 "VALUES ( '$fldid', '0', '$flddef' )");
355 echo "<p class='text-success'>" . xlt("Updating Access Controls") . "..." . "</p><br />\n";
356 require("acl_upgrade.php");
357 echo "<br />\n";
359 $canRealPatchBeApplied = $versionService->canRealPatchBeApplied($desiredVersion);
360 $line = "Updating version indicators";
362 if ($canRealPatchBeApplied) {
363 $line = $line . ". " . xlt("Patch was also installed, updating version patch indicator");
366 echo "<p class='text-success'>" . $line . "...</p><br />\n";
367 $result = $versionService->update($desiredVersion);
369 if (!$result) {
370 echo "<p class='text-danger'>" . xlt("Version could not be updated") . "</p><br />\n";
371 exit();
374 echo "<p><p class='text-success'>" . xlt("Database and Access Control upgrade finished.") . "</p></p>\n";
375 echo "</div></body></html>\n";
376 exit();
379 </div>
380 </div>
381 </div>
382 </body>
383 </html>