Remove $GLOBALS['cfg'] from BrowseForeigners class
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
This commit is contained in:
parent
e38a46f7ae
commit
8123a81235
@ -7,6 +7,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin;
|
||||
|
||||
use PhpMyAdmin\Config\Settings;
|
||||
|
||||
use function __;
|
||||
use function array_keys;
|
||||
use function asort;
|
||||
@ -22,17 +24,11 @@ use function mb_substr;
|
||||
*/
|
||||
class BrowseForeigners
|
||||
{
|
||||
private int $limitChars;
|
||||
private int $maxRows;
|
||||
private int $repeatCells;
|
||||
private bool $showAll;
|
||||
private Settings $settings;
|
||||
|
||||
public function __construct(public Template $template)
|
||||
public function __construct(public Template $template, Config $config)
|
||||
{
|
||||
$this->limitChars = (int) $GLOBALS['cfg']['LimitChars'];
|
||||
$this->maxRows = (int) $GLOBALS['cfg']['MaxRows'];
|
||||
$this->repeatCells = (int) $GLOBALS['cfg']['RepeatCells'];
|
||||
$this->showAll = (bool) $GLOBALS['cfg']['ShowAll'];
|
||||
$this->settings = $config->getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +62,7 @@ class BrowseForeigners
|
||||
$rightKeynameIsSelected = false;
|
||||
$leftKeynameIsSelected = false;
|
||||
|
||||
if ($this->repeatCells > 0 && $horizontalCount > $this->repeatCells) {
|
||||
if ($this->settings->repeatCells > 0 && $horizontalCount > $this->settings->repeatCells) {
|
||||
$output .= $header;
|
||||
$horizontalCount = 0;
|
||||
}
|
||||
@ -160,8 +156,8 @@ class BrowseForeigners
|
||||
$gotoPage = $this->getHtmlForGotoPage($foreignData);
|
||||
$foreignShowAll = $this->template->render('table/browse_foreigners/show_all', [
|
||||
'foreign_data' => $foreignData,
|
||||
'show_all' => $this->showAll,
|
||||
'max_rows' => $this->maxRows,
|
||||
'show_all' => $this->settings->showAll,
|
||||
'max_rows' => $this->settings->maxRows,
|
||||
]);
|
||||
|
||||
$output = '<form class="ajax" '
|
||||
@ -263,12 +259,11 @@ class BrowseForeigners
|
||||
*/
|
||||
private function getDescriptionAndTitle(string $description): array
|
||||
{
|
||||
if (mb_strlen($description) <= $this->limitChars) {
|
||||
if (mb_strlen($description) <= $this->settings->limitChars) {
|
||||
$descriptionTitle = '';
|
||||
} else {
|
||||
$descriptionTitle = $description;
|
||||
$description = mb_substr($description, 0, $this->limitChars)
|
||||
. '...';
|
||||
$description = mb_substr($description, 0, $this->settings->limitChars) . '...';
|
||||
}
|
||||
|
||||
return [
|
||||
@ -289,13 +284,13 @@ class BrowseForeigners
|
||||
return '';
|
||||
}
|
||||
|
||||
$pageNow = (int) floor($pos / $this->maxRows) + 1;
|
||||
$nbTotalPage = (int) ceil($foreignData['the_total'] / $this->maxRows);
|
||||
$pageNow = (int) floor($pos / $this->settings->maxRows) + 1;
|
||||
$nbTotalPage = (int) ceil($foreignData['the_total'] / $this->settings->maxRows);
|
||||
|
||||
if ($foreignData['the_total'] > $this->maxRows) {
|
||||
if ($foreignData['the_total'] > $this->settings->maxRows) {
|
||||
return Util::pageselector(
|
||||
'pos',
|
||||
$this->maxRows,
|
||||
$this->settings->maxRows,
|
||||
$pageNow,
|
||||
$nbTotalPage,
|
||||
200,
|
||||
@ -323,6 +318,6 @@ class BrowseForeigners
|
||||
|
||||
isset($_POST['pos']) ? $pos = $_POST['pos'] : $pos = 0;
|
||||
|
||||
return 'LIMIT ' . $pos . ', ' . $this->maxRows . ' ';
|
||||
return 'LIMIT ' . $pos . ', ' . $this->settings->maxRows . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1182,7 +1182,7 @@ final class Settings
|
||||
*
|
||||
* @link https://docs.phpmyadmin.net/en/latest/config.html#cfg_ShowAll
|
||||
*/
|
||||
public bool $ShowAll;
|
||||
public bool $showAll;
|
||||
|
||||
/**
|
||||
* Number of rows displayed when browsing a result set. If the result
|
||||
@ -1197,7 +1197,7 @@ final class Settings
|
||||
*
|
||||
* @psalm-var positive-int
|
||||
*/
|
||||
public int $MaxRows;
|
||||
public int $maxRows;
|
||||
|
||||
/**
|
||||
* default for 'ORDER BY' clause (valid values are 'ASC', 'DESC' or 'SMART' -ie
|
||||
@ -1809,7 +1809,7 @@ final class Settings
|
||||
*
|
||||
* @psalm-var positive-int
|
||||
*/
|
||||
public int $LimitChars;
|
||||
public int $limitChars;
|
||||
|
||||
/**
|
||||
* Where to show the edit/copy/delete links in browse mode
|
||||
@ -1894,7 +1894,7 @@ final class Settings
|
||||
*
|
||||
* @psalm-var 0|positive-int
|
||||
*/
|
||||
public int $RepeatCells;
|
||||
public int $repeatCells;
|
||||
|
||||
/**
|
||||
* Set to true if you want DB-based query history.If false, this utilizes
|
||||
@ -2503,8 +2503,8 @@ final class Settings
|
||||
$this->HideStructureActions = $this->setHideStructureActions($settings);
|
||||
$this->ShowColumnComments = $this->setShowColumnComments($settings);
|
||||
$this->TableNavigationLinksMode = $this->setTableNavigationLinksMode($settings);
|
||||
$this->ShowAll = $this->setShowAll($settings);
|
||||
$this->MaxRows = $this->setMaxRows($settings);
|
||||
$this->showAll = $this->setShowAll($settings);
|
||||
$this->maxRows = $this->setMaxRows($settings);
|
||||
$this->Order = $this->setOrder($settings);
|
||||
$this->SaveCellsAtOnce = $this->setSaveCellsAtOnce($settings);
|
||||
$this->GridEditing = $this->setGridEditing($settings);
|
||||
@ -2551,14 +2551,14 @@ final class Settings
|
||||
$this->TextareaAutoSelect = $this->setTextareaAutoSelect($settings);
|
||||
$this->CharTextareaCols = $this->setCharTextareaCols($settings);
|
||||
$this->CharTextareaRows = $this->setCharTextareaRows($settings);
|
||||
$this->LimitChars = $this->setLimitChars($settings);
|
||||
$this->limitChars = $this->setLimitChars($settings);
|
||||
$this->RowActionLinks = $this->setRowActionLinks($settings);
|
||||
$this->RowActionLinksWithoutUnique = $this->setRowActionLinksWithoutUnique($settings);
|
||||
$this->TablePrimaryKeyOrder = $this->setTablePrimaryKeyOrder($settings);
|
||||
$this->RememberSorting = $this->setRememberSorting($settings);
|
||||
$this->ShowBrowseComments = $this->setShowBrowseComments($settings);
|
||||
$this->ShowPropertyComments = $this->setShowPropertyComments($settings);
|
||||
$this->RepeatCells = $this->setRepeatCells($settings);
|
||||
$this->repeatCells = $this->setRepeatCells($settings);
|
||||
$this->QueryHistoryDB = $this->setQueryHistoryDB($settings);
|
||||
$this->QueryHistoryMax = $this->setQueryHistoryMax($settings);
|
||||
$this->BrowseMIME = $this->setBrowseMIME($settings);
|
||||
@ -2700,8 +2700,8 @@ final class Settings
|
||||
'HideStructureActions' => $this->HideStructureActions,
|
||||
'ShowColumnComments' => $this->ShowColumnComments,
|
||||
'TableNavigationLinksMode' => $this->TableNavigationLinksMode,
|
||||
'ShowAll' => $this->ShowAll,
|
||||
'MaxRows' => $this->MaxRows,
|
||||
'ShowAll' => $this->showAll,
|
||||
'MaxRows' => $this->maxRows,
|
||||
'Order' => $this->Order,
|
||||
'SaveCellsAtOnce' => $this->SaveCellsAtOnce,
|
||||
'GridEditing' => $this->GridEditing,
|
||||
@ -2748,14 +2748,14 @@ final class Settings
|
||||
'TextareaAutoSelect' => $this->TextareaAutoSelect,
|
||||
'CharTextareaCols' => $this->CharTextareaCols,
|
||||
'CharTextareaRows' => $this->CharTextareaRows,
|
||||
'LimitChars' => $this->LimitChars,
|
||||
'LimitChars' => $this->limitChars,
|
||||
'RowActionLinks' => $this->RowActionLinks,
|
||||
'RowActionLinksWithoutUnique' => $this->RowActionLinksWithoutUnique,
|
||||
'TablePrimaryKeyOrder' => $this->TablePrimaryKeyOrder,
|
||||
'RememberSorting' => $this->RememberSorting,
|
||||
'ShowBrowseComments' => $this->ShowBrowseComments,
|
||||
'ShowPropertyComments' => $this->ShowPropertyComments,
|
||||
'RepeatCells' => $this->RepeatCells,
|
||||
'RepeatCells' => $this->repeatCells,
|
||||
'QueryHistoryDB' => $this->QueryHistoryDB,
|
||||
'QueryHistoryMax' => $this->QueryHistoryMax,
|
||||
'BrowseMIME' => $this->BrowseMIME,
|
||||
|
||||
@ -17,7 +17,7 @@ return [
|
||||
],
|
||||
'browse_foreigners' => [
|
||||
'class' => PhpMyAdmin\BrowseForeigners::class,
|
||||
'arguments' => ['@template'],
|
||||
'arguments' => ['@template', '@config'],
|
||||
],
|
||||
'config' => [
|
||||
'class' => PhpMyAdmin\Config::class,
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
<MixedArgument>
|
||||
<code>$descriptions[$indexByDescription]</code>
|
||||
<code>$descriptions[$indexByKeyname]</code>
|
||||
<code><![CDATA[$foreignData['the_total'] / $this->maxRows]]></code>
|
||||
<code><![CDATA[$foreignData['the_total'] / $this->settings->maxRows]]></code>
|
||||
<code>$horizontalCount</code>
|
||||
<code>$indexByDescription</code>
|
||||
</MixedArgument>
|
||||
@ -97,23 +97,18 @@
|
||||
</MixedOperand>
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$_POST['foreign_filter']]]></code>
|
||||
<code><![CDATA[$pos / $this->settings->maxRows]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyInvalidCast>
|
||||
<code><![CDATA[$_POST['rownumber']]]></code>
|
||||
</PossiblyInvalidCast>
|
||||
<PossiblyInvalidOperand>
|
||||
<code>$pos</code>
|
||||
<code><![CDATA[$this->maxRows]]></code>
|
||||
<code><![CDATA[$this->settings->maxRows]]></code>
|
||||
</PossiblyInvalidOperand>
|
||||
<PossiblyUndefinedArrayOffset>
|
||||
<code><![CDATA[$foreignData['foreign_display']]]></code>
|
||||
</PossiblyUndefinedArrayOffset>
|
||||
<RedundantCast>
|
||||
<code><![CDATA[(bool) $GLOBALS['cfg']['ShowAll']]]></code>
|
||||
<code><![CDATA[(int) $GLOBALS['cfg']['LimitChars']]]></code>
|
||||
<code><![CDATA[(int) $GLOBALS['cfg']['MaxRows']]]></code>
|
||||
<code><![CDATA[(int) $GLOBALS['cfg']['RepeatCells']]]></code>
|
||||
</RedundantCast>
|
||||
</file>
|
||||
<file src="libraries/classes/Cache.php">
|
||||
<MixedAssignment>
|
||||
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace PhpMyAdmin\Tests;
|
||||
|
||||
use PhpMyAdmin\BrowseForeigners;
|
||||
use PhpMyAdmin\Config;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
/** @covers \PhpMyAdmin\BrowseForeigners */
|
||||
@ -21,11 +22,7 @@ class BrowseForeignersTest extends AbstractTestCase
|
||||
|
||||
parent::setTheme();
|
||||
|
||||
$GLOBALS['cfg']['LimitChars'] = 50;
|
||||
$GLOBALS['cfg']['MaxRows'] = 25;
|
||||
$GLOBALS['cfg']['RepeatCells'] = 100;
|
||||
$GLOBALS['cfg']['ShowAll'] = false;
|
||||
$this->browseForeigners = new BrowseForeigners(new Template());
|
||||
$this->browseForeigners = new BrowseForeigners(new Template(), new Config());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,8 +46,9 @@ class BrowseForeignersTest extends AbstractTestCase
|
||||
$this->browseForeigners->getForeignLimit(null),
|
||||
);
|
||||
|
||||
$GLOBALS['cfg']['MaxRows'] = 50;
|
||||
$browseForeigners = new BrowseForeigners(new Template());
|
||||
$config = new Config();
|
||||
$config->settings['MaxRows'] = 50;
|
||||
$browseForeigners = new BrowseForeigners(new Template(), $config);
|
||||
|
||||
$this->assertEquals(
|
||||
'LIMIT 10, 50 ',
|
||||
@ -132,8 +130,9 @@ class BrowseForeignersTest extends AbstractTestCase
|
||||
),
|
||||
);
|
||||
|
||||
$GLOBALS['cfg']['LimitChars'] = 5;
|
||||
$browseForeigners = new BrowseForeigners(new Template());
|
||||
$config = new Config();
|
||||
$config->settings['LimitChars'] = 5;
|
||||
$browseForeigners = new BrowseForeigners(new Template(), $config);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
|
||||
@ -132,8 +132,6 @@ class SettingsTest extends TestCase
|
||||
'HideStructureActions' => true,
|
||||
'ShowColumnComments' => true,
|
||||
'TableNavigationLinksMode' => 'icons',
|
||||
'ShowAll' => false,
|
||||
'MaxRows' => 25,
|
||||
'Order' => 'SMART',
|
||||
'SaveCellsAtOnce' => false,
|
||||
'GridEditing' => 'double-click',
|
||||
@ -214,14 +212,12 @@ class SettingsTest extends TestCase
|
||||
'TextareaAutoSelect' => false,
|
||||
'CharTextareaCols' => 40,
|
||||
'CharTextareaRows' => 7,
|
||||
'LimitChars' => 50,
|
||||
'RowActionLinks' => 'left',
|
||||
'RowActionLinksWithoutUnique' => false,
|
||||
'TablePrimaryKeyOrder' => 'NONE',
|
||||
'RememberSorting' => true,
|
||||
'ShowBrowseComments' => true,
|
||||
'ShowPropertyComments' => true,
|
||||
'RepeatCells' => 100,
|
||||
'QueryHistoryDB' => false,
|
||||
'QueryHistoryMax' => 25,
|
||||
'BrowseMIME' => true,
|
||||
@ -458,8 +454,6 @@ class SettingsTest extends TestCase
|
||||
['HideStructureActions', null, true],
|
||||
['ShowColumnComments', null, true],
|
||||
['TableNavigationLinksMode', null, 'icons'],
|
||||
['ShowAll', null, false],
|
||||
['MaxRows', null, 25],
|
||||
['Order', null, 'SMART'],
|
||||
['SaveCellsAtOnce', null, false],
|
||||
['GridEditing', null, 'double-click'],
|
||||
@ -506,14 +500,12 @@ class SettingsTest extends TestCase
|
||||
['TextareaAutoSelect', null, false],
|
||||
['CharTextareaCols', null, 40],
|
||||
['CharTextareaRows', null, 7],
|
||||
['LimitChars', null, 50],
|
||||
['RowActionLinks', null, 'left'],
|
||||
['RowActionLinksWithoutUnique', null, false],
|
||||
['TablePrimaryKeyOrder', null, 'NONE'],
|
||||
['RememberSorting', null, true],
|
||||
['ShowBrowseComments', null, true],
|
||||
['ShowPropertyComments', null, true],
|
||||
['RepeatCells', null, 100],
|
||||
['QueryHistoryDB', null, false],
|
||||
['QueryHistoryMax', null, 25],
|
||||
['BrowseMIME', null, true],
|
||||
@ -651,8 +643,6 @@ class SettingsTest extends TestCase
|
||||
['HideStructureActions', false, false],
|
||||
['ShowColumnComments', false, false],
|
||||
['TableNavigationLinksMode', 'text', 'text'],
|
||||
['ShowAll', true, true],
|
||||
['MaxRows', 1, 1],
|
||||
['Order', 'ASC', 'ASC'],
|
||||
['SaveCellsAtOnce', true, true],
|
||||
['GridEditing', 'click', 'click'],
|
||||
@ -699,14 +689,12 @@ class SettingsTest extends TestCase
|
||||
['TextareaAutoSelect', true, true],
|
||||
['CharTextareaCols', 1, 1],
|
||||
['CharTextareaRows', 1, 1],
|
||||
['LimitChars', 1, 1],
|
||||
['RowActionLinks', 'none', 'none'],
|
||||
['RowActionLinksWithoutUnique', true, true],
|
||||
['TablePrimaryKeyOrder', 'DESC', 'DESC'],
|
||||
['RememberSorting', false, false],
|
||||
['ShowBrowseComments', false, false],
|
||||
['ShowPropertyComments', false, false],
|
||||
['RepeatCells', 0, 0],
|
||||
['QueryHistoryDB', true, true],
|
||||
['QueryHistoryMax', 1, 1],
|
||||
['BrowseMIME', false, false],
|
||||
@ -963,8 +951,6 @@ class SettingsTest extends TestCase
|
||||
['ShowDbStructureLastCheck', 1, true],
|
||||
['HideStructureActions', 0, false],
|
||||
['ShowColumnComments', 0, false],
|
||||
['ShowAll', 1, true],
|
||||
['MaxRows', '1', 1],
|
||||
['SaveCellsAtOnce', 1, true],
|
||||
['ShowFunctionFields', 0, false],
|
||||
['ShowFieldTypesInDataEditView', 0, false],
|
||||
@ -994,12 +980,10 @@ class SettingsTest extends TestCase
|
||||
['TextareaAutoSelect', 1, true],
|
||||
['CharTextareaCols', '1', 1],
|
||||
['CharTextareaRows', '1', 1],
|
||||
['LimitChars', '1', 1],
|
||||
['RowActionLinksWithoutUnique', 1, true],
|
||||
['RememberSorting', 0, false],
|
||||
['ShowBrowseComments', 0, false],
|
||||
['ShowPropertyComments', 0, false],
|
||||
['RepeatCells', '0', 0],
|
||||
['QueryHistoryDB', 1, true],
|
||||
['QueryHistoryMax', '1', 1],
|
||||
['BrowseMIME', 0, false],
|
||||
@ -1061,7 +1045,6 @@ class SettingsTest extends TestCase
|
||||
['NavigationTreeDefaultTabTable2', 'invalid', ''],
|
||||
['NavigationWidth', -1, 240],
|
||||
['TableNavigationLinksMode', 'invalid', 'icons'],
|
||||
['MaxRows', 0, 25],
|
||||
['Order', 'invalid', 'SMART'],
|
||||
['GridEditing', 'invalid', 'double-click'],
|
||||
['RelationalDisplay', 'invalid', 'K'],
|
||||
@ -1087,10 +1070,8 @@ class SettingsTest extends TestCase
|
||||
['TextareaRows', 0, 15],
|
||||
['CharTextareaCols', 0, 40],
|
||||
['CharTextareaRows', 0, 7],
|
||||
['LimitChars', 0, 50],
|
||||
['RowActionLinks', 'invalid', 'left'],
|
||||
['TablePrimaryKeyOrder', 'invalid', 'NONE'],
|
||||
['RepeatCells', -1, 100],
|
||||
['QueryHistoryMax', 0, 25],
|
||||
['MaxExactCount', 0, 50000],
|
||||
['MaxExactCountViews', -1, 0],
|
||||
@ -1130,6 +1111,82 @@ class SettingsTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider booleanWithDefaultFalseProvider */
|
||||
public function testShowAll(mixed $actual, bool $expected): void
|
||||
{
|
||||
$settings = new Settings(['ShowAll' => $actual]);
|
||||
$settingsArray = $settings->asArray();
|
||||
$this->assertSame($expected, $settings->showAll);
|
||||
$this->assertArrayHasKey('ShowAll', $settingsArray);
|
||||
$this->assertSame($expected, $settingsArray['ShowAll']);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{mixed, bool}> */
|
||||
public static function booleanWithDefaultFalseProvider(): iterable
|
||||
{
|
||||
yield 'null value' => [null, false];
|
||||
yield 'valid value' => [false, false];
|
||||
yield 'valid value 2' => [true, true];
|
||||
yield 'valid value with type coercion' => [1, true];
|
||||
}
|
||||
|
||||
/** @dataProvider valuesForMaxRowsProvider */
|
||||
public function testMaxRows(mixed $actual, int $expected): void
|
||||
{
|
||||
$settings = new Settings(['MaxRows' => $actual]);
|
||||
$settingsArray = $settings->asArray();
|
||||
$this->assertSame($expected, $settings->maxRows);
|
||||
$this->assertArrayHasKey('MaxRows', $settingsArray);
|
||||
$this->assertSame($expected, $settingsArray['MaxRows']);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{mixed, int}> */
|
||||
public static function valuesForMaxRowsProvider(): iterable
|
||||
{
|
||||
yield 'null value' => [null, 25];
|
||||
yield 'valid value' => [1, 1];
|
||||
yield 'valid value with type coercion' => ['2', 2];
|
||||
yield 'invalid value' => [0, 25];
|
||||
}
|
||||
|
||||
/** @dataProvider valuesForLimitCharsProvider */
|
||||
public function testLimitChars(mixed $actual, int $expected): void
|
||||
{
|
||||
$settings = new Settings(['LimitChars' => $actual]);
|
||||
$settingsArray = $settings->asArray();
|
||||
$this->assertSame($expected, $settings->limitChars);
|
||||
$this->assertArrayHasKey('LimitChars', $settingsArray);
|
||||
$this->assertSame($expected, $settingsArray['LimitChars']);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{mixed, int}> */
|
||||
public static function valuesForLimitCharsProvider(): iterable
|
||||
{
|
||||
yield 'null value' => [null, 50];
|
||||
yield 'valid value' => [1, 1];
|
||||
yield 'valid value with type coercion' => ['2', 2];
|
||||
yield 'invalid value' => [0, 50];
|
||||
}
|
||||
|
||||
/** @dataProvider valuesForRepeatCellsProvider */
|
||||
public function testRepeatCells(mixed $actual, int $expected): void
|
||||
{
|
||||
$settings = new Settings(['RepeatCells' => $actual]);
|
||||
$settingsArray = $settings->asArray();
|
||||
$this->assertSame($expected, $settings->repeatCells);
|
||||
$this->assertArrayHasKey('RepeatCells', $settingsArray);
|
||||
$this->assertSame($expected, $settingsArray['RepeatCells']);
|
||||
}
|
||||
|
||||
/** @return iterable<string, array{mixed, int}> */
|
||||
public static function valuesForRepeatCellsProvider(): iterable
|
||||
{
|
||||
yield 'null value' => [null, 100];
|
||||
yield 'valid value' => [0, 0];
|
||||
yield 'valid value with type coercion' => ['1', 1];
|
||||
yield 'invalid value' => [-1, 100];
|
||||
}
|
||||
|
||||
/** @dataProvider booleanWithDefaultTrueProvider */
|
||||
public function testZeroConf(mixed $actual, bool $expected): void
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user