Move ticks from filesystem into database. I'm going to preserve the filesystem and just delete it manually. I'll add a login warning, but I'm pretty sure I'm the only person who'll ever be affected by this. Co-authored-by: Greg Sarjeant <1686767+gsarjeant@users.noreply.github.com> Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/19
153 lines
5.4 KiB
PHP
153 lines
5.4 KiB
PHP
<?php
|
|
// Validates that required directories exists
|
|
// and files have correct formats
|
|
class Filesystem {
|
|
public function validate(): void{
|
|
$this->validateStorageDir();
|
|
$this->validateStorageSubdirs();
|
|
$this->migrateTickFiles();
|
|
$this->moveTicksToDatabase();
|
|
}
|
|
|
|
// Make sure the storage/ directory exists and is writable
|
|
private function validateStorageDir(): void{
|
|
if (!is_dir(STORAGE_DIR)) {
|
|
throw new SetupException(
|
|
STORAGE_DIR . "does not exist. Please check your installation.",
|
|
'storage_missing'
|
|
);
|
|
}
|
|
|
|
if (!is_writable(STORAGE_DIR)) {
|
|
throw new SetupException(
|
|
STORAGE_DIR . "is not writable. Exiting.",
|
|
'storage_permissions'
|
|
);
|
|
}
|
|
}
|
|
|
|
// validate that the required storage subdirectories exist
|
|
// attempt to create them if they don't
|
|
private function validateStorageSubdirs(): void {
|
|
$storageSubdirs = array();
|
|
$storageSubdirs[] = CSS_UPLOAD_DIR;
|
|
$storageSubdirs[] = DATA_DIR;
|
|
$storageSubdirs[] = TICKS_DIR;
|
|
|
|
foreach($storageSubdirs as $storageSubdir){
|
|
if (!is_dir($storageSubdir)) {
|
|
if (!mkdir($storageSubdir, 0770, true)) {
|
|
throw new SetupException(
|
|
"Failed to create required directory: $dir",
|
|
'directory_creation'
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!is_writable($storageSubdir)) {
|
|
if (!chmod($storageSubdir, 0770)) {
|
|
throw new SetupException(
|
|
"Required directory is not writable: $dir",
|
|
'directory_permissions'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Delete this sometime before 1.0
|
|
// Add mood to tick files
|
|
private function migrateTickFiles(): void {
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator(TICKS_DIR, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($files as $file) {
|
|
if ($file->isFile() && str_ends_with($file->getFilename(), '.txt')) {
|
|
$this->migrateTickFile($file->getPathname());
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Delete this sometime before 1.0
|
|
// Add mood field to tick files if necessary
|
|
private function migrateTickFile($filepath): void {
|
|
$lines = file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
$modified = false;
|
|
|
|
// &$line creates a reference to the line in the array ($lines)
|
|
// so I can modify it in place
|
|
foreach ($lines as &$line) {
|
|
$fields = explode('|', $line);
|
|
if (count($fields) === 2) {
|
|
// Convert id|text to id|emoji|text
|
|
$line = $fields[0] . '||' . $fields[1];
|
|
$modified = true;
|
|
}
|
|
}
|
|
unset($line);
|
|
|
|
if ($modified) {
|
|
file_put_contents($filepath, implode("\n", $lines) . "\n");
|
|
// TODO: log properly
|
|
}
|
|
}
|
|
|
|
// TODO: Delete this sometime before 1.0
|
|
// Move ticks into database
|
|
private function moveTicksToDatabase(){
|
|
// It's a temporary migration function, so I'm not going to sweat the
|
|
// order of operations to let me reuse the global database.
|
|
$db = Database::get();
|
|
$count = $db->query("SELECT COUNT(*) FROM tick")->fetchColumn();
|
|
|
|
// Only migrate from filesystem if there are no ticks already in the database.
|
|
if ($count !== 0){
|
|
return;
|
|
}
|
|
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator(TICKS_DIR, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($files as $file) {
|
|
if ($file->isFile() && str_ends_with($file->getFilename(), '.txt')) {
|
|
// Construct the date from the path and filename
|
|
$dir = pathinfo($file, PATHINFO_DIRNAME);
|
|
$dir_parts = explode('/', trim($dir, '/'));
|
|
[$year, $month] = array_slice($dir_parts, -2);
|
|
$day = pathinfo($file, PATHINFO_FILENAME);
|
|
|
|
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
// Get the time and the text, but discard the mood.
|
|
// I've decided against using it
|
|
$fields = explode('|', $line);
|
|
$time = $fields[0];
|
|
$tick = $fields[2];
|
|
|
|
$dateTime = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', "$year-$month-$day $time");
|
|
$tickDateTimeUTC = $dateTime->format('Y-m-d H:i:s');
|
|
|
|
$ticks[] = [$tickDateTimeUTC, $tick];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort the ticks by dateTime
|
|
usort($ticks, function($a, $b) {
|
|
return strcmp($a[0], $b[0]);
|
|
});
|
|
|
|
// Save ticks to database
|
|
foreach ($ticks as $tick){
|
|
// Yes, silly, but I'm testing out the datetime/string SQLite conversion
|
|
$dateTime = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', "$tick[0]");
|
|
$timestamp = $dateTime->format('Y-m-d H:i:s');
|
|
$tickText = $tick[1];
|
|
|
|
$stmt = $db->prepare("INSERT INTO tick(timestamp, tick) values (?, ?)");
|
|
$stmt->execute([$timestamp, $tickText]);
|
|
}
|
|
}
|
|
} |