Commit d0193ea9 authored by Carsten  Rose's avatar Carsten Rose
Browse files

#2739: beforeDelete / afterDelete - dieser commit hat noch bugs.

parent f11df9ed
......@@ -260,7 +260,9 @@ Server Response
### Record delete
Request the deletion of the record identified by the SIP.
Request the deletion of the record identified by the SIP. The SIP references always the recordId SIP_
might reference a form 'SIP_FORM' (whereas a tableName is derived from) or
reference a tableName directly 'SIP_TABLE'.
Request
: api/delete.php
......
......@@ -11,7 +11,7 @@ namespace qfq;
use qfq;
require_once(__DIR__ . '/../qfq/QuickFormQuery.php');
//require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/Constants.php');
......@@ -54,10 +54,51 @@ try {
$qfq = new \qfq\QuickFormQuery(['bodytext' => '']);
$result = $qfq->delete();
$qfq->delete();
$targetUrl = Store::getVar(SIP_TARGET_URL, STORE_SIP);
$modeAnswer = Store::getVar(SIP_MODE_ANSWER, STORE_SIP);
switch ($modeAnswer) {
case MODE_JSON:
$answer[API_MESSAGE] = 'delete: success';
$answer[API_REDIRECT] = API_ANSWER_REDIRECT_CLIENT;
$answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
$result[MSG_HEADER] = "Content-Type: application/json";
$result[MSG_CONTENT] = json_encode($answer);
break;
case MODE_HTML:
if ($targetUrl === false || $targetUrl === '') {
$result[MSG_CONTENT] = 'Missing target URL. ' . ERROR_MISSING_VALUE;
}
$result[MSG_HEADER] = "Location: $targetUrl";
$result[MSG_CONTENT] = '';
break;
default:
throw new CodeException('Unknown mode: ' . $modeAnswer, ERROR_UNKNOWN_MODE);
break;
}
} catch (qfq\UserFormException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
$val = Store::getVar(SYSTEM_FORM_ELEMENT, STORE_SYSTEM);
if ($val !== false)
$answer[API_FIELD_NAME] = $val;
$val = Store::getVar(SYSTEM_FORM_ELEMENT_MESSAGE, STORE_SYSTEM);
if ($val !== false)
$answer[API_FIELD_MESSAGE] = $val;
$result[MSG_CONTENT] = json_encode($answer);
} catch (qfq\CodeException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
} catch (qfq\DbException $e) {
......
......@@ -1446,8 +1446,20 @@ abstract class AbstractBuildForm {
}
if ($flagDelete) {
$s = $this->createDeleteUrl($targetTableName, $row[$nameColumnId], RETURN_SIP);
$rowHtml .= Support::wrapTag('<td>', Support::wrapTag("<button type='button' class='record-delete btn btn-default' data-sip='$s'>", '<span class="glyphicon ' . GLYPH_ICON_DELETE . '"></span>'));
$toolTip = 'Delete';
if ($this->showDebugInfo) {
$toolTip .= PHP_EOL . "form = '" . $this->formSpec[F_NAME] . "'" . PHP_EOL . "table = '" . $this->formSpec[F_TABLE_NAME] . "'" . PHP_EOL . "r = '" . $row[$nameColumnId] . "'";
}
// $buttonDelete = $this->buildButtonCode('delete-button', $toolTip, GLYPH_ICON_DELETE, $disabled);
$s = $this->createDeleteUrl($formElement[SUBRECORD_PARAMETER_FORM], $targetTableName, $row[$nameColumnId], RETURN_SIP);
// $rowHtml .= Support::wrapTag('<td>', Support::wrapTag("<button type='button' class='record-delete btn btn-default' data-sip='$s'>", '<span class="glyphicon ' . GLYPH_ICON_DELETE . '"></span>'));
$rowHtml .= Support::wrapTag('<td>', Support::wrapTag("<button type='button' class='record-delete btn btn-default' data-sip='$s' " . Support::doAttribute('title', $toolTip) . ">", '<span class="glyphicon ' . GLYPH_ICON_DELETE . '"></span>'));
}
Support::setIfNotSet($row, FE_SUBRECORD_ROW_CLASS);
......@@ -1729,14 +1741,23 @@ abstract class AbstractBuildForm {
/**
* Create a link (incl. SIP) to delete the current record.
*
* @param string $formName if there is a form, specify that
* @param string $tableName if there is no form , specify the table from where to delete the record.
* @param int $recordId record to delete
* @param string $mode
* * mode=RETURN_URL: return complete URL
* * mode=RETURN_SIP: returns only the sip
* * mode=RETURN_ARRAY: returns array with url ('_url') and all decoded and created parameters.
* @return string String: "API_DIR/delete.php?sip=...."
* @throws CodeException
*/
public function createDeleteUrl($table, $recordId, $mode = RETURN_URL) {
public function createDeleteUrl($formName, $tableName, $recordId, $mode = RETURN_URL) {
//TODO: Umstellen auf Benutzung der Link Klasse.
$queryStringArray = [
SIP_TABLE => $table,
SIP_FORM => $formName,
SIP_TABLE => $tableName,
SIP_RECORD_ID => $recordId,
SIP_MODE_ANSWER => MODE_JSON
];
......@@ -2228,16 +2249,16 @@ abstract class AbstractBuildForm {
* @param $toolTip
* @return string
*/
private function createDeleteLink($table, $recordId, $symbol, $toolTip) {
if ($this->showDebugInfo) {
$toolTip .= PHP_EOL . "table = '$table'" . PHP_EOL . "id = '$recordId'";
}
$url = $this->createDeleteUrl($table, $recordId);
return Support::wrapTag('<a ' . Support::doAttribute('href', $url) . ' title="' . $toolTip . '">', $symbol);
}
// private function createDeleteLink($table, $recordId, $symbol, $toolTip) {
//
// if ($this->showDebugInfo) {
// $toolTip .= PHP_EOL . "table = '$table'" . PHP_EOL . "id = '$recordId'";
// }
//
// $url = $this->createDeleteUrl('', $table, $recordId);
//
// return Support::wrapTag('<a ' . Support::doAttribute('href', $url) . ' title="' . $toolTip . '">', $symbol);
//
// }
}
\ No newline at end of file
......@@ -170,7 +170,7 @@ class BuildFormBootstrap extends AbstractBuildForm {
$toolTip = 'Delete';
if ($this->showDebugInfo && $recordId > 0) {
$toolTip .= PHP_EOL . "table = '" . $this->formSpec[F_TABLE_NAME] . "'" . PHP_EOL . "r = '" . $recordId . "'";
$toolTip .= PHP_EOL . "form = '" . $this->formSpec[F_NAME] . "'" . PHP_EOL . "table = '" . $this->formSpec[F_TABLE_NAME] . "'" . PHP_EOL . "r = '" . $recordId . "'";
}
$disabled = ($recordId > 0) ? '' : 'disabled';
......@@ -345,7 +345,7 @@ class BuildFormBootstrap extends AbstractBuildForm {
$tabId = $this->getTabId();
if (0 < ($recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP))) {
$deleteUrl = $this->createDeleteUrl($this->formSpec[F_TABLE_NAME], $recordId);
$deleteUrl = $this->createDeleteUrl($this->formSpec[F_NAME], $this->formSpec[F_TABLE_NAME], $recordId);
}
$actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
......
......@@ -77,7 +77,7 @@ class BuildFormTable extends AbstractBuildForm {
$formEditUrl = $this->createFormEditUrl();
$html .= "<p><a " . Support::doAttribute('href', $formEditUrl) . ">Edit</a><small>[$sipParamString]</small></p>";
$deleteUrl = $this->createDeleteUrl($this->formSpec[F_TABLE_NAME], $this->store->getVar(SIP_RECORD_ID, STORE_SIP));
$deleteUrl = $this->createDeleteUrl($this->formSpec[F_NAME], $this->formSpec[F_TABLE_NAME], $this->store->getVar(SIP_RECORD_ID, STORE_SIP));
$html .= "<p><a " . Support::doAttribute('href', $deleteUrl) . ">Delete</a>";
$html .= $this->wrapItem(WRAP_SETUP_TITLE, $this->formSpec['title'], true);
......
......@@ -23,6 +23,7 @@ const SESSION_FE_USER_GROUP = 'feUserGroup';
const FORM_LOAD = 'form_load';
const FORM_SAVE = 'form_save';
const FORM_UPDATE = 'form_update';
const FORM_DELETE = 'form_delete';
const FORM_PERMISSION_SIP = 'sip';
const FORM_PERMISSION_LOGGED_IN = 'logged_id';
const FORM_PERMISSION_LOGGED_OUT = 'logged_out';
......
......@@ -37,19 +37,16 @@ class Delete {
* If the table has a column named COLUMN_PATH_FILE_NAME and the value of that specific record column points
* to a file: delete such a file if their are no other records in the same table which also have a reference to that file.
*
* @param array $form
* @param string $tableName
* @param integer $recordId
* @param array $msg
* @return bool
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
public function process(array $form, $recordId, array &$msg) {
$rc = false;
public function process($tableName, $recordId) {
$msg = array();
if (!isset($form[F_TABLE_NAME]) || $form[F_TABLE_NAME] === '' || $form[F_TABLE_NAME] === false ) {
if ($tableName === false || $tableName === '') {
throw new CodeException('Missing table name', ERROR_MISSING_TABLE_NAME);
}
......@@ -65,38 +62,52 @@ class Delete {
}
// Read record first.
$row = $this->db->sql("SELECT * FROM " . $form[F_TABLE_NAME] . " WHERE id=?", ROW_EXPECT_0_1, [$recordId]);
$row = $this->db->sql("SELECT * FROM $tableName WHERE id=?", ROW_EXPECT_0_1, [$recordId]);
if (count($row) > 0) {
foreach ($row AS $key => $file) {
if (false === strpos($key, COLUMN_PATH_FILE_NAME)) {
continue;
}
// check if there is a file referenced in the record which have to be deleted too.
if ($file !== '' && is_writable($file)) {
// check if there are other records referencing the same file: do not delete the file now.
// This check won't find duplicates, if they are spread over different columns or tables.
$samePathFileName = $this->db->sql("SELECT COUNT(id) AS cnt FROM " . $form[F_TABLE_NAME] . " WHERE " . $key . " LIKE ?", ROW_EXPECT_1, [$file]);
if ($samePathFileName['cnt'] === 1) {
if (!unlink($file)) {
$msg[MSG_CONTENT] = "Error deleting file: " . $file;
$msg[MSG_ERROR_CODE] = ERROR_IO_UNLINK;
}
}
}
}
$this->deleteReferencedFiles($row, $tableName);
$this->db->sql("DELETE FROM " . $form[F_TABLE_NAME] . " WHERE id =? LIMIT 1", ROW_REGULAR, [$recordId]);
$rc = true;
$this->db->sql("DELETE FROM $tableName WHERE id =? LIMIT 1", ROW_REGULAR, [$recordId]);
} else {
$msg[MSG_CONTENT] = "Record $recordId not found in table '" . $form[F_TABLE_NAME] . "'.";
$msg[MSG_ERROR_CODE] = ERROR_RECORD_NOT_FOUND;
throw new UserFormException("Record $recordId not found in table '$tableName'.", ERROR_RECORD_NOT_FOUND);
}
chdir($cwd);
}
/**
* Iterates over array $row and searches for column names with substring COLUMN_PATH_FILE_NAME.
* For any found, check if it references a writeable file.
* If yes: check if there are other records (same table, same column) which references the same file.
* If no: delete the file
* If yes: do nothing, continue with the next column.
* If no: do nothing, continue with the next column.
*
* @param array $row
* @param $tableName
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
private function deleteReferencedFiles(array $row, $tableName) {
foreach ($row AS $key => $file) {
if (false === strpos($key, COLUMN_PATH_FILE_NAME)) {
continue;
}
return $rc;
// check if there is a file referenced in the record which have to be deleted too.
if ($file !== '' && is_writable($file)) {
// check if there are other records referencing the same file: do not delete the file now.
// This check won't find duplicates, if they are spread over different columns or tables.
$samePathFileName = $this->db->sql("SELECT COUNT(id) AS cnt FROM $tableName WHERE $key LIKE ?", ROW_EXPECT_1, [$file]);
if ($samePathFileName['cnt'] === 1) {
if (!unlink($file)) {
throw new UserFormException("Error deleting file: $file", ERROR_IO_UNLINK);
}
}
}
}
}
}
\ No newline at end of file
......@@ -201,11 +201,13 @@ class QuickFormQuery {
/**
* Process form.
* $mode=FORM_LOAD: The whole form will be rendered as HTML Code, including the values of all form elements
* $mode=FORM_UPDATE: States and values of all form elements will be returned as JSON.
* $mode=FORM_SAVE: The submitted form will be saved. Return Failure or Success as JSON.
* $mode=
* FORM_LOAD: The whole form will be rendered as HTML Code, including the values of all form elements
* FORM_UPDATE: States and values of all form elements will be returned as JSON.
* FORM_SAVE: The submitted form will be saved. Return Failure or Success as JSON.
* FORM_DELETE:
*
* @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
* @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE | FORM_DELETE
* @return array|string
* @throws CodeException
* @throws UserFormException
......@@ -221,11 +223,26 @@ class QuickFormQuery {
}
$formName = $this->loadFormSpecification($mode, $foundInStore);
if ($formName === false) {
if ($formName === false && $mode !== FORM_DELETE) {
// No form found: do nothing
return '';
}
$sipFound = $this->validateForm($foundInStore);
if ($formName !== false) {
// Validate only if there is a 'real' form (not a FORM_DELETE with only a tablename).
$sipFound = $this->validateForm($foundInStore);
} else {
// FORM_DELETE without a form definition: Fake the form wiht only a tableName.
$table = $this->store->getVar(SIP_TABLE, STORE_SIP);
if ($table === false) {
return '';
}
$sipFound = true;
$this->formSpec[F_NAME] = '';
$this->formSpec[F_TABLE_NAME] = $table;
}
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT);
// For 'new' record always create a new TAB-uniq (for this current form, nowhere else used) SIP.
// With such a TAB-uniq SIP, multiple TABs and following repeated NEWs are easily implemented.
......@@ -233,20 +250,26 @@ class QuickFormQuery {
$this->store->createSipAfterFormLoad($formName);
}
$this->store->fillStoreTableDefaultColumnType($this->formSpec[F_TABLE_NAME]);
if ($mode === FORM_DELETE) {
switch ($this->formSpec['render']) {
case 'plain':
$build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative);
break;
case 'table':
$build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative);
break;
case 'bootstrap':
$build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative);
break;
default:
throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
$build = new Delete();
} else {
$this->store->fillStoreTableDefaultColumnType($this->formSpec[F_TABLE_NAME]);
switch ($this->formSpec['render']) {
case 'plain':
$build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative);
break;
case 'table':
$build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative);
break;
case 'bootstrap':
$build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative);
break;
default:
throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
}
}
$formAction = new FormAction($this->formSpec, $this->db, $this->phpUnit);
......@@ -260,6 +283,14 @@ class QuickFormQuery {
$formAction->elements($recordId, $this->feSpecAction, FE_TYPE_AFTER_LOAD);
break;
case FORM_DELETE:
$formAction->elements($recordId, $this->feSpecAction, FE_TYPE_BEFORE_DELETE);
$build->process($this->formSpec[F_TABLE_NAME], $recordId);
$formAction->elements($recordId, $this->feSpecAction, FE_TYPE_AFTER_DELETE);
break;
case FORM_SAVE:
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
......@@ -391,7 +422,12 @@ class QuickFormQuery {
['no', $this->formSpec["id"], 'native']);
break;
case FORM_DELETE:
$this->feSpecNative = array();
break;
default:
break;
}
HelperFormElement::explodeParameterInArrayElements($this->feSpecNative);
......@@ -416,7 +452,7 @@ class QuickFormQuery {
*
* @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE
* @param string $foundInStore
* @return array|bool|mixed|null|string Formname (Form.name) or FALSE, if no formname found.
* @return bool|string Formname (Form.name) or FALSE (if no formname found)
* @throws CodeException
* @throws UserFormException
*/
......@@ -429,7 +465,8 @@ class QuickFormQuery {
break;
case FORM_SAVE:
case FORM_UPDATE:
$store = STORE_SIP;
case FORM_DELETE:
$store = STORE_SIP;
break;
default:
throw new CodeException("Unknown mode: $mode.", ERROR_UNKNOWN_MODE);
......@@ -438,6 +475,10 @@ class QuickFormQuery {
$storeFormName = $this->store->getVar(SIP_FORM, $store, '', $foundInStore);
$formName = $this->eval->parse($storeFormName, 0, $dummy, $foundInStore);
// if($mode===FORM_DELETE && $formName===false) {
// return "";
// }
// If the formname is '': no formname name.
if ($formName === '' || $foundInStore === '')
return false;
......@@ -614,67 +655,11 @@ class QuickFormQuery {
/**
* Delete a record (tablename and recordid are given) or process a 'delete form'
*
* @return mixed
* @throws CodeException
*/
public function delete() {
$msg = array();
#TODO: implement 'delete form'
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
$table = $this->store->getVar(SIP_TABLE, STORE_SIP);
$form = $this->store->getVar(SIP_FORM, STORE_SIP);
$targetUrl = $this->store->getVar(SIP_TARGET_URL, STORE_SIP);
$modeAnswer = $this->store->getVar(SIP_MODE_ANSWER, STORE_SIP);
// fake until 'delete by form' is implemented too
$form = [F_TABLE_NAME => $table];
$delete = new Delete();
$rc = $delete->process($form, $recordId, $msg);
switch ($modeAnswer) {
case MODE_JSON:
if ($rc === true) {
$answer[API_MESSAGE] = 'delete: success';
$answer[API_REDIRECT] = API_ANSWER_REDIRECT_CLIENT;
$answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
} else {
$answer[API_MESSAGE] = $msg[MSG_CONTENT];
$answer[API_REDIRECT] = API_ANSWER_REDIRECT_NO;
$answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
}
$result[MSG_HEADER] = "Content-Type: application/json";
$result[MSG_CONTENT] = json_encode($answer);
break;
case MODE_HTML:
if ($targetUrl === false || $targetUrl === '') {
$result[MSG_CONTENT] = 'Missing target URL. ' . ERROR_MISSING_VALUE;
//TODO: vermutlich muss hier $rc=false gesetzt werden. Habe zur Zeit keine Moeglichkeit das zu testen.
// $rc = false;
}
if ($rc === true) {
$result[MSG_HEADER] = "Location: $targetUrl";
$result[MSG_CONTENT] = '';
} else {
$result[MSG_CONTENT] = $msg[MSG_CONTENT];
}
break;
default:
throw new CodeException('Unknown mode: ' . $modeAnswer, ERROR_UNKNOWN_MODE);
break;
}
return $result;
$this->doForm(FORM_DELETE);
}
/**
......
Markdown is supported
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