Commit 6c9b62ba authored by Carsten  Rose's avatar Carsten Rose

Merge branch 'F11118Variable-randomUniq' into 'develop'

F11118 variable random uniq

See merge request !283
parents 9375f88f 7da98a6d
Pipeline #3769 passed with stages
in 3 minutes and 51 seconds
......@@ -255,7 +255,10 @@ Store: *VARS* - V
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| Name | Explanation |
+=========================+============================================================================================================================================+
| random | Random string with length of 32 alphanum chars (lower & upper case). This is variable is always filled. |
| random | Random string with length of 32 alphanum chars (lower & upper case). This variable is always filled. Each access gives a new value. |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| randomUniq | Like {{random:V}} but it's guaranteed that it's uniq. Optional: as *default* define an expiration time. |
| | Example: ``{{randomUniq:V}}``, ``{{randomUniq:V:::2020-12-15}}``, ``{{randomUniq:V:::+ 10 sec}}`` (Relative: sec, min, day). |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| slaveId | see :ref:`slave-id` |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
......
......@@ -86,7 +86,7 @@ Store variables
.. note::
Syntax: {{ variable name : :ref:`store` : :ref:`sanitize-class` : :ref:`variable-escape` : :ref:`variable-default` : :ref:`variable-type-message-violate` }}
{{ *variable name* : :ref:`store` : :ref:`sanitize-class` : :ref:`variable-escape` : :ref:`variable-default` : :ref:`variable-type-message-violate` }}
Example::
......
......@@ -724,6 +724,7 @@ const SIP_EXCLUDE_XDEBUG_SESSION_START = 'XDEBUG_SESSION_START';
const ACTION_KEYWORD_SLAVE_ID = 'slaveId';
const VAR_RANDOM = 'random';
const VAR_RANDOM_UNIQ = 'randomUniq';
const VAR_FILE_DESTINATION = 'fileDestination';
const VAR_SLAVE_ID = ACTION_KEYWORD_SLAVE_ID;
const VAR_FILENAME = 'filename'; // Original filename of an uploaded file.
......
......@@ -194,7 +194,9 @@ $UPDATE_ARRAY = array(
"ALTER TABLE `FormElement` CHANGE `label` `label` VARCHAR(1023) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '';",
],
'20.9.0' => [
"CREATE TABLE `Uniq` (`id` int(11) NOT NULL AUTO_INCREMENT, `random` char(32) NOT NULL, `expire` datetime NOT NULL, `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `random` (`random`) USING BTREE, KEY `expire` (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;);",
],
);
......
......@@ -392,7 +392,8 @@ class Evaluate {
$typeMessageViolate = ($arrToken[VAR_INDEX_MESSAGE] === null || $arrToken[VAR_INDEX_MESSAGE] === '') ? SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS : $arrToken[VAR_INDEX_MESSAGE];
// search for value in stores
$value = $this->store::getVar($arrToken[VAR_INDEX_VALUE], $arrToken[VAR_INDEX_STORE], $arrToken[VAR_INDEX_SANATIZE], $foundInStore, $typeMessageViolate);
$value = $this->store::getVar($arrToken[VAR_INDEX_VALUE], $arrToken[VAR_INDEX_STORE], $arrToken[VAR_INDEX_SANATIZE],
$foundInStore, $typeMessageViolate, $arrToken[VAR_INDEX_DEFAULT]);
// escape ticks
if (is_string($value)) {
......
......@@ -9,12 +9,12 @@
namespace IMATHUZH\Qfq\Core\Store;
use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\Logger;
use IMATHUZH\Qfq\Core\Helper\OnArray;
use IMATHUZH\Qfq\Core\Helper\Sanitize;
use IMATHUZH\Qfq\Core\Helper\Support;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
/*
* Stores:
......@@ -296,7 +296,7 @@ class Store {
$config[SYSTEM_SEND_E_MAIL] = $config[SYSTEM_EXT_PATH] . '/Classes/External/sendEmail';
// Make path absolute
foreach ([SYSTEM_MAIL_LOG, SYSTEM_QFQ_LOG, SYSTEM_SQL_LOG] AS $key) {
foreach ([SYSTEM_MAIL_LOG, SYSTEM_QFQ_LOG, SYSTEM_SQL_LOG] as $key) {
if (!empty($config[$key]) && $config[$key][0] != '/') {
$config[$key] = HelperFile::joinPathFilename($config[SYSTEM_SITE_PATH], $config[$key]);
}
......@@ -677,9 +677,11 @@ class Store {
* @return string|array a) if found: value, b) false. STORE_EXTRA returns an array for the given key.
* @throws \CodeException
* @throws \UserFormException SANITIZE_TYPE_MESSAGE_VIOLATE_ZERO | SANITIZE_TYPE_MESSAGE_VIOLATE_EMPTY | SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS
* @throws \UserReportException
* @throws \DbException
*/
public static function getVar($key, $useStores = STORE_USE_DEFAULT, $sanitizeClass = '', &$foundInStore = '',
$typeMessageViolate = SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS) {
$typeMessageViolate = SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS, $default = '') {
// no store specified?
if ($useStores === "" || $useStores === null) {
......@@ -713,6 +715,8 @@ class Store {
case STORE_VAR:
if ($finalKey === VAR_RANDOM) {
return Support::randomAlphaNum(RANDOM_LENGTH);
} elseif ($finalKey === VAR_RANDOM_UNIQ) {
return StoreVar::randomUniq($default);
} else {
continue 2; // no value provided, continue with while loop
}
......
<?php
namespace IMATHUZH\Qfq\Core\Store;
use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Helper\Support;
//use IMATHUZH\Qfq\Core\Store\Store;
/**
* Class StoreVar
* @package Classes\Core\Store
*/
class StoreVar {
/**
* @var Database - Database instantiated class
*/
private static $db = null; /* QFQ DB */
/**
* @var Store - Database instantiated class
*/
private static $store = null;
/**
* @var null
*/
private static $purged = false;
/**
* Returns a random 32 char string. Take care that it is really uniq.
*
* @param string $expire // Empty (no expiration), 'yyy-mm-dd', 'yyyy-dd-mm hh:mm:ss', '+ 1 day', '+10 sec'
* @return string
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
* @throws \UserReportException
*/
public static function randomUniq($expire = '') {
if (self::$store == null) {
self::$store = Store::getInstance();
}
if (self::$db == null) {
self::$db = new Database(self::$store->getVar(SYSTEM_DB_INDEX_QFQ, STORE_SYSTEM));
}
$expire = trim($expire);
if ($expire == '') {
$expire = '9999-12-31';
}
if ($expire[0] == '+') {
// $expire: '+1 day' >> '+ INTERVAL 1 day'
$expire = "NOW() + INTERVAL " . SUBSTR($expire, 1);
} else {
$expire = "CONVERT('$expire', DATETIME)";
}
// Purge expired records
if (!self::$purged) {
self::$db->sql("DELETE FROM Uniq WHERE expire<NOW()");
self::$purged = true;
}
$i = 100;
while ($i--) {
// Get new random string
$random = Support::randomAlphaNum(RANDOM_LENGTH);
try {
// Try to save random - if succeed, all is fine else try again.
self::$db->sql("INSERT INTO Uniq (`random`,`expire`) VALUES ( '$random', $expire)");
} catch (\DbException $e) {
$message = $e->getMessage();
// In case there was an error: check for duplicate key
// message: {"toUser":"SQL error","support":"","os":"[ mysqli: 1062 ] Duplicate entry '...' for key 'random'"}
if (strpos($message, '1062') !== false && strpos($message, "for key 'random'") !== false) {
continue; // Key already exist, new try.
} else {
throw new \DbException($message, $e->getCode()); // Something else happened: report.
}
}
return $random;
}
throw new \DbException('Too much iterations to find {{randomUniq:V}}');
}
}
\ No newline at end of file
......@@ -57,44 +57,44 @@ CREATE TABLE IF NOT EXISTS `Form`
CREATE TABLE IF NOT EXISTS `FormElement`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`formId` INT(11) NOT NULL,
`feIdContainer` INT(11) NOT NULL DEFAULT '0',
`dynamicUpdate` ENUM ('yes', 'no') NOT NULL DEFAULT 'no',
`id` INT(11) NOT NULL AUTO_INCREMENT,
`formId` INT(11) NOT NULL,
`feIdContainer` INT(11) NOT NULL DEFAULT '0',
`dynamicUpdate` ENUM ('yes', 'no') NOT NULL DEFAULT 'no',
`enabled` ENUM ('yes', 'no') NOT NULL DEFAULT 'yes',
`enabled` ENUM ('yes', 'no') NOT NULL DEFAULT 'yes',
`name` VARCHAR(255) NOT NULL DEFAULT '',
`label` VARCHAR(1023) NOT NULL DEFAULT '',
`name` VARCHAR(255) NOT NULL DEFAULT '',
`label` VARCHAR(1023) NOT NULL DEFAULT '',
`mode` ENUM ('show', 'required', 'readonly', 'hidden') NOT NULL DEFAULT 'show',
`modeSql` TEXT NOT NULL,
`class` ENUM ('native', 'action', 'container') NOT NULL DEFAULT 'native',
`type` ENUM ('checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra',
`mode` ENUM ('show', 'required', 'readonly', 'hidden') NOT NULL DEFAULT 'show',
`modeSql` TEXT NOT NULL,
`class` ENUM ('native', 'action', 'container') NOT NULL DEFAULT 'native',
`type` ENUM ('checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra',
'gridJQW', 'text', 'editor', 'annotate', 'time', 'note', 'password', 'radio', 'select',
'subrecord', 'upload', 'imageCut', 'fieldset', 'pill', 'templateGroup', 'beforeLoad',
'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave',
'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail', 'paste') NOT NULL DEFAULT 'text',
`subrecordOption` SET ('edit', 'delete', 'new') NOT NULL DEFAULT '',
`encode` ENUM ('none', 'specialchar') NOT NULL DEFAULT 'specialchar',
`checkType` ENUM ('auto', 'alnumx', 'digit', 'numerical', 'email', 'pattern', 'allbut',
'all') NOT NULL DEFAULT 'auto',
`checkPattern` VARCHAR(255) NOT NULL DEFAULT '',
`onChange` VARCHAR(255) NOT NULL DEFAULT '',
`ord` INT(11) NOT NULL DEFAULT '0',
`tabindex` INT(11) NOT NULL DEFAULT '0',
`size` VARCHAR(255) NOT NULL DEFAULT '',
`maxLength` VARCHAR(255) NOT NULL DEFAULT '',
`labelAlign` ENUM ('default', 'left', 'center', 'right') NOT NULL DEFAULT 'default',
`bsLabelColumns` VARCHAR(255) NOT NULL DEFAULT '',
`bsInputColumns` VARCHAR(255) NOT NULL DEFAULT '',
`bsNoteColumns` VARCHAR(255) NOT NULL DEFAULT '',
`rowLabelInputNote` SET ('row', 'label', '/label', 'input', '/input', 'note', '/note', '/row') NOT NULL DEFAULT 'row,label,/label,input,/input,note,/note,/row',
`note` TEXT NOT NULL,
`adminNote` TEXT NOT NULL,
'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail', 'paste') NOT NULL DEFAULT 'text',
`subrecordOption` SET ('edit', 'delete', 'new') NOT NULL DEFAULT '',
`encode` ENUM ('none', 'specialchar') NOT NULL DEFAULT 'specialchar',
`checkType` ENUM ('auto', 'alnumx', 'digit', 'numerical', 'email', 'pattern', 'allbut',
'all') NOT NULL DEFAULT 'auto',
`checkPattern` VARCHAR(255) NOT NULL DEFAULT '',
`onChange` VARCHAR(255) NOT NULL DEFAULT '',
`ord` INT(11) NOT NULL DEFAULT '0',
`tabindex` INT(11) NOT NULL DEFAULT '0',
`size` VARCHAR(255) NOT NULL DEFAULT '',
`maxLength` VARCHAR(255) NOT NULL DEFAULT '',
`labelAlign` ENUM ('default', 'left', 'center', 'right') NOT NULL DEFAULT 'default',
`bsLabelColumns` VARCHAR(255) NOT NULL DEFAULT '',
`bsInputColumns` VARCHAR(255) NOT NULL DEFAULT '',
`bsNoteColumns` VARCHAR(255) NOT NULL DEFAULT '',
`rowLabelInputNote` SET ('row', 'label', '/label', 'input', '/input', 'note', '/note', '/row') NOT NULL DEFAULT 'row,label,/label,input,/input,note,/note,/row',
`note` TEXT NOT NULL,
`adminNote` TEXT NOT NULL,
`tooltip` VARCHAR(255) NOT NULL DEFAULT '',
`placeholder` VARCHAR(2048) NOT NULL DEFAULT '',
......@@ -679,4 +679,18 @@ CREATE TABLE IF NOT EXISTS `Setting`
KEY `name` (`name`),
KEY `typeFeUserUidTableIdPublic` (`type`, `feUser`, `tableId`, `public`) USING BTREE
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
\ No newline at end of file
DEFAULT CHARSET = utf8mb4;
CREATE TABLE `Uniq`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`random` char(32) NOT NULL,
`expire` datetime NOT NULL,
`modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `random` (`random`) USING BTREE,
KEY `expire` (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
......@@ -35,7 +35,7 @@ class DatabaseUpdateTest extends AbstractDatabaseTest {
public function testCheckNupdate() {
// $countQfqTables = 9;
$countQfqTables = 10;
$countQfqTables = 11;
$store = Store::getInstance();
......
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