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

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 ...@@ -255,7 +255,10 @@ Store: *VARS* - V
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ +-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
| Name | Explanation | | 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` | | slaveId | see :ref:`slave-id` |
+-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+ +-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
......
...@@ -86,7 +86,7 @@ Store variables ...@@ -86,7 +86,7 @@ Store variables
.. note:: .. 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:: Example::
......
...@@ -724,6 +724,7 @@ const SIP_EXCLUDE_XDEBUG_SESSION_START = 'XDEBUG_SESSION_START'; ...@@ -724,6 +724,7 @@ const SIP_EXCLUDE_XDEBUG_SESSION_START = 'XDEBUG_SESSION_START';
const ACTION_KEYWORD_SLAVE_ID = 'slaveId'; const ACTION_KEYWORD_SLAVE_ID = 'slaveId';
const VAR_RANDOM = 'random'; const VAR_RANDOM = 'random';
const VAR_RANDOM_UNIQ = 'randomUniq';
const VAR_FILE_DESTINATION = 'fileDestination'; const VAR_FILE_DESTINATION = 'fileDestination';
const VAR_SLAVE_ID = ACTION_KEYWORD_SLAVE_ID; const VAR_SLAVE_ID = ACTION_KEYWORD_SLAVE_ID;
const VAR_FILENAME = 'filename'; // Original filename of an uploaded file. const VAR_FILENAME = 'filename'; // Original filename of an uploaded file.
......
...@@ -194,7 +194,9 @@ $UPDATE_ARRAY = array( ...@@ -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 '';", "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 { ...@@ -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]; $typeMessageViolate = ($arrToken[VAR_INDEX_MESSAGE] === null || $arrToken[VAR_INDEX_MESSAGE] === '') ? SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS : $arrToken[VAR_INDEX_MESSAGE];
// search for value in stores // 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 // escape ticks
if (is_string($value)) { if (is_string($value)) {
......
...@@ -9,12 +9,12 @@ ...@@ -9,12 +9,12 @@
namespace IMATHUZH\Qfq\Core\Store; namespace IMATHUZH\Qfq\Core\Store;
use IMATHUZH\Qfq\Core\Database\Database; use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser; use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\Logger; use IMATHUZH\Qfq\Core\Helper\Logger;
use IMATHUZH\Qfq\Core\Helper\OnArray; use IMATHUZH\Qfq\Core\Helper\OnArray;
use IMATHUZH\Qfq\Core\Helper\Sanitize; use IMATHUZH\Qfq\Core\Helper\Sanitize;
use IMATHUZH\Qfq\Core\Helper\Support; use IMATHUZH\Qfq\Core\Helper\Support;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
/* /*
* Stores: * Stores:
...@@ -296,7 +296,7 @@ class Store { ...@@ -296,7 +296,7 @@ class Store {
$config[SYSTEM_SEND_E_MAIL] = $config[SYSTEM_EXT_PATH] . '/Classes/External/sendEmail'; $config[SYSTEM_SEND_E_MAIL] = $config[SYSTEM_EXT_PATH] . '/Classes/External/sendEmail';
// Make path absolute // 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] != '/') { if (!empty($config[$key]) && $config[$key][0] != '/') {
$config[$key] = HelperFile::joinPathFilename($config[SYSTEM_SITE_PATH], $config[$key]); $config[$key] = HelperFile::joinPathFilename($config[SYSTEM_SITE_PATH], $config[$key]);
} }
...@@ -677,9 +677,11 @@ class Store { ...@@ -677,9 +677,11 @@ class Store {
* @return string|array a) if found: value, b) false. STORE_EXTRA returns an array for the given key. * @return string|array a) if found: value, b) false. STORE_EXTRA returns an array for the given key.
* @throws \CodeException * @throws \CodeException
* @throws \UserFormException SANITIZE_TYPE_MESSAGE_VIOLATE_ZERO | SANITIZE_TYPE_MESSAGE_VIOLATE_EMPTY | SANITIZE_TYPE_MESSAGE_VIOLATE_CLASS * @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 = '', 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? // no store specified?
if ($useStores === "" || $useStores === null) { if ($useStores === "" || $useStores === null) {
...@@ -713,6 +715,8 @@ class Store { ...@@ -713,6 +715,8 @@ class Store {
case STORE_VAR: case STORE_VAR:
if ($finalKey === VAR_RANDOM) { if ($finalKey === VAR_RANDOM) {
return Support::randomAlphaNum(RANDOM_LENGTH); return Support::randomAlphaNum(RANDOM_LENGTH);
} elseif ($finalKey === VAR_RANDOM_UNIQ) {
return StoreVar::randomUniq($default);
} else { } else {
continue 2; // no value provided, continue with while loop 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
...@@ -680,3 +680,17 @@ CREATE TABLE IF NOT EXISTS `Setting` ...@@ -680,3 +680,17 @@ CREATE TABLE IF NOT EXISTS `Setting`
KEY `typeFeUserUidTableIdPublic` (`type`, `feUser`, `tableId`, `public`) USING BTREE KEY `typeFeUserUidTableIdPublic` (`type`, `feUser`, `tableId`, `public`) USING BTREE
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4; 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 { ...@@ -35,7 +35,7 @@ class DatabaseUpdateTest extends AbstractDatabaseTest {
public function testCheckNupdate() { public function testCheckNupdate() {
// $countQfqTables = 9; // $countQfqTables = 9;
$countQfqTables = 10; $countQfqTables = 11;
$store = Store::getInstance(); $store = Store::getInstance();
......
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