Commit e7b7101e authored by Marc Egger's avatar Marc Egger
Browse files

Refs #12145 import system forms from files instead of db

parent 495b656e
Pipeline #5107 passed with stages
in 3 minutes and 53 seconds
......@@ -1478,6 +1478,7 @@ const T3DATA_HEADER = 'header';
const T3DATA_REPORT_PATH_FILENAME = 'reportPathFileName';
const REPORT_INLINE_BODYTEXT = 'bodytext';
const REPORT_FILE_EXTENSION = '.qfqr';
const FORM_FILE_EXTENSION = '.json';
// Special Column to check for uploads
const COLUMN_PATH_FILE_NAME = 'pathFileName';
......
......@@ -178,6 +178,8 @@ class DatabaseUpdate {
$this->db->playSqlFile(__DIR__ . '/../../Sql/formEditor.sql');
}
FormAsFile::importSystemForms($this->db);
Logger::logMessage(date('Y.m.d H:i:s ') . ": Updated from QFQ version '$old' to '$new'", Path::absoluteQfqLogFile());
// Finally write the latest version number.
......
......@@ -43,7 +43,7 @@ class FormAsFile {
$fileReadException = function () use ($absoluteFormFilePath, $database, $formName) {
// Search for form name: convert all to lowercase and compare.
$addHint = implode(', ', array_filter(self::formFileNames($database),
$addHint = implode(', ', array_filter(self::formFileNames(self::formPath($database)),
function ($f) use ($formName) {
return strtolower($f) === strtolower($formName);
}));
......@@ -90,6 +90,25 @@ class FormAsFile {
return true;
}
/**
* Import all forms found in Resources/Private/Form into the database.
*
* @param Database $database
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
public static function importSystemForms(Database $database) {
$absoluteSystemFormPath = Path::absoluteExt(Path::EXT_TO_FORM_SYSTEM);
$formNames = self::formFileNames($absoluteSystemFormPath);
# import each json file into db (overwrite)
foreach ($formNames as $formName) {
$fileContents = HelperFile::file_get_contents(Path::join($absoluteSystemFormPath, $formName . FORM_FILE_EXTENSION));
self::jsonToDatabase($formName, $fileContents, $database);
}
}
/**
* Remove the form from the DB and insert it using the given form json.
*
......@@ -264,7 +283,7 @@ class FormAsFile {
}
// copy file
$absoluteBackupFilePath = self::newBackupPathFileName(basename($absoluteFormFilePath, '.json'), 'file');
$absoluteBackupFilePath = self::newBackupPathFileName(basename($absoluteFormFilePath, FORM_FILE_EXTENSION), 'file');
$success = copy($absoluteFormFilePath, $absoluteBackupFilePath);
if ($success === false) {
Thrower::userFormException('Error while trying to backup form file.', "Can't copy file $absoluteFormFilePath to $absoluteBackupFilePath");
......@@ -433,7 +452,7 @@ class FormAsFile {
public static function importAllForms(Database $database, bool $enforceWritable = false, bool $deleteFromDB = false) // : void
{
// Import all form files
$formFileNames = self::formFileNames($database);
$formFileNames = self::formFileNames(self::formPath($database));
foreach ($formFileNames as $formFileName) {
self::importForm($formFileName, $database);
if ($enforceWritable) {
......@@ -468,7 +487,7 @@ class FormAsFile {
self::exportForm($formNameDB, $database);
}
if ($deleteFiles) {
$formFileNames = self::formFileNames($database);
$formFileNames = self::formFileNames(self::formPath($database));
$filesToDelete = array_diff($formFileNames, $formNamesDB);
foreach ($filesToDelete as $fileToDelete) {
self::deleteFormFile($fileToDelete, $database, "Export all forms from database. No form with name '$fileToDelete' in database.");
......@@ -661,7 +680,7 @@ class FormAsFile {
ERROR_MESSAGE_TO_DEVELOPER => "Form name '$formName' not valid. Name may only consist of alphanumeric characters and _ . -"]),
ERROR_FORM_INVALID_NAME);
}
return self::formPath($database) . '/' . $formName . ".json";
return self::formPath($database) . '/' . $formName . FORM_FILE_EXTENSION;
}
/**
......@@ -709,22 +728,16 @@ class FormAsFile {
}
/**
* Return array of form file names contained in the form path (without suffix).
* Return array of form file names contained in the given directory (without suffix).
*
* @param Database $database
* @param string $absoluteDirPath
* @return array|false [formName]
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
*/
private static function formFileNames(Database $database): array {
$formPath = self::formPath($database);
$files = scandir($formPath);
private static function formFileNames(string $absoluteDirPath): array {
$files = scandir($absoluteDirPath);
if ($files === false) {
throw new \UserFormException(json_encode([
ERROR_MESSAGE_TO_USER => "Reading directory failed.",
ERROR_MESSAGE_TO_DEVELOPER => "Can't read directory: " . $formPath]),
ERROR_IO_READ_FILE);
Thrower::userFormException("Reading directory failed.", "Can't read directory: " . $absoluteDirPath);
}
return $jsonFileNames = array_reduce($files, function ($result, $file) {
$fileInfo = pathinfo($file);
......@@ -849,12 +862,12 @@ class FormAsFile {
}
}
$absoluteBackupFilePath = Path::join($absoluteBackupPath, $formName . '.json.' . date('Y-m-d_H-i-s') . ".$tag");
$absoluteBackupFilePath = Path::join($absoluteBackupPath, $formName . FORM_FILE_EXTENSION . '.' . date('Y-m-d_H-i-s') . ".$tag");
// add index to filename if backup file with current timestamp already exists
$index = 1;
while (file_exists($absoluteBackupFilePath)) {
$absoluteBackupFilePath = Path::join($absoluteBackupPath, $formName . '.json.' . date('Y-m-d_H-i-s') . ".$index.$tag");
$absoluteBackupFilePath = Path::join($absoluteBackupPath, $formName . FORM_FILE_EXTENSION . '.' . date('Y-m-d_H-i-s') . ".$index.$tag");
$index++;
if ($index > 20) {
Thrower::userFormException('Error while trying to backup form file.', 'Infinite loop.');
......
......@@ -36,6 +36,7 @@ class Path
// Report
const EXT_TO_REPORT_SYSTEM = 'Resources/Private/Report';
const EXT_TO_FORM_SYSTEM = 'Resources/Private/Form';
// Javascript
const EXT_TO_JAVASCRIPT = 'Resources/Public/JavaScript';
......
......@@ -1294,6 +1294,11 @@ class Report {
break;
case COLUMN_FORM_JSON:
// DEBUG !!
// Thrower::userFormException($this->store->getVar(TOKEN_DB_INDEX, STORE_TYPO3));
// END DEBUG !!
$content .= Support::encryptDoubleCurlyBraces(FormAsFile::renderColumnFormJson($columnValue, $this->db));
break;
case COLUMN_FORM_JSON_BASE_64:
......
......@@ -601,7 +601,7 @@ class Config {
foreach ($names as $name) {
if (!isset($config[$name])) {
throw new \UserFormException ("Missing configuration in `" . CONFIG_QFQ_PHP . "`: $name", ERROR_MISSING_CONFIG_INI_VALUE);
throw new \UserFormException ("Missing configuration in `" . CONFIG_QFQ_JSON . "`: $name", ERROR_MISSING_CONFIG_INI_VALUE);
}
}
}
......
This diff is collapsed.
{
"title": "Copy a form",
"noteInternal": "",
"tableName": "Clipboard",
"primaryKey": "",
"permitNew": "sip",
"permitEdit": "sip",
"restMethod": "",
"escapeTypeDefault": "c",
"render": "bootstrap",
"requiredParameterNew": "",
"requiredParameterEdit": "",
"dirtyMode": "exclusive",
"showButton": "close,save",
"multiMode": "none",
"multiSql": "",
"multiDetailForm": "",
"multiDetailFormParameter": "",
"forwardMode": "url-sip-skip-history",
"forwardPage": "?id={{pageId:T}}&form=form&r={{formId:P0}}",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"parameter": "submitButtonText = Copy Form",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"recordLockTimeoutSeconds": 900,
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41",
"FormElement_ff": [
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "idSrc",
"label": "Source Form",
"mode": "show",
"modeSql": "",
"class": "native",
"type": "select",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "auto",
"checkPattern": "",
"onChange": "",
"ord": 10,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "{{!SELECT f.id, CONCAT(f.name, \" \/ \", f.title) FROM Form AS f ORDER BY f.name}}",
"parameter": "",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "myNewFormName",
"label": "New Form Name",
"mode": "show",
"modeSql": "",
"class": "native",
"type": "text",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "auto",
"checkPattern": "",
"onChange": "",
"ord": 20,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "",
"parameter": "",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "clearClipboard",
"label": "",
"mode": "show",
"modeSql": "",
"class": "action",
"type": "beforeSave",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "auto",
"checkPattern": "",
"onChange": "",
"ord": 100,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "",
"parameter": "sqlValidate={{!SELECT f.id FROM Form AS f WHERE f.name LIKE \"{{myNewFormName:FE:alnumx}}\" LIMIT 1}}\nexpectRecords = 0\nmessageFail = There is already a form \"{{myNewFormName:FE}}\"\nsqlAfter={{DELETE FROM Clipboard WHERE cookie=\"{{cookieQfq:C0:alnumx}}\" }}",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "updateClipboardRecord",
"label": "",
"mode": "show",
"modeSql": "",
"class": "action",
"type": "afterSave",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "auto",
"checkPattern": "",
"onChange": "",
"ord": 110,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "",
"parameter": "sqlAfter={{UPDATE Clipboard AS c, Form AS f SET c.cookie=\"{{cookieQfq:C0:alnumx}}\", c.formIdPaste=f.id \/* PasteForm *\/ WHERE c.id={{id:R}} AND f.name=\"{{form:SE}}\" }}",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "formId",
"label": "",
"mode": "show",
"modeSql": "",
"class": "action",
"type": "paste",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "auto",
"checkPattern": "",
"onChange": "",
"ord": 200,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "{{!SELECT {{id:P}} AS id, \"{{myNewFormName:FE:allbut}}\" AS name}}",
"parameter": "recordDestinationTable=Form",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "formElementId",
"label": "",
"mode": "show",
"modeSql": "",
"class": "action",
"type": "paste",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "auto",
"checkPattern": "",
"onChange": "",
"ord": 210,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "{{!SELECT fe.id AS id, {{formId:P}} AS formId FROM FormElement AS fe WHERE fe.formId={{id:P}} ORDER BY fe.ord}}",
"parameter": "recordDestinationTable=FormElement\ntranslateIdColumn=feIdContainer",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
}
]
}
\ No newline at end of file
{
"title": "autoCron",
"noteInternal": "",
"tableName": "Cron",
"primaryKey": "",
"permitNew": "sip",
"permitEdit": "sip",
"restMethod": "",
"escapeTypeDefault": "c",
"render": "bootstrap",
"requiredParameterNew": "",
"requiredParameterEdit": "",
"dirtyMode": "none",
"showButton": "new,delete,close,save",
"multiMode": "none",
"multiSql": "",
"multiDetailForm": "",
"multiDetailFormParameter": "",
"forwardMode": "auto",
"forwardPage": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"parameter": "dbIndex={{indexQfq:Y}}",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"recordLockTimeoutSeconds": 900,
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41",
"FormElement_ff": [
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "status",
"label": "Enabled",
"mode": "show",
"modeSql": "",
"class": "native",
"type": "checkbox",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "alnumx",
"checkPattern": "",
"onChange": "",
"ord": 10,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "",
"parameter": "",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "yes",
"enabled": "yes",
"name": "type",
"label": "Type",
"mode": "show",
"modeSql": "",
"class": "native",
"type": "radio",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "alnumx",
"checkPattern": "",
"onChange": "",
"ord": 20,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "",
"parameter": "buttonClass=btn-default",
"parameterLanguageA": "",
"parameterLanguageB": "",
"parameterLanguageC": "",
"parameterLanguageD": "",
"clientJs": "",
"feGroup": "",
"deleted": "no",
"modified": "2021-04-06 12:05:41",
"created": "2021-04-06 12:05:41"
},
{
"dynamicUpdate": "no",
"enabled": "yes",
"name": "nextRun",
"label": "Next run",
"mode": "show",
"modeSql": "",
"class": "native",
"type": "text",
"subrecordOption": "",
"encode": "specialchar",
"checkType": "alnumx",
"checkPattern": "",
"onChange": "",
"ord": 30,
"tabindex": 0,
"size": "",
"maxLength": "",
"labelAlign": "default",
"bsLabelColumns": "",
"bsInputColumns": "",
"bsNoteColumns": "",
"rowLabelInputNote": "row,label,\/label,input,\/input,note,\/note,\/row",
"note": "",
"adminNote": "",
"tooltip": "",
"placeholder": "",
"value": "",
"sql1": "",
"parameter": "extraButtonInfo = Cronjob will be started if specified timestamp is over. If timestamp=0: Job will never be started<br>Every time the jobs runs, this timestamp will be increased automatically by \"frequency\".",