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

Implemented _paged, _Paged and _link: delete.

delete.php: extended to support returning JSON, HTML redirect (Location) or HTML error messages.
Report.php: new adjustDeleteParameter()
Delete.php: new.
QuickFormQuery.php: delete() extended to support returning JSON and HTML
parent 58a1f24c
......@@ -47,18 +47,14 @@ require_once(__DIR__ . '/../qfq/Constants.php');
$answer = array();
$answer[API_REDIRECT] = API_ANSWER_REDIRECT_NO;
$answer[API_MESSAGE] = '';
$answer[API_MESSAGE] = 'Something failed';
$answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
try {
$qfq = new \qfq\QuickFormQuery(['bodytext' => '']);
$qfq->delete();
$answer[API_MESSAGE] = 'delete: success';
$answer[API_REDIRECT] = API_ANSWER_REDIRECT_CLIENT;
$answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
$result = $qfq->delete();
} catch (qfq\UserFormException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
......@@ -70,6 +66,16 @@ try {
$answer[API_MESSAGE] = "Generic Exception: " . $e->getMessage();
}
header("Content-Type: application/json");
echo json_encode($answer);
// Fallbak. in case an exception has been thrown and $result is not filled.
if (!isset($result[MSG_HEADER]) && !isset($result[MSG_CONTENT])) {
$result[MSG_HEADER] = "Content-Type: application/json";
$result[MSG_CONTENT] = json_encode($answer);
}
if (isset($result[MSG_HEADER]) && $result[MSG_HEADER] !== '')
header($result[MSG_HEADER]);
echo $result[MSG_CONTENT];
<?php
/**
* Created by PhpStorm.
* User: crose
* Date: 6/1/16
* Time: 8:52 AM
*/
namespace qfq;
require_once(__DIR__ . '/Constants.php');
require_once(__DIR__ . '/Database.php');
class Delete {
/**
* @var Database
*/
private $db = null;
/**
*
*/
public function __construct() {
$this->db = new Database();
}
/**
* Deletes the record id=$recordId from table $form[F_TABLE_NAME].
* If the tables 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 integer $recordId
* @param array $msg
* @return bool
* @throws CodeException
* @throws DbException
*/
public function process(array $form, $recordId, array &$msg) {
$rc = false;
$msg = array();
if (!isset($form[F_TABLE_NAME]) || $form[F_TABLE_NAME] === '') {
throw new CodeException('Missing table name', ERROR_MISSING_TABLE_NAME);
}
if ($recordId === 0 || $recordId === '') {
throw new CodeException('Invalid record id', ERROR_MISSING_RECORD_ID);
}
// Read record first.
$row = $this->db->sql("SELECT * FROM " . $form[F_TABLE_NAME] . " 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->db->sql("DELETE FROM " . $form[F_TABLE_NAME] . " WHERE id =? LIMIT 1", ROW_REGULAR, [$recordId]);
$rc = true;
} else {
$msg[MSG_CONTENT] = "Record $recordId not found in table '" . $form[F_TABLE_NAME] . "'.";
$msg[MSG_ERROR_CODE] = ERROR_RECORD_NOT_FOUND;
}
return $rc;
}
}
\ No newline at end of file
......@@ -23,24 +23,25 @@ use qfq;
//use qfq\Store;
require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/store/FillStoreForm.php');
require_once(__DIR__ . '/../qfq/store/Session.php');
require_once(__DIR__ . '/../qfq/Constants.php');
require_once(__DIR__ . '/../qfq/Save.php');
require_once(__DIR__ . '/../qfq/helper/KeyValueStringParser.php');
require_once(__DIR__ . '/../qfq/helper/HelperFormElement.php');
require_once(__DIR__ . '/../qfq/exceptions/UserFormException.php');
require_once(__DIR__ . '/../qfq/exceptions/CodeException.php');
require_once(__DIR__ . '/../qfq/exceptions/DbException.php');
require_once(__DIR__ . '/../qfq/exceptions/ErrorHandler.php');
require_once(__DIR__ . '/../qfq/Database.php');
require_once(__DIR__ . '/../qfq/Evaluate.php');
require_once(__DIR__ . '/../qfq/BuildFormPlain.php');
require_once(__DIR__ . '/../qfq/BuildFormTable.php');
require_once(__DIR__ . '/../qfq/BuildFormBootstrap.php');
require_once(__DIR__ . '/../qfq/report/Report.php');
require_once(__DIR__ . '/../qfq/BodytextParser.php');
require_once(__DIR__ . '/store/Store.php');
require_once(__DIR__ . '/store/FillStoreForm.php');
require_once(__DIR__ . '/store/Session.php');
require_once(__DIR__ . '/Constants.php');
require_once(__DIR__ . '/Save.php');
require_once(__DIR__ . '/helper/KeyValueStringParser.php');
require_once(__DIR__ . '/helper/HelperFormElement.php');
require_once(__DIR__ . '/exceptions/UserFormException.php');
require_once(__DIR__ . '/exceptions/CodeException.php');
require_once(__DIR__ . '/exceptions/DbException.php');
require_once(__DIR__ . '/exceptions/ErrorHandler.php');
require_once(__DIR__ . '/Database.php');
require_once(__DIR__ . '/Evaluate.php');
require_once(__DIR__ . '/BuildFormPlain.php');
require_once(__DIR__ . '/BuildFormTable.php');
require_once(__DIR__ . '/BuildFormBootstrap.php');
require_once(__DIR__ . '/report/Report.php');
require_once(__DIR__ . '/BodytextParser.php');
require_once(__DIR__ . '/Delete.php');
require_once(__DIR__ . '/form/FormAction.php');
/*
......@@ -596,23 +597,65 @@ class QuickFormQuery {
/**
* Delete a record (tablename and recordid are given) or process a 'delete form'
*
* @return mixed
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
public function delete() {
$msg = array();
#TODO: implement 'delete form'
// simple delete: table and recordId are given
$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);
if ($recordId === false || $recordId < 1 || $table === false || $table === '') {
throw new UserFormException("Invalid or missing parameter: recordId=$recordId, table=$table", ERROR_INVALID_OR_MISSING_PARAMETER);
break;
case MODE_HTML:
if ($targetUrl === false || $targetUrl === '') {
$result[MSG_CONTENT] = 'Missing target URL. ' . ERROR_MISSING_VALUE;
}
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;
}
$this->db->sql("DELETE FROM $table WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
return $result;
}
}
\ No newline at end of file
......@@ -15,12 +15,12 @@ require_once(__DIR__ . '/Define.php');
require_once(__DIR__ . '/Utils.php');
require_once(__DIR__ . '/Variables.php');
require_once(__DIR__ . '/Error.php');
//require_once(__DIR__ . '/Db.php');
require_once(__DIR__ . '/../Database.php');
require_once(__DIR__ . '/Link.php');
require_once(__DIR__ . '/Sendmail.php');
require_once(__DIR__ . '/../exceptions/UserReportExtension.php');
require_once(__DIR__ . '/../Evaluate.php');
require_once(__DIR__ . '/../helper/KeyValueStringParser.php');
const DEFAULT_QUESTION = 'question';
const DEFAULT_ICON = 'icon';
......@@ -120,12 +120,8 @@ class Report {
$this->showDebugInfo = ($this->store->getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM) === 'yes');
$this->pageDefaults[DEFAULT_QUESTION]["pagec"] = "Please confirm!";
$this->pageDefaults[DEFAULT_QUESTION]["paged"] = "Do you really want to delete the record?";
// $this->pageDefaults["sip"]["paged"] = TOKEN_SIP;
// $this->pageDefaults["sip"]["pagee"] = TOKEN_SIP;
// $this->pageDefaults["sip"]["pagen"] = TOKEN_SIP;
$this->pageDefaults[DEFAULT_QUESTION]["pagec"] = "Please confirm!:info";
$this->pageDefaults[DEFAULT_QUESTION]["paged"] = "Do you really want to delete the record?:warning";
$this->pageDefaults[DEFAULT_ICON]["paged"] = TOKEN_DELETE;
$this->pageDefaults[DEFAULT_ICON]["pagee"] = TOKEN_EDIT;
......@@ -269,7 +265,7 @@ class Report {
* Sorts the associative array.
*
* @param array $ary : The unsorted Level Array
* @param string $clause: the sort argument 0 ASC, 1 ASC... according to the number of columns
* @param string $clause : the sort argument 0 ASC, 1 ASC... according to the number of columns
* @param bool|true $ascending
*/
private function sortIndexArray(array &$ary, $clause, $ascending = true) {
......@@ -607,10 +603,10 @@ class Report {
case "Pagei":
case "Pagen":
case "Pages":
$pageColumnName = strtolower($columnName);
$tokenizedValue = $this->doFixColPosPage($columnName, $columnValue);
$linkValue = $this->doPage($pageColumnName, $tokenizedValue);
$content .= $this->link->renderLink($linkValue);
$pageColumnName = strtolower($columnName);
$tokenizedValue = $this->doFixColPosPage($columnName, $columnValue);
$linkValue = $this->doPage($pageColumnName, $tokenizedValue);
$content .= $this->link->renderLink($linkValue);
break;
case "page":
......@@ -622,7 +618,7 @@ class Report {
case "pagen":
case "pages":
$linkValue = $this->doPage($columnName, $columnValue);
$content .= $this->link->renderLink($linkValue);
$content .= $this->link->renderLink($linkValue);
break;
case "bullet":
......@@ -925,16 +921,77 @@ class Report {
if ($defaultImage !== '') {
$columnValue .= $defaultImage . "|";
}
if ($defaultSip !== '') {
$columnValue .= $defaultSip . "|";
}
if ($defaultQuestion !== '') {
$columnValue .= $defaultQuestion . "|";
}
if ($columnName === 'paged') {
$columnValue = $this->adjustDeleteParameter($columnValue);
}
return ($columnValue);
}
/**
* @param $columnValue
* @return string
* @throws UserFormException
*/
private function adjustDeleteParameter($columnValue) {
$kvp = new KeyValueStringParser();
// Split in: [p => 'delete&r=100&table=note&..', 'D' => ''... ],
$param = $kvp->parse($columnValue, ':', '|');
// decode TOKEN_PAGE and fill TOKEN_URL_PARAM with it
if (isset($param[TOKEN_PAGE])) {
$args = $kvp->parse($param[TOKEN_PAGE], '=', '&');
// Remove the pageId. Instead set api/delete.php URL.
// Depending if a pageId is given by first arg (without the keyname 'id') or somewhere with 'id=': remove such position.
if (isset($args['id'])) {
unset($args['id']);
} else {
array_shift($args);
}
// Concat already existing $param[TOKEN_URL_PARAM] with additional from TOKEN_PAE
Support::setIfNotSet($param, TOKEN_URL_PARAM);
if($param[TOKEN_URL_PARAM]!=='') {
$param[TOKEN_URL_PARAM] .= '&';
}
if(count($args)>0) {
$param[TOKEN_URL_PARAM] .= $kvp->unparse($args, '=', '&');
}
// Delete TOKEN_PAGE
unset($param[TOKEN_PAGE]);
} else {
throw new UserFormException('Missing parameter for _page or _Page', ERROR_MISSING_VALUE);
}
$param[TOKEN_URL_PARAM] .= '&' . SIP_MODE_ANSWER . '=' . MODE_HTML;
$param[TOKEN_URL_PARAM] .= '&' . SIP_TARGET_URL . '=' . $_SERVER['REQUEST_URI'];
if (!isset($param[TOKEN_URL])) {
$param[TOKEN_URL] = API_DIR . '/delete.php';
if(!isset($param[TOKEN_CLASS])) {
$param[TOKEN_CLASS] = TOKEN_CLASS_NONE; // no_class: By default a button will be rendered. TOKEN_URL typically implies class external. That does not match.
}
}
return $kvp->unparse($param, ':', '|');
}
/**
* Generate SortArgument
*
......
<?php
/**
* Created by PhpStorm.
* User: crose
* Date: 1/15/16
* Time: 8:24 AM
*/
namespace qfq;
require_once(__DIR__ . '/AbstractDatabaseTest.php');
require_once(__DIR__ . '/../../qfq/Delete.php');
class DeleteTest extends \AbstractDatabaseTest {
/**
* @expectedException \qfq\CodeException
*/
public function testProcessException() {
$msg = array();
$delete = new Delete();
// empty 'form' not allowed
$delete->process(array(), 123, $msg);
}
/**
* @expectedException \qfq\CodeException
*/
public function testProcessException1() {
$msg = array();
$delete = new Delete();
// 'form' with empty tablename not allowed
$form[F_TABLE_NAME] = '';
$delete->process($form, 123, $msg);
}
/**
* @expectedException \qfq\CodeException
*/
public function testProcessException2() {
$msg = array();
$delete = new Delete();
// empty record id not allowed
$form[F_TABLE_NAME] = 'Person';
$delete->process($form, '', $msg);
}
/**
* @expectedException \qfq\CodeException
*/
public function testProcessException3() {
$msg = array();
$delete = new Delete();
$form[F_TABLE_NAME] = 'Person';
// record id = 0 not allowed
$delete->process($form, 0, $msg);
}
/**
* @expectedException \qfq\CodeException
*/
public function testProcessException4() {
$msg = array();
$delete = new Delete();
// unknown table not allowed
$form[F_TABLE_NAME] = 'UnknownTable';
$delete->process($form, 0, $msg);
}
/**
*/
public function testProcessRecordNotFound() {
$msg = array();
$delete = new Delete();
// unknown table not allowed
$form[F_TABLE_NAME] = 'Person';
$rc = $delete->process($form, 100, $msg);
$this->assertEquals(false, $rc);
$expect = ['content' => "Record 100 not found in table 'Person'.", 'errorCode' => 1065];
$this->assertEquals($expect, $msg);
}
/**
*/
public function testProcess() {
$msg = array();
$delete = new Delete();
// unknown table not allowed
$form[F_TABLE_NAME] = 'Person';
$rc = $delete->process($form, 1, $msg);
$this->assertEquals(true, $rc);
$this->assertEquals(array(), $msg);
$count = $this->db->sql('SELECT COUNT(id) FROM Person', ROW_IMPLODE_ALL);
$this->assertEquals('1', $count);
}
protected function setUp() {
$this->store = Store::getInstance('form=TestFormName', true);
parent::setUp();
$this->store->setVar('form', 'TestFormName', STORE_TYPO3);
$this->executeSQLFile(__DIR__ . '/fixtures/Generic.sql', true);
}
}
......@@ -8,13 +8,8 @@
namespace qfq;
//use qfq\Store;
//use qfq;
require_once(__DIR__ . '/AbstractDatabaseTest.php');
require_once(__DIR__ . '/../../qfq/form/FormAction.php');
//require_once(__DIR__ . '/../../qfq/Database.php');
//require_once(__DIR__ . '/../../qfq/store/Store.php');
class FormActionTest extends \AbstractDatabaseTest {
......
......@@ -655,7 +655,7 @@ EOF;
public function testReportPageD() {
$js = <<<EOF
id="12345" onClick="var alert = new QfqNS.Alert({ message: 'Do you really want to delete the record?', type: 'info', modal: true, timeout: 0, buttons: [
id="12345" onClick="var alert = new QfqNS.Alert({ message: 'Do you really want to delete the record?', type: 'warning', modal: true, timeout: 0, buttons: [
{ label: 'Ok', eventName: 'ok' },
{ label: 'Cancel',eventName: 'cancel'}
] } );
......@@ -667,9 +667,10 @@ alert.show();
return false;"
EOF;
// _paged: incl. alert
$result = $this->report->process("10.sql = SELECT 'p:form' AS _paged FROM Person ORDER BY id LIMIT 1");
$this->assertEquals('<a href="index.php?id=form&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/qfq/api/delete.php?id=&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
// _paged: other than defaults for the alert.
$js = str_replace('Do you really want to delete the record?', 'Move to trash?', $js);
......@@ -678,12 +679,13 @@ EOF;
$js = str_replace('Cancel', 'no', $js);
$js = str_replace('timeout: 0', 'timeout: 10000', $js);
$js = str_replace('modal: true', 'modal: false', $js);
$js = str_replace("type: 'warning'", "type: 'success'", $js);
$result = $this->report->process("10.sql = SELECT 'p:form|q:Move to trash?:success:yes:no:10:0' AS _paged FROM Person ORDER BY id LIMIT 1");
$this->assertEquals('<a href="index.php?id=form&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/qfq/api/delete.php?id=&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
$result = $this->report->process("10.sql = SELECT 'p:form|q:Move to trash?:success:yes:no:10:0|t:click me' AS _paged FROM Person ORDER BY id LIMIT 1");
$this->assertEquals('<a href="index.php?id=form&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span> click me</a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/qfq/api/delete.php?id=&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span> click me</a>', $result);
}
......@@ -693,7 +695,7 @@ EOF;
public function testReportPageFixD() {
$js = <<<EOF
id="12345" onClick="var alert = new QfqNS.Alert({ message: 'Do you really want to delete the record?', type: 'info', modal: true, timeout: 0, buttons: [
id="12345" onClick="var alert = new QfqNS.Alert({ message: 'Do you really want to delete the record?', type: 'warning', modal: true, timeout: 0, buttons: [
{ label: 'Ok', eventName: 'ok' },
{ label: 'Cancel',eventName: 'cancel'}
] } );
......@@ -707,7 +709,7 @@ EOF;
// _paged: incl. alert
$result = $this->report->process("10.sql = SELECT 'form' AS _Paged FROM Person ORDER BY id LIMIT 1");
$this->assertEquals('<a href="index.php?id=form&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/qfq/api/delete.php?id=&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
// _paged: other than defaults for the alert.
$js = str_replace('Do you really want to delete the record?', 'Move to trash?', $js);
......@@ -716,12 +718,13 @@ EOF;
$js = str_replace('Cancel', 'no', $js);
$js = str_replace('timeout: 0', 'timeout: 10000', $js);
$js = str_replace('modal: true', 'modal: false', $js);
$js = str_replace("type: 'warning'", "type: 'success'", $js);
$result = $this->report->process("10.sql = SELECT 'form|||Move to trash?:success:yes:no:10:0' AS _Paged FROM Person ORDER BY id LIMIT 1");
$this->assertEquals('<a href="index.php?id=form&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/qfq/api/delete.php?id=&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span></a>', $result);
$result = $this->report->process("10.sql = SELECT 'form|click me||Move to trash?:success:yes:no:10:0' AS _Paged FROM Person ORDER BY id LIMIT 1");
$this->assertEquals('<a href="index.php?id=form&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span> click me</a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/qfq/api/delete.php?id=&s=badcaffee1234" class="btn btn-default" title="Delete" ' . $js . ' ><span class="glyphicon glyphicon-trash" ></span> click me</a>', $result);
}
......
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