Merge branch 'release/17.0' into v17/dev
# Conflicts: # version.json
This commit is contained in:
10
tests/Umbraco.Tests.AcceptanceTest/package-lock.json
generated
10
tests/Umbraco.Tests.AcceptanceTest/package-lock.json
generated
@@ -7,8 +7,8 @@
|
||||
"name": "acceptancetest",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "^2.0.40",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.7",
|
||||
"@umbraco/json-models-builders": "^2.0.41",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.10",
|
||||
"camelize": "^1.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"node-fetch": "^2.6.7"
|
||||
@@ -67,9 +67,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@umbraco/playwright-testhelpers": {
|
||||
"version": "17.0.0-beta.7",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-17.0.0-beta.7.tgz",
|
||||
"integrity": "sha512-5fhmVVSpJkH6Inx8nA9qqqvZzYuPdDxJdQF2IzY0oSf8C0eti+TJ2BKrYfTLmZTfVqmHUas72BMGser5pfpl9A==",
|
||||
"version": "17.0.0-beta.10",
|
||||
"resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-17.0.0-beta.10.tgz",
|
||||
"integrity": "sha512-ePvtWK2IG/j3TIL1w7xkZR63FHM32hIjZxaxJOQ4rYNuVxBKT7TTKEvASfdwpDBFnlAN186xZRGA9KJq+Jxijg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "2.0.41",
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
"typescript": "^4.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@umbraco/json-models-builders": "^2.0.40",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.7",
|
||||
"@umbraco/json-models-builders": "^2.0.41",
|
||||
"@umbraco/playwright-testhelpers": "^17.0.0-beta.10",
|
||||
"camelize": "^1.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"node-fetch": "^2.6.7"
|
||||
|
||||
@@ -21,7 +21,7 @@ test('can create a data type using create options', async ({umbracoApi, umbracoU
|
||||
await umbracoUi.dataType.clickDataTypesMenu();
|
||||
|
||||
// Act
|
||||
await umbracoUi.dataType.clickCreateActionWithOptionName('New Data Type');
|
||||
await umbracoUi.dataType.clickCreateActionWithOptionName('Data Type');
|
||||
await umbracoUi.dataType.enterDataTypeName(dataTypeName);
|
||||
await umbracoUi.dataType.clickSelectAPropertyEditorButton();
|
||||
await umbracoUi.dataType.selectAPropertyEditor('Text Box');
|
||||
@@ -60,7 +60,7 @@ test('can create a data type in a folder using create options', async ({umbracoA
|
||||
await umbracoUi.dataType.goToDataType(dataTypeFolderName);
|
||||
|
||||
// Act
|
||||
await umbracoUi.dataType.clickCreateActionWithOptionName('New Data Type');
|
||||
await umbracoUi.dataType.clickCreateActionWithOptionName('Data Type');
|
||||
await umbracoUi.dataType.enterDataTypeName(dataTypeName);
|
||||
await umbracoUi.dataType.clickSelectAPropertyEditorButton();
|
||||
await umbracoUi.dataType.selectAPropertyEditor('Text Box');
|
||||
|
||||
@@ -9,7 +9,6 @@ test('can click on buttons', {tag: '@release'}, async ({umbracoUi}) => {
|
||||
// Arrange
|
||||
const getTheHelpYouNeedDocumentationUrl = 'https://docs.umbraco.com/umbraco-cms';
|
||||
const goToTheForumUrl = 'https://forum.umbraco.com/';
|
||||
const chatWithTheCommunityUrl = 'https://discord.umbraco.com';
|
||||
const getCertifiedUrl = 'https://umbraco.com/training/';
|
||||
const getTheHelpYouNeedSupportUrl = 'https://umbraco.com/support/';
|
||||
const watchTheVideosUrl = 'https://www.youtube.com/c/UmbracoLearningBase';
|
||||
@@ -20,7 +19,6 @@ test('can click on buttons', {tag: '@release'}, async ({umbracoUi}) => {
|
||||
// Assert
|
||||
await umbracoUi.welcomeDashboard.doesButtonWithLabelInBoxHaveLink('Get the help you need', 'Documentation', getTheHelpYouNeedDocumentationUrl);
|
||||
await umbracoUi.welcomeDashboard.doesButtonWithLabelInBoxHaveLink('Go to the forum', 'Community', goToTheForumUrl);
|
||||
await umbracoUi.welcomeDashboard.doesButtonWithLabelInBoxHaveLink('Chat with the community', 'Community', chatWithTheCommunityUrl);
|
||||
await umbracoUi.welcomeDashboard.doesButtonWithLabelInBoxHaveLink('Get Certified', 'Training', getCertifiedUrl);
|
||||
await umbracoUi.welcomeDashboard.doesButtonWithLabelInBoxHaveLink('Get the help you need', 'Support', getTheHelpYouNeedSupportUrl);
|
||||
await umbracoUi.welcomeDashboard.doesButtonWithLabelInBoxHaveLink('Watch the videos', 'Videos', watchTheVideosUrl);
|
||||
|
||||
@@ -9,7 +9,6 @@ const allPermissions = {
|
||||
'Create',
|
||||
'Notifications',
|
||||
'Publish',
|
||||
'Set permissions',
|
||||
'Unpublish',
|
||||
'Update',
|
||||
'Duplicate',
|
||||
@@ -25,7 +24,6 @@ const allPermissions = {
|
||||
'Umb.Document.Create',
|
||||
'Umb.Document.Notifications',
|
||||
'Umb.Document.Publish',
|
||||
'Umb.Document.Permissions',
|
||||
'Umb.Document.Unpublish',
|
||||
'Umb.Document.Update',
|
||||
'Umb.Document.Duplicate',
|
||||
|
||||
@@ -30,15 +30,12 @@ test('the default configuration of Administrators is correct', {tag: '@release'}
|
||||
"Umb.Document.PublicAccess",
|
||||
"Umb.Document.CultureAndHostnames",
|
||||
"Umb.Document.Publish",
|
||||
"Umb.Document.Permissions",
|
||||
"Umb.Document.Unpublish",
|
||||
"Umb.Document.Read",
|
||||
"Umb.Document.CreateBlueprint",
|
||||
"Umb.Document.Notifications",
|
||||
"Umb.Document.PropertyValue.Read",
|
||||
"Umb.Document.PropertyValue.Write"
|
||||
];
|
||||
const granularPermissions = [];
|
||||
const granularPermissions: any = [];
|
||||
const hasAccessToAllLanguages = true;
|
||||
const documentRootAccess = true;
|
||||
const mediaRootAccess = true;
|
||||
@@ -50,8 +47,8 @@ test('the default configuration of Administrators is correct', {tag: '@release'}
|
||||
|
||||
// Act
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Assign access', ConstantHelper.userGroupAssignAccessSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Default permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Granular permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document Property Value permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesPermissionsSettingsHaveValue(ConstantHelper.userGroupPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesUserGroupHavePermissionEnabled(uiPermissions);
|
||||
await umbracoUi.userGroup.doesUserGroupHaveSections(uiSections);
|
||||
@@ -88,10 +85,8 @@ test('the default configuration of Editors is correct', {tag: '@release'}, async
|
||||
"Umb.Document.Read",
|
||||
"Umb.Document.CreateBlueprint",
|
||||
"Umb.Document.Notifications",
|
||||
"Umb.Document.PropertyValue.Read",
|
||||
"Umb.Document.PropertyValue.Write"
|
||||
];
|
||||
const granularPermissions = [];
|
||||
const granularPermissions: string[] = [];
|
||||
const hasAccessToAllLanguages = true;
|
||||
const documentRootAccess = true;
|
||||
const mediaRootAccess = true;
|
||||
@@ -103,8 +98,8 @@ test('the default configuration of Editors is correct', {tag: '@release'}, async
|
||||
|
||||
// Act
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Assign access', ConstantHelper.userGroupAssignAccessSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Default permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Granular permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document Property Value permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesPermissionsSettingsHaveValue(ConstantHelper.userGroupPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesUserGroupHavePermissionEnabled(uiPermissions);
|
||||
await umbracoUi.userGroup.doesUserGroupHaveSections(uiSections);
|
||||
@@ -123,9 +118,9 @@ test('the default configuration of Editors is correct', {tag: '@release'}, async
|
||||
test('the default configuration of Sensitive data is correct', {tag: '@release'}, async ({umbracoApi, umbracoUi}) => {
|
||||
// Arrange
|
||||
const userGroupName = 'Sensitive data';
|
||||
const sections = [];
|
||||
const fallbackPermissions = [];
|
||||
const granularPermissions = [];
|
||||
const sections: string[] = [];
|
||||
const fallbackPermissions: string[] = [];
|
||||
const granularPermissions: string[] = [];
|
||||
const hasAccessToAllLanguages = false;
|
||||
const documentRootAccess = false;
|
||||
const mediaRootAccess = false;
|
||||
@@ -137,8 +132,8 @@ test('the default configuration of Sensitive data is correct', {tag: '@release'}
|
||||
|
||||
// Act
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Assign access', ConstantHelper.userGroupAssignAccessSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Default permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Granular permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document Property Value permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesPermissionsSettingsHaveValue(ConstantHelper.userGroupPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesUserGroupHavePermissionEnabled(uiPermissions);
|
||||
await umbracoUi.userGroup.doesUserGroupHaveSections(uiSections);
|
||||
@@ -161,10 +156,8 @@ test('the default configuration of Translators data is correct', {tag: '@release
|
||||
const fallbackPermissions = [
|
||||
"Umb.Document.Update",
|
||||
"Umb.Document.Read",
|
||||
"Umb.Document.PropertyValue.Read",
|
||||
"Umb.Document.PropertyValue.Write"
|
||||
];
|
||||
const granularPermissions = [];
|
||||
const granularPermissions: string[] = [];
|
||||
const hasAccessToAllLanguages = true;
|
||||
const documentRootAccess = true;
|
||||
const mediaRootAccess = true;
|
||||
@@ -176,14 +169,15 @@ test('the default configuration of Translators data is correct', {tag: '@release
|
||||
|
||||
// Act
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Assign access', ConstantHelper.userGroupAssignAccessSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Default permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Granular permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document Property Value permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesPermissionsSettingsHaveValue(ConstantHelper.userGroupPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesUserGroupHavePermissionEnabled(uiPermissions);
|
||||
await umbracoUi.userGroup.doesUserGroupHaveSections(uiSections);
|
||||
await umbracoUi.userGroup.doesUserGroupSectionsHaveCount(uiSections.length);
|
||||
expect(await umbracoApi.userGroup.doesUserGroupHaveSections(userGroupName, sections)).toBeTruthy();
|
||||
expect(await umbracoApi.userGroup.doesUserGroupHaveFallbackPermissions(userGroupName, fallbackPermissions)).toBeTruthy();
|
||||
// Fixme - Uncomment this when the front-end is ready. Currently the fallbackPermissions includes some unnecessary values such as ":", "5", "T"
|
||||
// expect(await umbracoApi.userGroup.doesUserGroupHaveFallbackPermissions(userGroupName, fallbackPermissions)).toBeTruthy();
|
||||
const userGroupData = await umbracoApi.userGroup.getByName(userGroupName);
|
||||
expect(userGroupData.hasAccessToAllLanguages).toEqual(hasAccessToAllLanguages);
|
||||
expect(userGroupData.documentRootAccess).toEqual(documentRootAccess);
|
||||
@@ -200,10 +194,8 @@ test('the default configuration of Writers data is correct', {tag: '@release'},
|
||||
"Umb.Document.Update",
|
||||
"Umb.Document.Read",
|
||||
"Umb.Document.Notifications",
|
||||
"Umb.Document.PropertyValue.Read",
|
||||
"Umb.Document.PropertyValue.Write"
|
||||
];
|
||||
const granularPermissions = [];
|
||||
const granularPermissions: string[] = [];
|
||||
const hasAccessToAllLanguages = true;
|
||||
const documentRootAccess = true;
|
||||
const mediaRootAccess = true;
|
||||
@@ -215,15 +207,15 @@ test('the default configuration of Writers data is correct', {tag: '@release'},
|
||||
|
||||
// Act
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Assign access', ConstantHelper.userGroupAssignAccessSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Default permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Granular permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document permissions', ConstantHelper.userGroupDefaultPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesSettingHaveValue('Document Property Value permissions', ConstantHelper.userGroupGranularPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesPermissionsSettingsHaveValue(ConstantHelper.userGroupPermissionsSettings);
|
||||
await umbracoUi.userGroup.doesUserGroupHavePermissionEnabled(uiPermissions);
|
||||
await umbracoUi.userGroup.doesUserGroupHaveSections(uiSections);
|
||||
await umbracoUi.userGroup.doesUserGroupSectionsHaveCount(uiSections.length);
|
||||
expect(await umbracoApi.userGroup.doesUserGroupHaveSections(userGroupName, sections)).toBeTruthy();
|
||||
// Fixme - Uncomment this when the front-end is ready. Currently the fallbackPermissions includes some unnecessary values such as ":", "5", "T"
|
||||
//expect(await umbracoApi.userGroup.doesUserGroupHaveFallbackPermissions(userGroupName, fallbackPermissions)).toBeTruthy();
|
||||
// expect(await umbracoApi.userGroup.doesUserGroupHaveFallbackPermissions(userGroupName, fallbackPermissions)).toBeTruthy();
|
||||
const userGroupData = await umbracoApi.userGroup.getByName(userGroupName);
|
||||
expect(userGroupData.hasAccessToAllLanguages).toEqual(hasAccessToAllLanguages);
|
||||
expect(userGroupData.documentRootAccess).toEqual(documentRootAccess);
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using Umbraco.Cms.Api.Management.Controllers.Help;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.ManagementApi.Help;
|
||||
|
||||
public class GetHelpControllerTests : ManagementApiUserGroupTestBase<GetHelpController>
|
||||
{
|
||||
protected override Expression<Func<GetHelpController, object>> MethodSelector =>
|
||||
x => x.Get(CancellationToken.None, "TestSection", "TestTree", 0, 100, "https://our.umbraco.com");
|
||||
|
||||
protected override UserGroupAssertionModel AdminUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.BadRequest
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel EditorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.BadRequest
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel SensitiveDataUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.BadRequest
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel TranslatorUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.BadRequest
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel WriterUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.BadRequest
|
||||
};
|
||||
|
||||
protected override UserGroupAssertionModel UnauthorizedUserGroupAssertionModel => new()
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.Unauthorized
|
||||
};
|
||||
}
|
||||
@@ -132,7 +132,7 @@ public class TypeLoaderTests
|
||||
public void GetDataEditors()
|
||||
{
|
||||
var types = _typeLoader.GetDataEditors();
|
||||
Assert.AreEqual(41, types.Count());
|
||||
Assert.AreEqual(42, types.Count());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Globalization;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models.Validation;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors;
|
||||
|
||||
[TestFixture]
|
||||
public class EntityDataPickerPropertyValueEditorTests
|
||||
{
|
||||
[TestCase(1, false)]
|
||||
[TestCase(2, true)]
|
||||
[TestCase(3, true)]
|
||||
public void Validates_Is_Greater_Than_Or_Equal_To_Configured_Min(int numberOfSelections, bool expectedSuccess)
|
||||
{
|
||||
var editor = CreateValueEditor();
|
||||
var value = new EntityDataPickerPropertyEditor.EntityDataPickerDto
|
||||
{
|
||||
Ids = [.. Enumerable.Range(1, numberOfSelections).Select(i => i.ToString())],
|
||||
};
|
||||
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
|
||||
var serializedValue = serializer.Serialize(value);
|
||||
var result = editor.Validate(serializedValue, false, null, PropertyValidationContext.Empty());
|
||||
if (expectedSuccess)
|
||||
{
|
||||
Assert.IsEmpty(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(1, result.Count());
|
||||
|
||||
var validationResult = result.First();
|
||||
Assert.AreEqual("validation_entriesShort", validationResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(3, true)]
|
||||
[TestCase(4, true)]
|
||||
[TestCase(5, false)]
|
||||
public void Validates_Is_Less_Than_Or_Equal_To_Configured_Max(int numberOfSelections, bool expectedSuccess)
|
||||
{
|
||||
var editor = CreateValueEditor();
|
||||
var value = new EntityDataPickerPropertyEditor.EntityDataPickerDto
|
||||
{
|
||||
Ids = [.. Enumerable.Range(1, numberOfSelections).Select(i => i.ToString())],
|
||||
};
|
||||
var serializer = new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory());
|
||||
var serializedValue = serializer.Serialize(value);
|
||||
var result = editor.Validate(serializedValue, false, null, PropertyValidationContext.Empty());
|
||||
if (expectedSuccess)
|
||||
{
|
||||
Assert.IsEmpty(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(1, result.Count());
|
||||
|
||||
var validationResult = result.First();
|
||||
Assert.AreEqual("validation_entriesExceed", validationResult.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static EntityDataPickerPropertyEditor.EntityDataPickerPropertyValueEditor CreateValueEditor()
|
||||
{
|
||||
var localizedTextServiceMock = new Mock<ILocalizedTextService>();
|
||||
localizedTextServiceMock.Setup(x => x.Localize(
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<CultureInfo>(),
|
||||
It.IsAny<IDictionary<string, string>>()))
|
||||
.Returns((string key, string alias, CultureInfo culture, IDictionary<string, string> args) => $"{key}_{alias}");
|
||||
return new EntityDataPickerPropertyEditor.EntityDataPickerPropertyValueEditor(
|
||||
Mock.Of<IShortStringHelper>(),
|
||||
new SystemTextJsonSerializer(new DefaultJsonSerializerEncoderFactory()),
|
||||
Mock.Of<IIOHelper>(),
|
||||
new DataEditorAttribute("alias"),
|
||||
localizedTextServiceMock.Object)
|
||||
{
|
||||
ConfigurationObject = new EntityDataPickerConfiguration
|
||||
{
|
||||
DataSource = "testDataSource",
|
||||
ValidationLimit = new EntityDataPickerConfiguration.NumberRange
|
||||
{
|
||||
Min = 2,
|
||||
Max = 4
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user