Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66b7e81463 | ||
|
|
cf58f86802 | ||
|
|
ff644310cd | ||
|
|
58988b9c0f |
@@ -155,37 +155,57 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
$db->dbExec("DROP TABLE table_without_primary_key");
|
||||
$db->dbExec("DROP TABLE test_meta");
|
||||
}
|
||||
$base_table = "uid VARCHAR, " // uid is for internal reference tests
|
||||
. "row_int INT, "
|
||||
. "row_numeric NUMERIC, "
|
||||
. "row_varchar VARCHAR, "
|
||||
. "row_varchar_literal VARCHAR, "
|
||||
. "row_json JSON, "
|
||||
. "row_jsonb JSONB, "
|
||||
. "row_bytea BYTEA, "
|
||||
. "row_timestamp TIMESTAMP WITHOUT TIME ZONE, "
|
||||
. "row_date DATE, "
|
||||
. "row_interval INTERVAL, "
|
||||
. "row_array_int INT ARRAY, "
|
||||
. "row_array_varchar VARCHAR ARRAY"
|
||||
. ") WITHOUT OIDS";
|
||||
// uid is for internal reference tests
|
||||
$base_table = <<<EOM
|
||||
uid VARCHAR,
|
||||
row_int INT,
|
||||
row_numeric NUMERIC,
|
||||
row_varchar VARCHAR,
|
||||
row_varchar_literal VARCHAR,
|
||||
row_json JSON,
|
||||
row_jsonb JSONB,
|
||||
row_bytea BYTEA,
|
||||
row_timestamp TIMESTAMP WITHOUT TIME ZONE,
|
||||
row_date DATE,
|
||||
row_interval INTERVAL,
|
||||
row_array_int INT ARRAY,
|
||||
row_array_varchar VARCHAR ARRAY
|
||||
)
|
||||
WITHOUT OIDS
|
||||
EOM;
|
||||
// create the tables
|
||||
$db->dbExec(
|
||||
"CREATE TABLE table_with_primary_key ("
|
||||
// primary key name is table + '_id'
|
||||
<<<EOM
|
||||
CREATE TABLE table_with_primary_key (
|
||||
table_with_primary_key_id SERIAL PRIMARY KEY,
|
||||
$base_table
|
||||
EOM
|
||||
/* "CREATE TABLE table_with_primary_key ("
|
||||
// primary key name is table + '_id'
|
||||
. "table_with_primary_key_id SERIAL PRIMARY KEY, "
|
||||
. $base_table
|
||||
. $base_table */
|
||||
);
|
||||
$db->dbExec(
|
||||
"CREATE TABLE table_without_primary_key ("
|
||||
. $base_table
|
||||
<<<EOM
|
||||
CREATE TABLE table_without_primary_key (
|
||||
$base_table
|
||||
EOM
|
||||
/* "CREATE TABLE table_without_primary_key ("
|
||||
. $base_table */
|
||||
);
|
||||
// create simple table for meta test
|
||||
$db->dbExec(
|
||||
"CREATE TABLE test_meta ("
|
||||
<<<EOM
|
||||
CREATE TABLE test_meta (
|
||||
row_1 VARCHAR,
|
||||
row_2 INT
|
||||
) WITHOUT OIDS
|
||||
EOM
|
||||
/* "CREATE TABLE test_meta ("
|
||||
. "row_1 VARCHAR, "
|
||||
. "row_2 INT"
|
||||
. ") WITHOUT OIDS"
|
||||
. ") WITHOUT OIDS" */
|
||||
);
|
||||
// set some test schema
|
||||
$db->dbExec("CREATE SCHEMA IF NOT EXISTS testschema");
|
||||
@@ -3893,6 +3913,38 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
]
|
||||
]
|
||||
],
|
||||
// same but as EOM
|
||||
'single insert (PK), EOM string' => [
|
||||
<<<EOM
|
||||
INSERT INTO table_with_primary_key (
|
||||
row_varchar, row_varchar_literal, row_int, row_date
|
||||
) VALUES (
|
||||
'Text', 'Other', 123, '2022-03-01'
|
||||
)
|
||||
RETURNING row_varchar, row_varchar_literal, row_int, row_date
|
||||
EOM,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
[
|
||||
'row_varchar' => 'Text',
|
||||
'row_varchar_literal' => 'Other',
|
||||
'row_int' => 123,
|
||||
'row_date' => '2022-03-01',
|
||||
// 'table_with_primary_key_id' => "/^\d+$/",
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
|
||||
],
|
||||
[
|
||||
0 => [
|
||||
'row_varchar' => 'Text',
|
||||
'row_varchar_literal' => 'Other',
|
||||
'row_int' => 123,
|
||||
'row_date' => '2022-03-01',
|
||||
// 'table_with_primary_key_id' => "/^\d+$/",
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
|
||||
]
|
||||
]
|
||||
],
|
||||
// double insert (PK)
|
||||
'dobule insert (PK)' => [
|
||||
"INSERT INTO table_with_primary_key "
|
||||
@@ -3910,14 +3962,14 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'row_varchar_literal' => 'Other',
|
||||
'row_int' => 123,
|
||||
'row_date' => '2022-03-01',
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 3,
|
||||
],
|
||||
1 => [
|
||||
'row_varchar' => 'Foxtrott',
|
||||
'row_varchar_literal' => 'Tango',
|
||||
'row_int' => 789,
|
||||
'row_date' => '1982-10-15',
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 3,
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 4,
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -3926,14 +3978,14 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'row_varchar_literal' => 'Other',
|
||||
'row_int' => 123,
|
||||
'row_date' => '2022-03-01',
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 3,
|
||||
],
|
||||
1 => [
|
||||
'row_varchar' => 'Foxtrott',
|
||||
'row_varchar_literal' => 'Tango',
|
||||
'row_int' => 789,
|
||||
'row_date' => '1982-10-15',
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 3,
|
||||
'table_with_primary_key_id' => $table_with_primary_key_id + 4,
|
||||
],
|
||||
]
|
||||
],
|
||||
@@ -3961,7 +4013,35 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
'row_date' => '2022-03-01',
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
// same as above but as EOM string
|
||||
'single insert (No PK), EOM string' => [
|
||||
<<<EOM
|
||||
INSERT INTO table_without_primary_key (
|
||||
row_varchar, row_varchar_literal, row_int, row_date
|
||||
) VALUES (
|
||||
'Text', 'Other', 123, '2022-03-01'
|
||||
)
|
||||
RETURNING row_varchar, row_varchar_literal, row_int, row_date
|
||||
EOM,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
[
|
||||
'row_varchar' => 'Text',
|
||||
'row_varchar_literal' => 'Other',
|
||||
'row_int' => 123,
|
||||
'row_date' => '2022-03-01',
|
||||
],
|
||||
[
|
||||
0 => [
|
||||
'row_varchar' => 'Text',
|
||||
'row_varchar_literal' => 'Other',
|
||||
'row_int' => 123,
|
||||
'row_date' => '2022-03-01',
|
||||
]
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -4008,11 +4088,13 @@ final class CoreLibsDBIOTest extends TestCase
|
||||
|
||||
$this->assertEquals(
|
||||
$expected_ret_ext,
|
||||
$returning_ext
|
||||
$returning_ext,
|
||||
'Returning extended failed'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$expected_ret_arr,
|
||||
$returning_arr
|
||||
$returning_arr,
|
||||
'Returning Array failed'
|
||||
);
|
||||
|
||||
// print "EXT: " . print_r($returning_ext, true) . "\n";
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Fix for edit_schemes.php DB settings
|
||||
|
||||
-- will not change file name only visual name
|
||||
UPDATE edit_page SET name = 'Edit Schemas' WHERE filename = 'edit_schemes.php';
|
||||
|
||||
-- will change BOTH, must have file name renamed too
|
||||
UPDATE edit_page SET name = 'Edit Schemas', filename = 'edit_schemas.php' WHERE filename = 'edit_schemes.php';
|
||||
57
README.md
57
README.md
@@ -2,19 +2,20 @@
|
||||
|
||||
## Code Standard
|
||||
|
||||
* Uses PSR-12
|
||||
* tab indent instead of 4 spaces indent
|
||||
* Warning at 120 character length, error at 240 character length
|
||||
* Uses PSR-12
|
||||
* tab indent instead of 4 spaces indent
|
||||
* Warning at 120 character length, error at 240 character length
|
||||
|
||||
## General information
|
||||
|
||||
Base PHP class files to setup any project
|
||||
* login
|
||||
* database wrapper
|
||||
* basic helper class for debugging and other features
|
||||
* admin/frontend split
|
||||
* domain controlled database/settings split
|
||||
* dynamic layout groups
|
||||
|
||||
* login
|
||||
* database wrapper
|
||||
* basic helper class for debugging and other features
|
||||
* admin/frontend split
|
||||
* domain controlled database/settings split
|
||||
* dynamic layout groups
|
||||
|
||||
## NOTE
|
||||
|
||||
@@ -50,7 +51,6 @@ pslam is setup but not configured
|
||||
With phpunit (`4dev/checking/phpunit.sh`)
|
||||
`phpunit -c $phpunit.xml 4dev/tests/`
|
||||
|
||||
|
||||
## Other Notes
|
||||
|
||||
### Session used
|
||||
@@ -58,29 +58,38 @@ With phpunit (`4dev/checking/phpunit.sh`)
|
||||
The following classes use _SESSION
|
||||
The main one is ACL\Login, this class will fail without a session started
|
||||
|
||||
* \CoreLibs\ACL\Login
|
||||
* \CoreLibs\Admin\Backend
|
||||
* \CoreLibs\Output\Form\Generate
|
||||
* \CoreLibs\Output\Form\Token
|
||||
* \CoreLibs\Template\SmartyExtend
|
||||
* \CoreLibs\ACL\Login
|
||||
* \CoreLibs\Admin\Backend
|
||||
* \CoreLibs\Output\Form\Generate
|
||||
* \CoreLibs\Output\Form\Token
|
||||
* \CoreLibs\Template\SmartyExtend
|
||||
|
||||
### Class extends
|
||||
|
||||
The following classes extend these classes
|
||||
|
||||
* \CoreLibs\ACL\Login extends \CoreLibs\DB\IO
|
||||
* \CoreLibs\Admin\Backend extends \CoreLibs\DB\IO
|
||||
* \CoreLibs\DB\Extended\ArrayIO extends \CoreLibs\DB\IO
|
||||
* \CoreLibs\Output\Form\Generate extends \CoreLibs\DB\Extended\ArrayIO
|
||||
* \CoreLibs\Template\SmartyExtend extends SmartyBC
|
||||
* \CoreLibs\ACL\Login extends \CoreLibs\DB\IO
|
||||
* \CoreLibs\Admin\Backend extends \CoreLibs\DB\IO
|
||||
* \CoreLibs\DB\Extended\ArrayIO extends \CoreLibs\DB\IO
|
||||
* \CoreLibs\Output\Form\Generate extends \CoreLibs\DB\Extended\ArrayIO
|
||||
* \CoreLibs\Template\SmartyExtend extends SmartyBC
|
||||
|
||||
### Class used
|
||||
|
||||
The following classes use the following classes
|
||||
|
||||
* \CoreLibs\ACL\Login uses \CoreLibs\Debug\Logger, \CoreLibs\Language\L10n
|
||||
* \CoreLibs\DB\IO uses \CoreLibs\Debug\Logger, \CoreLibs\DB\SQL\PgSQL
|
||||
* \CoreLibs\Admin\Backend uses \CoreLibs\Debug\Logger, \CoreLibs\Language\L10n
|
||||
* \CoreLibs\Output\Form\Generate uses \CoreLibs\Debug\Logger, \CoreLibs\Language\L10n
|
||||
* \CoreLibs\ACL\Login uses \CoreLibs\Debug\Logging, \CoreLibs\Language\L10n
|
||||
* \CoreLibs\DB\IO uses \CoreLibs\Debug\Logging, \CoreLibs\DB\SQL\PgSQL
|
||||
* \CoreLibs\Admin\Backend uses \CoreLibs\Debug\Logging, \CoreLibs\Language\L10n
|
||||
* \CoreLibs\Output\Form\Generate uses \CoreLibs\Debug\Logging, \CoreLibs\Language\L10n
|
||||
* \CoreLibs\Template\SmartyExtend uses \CoreLibs\Language\L10n
|
||||
* \CoreLibs\Language\L10n uses FileReader, GetTextReader
|
||||
* \CoreLibs\Admin\EditBase uses \CoreLibs\Debug\Logging, \CoreLibs\Language\L10n
|
||||
|
||||
### Class internal load
|
||||
|
||||
Loads classes internal (not passed in, not extend)
|
||||
|
||||
* \CoreLibs\Admin\EditBase loads \CoreLibs\Template\SmartyExtend, \CoreLibs\Output\Form\Generate
|
||||
* \CoreLibs\Output\From\Generate loads \CoreLibs\Debug\Logging, \CoreLibs\Language\L10n if not passed on
|
||||
* \CoreLibs\Output\From\Generate loads \CoreLibs\Output\From\TableArrays
|
||||
|
||||
@@ -118,7 +118,8 @@ print "<b>TRUNCATE test_foobar</b><br>";
|
||||
$query = "TRUNCATE test_foobar";
|
||||
$db->dbExec($query);
|
||||
|
||||
$status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO TEST " . time() . "') RETURNING test");
|
||||
$status = $db->dbExec("INSERT INTO test_foo (test, number_a) VALUES "
|
||||
. "('FOO TEST " . time() . "', 1) RETURNING test, number_a");
|
||||
print "DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>"
|
||||
. "QUERY: " . $db->dbGetQuery() . " |<br>"
|
||||
. "DB OBJECT: <pre>" . print_r($status, true) . "</pre>| "
|
||||
@@ -127,6 +128,29 @@ print "DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>"
|
||||
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
|
||||
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
|
||||
|
||||
var_dump($db->dbGetReturningExt());
|
||||
|
||||
// same as above but use an EOM string
|
||||
$some_time = time();
|
||||
$query = <<<EOM
|
||||
INSERT INTO test_foo (
|
||||
test, number_a
|
||||
) VALUES (
|
||||
'EOM FOO TEST $some_time', 1
|
||||
)
|
||||
RETURNING test, number_a
|
||||
EOM;
|
||||
$status = $db->dbExec($query);
|
||||
print "EOM STRING DIRECT INSERT STATUS: " . Support::printToString($status) . " |<br>"
|
||||
. "QUERY: " . $db->dbGetQuery() . " |<br>"
|
||||
. "DB OBJECT: <pre>" . print_r($status, true) . "</pre>| "
|
||||
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
|
||||
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
|
||||
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
|
||||
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
|
||||
|
||||
var_dump($db->dbGetReturningExt());
|
||||
|
||||
// should throw deprecated error
|
||||
// $db->getReturningExt();
|
||||
print "DIRECT INSERT PREVIOUS INSERTED: "
|
||||
@@ -150,6 +174,23 @@ print "PREPARE CURSOR RETURN:<br>";
|
||||
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
|
||||
print "KEY: " . $key . ': ' . $db->dbGetPrepareCursorValue('ins_test_foo', $key) . "<br>";
|
||||
}
|
||||
|
||||
$query = <<<EOM
|
||||
INSERT INTO test_foo (
|
||||
test
|
||||
) VALUES (
|
||||
$1
|
||||
)
|
||||
RETURNING test
|
||||
EOM;
|
||||
$db->dbPrepare("ins_test_foo_eom", $query);
|
||||
$status = $db->dbExecute("ins_test_foo_eom", ['EOM BAR TEST ' . time()]);
|
||||
print "EOM STRING PREPARE INSERT[ins_test_foo_eom] STATUS: " . Support::printToString($status) . " |<br>"
|
||||
. "QUERY: " . $db->dbGetQuery() . " |<br>"
|
||||
. "PRIMARY KEY: " . $db->dbGetInsertPK() . " | "
|
||||
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
|
||||
. "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "<br>";
|
||||
|
||||
// returning test with multiple entries
|
||||
// $status = $db->db_exec(
|
||||
// "INSERT INTO test_foo (test) VALUES "
|
||||
@@ -172,6 +213,26 @@ print "DIRECT MULTIPLE INSERT WITH RETURN STATUS: " . Support::printToString($st
|
||||
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
|
||||
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
|
||||
|
||||
$t_1 = time();
|
||||
$t_2 = time();
|
||||
$t_3 = time();
|
||||
$query = <<<EOM
|
||||
INSERT INTO test_foo (
|
||||
test
|
||||
) VALUES
|
||||
('EOM BAR 1 $t_1'),
|
||||
('EOM BAR 2 $t_2'),
|
||||
('EOM BAR 3 $t_3')
|
||||
RETURNING test_foo_id, test
|
||||
EOM;
|
||||
$status = $db->dbExec($query);
|
||||
print "EOM STRING DIRECT MULTIPLE INSERT WITH RETURN STATUS: " . Support::printToString($status) . " |<br>"
|
||||
. "QUERY: " . $db->dbGetQuery() . " |<br>"
|
||||
. "PRIMARY KEYS: " . print_r($db->dbGetInsertPK(), true) . " | "
|
||||
. "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | "
|
||||
. "RETURNING EXT[test]: " . print_r($db->dbGetReturningExt('test'), true) . " | "
|
||||
. "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "<br>";
|
||||
|
||||
// no returning, but not needed ;
|
||||
$status = $db->dbExec("INSERT INTO test_foo (test) VALUES ('FOO; TEST " . time() . "')");
|
||||
print "DIRECT INSERT NO RETURN STATUS: " . Support::printToString($status) . " |<br>"
|
||||
@@ -240,6 +301,24 @@ if ($db->dbPrepare('sel_test_foo', $q_prep) === false) {
|
||||
. $db->dbGetLastError() . "/" . $db->dbGetLastWarning() . "/"
|
||||
. "<pre>" . print_r($db->dbGetCombinedErrorHistory(), true) . "</pre><br>";
|
||||
}
|
||||
|
||||
echo "<hr>";
|
||||
print "EOM STYLE STRINGS<br>";
|
||||
$test_bar = $db->dbEscapeLiteral('SOMETHING DIFFERENT');
|
||||
// Test EOM block
|
||||
$q = <<<EOM
|
||||
SELECT test_foo_id, test, some_bool, string_a, number_a,
|
||||
-- comment
|
||||
number_a_numeric, some_time
|
||||
FROM test_foo
|
||||
WHERE test = $test_bar
|
||||
ORDER BY test_foo_id DESC LIMIT 5
|
||||
EOM;
|
||||
while (is_array($res = $db->dbReturn($q))) {
|
||||
print "ROW: <pre>" . print_r($res, true) . "</pre><br>";
|
||||
}
|
||||
echo "<hr>";
|
||||
|
||||
// NOTE: try to replacate connection still exists if script is run a second time
|
||||
// open pg bouncer connection
|
||||
$db_pgb = new CoreLibs\DB\IO($DB_CONFIG['test_pgbouncer'], $log);
|
||||
|
||||
@@ -1633,7 +1633,7 @@ EOM;
|
||||
$this->session->checkActiveSession() === true &&
|
||||
!empty($_SESSION['DEFAULT_LOCALE'])
|
||||
) {
|
||||
$locale = $_SESSION['DEFAULT_LOCALE'] ?? '';
|
||||
$locale = $_SESSION['DEFAULT_LOCALE'];
|
||||
} else {
|
||||
$locale = (defined('SITE_LOCALE') && !empty(SITE_LOCALE)) ?
|
||||
SITE_LOCALE :
|
||||
|
||||
@@ -1007,7 +1007,7 @@ class IO
|
||||
$this->pk_name_table[$table] : 'NULL';
|
||||
}
|
||||
if (
|
||||
!preg_match("/ returning /i", $this->query) &&
|
||||
!preg_match("/\s?returning /i", $this->query) &&
|
||||
$this->pk_name && $this->pk_name != 'NULL'
|
||||
) {
|
||||
// check if this query has a ; at the end and remove it
|
||||
@@ -1016,7 +1016,7 @@ class IO
|
||||
$this->query = !is_string($__query) ? $this->query : $__query;
|
||||
$this->query .= " RETURNING " . $this->pk_name;
|
||||
$this->returning_id = true;
|
||||
} elseif (preg_match("/ returning (.*)/i", $this->query, $matches)) {
|
||||
} elseif (preg_match("/\s?returning (.*)/i", $this->query, $matches)) {
|
||||
if ($this->pk_name && $this->pk_name != 'NULL') {
|
||||
// add the primary key if it is not in the returning set
|
||||
if (!preg_match("/$this->pk_name/", $matches[1])) {
|
||||
@@ -1030,7 +1030,7 @@ class IO
|
||||
// if we have an UPDATE and RETURNING, flag for true, but do not add anything
|
||||
if (
|
||||
$this->__checkQueryForUpdate($this->query) &&
|
||||
preg_match("/ returning (.*)/i", $this->query, $matches)
|
||||
preg_match("/\s?returning (.*)/i", $this->query, $matches)
|
||||
) {
|
||||
$this->returning_id = true;
|
||||
}
|
||||
@@ -2288,11 +2288,11 @@ class IO
|
||||
$this->prepare_cursor[$stm_name]['pk_name'] = $pk_name;
|
||||
}
|
||||
// if no returning, then add it
|
||||
if (!preg_match("/ returning /i", $query) && $this->prepare_cursor[$stm_name]['pk_name']) {
|
||||
if (!preg_match("/\s?returning /i", $query) && $this->prepare_cursor[$stm_name]['pk_name']) {
|
||||
$query .= " RETURNING " . $this->prepare_cursor[$stm_name]['pk_name'];
|
||||
$this->prepare_cursor[$stm_name]['returning_id'] = true;
|
||||
} elseif (
|
||||
preg_match("/ returning (.*)/i", $query, $matches) &&
|
||||
preg_match("/\s?returning (.*)/i", $query, $matches) &&
|
||||
$this->prepare_cursor[$stm_name]['pk_name']
|
||||
) {
|
||||
// if returning exists but not pk_name, add it
|
||||
|
||||
Reference in New Issue
Block a user