6 require_once
'sys.php';
30 public string $githubServer =
'https://raw.githubusercontent.com/YaWK/yawk.io/master/';
82 $allowUrlFopen = ini_get(
'allow_url_fopen');
87 echo
"allow_url_fopen is disabled, but required to use the update methods. Please enable allow_url_fopen in your php.ini file or ask your admin / hoster for assistance.";
93 echo
"Update server $this->updateServer not reachable. Update not possible at the moment - please try again later.<br>";
106 $ch = curl_init(
$url);
107 curl_setopt($ch, CURLOPT_NOBODY,
true);
108 curl_setopt($ch, CURLOPT_RETURNTRANSFER,
true);
109 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
110 curl_setopt($ch, CURLOPT_TIMEOUT, 30);
111 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,
false);
112 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,
false);
113 curl_setopt($ch, CURLOPT_USERAGENT,
'Mozilla/5.0 (compatible; UpdateClient/1.0)');
116 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
120 if ($httpCode >= 200 && $httpCode < 300) {
135 $this->updateSettings = parse_ini_file($this->localUpdateSystemPath.$this->updateFile);
136 if (count($this->updateSettings) < 1)
138 echo
"Error: Unable to read update.ini from local update folder. Check if this file exists: ".$this->localUpdateSystemPath.$this->updateFile;
143 $this->updateVersion = $this->updateSettings[
'version'];
164 if (is_array($successfulMigrations) && (count($successfulMigrations) > 0))
166 foreach ($successfulMigrations as $migrationVersion) {
168 if (!
$db->query(
"INSERT INTO {migrations} (`version`, `executed_at`) VALUES ('$migrationVersion', NOW())"))
170 sys::setSyslog(
$db, 53, 0,
"Error recording migration for version $migrationVersion: " .
$db->error .
" ", 0, 0, 0, 0);
171 $output .=
"Error recording migration for version $migrationVersion: " .
$db->error .
"<br>";
175 sys::setSyslog(
$db, 53, 0,
"Migration record for version $migrationVersion successful.", 0, 0, 0, 0);
176 $output .=
"Migration for version $migrationVersion executed successfully.<br>";
182 sys::setSyslog(
$db, 54, 0,
"No Migrations to record. Is there a logical error?", 0, 0, 0, 0);
183 $output .=
"No migrations to record.<br>";
197 require_once
'sys.php';
199 ini_set(
'display_errors', 1);
200 error_reporting(E_ALL);
205 $successfulMigrations = 0;
206 $failedMigrations = 0;
207 $successfulMigrationVersions = array();
210 sys::setSyslog(
$db, 56, 0,
"runMigrations initialized. Updating from $this->currentVersion to $this->updateVersion", 0, 0, 0, 0);
215 $db->beginTransaction();
218 $migrationUrlBase = $this->updateServer .
'migration-';
219 $currentVersionParts = explode(
'.', $this->currentVersion);
220 $updateVersionParts = explode(
'.', $this->updateVersion);
222 $startIndex = (int)$currentVersionParts[2] + 1;
224 $endIndex = (int)$updateVersionParts[2];
225 $output .=
'<br><br><div class="panel-group animated fadeIn slow delay-2s" id="accordion" role="tablist" aria-multiselectable="true">
226 <div class="panel panel-default">
227 <div class="panel-heading" role="tab" id="headingMigration">
228 <h4 class="panel-title">
229 <a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseMigration" aria-expanded="true" aria-controls="collapseMigration">
230 '.$lang[
'UPDATE_MIGRATIONS'].
'<br>
231 <small>(database updates)</small>
235 <div id="collapseMigration" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingMigration">
236 <div class="panel-body">';
239 for (
$i = $startIndex;
$i <= $endIndex;
$i++)
243 $migrationVersion = $currentVersionParts[0] .
'.' . $currentVersionParts[1] .
'.' .
$i;
245 $migrationUrl = $migrationUrlBase . $migrationVersion .
'.sql';
248 $migrationRecord =
$db->query(
"SELECT version FROM {migrations} WHERE version = '$migrationVersion'")->fetch_assoc();
251 if ($migrationRecord !==
null)
253 $output .=
"$migrationVersion : ".$lang[
'UPDATE_MIGRATION_ALREADY_EXEC'].
"<br>";
254 sys::setSyslog(
$db, 53, 1,
"Migration for build $migrationVersion was already executed.", 0, 0, 0, 0);
262 $migrationSql = @file_get_contents($migrationUrl);
266 if ($migrationSql ===
false)
268 $output .=
$lang[
"UPDATE_NO_MIGRATION_REQUIRED_FOR_BUILD"].
" ".$migrationVersion.
"<br>";
269 sys::setSyslog(
$db, 53, 0,
"No migration required for build $migrationVersion", 0, 0, 0, 0);
275 $output .=
"<span style=\"text-success\"><b>".$lang[
'UPDATE_FETCHED_MIGRATION_FILE_FROM'].
"</b>".$migrationUrl.
"</span><br>";
276 sys::setSyslog(
$db, 54, 0,
"<b>Fetched migration file</b> from $migrationUrl", 0, 0, 0, 0);
277 $successfulMigrations++;
278 $successfulMigrationVersions[] = $migrationVersion;
281 $output .=
"Multi-query SQL: " . $migrationSql .
"<br>";
284 $sqlStatements = explode(
';', $migrationSql);
286 $statementCounter = 0;
288 foreach ($sqlStatements as $sqlStatement)
292 $sqlStatement = trim($sqlStatement);
294 if (!empty($sqlStatement))
296 if (!
$db->query($sqlStatement))
298 $output .= $migrationVersion.
" ".
$lang[
'UPDATE_ERROR_EXEC_FAILED'].
" ".$statementCounter .
$db->error .
"<br>";
299 sys::setSyslog(
$db, 56, 2,
"Error executing migration statement #$statementCounter for version (query failed) $migrationVersion: " .
$db->error .
" ", 0, 0, 0, 0);
305 $output .=
"<span style=\"text-success\">$migrationVersion ".$lang[
'UPDATE_ERROR_EXEC_SUCCESS'].
" $statementCounter</span><br>";
306 sys::setSyslog(
$db, 53, 0,
"executed migration statement #$statementCounter : $migrationUrlBase$migrationVersion.sql ", 0, 0, 0, 0);
316 if ($successfulMigrations > 0)
319 foreach ($successfulMigrationVersions as $successfulMigrationVersion)
321 $output .=
"<span style=\"text-success\">$successfulMigrationVersion ".$lang[
'UPDATE_MIGRATION_RECORDED'].
"</span><br>";
324 sys::setSyslog(
$db, 54, 0,
"<b>Migration complete</b>Migrations executed successfully.", 0, 0, 0, 0);
325 $this->migrationSuccessful =
true;
330 catch (\Exception $e)
332 if (
$db->rollback() ===
true)
334 sys::setSyslog(
$db, 56, 2,
"<b>Rolled back transaction</b> because there was an error executing migrations: " . $e->getMessage() .
" ", 0, 0, 0, 0);
335 $output .=
$lang[
'UPDATE_MIGRATION_ROLLED_BACK'] . $e->getMessage() .
"\n";
339 sys::setSyslog(
$db, 56, 2,
"<b>Rollback FAILED!</b>, additionally there was an error during migrations: " . $e->getMessage() .
" ", 0, 0, 0, 0);
340 $output .=
$lang[
'UPDATE_MIGRATION_ROLLBACK_FAILED'] . $e->getMessage() .
"\n";
342 $this->migrationSuccessful =
false;
345 $output .=
"</div></div></div>";
354 sys::setSyslog(
$db, 56, 2,
"No migrations were executed. Output is empty. output was not filled with any value during runMigrations(). (this is not possible?!)", 0, 0, 0, 0);
355 echo
$lang[
'UPDATE_NO_MIGRATION_EXECUTED'];
369 $updateSucceed =
false;
381 $basedir = substr($basedir, 0, -14);
386 if (file_exists($basedir.$this->localUpdateSystemPath . $this->updateFilesFile))
390 $this->updateFiles = parse_ini_file($basedir.$this->localUpdateSystemPath . $this->updateFilesFile);
391 if (count($this->updateFiles) < 1)
393 $response .=
"<span class=\"text-warning\"><p><i class=\"fa fa-exclamation-triangle text-warning\"></i>".$lang[
'UPDATE_FAST_FORWARD_INFO'].
"
394 <a href=\"#fastForwardUpdate\" id=\"fastForwardUpdateBtn\" class=\"btn btn-warning\">".
$lang[
'UPDATE_FAST_FORWARD_BTN'].
" <i class=\"fa fa-fast-forward\"></i></a></span><br>";
398 $totalUpdateFiles = count($this->updateFiles);
407 foreach ($this->updateFiles as $key =>
$value)
413 $fileUrl = $this->updateServer .
$value;
415 $file = file_get_contents($fileUrl);
427 if (!file_put_contents($basedir.$value, $file))
430 $response .=
"<b> class=\"text-danger\">".$lang[
'UPDATE_UNABLE_TO_WRITE_FILE'].
"</b> " .$basedir.$value .
"<br>";
435 $response .=
"<b class=\"text-success animated fadeIn slow\">".$lang[
'UPDATE_FILE_WRITTEN'].
"</b> " . $basedir.$value .
"<br>";
440 if ($fetchFailed > 0)
442 $response .=
"<b class=\"text-danger\">".$lang[
'UNABLE_TO_FETCH_FILES_FROM_REMOTE_SERVER'].
" $fetchFailed</b><br>";
444 else if ($fetchSucceed === $totalUpdateFiles)
446 $response .=
"<b class=\"text-success\">$fetchSucceed ".$lang[
'UPDATE_SUCCESSFULLY_FETCHED'].
"</b><br>";
449 if ($failedFiles > 0)
451 $response .=
"<b class=\"text-danger\">$failedFiles ".$lang[
'UPDATE_FAILED_FILES'].
"</b><br>";
453 else if ($successFiles === $totalUpdateFiles)
455 $response .=
"<b class=\"text-success\">".$lang[
'ALL'].
" ".$successFiles.
" ".
$lang[
'UPDATE_ALL_FILES_SUCCESS'].
"</b><br>";
458 if ($processedFiles === $totalUpdateFiles)
460 $response .=
"<b class=\"text-success\">".$lang[
'ALL'].
" ".$processedFiles.
" ".
$lang[
'UPDATE_FILES_PROCESSED'].
"</b><br>";
464 $response .=
"<b class=\"text-danger\">".$lang[
'UPDATE_NOT_ALL_FILES_PROCESSED'].
" ".$processedFiles.
" ".
$lang[
'OF'].
" ".$totalUpdateFiles.
" ".
$lang[
'UPDATE_FILES_PROCESSED'].
".</b><br>";
467 if ($successFiles === $totalUpdateFiles)
469 $updateSucceed =
true;
473 $response .=
"<h3 class=\"text-danger\">".$lang[
'UPDATE_FAILED'].
"</h3>";
479 $response .=
"<span class=\"text-danger\"><b>".$lang[
'ERROR'].
":</b> ".
$lang[
'UPDATE_FILES_INI_MISSING'].
" " .$basedir.$this->localUpdateSystemPath . $this->updateFilesFile .
"</span>";
483 if ($updateSucceed ===
true)
491 $response .=
"<h3 class=\"text-success\">".$lang[
'UPDATE_TO'].
" $updateVersion ".
$lang[
'COMPLETED_SUCCESSFULLY'].
"</b><h3>";
492 sys::setSyslog(
$db, 54, 0,
"<b>UPDATE COMPLETE</b> Migration and Files updated to $updateVersion.", 0, 0, 0, 0);
496 $response .=
"<h3 class=\"text-danger\">".$lang[
'UPDATE_FAILED_TO_WRITE_VERSION_NUMBER'].
" ($updateVersion)</h3>";
501 $response .=
"<h3 class=\"text-danger\">".$lang[
'UPDATE_FAILED_FROM'].
" $this->currentVersion ".
$lang[
'TO'].
" $updateVersion.</h3>";
517 $url = $this->updateServer.$this->updateFile;
520 $iniContent = file_get_contents(
$url);
523 if ($iniContent !==
false)
525 $config = parse_ini_string($iniContent);
528 foreach ($config as $key =>
$value)
530 $updateConfig[
'UPDATE'][$key] =
$value;
535 echo
"Error: Unable to read the remote INI file.";
537 return $updateConfig ??
false;
549 $this->base_dir = dirname(__FILE__);
550 $this->base_dir = substr($this->base_dir, 0, -15);
554 $updatePath =
'/system/update/';
555 $updateFolder = $this->base_dir.$updatePath;
557 if (!is_dir($updateFolder)){
558 if (!mkdir($updateFolder, 0777,
true) && !is_dir($updateFolder)) {
559 throw new \RuntimeException(sprintf(
'Unable to create "%s" - please check folder permissions or create folder by hand', $updateFolder));
562 $iniFileName =
'filebase.current.ini';
564 $output_file = $updateFolder.$iniFileName;
565 $writeIniFile =
true;
568 if (is_file($output_file)){
569 unlink($output_file);
573 $output_handle = fopen($output_file,
'w');
574 if (!$output_handle) {
576 die(
"Failed to open file for writing: $output_file");
580 $input_folder = realpath($input_folder);
583 echo
"<p>".$lang[
'UPDATE_BUILDING_LOCAL_FILEBASE'].
" <i><small>$input_folder</small></i></p>";
584 $root_files = array();
585 $subfolder_files = array();
586 foreach(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($input_folder)) as $file_path)
589 if ($file_path->isDir()
590 || $file_path->getFilename()[0] ==
'.'
591 || strpos($file_path,
'/.git/') !==
false
592 || strpos($file_path,
'/.github/') !==
false
593 || strpos($file_path,
'/.idea/') !==
false
594 || strpos($file_path,
'/content/') !==
false
595 || strpos($file_path,
'/media/') !==
false)
601 $md5_hash = md5_file($file_path);
604 $full_path = realpath($file_path);
607 $relative_path = substr($full_path, strlen($input_folder) + 1);
610 $path_components = explode(
'/', $relative_path);
613 if (count($path_components) == 1) {
614 $root_files[$relative_path] = $md5_hash;
616 $subfolder_files[$relative_path] = $md5_hash;
622 ksort($subfolder_files);
625 $hashes = array_merge($root_files, $subfolder_files);
628 $totalFiles = count($hashes);
629 $totalFilesVerified = 0;
630 $totalFilesFailed = 0;
633 foreach ($hashes as $relative_path => $md5_hash)
637 if (file_put_contents($output_file,
"$relative_path=$md5_hash\n", FILE_APPEND))
640 $totalFilesVerified++;
650 echo
'<p class="animated fadeIn slow delay-1s">'.$lang[
'UPDATE_INDEXING'].
' <b>'.$totalFiles.
'</b> '.
$lang[
'FILES'].
'. '.
$lang[
'VERIFIED'].
' <b>'.$totalFilesVerified.
'</b> '.
$lang[
'FILES'].
'. '.
$lang[
'FAILED'].
' <b>'.$totalFilesFailed.
'</b> '.
$lang[
'FILES'].
'.</p>';
652 if (!is_file($output_file))
654 $iniFileWritten =
"<h4 class=\"text-danger\">".$iniFileName.
" ".
$lang[
'COULD_NOT_BE_WRITTEN'].
"</h4>";
658 $iniFileWritten =
"<p class=\"animated fadeIn slow delay-5s\">".$lang[
'UPDATE_HASH_VALUES_WRITTEN_TO'].
" <b><a href=\"$updatePath$iniFileName\" target=\"_blank\">$updatePath$iniFileName</a></b></p>";
662 if ($totalFiles == $totalFilesVerified)
664 $icon =
'<i class="fa fa-check-circle-o fa-2x text-success"></i>';
665 $done =
"<h4 class=\"text-success animated fadeIn slow delay-4s\">".$totalFiles.
" ".
$lang[
'UPDATE_VERIFICATION_SUCCESS'].
"</h4>";
667 $successColor =
' text-success';
672 $iconFalse =
'<i class="fa fa-exclamation-triangle fa-2x text-danger"></i>';
673 $done =
"<h4 class=\"text-danger\"><b>".$totalFilesFailed.
" ".
$lang[
'UPDATE_VERIFICATION_FAILED'].
"</b></h4>";
674 $failedColor =
' text-danger';
679 <p class=\"animated fadeIn slow delay-2s$successColor\">$icon ".$lang[
'UPDATE_GENERATED_HASH_VALUES'].
" <b>$totalFilesVerified / $totalFiles</b></p>";
680 echo
"<h4 class=\"animated fadeIn slow delay-4s\">$done</h4>
684 if ($totalFilesFailed > 0)
686 echo
"<p class=\"animated fadeIn slow delay-3s$failedColor\">$iconFalse ".$lang[
'UPDATE_FAILED_TO_VERIFY'].
" <b>$totalFilesFailed</b></p>";
690 if (!fclose($output_handle))
692 echo
'<p class="animated fadeIn slow delay-5s">failed to close: '.$updateFolder.$iniFileName.
'</p>';
706 $url = $this->updateServer.
'filebase.ini';
709 $iniContent = file_get_contents(
$url);
712 if ($iniContent !==
false)
714 $filebase = parse_ini_string($iniContent);
717 foreach ($filebase as $key =>
$value)
724 echo
"Error: Unable to read filebase from remote server. Check if this file exists: $url";
print $lang['FILEMAN_UPLOAD']
Mysqli database class; returns db connection object.
static setSetting($db, $property, $value, $lang)
Set value for property into settings database.
static getSetting($db, $property)
Get and return value for property from settings database.
The sys class - handles yawk's system core functions.
The update class - handles yawk's system update functions.
string $localUpdateSystemPath
recordMigration($db, $successfulMigrations)
record migration in database
bool $migrationSuccessful
__construct()
update constructor. Check if allow_url_fopen is enabled
isServerReachable(string $url)
check if update server is reachable
runMigrations(object $db, array $lang)
Run the migration SQL files.
getUpdateSettings()
get update settings from local update folder (system/update/update.ini) and return array|false
fetchFiles(object $db, string $currentVersion, string $updateVersion, array $lang)
read system/update/updateFiles.ini and fetch files from remote (GitHub) server
readUpdateFilebaseFromServer()
read filebase.ini from update server (https://update.yawk.io/filebase.ini) and return array|false
generateLocalFilebase(object $db, array $lang)
Read all files of current YaWK installation and write each file with path + MD5 hash to ini file to c...
readUpdateIniFromServer()
read update.ini from update server (https://update.yawk.io/update.ini) and return array|false
This class serves methods to create backup from files.
foreach($updateFilebase as $key=> $value) foreach($localFilebase as $filePath=> $localHash) $response