Commit 8c5525df authored by Rafael Ostertag's avatar Rafael Ostertag
Browse files

Merge remote-tracking branch 'origin/crose_work' into raos_work

parents 8bb9933f 96d9b8bd
......@@ -158,8 +158,8 @@ New Button
* Client stays on current page.
File Handling
-------------
File Handling: Upload
---------------------
* No previous uploaded file present
1. User presses the Browse button
1. User selects file
......@@ -173,6 +173,39 @@ File Handling
1. File delete button gets disabled and hidden
1. Browse button gets enabled and displayed
Form Build (load)
.................
* The upload functionality consist of three elements
* 1) A <div> tag with a) an optional filename of an earlier uploaded file plus and b) a trash Button.
* 2) The 'browse' button (<input type='file' name='_upload_<feName>'>). This element will not be send by post.
* 3) A HTML hidden element with name=<feName> containing the <sipUpload>.
* A new uniq SIP (sipUpload) will be created for every upload formElement. These 'sipUpload' will be assigned to the browse button and to the delete button.
* The individual sipUpload is necessary to correctly handle multiple simustaenously forms when using r=0. Also, through this uniq id it's easy to distinguish between asynchron uploaded files.
* The SIP contains the '_FILES' information submitted during the upload.
* Via the hidden element <feName> 'save()' access the form individual upload status informations.
Upload to server, before 'save'
...............................
* If a user open's a file for upload via the browse button, that file is immediately transmitted to the server. The user will see a turning wheel during the upload time.
* On success the 'Browse; Button disappears and the filename plus the delete button will be displayed (client logic).
* The uploaded file will be checked: maxsize, mime type, check script.
* The uploaded file is still temporary. It has been renamed from $_SESSION['X'][<uploadSip>][FILES_TMP_NAME] to $_SESSION['X'][<uploadSip>][FILES_TMP_NAME].cached
* The upload action will be saved in the user session.
* $_SESSION['X'][<uploadSip>][FILES_TMP_NAME|FILES_NAME|FILES_ERROR|FILE_SIZE]
* Clicks the user on delete button.
* In the usersession a flagDelete will be set: $_SESSION['X'][<uploadSip>]['flagDelete']='1'
* An optional previous upload file (still not saved on the final place) will be deleted.
* An optional existing variable $_SESSION['X'][<uploadSip>][FILES_TMP_NAME] will be deleted. The 'flagDelete' must not be change - it's later needed to detect to delete earlier uploaded files.
Form save
.........
* Before building the insert/update, process all 'uploads'.
* Get every uniq sipUpload to every upload formElement. Get the corresponding temporary uploaded filename.
* If $_SESSION['X'][<uploadSip>]['flagDelete']='1' is set, delete prefious uploaded file.
* Calculate <destination>
* mv <file>.cached <destination>
* clientvalue[<feName>] = <destination>
* delete $_SESSION['X'][<uploadSip>]
Formelement type: DATE / DATETIME / TIME
----------------------------------------
......
......@@ -6,6 +6,31 @@
* Time: 8:02 PM
*/
namespace qfq;
use qfq;
require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/Constants.php');
require_once(__DIR__ . '/../qfq/File.php');
/**
* Return JSON encoded answer
*
* status: success|error
* message: <message>
*
* Description:
*
* Upload successfull & File accepted by server.
* status = 'success'
* message = <message>
*
* Upload failed:
* status = 'error'
* message = <message>
*/
$answer = array();
$answer[API_MESSAGE] = '';
......@@ -13,12 +38,12 @@ $answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
try {
$upload = new \qfq\File();
$fileUpload = new \qfq\File();
$upload->process();
$fileUpload->process();
$answer[API_MESSAGE] = 'upload: success';
$answer[API_REDIRECT] = API_ANSWER_REDIRECT_NO;
// $answer[API_REDIRECT] = API_ANSWER_REDIRECT_NO;
$answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
} catch (qfq\UserFormException $e) {
......
This diff is collapsed.
......@@ -8,19 +8,30 @@
namespace qfq;
const NESTING_TOKEN_OPEN = '#&nesting-open-&#';
const NESTING_TOKEN_CLOSE = '#&nesting-close&#';
const NESTING_TOKEN_LENGTH = 17;
class BodytextParser {
/**
* @param $bodytext
* @return mixed
*/
public function process($bodytext) {
$bodytext = $this->stripAndRemoveComment($bodytext);
$bodytext = $this->trimAndRemoveCommentAndEmptyLine($bodytext);
// Encrypt double curly braces to prevent false positives with nesting: form = {{form}}\n
$bodytext = Support::encryptDoubleCurlyBraces($bodytext);
$bodytext = $this->encryptNestingDelimeter($bodytext);
$bodytext = $this->joinLine($bodytext);
$bodytext = $this->unNest($bodytext);
$bodytext = $this->stripAndRemoveComment($bodytext);
$bodytext = $this->trimAndRemoveCommentAndEmptyLine($bodytext);
$bodytext = Support::decryptDoubleCurlyBraces($bodytext);
if (strpos($bodytext, NESTING_TOKEN_OPEN) !== false) {
throw new \qfq\UserFormException("Missing close delimiter: $bodytext", ERROR_MISSING_CLOSE_DELIMITER);
}
return $bodytext;
}
......@@ -31,7 +42,7 @@ class BodytextParser {
* @return string
*/
private function stripAndRemoveComment($bodytext) {
private function trimAndRemoveCommentAndEmptyLine($bodytext) {
$data = array();
$src = explode(PHP_EOL, $bodytext);
......@@ -46,7 +57,22 @@ class BodytextParser {
return implode(PHP_EOL, $data);
}
//PREG_SPLIT_DELIM_CAPTURE
/**
* Encrypt '{\n' and '}\n' by more complex token.
*
* @param $bodytext
* @return mixed
*/
private function encryptNestingDelimeter($bodytext) {
// Take care that a trailing '}' will be recognised: add '\n'
if (substr($bodytext, -1) === '}') {
$bodytext .= "\n";
}
$bodytext = str_replace("{\n", NESTING_TOKEN_OPEN, $bodytext);
$bodytext = str_replace("}\n", NESTING_TOKEN_CLOSE, $bodytext);
return $bodytext;
}
/**
* Join lines, which do not begin with '<level>.<keyword>[ ]='
......@@ -88,24 +114,28 @@ class BodytextParser {
return implode(PHP_EOL, $data);
}
//PREG_SPLIT_DELIM_CAPTURE
/**
* @param $bodytext
* @return mixed|string
* @throws UserFormException
*/
private function unNest($bodytext) {
// Replace '\{' | '\}' by internal token. All remaining '}' | '{' means: 'nested'
$bodytext = str_replace('\{', '#&[_#', $bodytext);
$bodytext = str_replace('\}', '#&]_#', $bodytext);
$bodytext = Support::encryptDoubleCurlyBraces($bodytext);
// $bodytext = str_replace('\{', '#&[_#', $bodytext);
// $bodytext = str_replace('\}', '#&]_#', $bodytext);
// $bodytext = Support::encryptDoubleCurlyBraces($bodytext);
$result = $bodytext;
$posFirstClose = strpos($result, '}');
$posFirstClose = strpos($result, NESTING_TOKEN_CLOSE);
while ($posFirstClose !== false) {
$posMatchOpen = strrpos(substr($result, 0, $posFirstClose), '{');
$posMatchOpen = strrpos(substr($result, 0, $posFirstClose), NESTING_TOKEN_OPEN);
if ($posMatchOpen === false) {
$result = $this->decryptNestingDelimeter($result);
throw new \qfq\UserFormException("Missing open delimiter: $result", ERROR_MISSING_OPEN_DELIMITER);
}
......@@ -113,12 +143,12 @@ class BodytextParser {
if ($pre === false)
$pre = '';
$post = substr($result, $posFirstClose + 1);
$post = substr($result, $posFirstClose + NESTING_TOKEN_LENGTH);
if ($post === false)
$post = '';
// trim also removes '\n'
$match = trim(substr($result, $posMatchOpen + 1, $posFirstClose - $posMatchOpen - 1));
$match = trim(substr($result, $posMatchOpen + NESTING_TOKEN_LENGTH, $posFirstClose - $posMatchOpen - NESTING_TOKEN_LENGTH));
// "10.sql = SELECT...\n20 {\n
$levelStartPos = strrpos(trim($pre), PHP_EOL);
......@@ -138,14 +168,27 @@ class BodytextParser {
}
$result = $pre . $post;
$posFirstClose = strpos($result, '}');
$posFirstClose = strpos($result, NESTING_TOKEN_CLOSE);
}
$result = str_replace('#&[_#', '{', $result);
$result = str_replace('#&]_#', '}', $result);
$result = Support::decryptDoubleCurlyBraces($result);
// $result = str_replace('#&[_#', '{', $result);
// $result = str_replace('#&]_#', '}', $result);
// $result = Support::decryptDoubleCurlyBraces($result);
return $result;
}
/**
* Decrypt complex token by '{\n' and '}\n'
*
* @param $bodytext
* @return mixed
*/
private function decryptNestingDelimeter($bodytext) {
$bodytext = str_replace(NESTING_TOKEN_OPEN, "{\n", $bodytext);
$bodytext = str_replace(NESTING_TOKEN_CLOSE, "}\n", $bodytext);
return $bodytext;
}
}
\ No newline at end of file
......@@ -46,34 +46,41 @@ class BuildFormBootstrap extends AbstractBuildForm {
*/
public function fillWrap() {
// $this->wrap[WRAP_SETUP_OUTER][WRAP_SETUP_START] = '<div class="tab-content">';
// $this->wrap[WRAP_SETUP_OUTER][WRAP_SETUP_END] = '</div>';
$this->wrap[WRAP_SETUP_TITLE][WRAP_SETUP_START] = '<div class="row hidden-xs"><div class="col-md-12"><h1>';
$this->wrap[WRAP_SETUP_TITLE][WRAP_SETUP_END] = '</h1></div></div>';
$this->wrap[WRAP_SETUP_TITLE][WRAP_SETUP_START] = "<div class='row hidden-xs'><div class='col-md-12'><h1>";
$this->wrap[WRAP_SETUP_TITLE][WRAP_SETUP_END] = "</h1></div></div>";
// Element: Label + Input + Note
$this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_START] = '<div class="form-group">';
$this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_END] = '</div>';
$this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = '<div class="col-md-2">';
$this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_END] = '</div>';
$this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START] = '<div class="col-md-6">';
$this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END] = '</div>';
$this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START] = '<div class="col-md-4">';
$this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END] = '</div>';
$this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_START] = "<div class='form-group'>";
$this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_END] = "</div>";
$this->wrap[WRAP_SETUP_SUBRECORD][WRAP_SETUP_START] = '<div class="col-md-12">';
$this->wrap[WRAP_SETUP_SUBRECORD][WRAP_SETUP_END] = '</div>';
$this->wrap[WRAP_SETUP_SUBRECORD][WRAP_SETUP_START] = "<div class='col-md-12'>";
$this->wrap[WRAP_SETUP_SUBRECORD][WRAP_SETUP_END] = "</div>";
$this->wrap[WRAP_SETUP_IN_FIELDSET][WRAP_SETUP_START] = '<p>';
$this->wrap[WRAP_SETUP_IN_FIELDSET][WRAP_SETUP_END] = '</p>';
$this->wrap[WRAP_SETUP_IN_FIELDSET][WRAP_SETUP_START] = "<p>";
$this->wrap[WRAP_SETUP_IN_FIELDSET][WRAP_SETUP_END] = "</p>";
// $this->feDivClass['radio'] = 'radio';
// $this->feDivClass['checkbox'] = 'checkbox';
}
/**
* @param $label
* @param $input
* @param $note
*/
public function fillWrapLabelInputNote($label, $input, $note) {
$this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = "<div class='col-md-$label'>";
$this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_END] = "</div>";
$this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START] = "<div class='col-md-$input'>";
$this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END] = "</div>";
$this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START] = "<div class='col-md-$note'>";
$this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END] = "</div>";
}
/**
* @return string
*/
......@@ -311,6 +318,9 @@ class BuildFormBootstrap extends AbstractBuildForm {
$deleteUrl = $this->createDeleteUrl($this->formSpec['tableName'], $recordId);
}
$actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
$actionDelete = FILE_ACTION . '=' . FILE_ACTION_DELETE;
$html .= '</form>'; // <form class="form-horizontal" ...
$html .= <<<EOF
<script type="text/javascript">
......@@ -318,14 +328,14 @@ class BuildFormBootstrap extends AbstractBuildForm {
'use strict';
QfqNS.Log.level = 0;
var qfqPage = new QfqNS.QfqPage({
tabsId: '$tabId',
formId: '$formId',
submitTo: 'typo3conf/ext/qfq/qfq/api/save.php',
deleteUrl: '$deleteUrl',
refreshUrl: "typo3conf/ext/qfq/qfq/api/load.php",
fileUploadTo: 'typo3conf/ext/qfq/qfq/api/file.php'
fileUploadTo: 'typo3conf/ext/qfq/qfq/api/file.php?$actionUpload',
fileDeleteUrl: 'typo3conf/ext/qfq/qfq/api/file.php?$actionDelete'
});
var qfqRecordList = new QfqNS.QfqRecordList('typo3conf/ext/qfq/qfq/api/delete.php');
......
......@@ -41,6 +41,9 @@ class BuildFormPlain extends AbstractBuildForm {
}
public function fillWrapLabelInputNote($label, $input, $note) {
}
/**
* @return string
*/
......@@ -67,7 +70,7 @@ class BuildFormPlain extends AbstractBuildForm {
$html = '';
// Construct Marshaller Name
// $buildElementFunctionName = 'build' . $this->buildElementFunctionName[$formElement['type']];
// $buildElementFunctionName = 'build' . $this->buildElementFunctionName[$formElement[FE_TYPE]];
if($formElement['nestedInFieldSet']==='no')
......
......@@ -45,6 +45,9 @@ class BuildFormTable extends AbstractBuildForm {
}
public function fillWrapLabelInputNote($label, $input, $note) {
}
/**
* @return string
*/
......@@ -94,9 +97,9 @@ class BuildFormTable extends AbstractBuildForm {
$html = '';
// Construct Marshaller Name
$buildElementFunctionName = 'build' . $this->buildElementFunctionName[$formElement['type']];
$buildElementFunctionName = 'build' . $this->buildElementFunctionName[$formElement[FE_TYPE]];
if ($formElement['type'] === 'subrecord') {
if ($formElement[FE_TYPE] === 'subrecord') {
// subrecord in render='table' are outside the table
$html .= $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement['label']);
$html .= $this->wrapItem(WRAP_SETUP_SUBRECORD, $htmlElement);
......
......@@ -33,6 +33,10 @@ const FORM_FORWARD_MODE_NO = 'no';
const FORM_FORWARD_MODE_AUTO = 'auto';
const FORM_FORWARD_MODE_PAGE = 'page';
const F_BS_LABEL_COLUMNS = 'bsLabelColumns';
const F_BS_INPUT_COLUMNS = 'bsInputColumns';
const F_BS_NOTE_COLUMNS = 'bsNoteColumns';
const SESSION_FE_USER_UID = 'fe_user_uid';
const RETURN_URL = 'return_url';
......@@ -101,7 +105,6 @@ const ERROR_USER_LOGGED_IN = 1018;
const ERROR_FORM_FORBIDDEN = 1019;
const ERROR_FORM_UNKNOWN_PERMISSION_MODE = 10120;
const ERROR_MULTI_SQL_MISSING = 1021;
const ERROR_MISSING_OPEN_DELIMITER = 1022;
const ERROR_RECURSION_TOO_DEEP = 1023;
const ERROR_CHECKBOXMODE_UNKNOWN = 1024;
const ERROR_MISSING_SQL1 = 1025;
......@@ -134,6 +137,8 @@ const ERROR_REQUIRED_VALUE_EMPTY = 1055;
const ERROR_DATE_UNEXPECTED_FORMAT = 1056;
const ERROR_NOT_APPLICABLE = 1057;
const ERROR_FORMELEMENT_TYPE = 1058;
const ERROR_MISSING_OPEN_DELIMITER = 1059;
const ERROR_MISSING_CLOSE_DELIMITER = 1060;
// Store
const ERROR_STORE_VALUE_ALREADY_CODPIED = 1100;
......@@ -143,6 +148,12 @@ const ERROR_STORE_KEY_EXIST = 1101;
const ERROR_IO_READ_FILE = 1200;
const ERROR_IO_WRITE = 1203;
const ERROR_IO_OPEN = 1204;
const ERROR_IO_UNLINK = 1205;
const ERROR_IO_FILE_EXIST = 1206;
const ERROR_IO_RENAME = 1207;
const ERROR_IO_INVALID_LINK = 1208;
const ERROR_IO_DIR_EXIST_AS_FILE = 1209;
const ERROR_IO_CHDIR = 1210;
//Report
const ERROR_UNKNOWN_LINK_QUALIFIER = 1300;
......@@ -151,6 +162,11 @@ const ERROR_MISSING_VALUE = 1302;
const ERROR_MULTIPLE_DEFINITION = 1303;
const ERROR_MULTIPLE_URL_PAGE_MAILTO_DEFINITION = 1304;
// Upload
const ERROR_UPLOAD = 1400;
const ERROR_UNKNOWN_ACTION = 1402;
const ERROR_NO_TARGET_PATH_FILE_NAME = 1403;
// KeyValueParser
const ERROR_KVP_VALUE_HAS_NO_KEY = 1900;
......@@ -201,8 +217,11 @@ const CLIENT_KEY_SEM_ID_USER = 'keySemIdUser';
const CLIENT_PAGE_ID = 'id';
const CLIENT_PAGE_TYPE = 'type';
const CLIENT_PAGE_LANGUAGE = 'L';
const CLIENT_UPLOAD_FE_NAME = 'uploadFeName';
const CLIENT_UPLOAD_DELETE = 'uploadDeleteOld';
const CLIENT_UPLOAD_FE_NAME = 'name';
const CLIENT_SIP_FOR_FORM = '_sipForForm';
const CLIENT_FE_NAME = '_feName';
const CLIENT_UPLOAD_FILENAME = '_filename';
// ALL $_SERVER variables: http://php.net/manual/en/reserved.variables.server.php
// The following exist and might be the most used ones.
......@@ -221,9 +240,6 @@ const CLIENT_REQUEST_URI = 'REQUEST_URI';
const CLIENT_SCRIPT_NAME = 'SCRIPT_NAME';
const CLIENT_PHP_SELF = 'PHP_SELF';
// Extra:
const EXTRA_UPLOAD_DELETE = CLIENT_UPLOAD_DELETE;
// T3 Bodytext Keywords
const TYPO3_FORM = CLIENT_FORM;
const TYPO3_RECORD_ID = CLIENT_RECORD_ID;
......@@ -254,6 +270,7 @@ const SYSTEM_CSS_CLASS_QFQ_CONTAINER = 'CSS_CLASS_QFQ_CONTAINER';
// computed automatically during runtime
const SYSTEM_PATH_EXT = 'EXT_PATH';
const SYSTEM_SITE_PATH = 'SITE_PATH';
// Information for: Log / Debug / Exception
const SYSTEM_SQL_RAW = 'sqlRaw'; // Type: SANITIZE_ALL / String. SQL Query (before substitute). Useful for error reporting.
......@@ -348,16 +365,20 @@ const GLYPH_ICON_CHECK = 'glyphicon glyphicon-ok';
const FE_MODE_SHOW = 'show';
const FE_MODE_READONLY = 'readonly';
const FE_MODE_REQUIRED = 'required';
const FE_MODE_LOCK = 'lock';
const FE_MODE_DISABLED = 'disabled';
const FE_SUBRECORD_ROW_CLASS = '_rowClass';
const FE_SUBRECORD_ROW_TITLE = '_rowTitle';
// FormElement columns: real
const FE_TYPE = 'type';
const FE_MODE = 'mode';
// FormElement columns: via parameter field
const FE_DATE_FORMAT = 'dateFormat'; // value: FORMAT_DATE_INTERNATIONAL | FORMAT_DATE_GERMAN
const FE_SHOW_SECONDS = 'showSeconds'; // value: 0|1
const FE_SHOW_ZERO = 'showZero'; // value: 0|1
const FE_PATH_FILE_NAME = 'pathFileName'; // Target pathFilename for an uploaded file.
// SUPPORT
const PARAM_T3_ALL = 't3 all';
......@@ -380,3 +401,21 @@ const QUERY_TYPE_UPDATE = 'type: update,replace,delete';
// Date/ DateTime formats
const FORMAT_DATE_INTERNATIONAL = 'yyyy-mm-dd';
const FORMAT_DATE_GERMAN = 'dd.mm.yyyy';
// $_FILES
const FILES_NAME = 'name';
const FILES_TMP_NAME = 'tmp_name';
const FILES_ERROR = 'error';
const FILES_SIZE = 'size';
const FILES_FLAG_DELETE = 'flagDelete';
const UPLOAD_CACHED = '.cached';
const FILE_ACTION = 'action';
const FILE_ACTION_UPLOAD = 'upload';
const FILE_ACTION_DELETE = 'delete';
// DATABASE
const DB_NUM_ROWS = 'numRows';
const DB_AFFECTED_ROWS = 'affectedRows';
const DB_INSERT_ID = 'insertId';
......@@ -208,7 +208,7 @@ class Database {
* @throws \qfq\CodeException
* @throws \qfq\DbException
*/
public function sql($sql, $mode = ROW_REGULAR, array $parameterArray = array(), $specificMessage = '', array &$keys = array()) {
public function sql($sql, $mode = ROW_REGULAR, array $parameterArray = array(), $specificMessage = '', array &$keys = array(), array &$stat = array()) {
$queryType = '';
$result = array();
$this->closeMysqliStmt();
......@@ -221,7 +221,8 @@ class Database {
if ($specificMessage)
$specificMessage .= " ";
$count = $this->prepareExecute($sql, $parameterArray, $queryType);
$count = $this->prepareExecute($sql, $parameterArray, $queryType, $stat);
if ($count === false) {
throw new DbException($specificMessage . "No idea why this error happens - please take some time and check this: $sql", ERROR_DB_GENERIC_CHECK);
}
......@@ -297,18 +298,20 @@ class Database {
/**
* Execute a prepared SQL statement like SELECT, INSERT, UPDATE, DELETE, SHOW, ...
*
* Returns the number of selected rows (SELECT, SHOW, ..) or the affected rows (UPDATE) or the last insert id (INSERT)
* Returns the number of selected rows (SELECT, SHOW, ..) or the affected rows (UPDATE, INSERT). $stat contains appropriate num_rows, insert_id or rows_affected.
*
* @param string $sql SQL statement with prepared statement variable.
* @param array $parameterArray parameter array for prepared statement execution.
* @param string $queryType returns QUERY_TYPE_SELECT | QUERY_TYPE_UPDATE | QUERY_TYPE_INSERT, depending on the query.
* @param array $stat
* @return int|mixed
* @throws \qfq\CodeException
* @throws \qfq\DbException
* @throws \qfq\UserFormException
*/
private function prepareExecute($sql, array $parameterArray = array(), &$queryType = '') {
private function prepareExecute($sql, array $parameterArray = array(), &$queryType, array &$stat) {
$result = 0;
$stat = array();
$this->store->setVar(SYSTEM_SQL_FINAL, $sql, STORE_SYSTEM);
$this->store->setVar(SYSTEM_SQL_PARAM_ARRAY, $parameterArray, STORE_SYSTEM);
......@@ -343,19 +346,23 @@ class Database {
}
$queryType = QUERY_TYPE_SELECT;
$this->mysqli_result = $result;
$count = $this->mysqli_result->num_rows;
$msg = 'Read rows: ' . $count;
$stat[DB_NUM_ROWS] = $this->mysqli_result->num_rows;
$count = $stat[DB_NUM_ROWS];
$msg = 'Read rows: ' . $stat[DB_NUM_ROWS];
break;
case 'INSERT':
$queryType = QUERY_TYPE_INSERT;
$count = $this->mysqli->insert_id;
$stat[DB_INSERT_ID] = $this->mysqli->insert_id;
$stat[DB_AFFECTED_ROWS] = $this->mysqli->affected_rows;
$count = $stat[DB_AFFECTED_ROWS];
$msg = 'ID: ' . $count;
break;
case 'UPDATE':
case 'REPLACE':
case 'DELETE':
$queryType = QUERY_TYPE_UPDATE;
$count = $this->mysqli->affected_rows;
$stat[DB_AFFECTED_ROWS] = $this->mysqli->affected_rows;
$count = $stat[DB_AFFECTED_ROWS];
$msg = 'Affected rows: ' . $count;
break;
default:
......@@ -508,12 +515,14 @@ class Database {
*
* @param $sql
* @param array $keys
* @param array $stat
* @return array|bool
* @throws \qfq\CodeException
* @throws \qfq\DbException
*/
public function sqlKeys($sql, array &$keys) {
public function sqlKeys($sql, array &$keys, array &$stat = array()) {
return $this->sql($sql, ROW_KEYS, array(), '', $keys);
return $this->sql($sql, ROW_KEYS, array(), '', $keys, $stat);
}
/**
......
......@@ -60,7 +60,7 @@ class Evaluate {
}
/**
* Recursive evaluation of 'line'. Constant string, Variables or SQL Query or all of them.
* Recursive evaluation of 'line'. Constant string, Variables or SQL Query or all of them. All queries will be fired.
*
* Token to replace have to be enclosed by '{{' and '}}'
*
......
......@@ -13,35 +13,98 @@ require_once(__DIR__ . '/Constants.php');
class File {
private $uploadErrMsg = array();
/**
* @var null|Store
* @var Store
*/
private $store = null;
public function __construct($phpUnit = false) {
$this->store = Store::getInstance('', $phpUnit);
// $sessionName = $this->store->getVar(SYSTEM_SESSION_NAME, STORE_SYSTEM);
// $this->sip = new Sip($sessionName);
$this->uploadErrMsg = [
UPLOAD_ERR_INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini",
UPLOAD_ERR_FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form",