Commit 3de494ea authored by Marc Egger's avatar Marc Egger
Browse files

Refs #10120 update all forms when report operates on Form/FormElement. (finished)

parent 2541d843
Pipeline #3614 failed with stages
in 42 seconds
......@@ -31,35 +31,45 @@ use IMATHUZH\Qfq\Core\Helper\SqlQuery;
// TODO: write tests (file test https://medium.com/weebly-engineering/phpunit-mocking-the-file-system-using-vfsstream-5d7d79b1eb2a)
// TODO: maybe: update button in form list which removes forms from DB if their file was deleted
// TODO: Carsten Fragen: add log messages somewhare?
// TODO: Tests:
// new form => form file created
// form field change => changed in form file
// form name change => old form file deleted, new form file created
// form delete => form file deleted
// form file not writeable => all the above abort before changes to DB are made
// form element create => added to form file
// form element changed => changed in form file
// form element deleted => removed from form file
// form file name changed => old form invalid, new form works
// form file parameter changed => changed in form editor
// form file form element added => added in form editor
// form file form element changed => changed in form editor
// form file form element removed => removed in form editor
// TODO: MAYBE
// TODO: Maybe: solve reference by ID after file change Problem (might not be a big deal since it only happens on git pull)
// Problem: FormEditor and form list might reference a form by an old id. In that case everything has to be reloaded. That's annoying.
// Variant 1: reference form by name in edit and delete button, not by id (only solver part of the problem)
// Variant 2: track old form ids and relay to new form automatically. Track old form ids in new Form column "oldIds"
// TODO: Make unittest for isFormQuery(). Example string:
//$sql = <<<END
//SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged FROM where
//FormFormElement,Form AS f ORDER BY f.name
//
//SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged From Order by Form AS f ORDER BY f.name
//
//SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged FRom having Form AS f ORDER BY f.name
//
//SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged from Form AS f ORDER BY f.name
//END;
// TODO: DON'T DO
// TODO: DONT DO: add column import-modification-date to Form and FormElement and set them to the same as modification date in both exportForm and importForm. compare the two modification dates in the beginning. If the actual modification is after importModification and fileStats are the same in file and in form, then exportForm.
// TODO: DONT DO: what to do if importModificationDate and fileStats have changed? > 1) export Form to new form file named <formName>mergeConflict_timestamp 2) importForm form file, overwrite DB
// TODO: TESTING
// new form => form file created
// form field change => changed in form file
// form name change => old form file deleted, new form file created
// form delete => form file deleted
// form file not writeable => all the above abort before changes to DB are made
// form element create => added to form file
// form element changed => changed in form file
// form element deleted => removed from form file
// form file name changed => old form invalid, new form works
// form file parameter changed => changed in form editor
// form file form element added => added in form editor
// form file form element changed => changed in form editor
// form file form element removed => removed in form editor
///////////////// JUST A TEST, DELETE ME! //////////////
// FormAsFile::exportForm($formName, $this->dbArray[$this->dbIndexQfq]);
// FormAsFile::importForm($formName, $this->dbArray[$this->dbIndexQfq]);
......@@ -103,12 +113,12 @@ class FormAsFile
ERROR_MESSAGE_TO_DEVELOPER => "Form definition file not found or no permission to read file: '$pathFileName'"]),
ERROR_FORM_NOT_FOUND);
if(!file_exists($pathFileName)) {
self::deleteForm($formName, $database);
self::deleteFormDB($formName, $database);
throw $fileReadException;
}
$fileStatsNew = self::formFileStatsJson($pathFileName);
if ($fileStatsNew === false) {
self::deleteForm($formName, $database);
self::deleteFormDB($formName, $database);
throw $fileReadException;
}
$F_ID = F_ID; // can't use constants in strings directly
......@@ -124,7 +134,7 @@ class FormAsFile
// Read form file
$fileContents = file_get_contents($pathFileName);
if ($fileContents === false) {
self::deleteForm($formName, $database);
self::deleteFormDB($formName, $database);
throw $fileReadException;
}
$formFromFile = json_decode($fileContents, true);
......@@ -132,7 +142,7 @@ class FormAsFile
// Delete old form from DB if it exists
if (array_key_exists(F_ID, $formFromDb)) {
$formId = $formFromDb[$F_ID];
self::deleteFormWithId($formId, $database);
self::deleteFormDBWithId($formId, $database);
}
// Insert new Form to DB (after filtering allowed columns and adding columns 'name' and 'fileStats')
......@@ -356,18 +366,6 @@ class FormAsFile
*/
public static function isFormQuery(string $sql): bool
{
// $sql = <<<END
// SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged FROM where
// FormFormElement,Form AS f ORDER BY f.name
//
// SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged From Order by Form AS f ORDER BY f.name
//
// SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged FRom having Form AS f ORDER BY f.name
//
// SELECT CONCAT('p:{{pageId:T}}&form=form&r=', f.id) as _pagee, f.id, f.name, f.title, f.tableName, CONCAT('form=form&r=', f.id) as _Paged from Form AS f ORDER BY f.name
//END;
// find substrings whihch start with FROM and are followed by Form or FormElement
preg_match_all('/(?i)FROM(?-i)(.*?)\b(' . TABLE_NAME_FORM . '|' . TABLE_NAME_FORM_ELEMENT . ')\b/s', $sql, $matches);
......@@ -385,32 +383,38 @@ class FormAsFile
/**
* Import all form files and delete forms in the DB for which there are not form files.
*
* @param Database $database
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
public static function updateAllForms(): void
public static function updateAllForms(Database $database): void
{
// if form dir does not exist, create it
// get content of form dir (flse => error)
// for each file check if it is json with pathinfo()
// does pathinfo always succeed?
// if a file is json, cut json away and use importForm()
// get all forms from DB
// delete all forms which are in DB but not in files
// Import all form files
$files = scandir(self::formPath());
$files = array_diff($files, array('.', '..'));
if ($files === false) {
throw new \UserFormException(json_encode([
ERROR_MESSAGE_TO_USER => "Reading directory failed.",
ERROR_MESSAGE_TO_DEVELOPER => "Can't read directory: " . self::formPath()]),
ERROR_IO_READ_FILE);
}
$formNamesFile = [];
foreach ($files as $file) {
$fileInfo = pathinfo($file);
if ($fileInfo['extension'] === 'json') {
$formNamesFile[] = $fileInfo['filename'];
self::importForm($fileInfo['filename'], $database);
}
}
/////// DEBUG /////////
throw new \UserFormException(json_encode([
ERROR_MESSAGE_TO_USER => json_encode($files, JSON_PRETTY_PRINT),
ERROR_MESSAGE_TO_DEVELOPER => json_encode([], JSON_PRETTY_PRINT)
]));
///////////////////////
// Delete all forms which are in DB but not in files
$NAME = F_NAME;
$FORM = TABLE_NAME_FORM;
$formNamesDB = array_column($database->sql("SELECT `$NAME` FROM `$FORM` ", ROW_REGULAR), $NAME);
$formsToDelete = array_diff($formNamesDB, $formNamesFile);
foreach ($formsToDelete as $formToDelete) {
self::deleteFormDB($formToDelete, $database);
}
}
/**
......@@ -452,7 +456,7 @@ class FormAsFile
* @throws \DbException
* @throws \UserFormException
*/
private static function deleteForm(string $formName, Database $database): void
private static function deleteFormDB(string $formName, Database $database): void
{
$F_ID = F_ID; // can't use constants in strings directly
$F_NAME = F_NAME; // can't use constants in strings directly
......@@ -460,7 +464,7 @@ class FormAsFile
$formFromDb = $database->sql("SELECT `$F_ID` FROM `$TABLE_NAME_FORM` AS f WHERE `f`.`$F_NAME` LIKE ? AND `f`.`deleted`='no'", ROW_EXPECT_0_1,
[$formName], "Multiple forms with the same name: '$formName'");
if (array_key_exists(F_ID, $formFromDb)) {
self::deleteFormWithId($formFromDb[$F_ID], $database);
self::deleteFormDBWithId($formFromDb[$F_ID], $database);
}
}
......@@ -473,7 +477,7 @@ class FormAsFile
* @throws \DbException
* @throws \UserFormException
*/
private static function deleteFormWithId(int $formId, Database $database): void
private static function deleteFormDBWithId(int $formId, Database $database): void
{
$F_ID = F_ID; // can't use constants in strings directly
$TABLE_NAME_FORM = TABLE_NAME_FORM; // can't use constants in strings directly
......@@ -513,32 +517,32 @@ class FormAsFile
// validate form name
if (!HelperFile::isValidFileName($formName)) {
throw new \UserFormException(json_encode([
ERROR_MESSAGE_TO_USER => 'Invalid form name.',
ERROR_MESSAGE_TO_DEVELOPER => "Reading/Writing Form file failed: Form name '$formName' not valid. Name may only consist of alphanumeric characters and _ . -"]),
ERROR_MESSAGE_TO_USER => 'Reading/Writing Form file failed.',
ERROR_MESSAGE_TO_DEVELOPER => "Form name '$formName' not valid. Name may only consist of alphanumeric characters and _ . -"]),
ERROR_FORM_INVALID_NAME);
}
// create form folder path
if (!is_dir(self::formPath())) {
$success = mkdir(self::formPath(), 0777, true);
if ($success === false) {
throw new \UserFormException(json_encode([
ERROR_MESSAGE_TO_USER => "Can't create form file path.",
ERROR_MESSAGE_TO_DEVELOPER => "Can't create path: " . self::formPath()]),
ERROR_IO_WRITE_FILE);
}
}
return self::formPath() . '/' . $formName . ".json";
}
/**
* Return the path of the form directory relative to CWD
* Return the path of the form directory relative to CWD.
* Create path if it doesn't exist.
*
* @return string
* @throws \UserFormException
*/
private static function formPath(): string
{
return HelperFile::correctRelativePathFileName(SYSTEM_FORM_FILE_PATH);
$formPath = HelperFile::correctRelativePathFileName(SYSTEM_FORM_FILE_PATH);
if (!is_dir($formPath)) {
$success = mkdir($formPath, 0777, true);
if ($success === false) {
throw new \UserFormException(json_encode([
ERROR_MESSAGE_TO_USER => "Can't create form file path.",
ERROR_MESSAGE_TO_DEVELOPER => "Can't create path: " . $formPath]),
ERROR_IO_WRITE_FILE);
}
}
return $formPath;
}
}
\ No newline at end of file
......@@ -492,9 +492,9 @@ class Report {
$this->store->setVar(SYSTEM_SQL_FINAL, $sql, STORE_SYSTEM);
// import form files if changed
// import form files if changed + delete Forms without form file
if (FormAsFile::isFormQuery($sql)) {
FormAsFile::updateAllForms();
FormAsFile::updateAllForms($this->db);
}
//Execute SQL. All errors have been already catched.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment