mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-24 16:41:35 -04:00
implement DRY principle (#6553)
This commit is contained in:
@@ -28,6 +28,7 @@ use TestHelpers\HttpRequestHelper;
|
||||
use TestHelpers\SetupHelper;
|
||||
use wapmorgan\UnifiedArchive\UnifiedArchive;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use \Psr\Http\Message\ResponseInterface;
|
||||
|
||||
require_once 'bootstrap.php';
|
||||
|
||||
@@ -118,18 +119,7 @@ class ArchiverContext implements Context {
|
||||
foreach ($headersTable as $row) {
|
||||
$headers[$row['header']] = $row ['value'];
|
||||
}
|
||||
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$queryString = $this->getArchiverQueryString($user, $resource, $addressType);
|
||||
$this->featureContext->setResponse(
|
||||
HttpRequestHelper::get(
|
||||
$this->featureContext->getBaseUrl() . '/archiver?' . $queryString,
|
||||
'',
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$headers
|
||||
)
|
||||
);
|
||||
$this->featureContext->setResponse($this->downloadArchive($user, $resource, $addressType, null, $headers));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,15 +141,36 @@ class ArchiverContext implements Context {
|
||||
string $owner,
|
||||
string $addressType
|
||||
): void {
|
||||
$this->featureContext->setResponse($this->downloadArchive($downloader, $resource, $addressType, $owner));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $downloader
|
||||
* @param string $resource
|
||||
* @param string $addressType
|
||||
* @param string|null $owner
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function downloadArchive(
|
||||
string $downloader,
|
||||
string $resource,
|
||||
string $addressType,
|
||||
?string $owner = null,
|
||||
?array $headers = null
|
||||
): ResponseInterface {
|
||||
$owner = $owner ?? $downloader;
|
||||
$downloader = $this->featureContext->getActualUsername($downloader);
|
||||
$queryString = $this->getArchiverQueryString($owner, $resource, $addressType);
|
||||
$this->featureContext->setResponse(
|
||||
HttpRequestHelper::get(
|
||||
$this->featureContext->getBaseUrl() . '/archiver?' . $queryString,
|
||||
'',
|
||||
$downloader,
|
||||
$this->featureContext->getPasswordForUser($downloader),
|
||||
)
|
||||
return HttpRequestHelper::get(
|
||||
$this->featureContext->getBaseUrl() . '/archiver?' . $queryString,
|
||||
'',
|
||||
$downloader,
|
||||
$this->featureContext->getPasswordForUser($downloader),
|
||||
$headers
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ use Behat\Gherkin\Node\TableNode;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use TestHelpers\HttpRequestHelper;
|
||||
use TestHelpers\WebDavHelper;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
require_once 'bootstrap.php';
|
||||
|
||||
@@ -55,20 +56,7 @@ class FilesVersionsContext implements Context {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function userTriesToGetFileVersions(string $user, string $file, string $fileOwner):void {
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$fileOwner = $this->featureContext->getActualUsername($fileOwner);
|
||||
$fileId = $this->featureContext->getFileIdForPath($fileOwner, $file);
|
||||
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $file user $fileOwner not found (the file may not exist)");
|
||||
$response = $this->featureContext->makeDavRequest(
|
||||
$user,
|
||||
"PROPFIND",
|
||||
$this->getVersionsPathForFileId($fileId),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
'2'
|
||||
);
|
||||
$this->featureContext->setResponse($response, $user);
|
||||
$this->featureContext->setResponse($this->getFileVersions($user, $file, $fileOwner));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,10 +69,28 @@ class FilesVersionsContext implements Context {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function userGetsFileVersions(string $user, string $file):void {
|
||||
$this->featureContext->setResponse($this->getFileVersions($user, $file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $file
|
||||
* @param string|null $fileOwner
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws JsonException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getFileVersions(
|
||||
string $user,
|
||||
string $file,
|
||||
?string $fileOwner = null
|
||||
): ResponseInterface {
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$fileId = $this->featureContext->getFileIdForPath($user, $file);
|
||||
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $file user $user not found (the file may not exist)");
|
||||
$response = $this->featureContext->makeDavRequest(
|
||||
$fileOwner = $fileOwner ? $this->featureContext->getActualUsername($fileOwner) : $user;
|
||||
$fileId = $this->featureContext->getFileIdForPath($fileOwner, $file);
|
||||
Assert::assertNotNull($fileId, __METHOD__ . " fileid of file $file user $fileOwner not found (the file may not exist)");
|
||||
return $this->featureContext->makeDavRequest(
|
||||
$user,
|
||||
"PROPFIND",
|
||||
$this->getVersionsPathForFileId($fileId),
|
||||
@@ -93,7 +99,6 @@ class FilesVersionsContext implements Context {
|
||||
null,
|
||||
'2'
|
||||
);
|
||||
$this->featureContext->setResponse($response, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -263,33 +263,6 @@ class GraphContext implements Context {
|
||||
$this->featureContext->thenTheHTTPStatusCodeShouldBe(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $requestingUser
|
||||
* @param $targetUser
|
||||
*
|
||||
* @return void
|
||||
* @throws JsonException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function userHasRetrievedUserUsingTheGraphApi(
|
||||
$requestingUser,
|
||||
$targetUser
|
||||
): void {
|
||||
$requester = $this->featureContext->getActualUsername($requestingUser);
|
||||
$requesterPassword = $this->featureContext->getPasswordForUser($requestingUser);
|
||||
$user = $this->featureContext->getActualUsername($targetUser);
|
||||
$userId = $this->featureContext->getAttributeOfCreatedUser($user, "id");
|
||||
$response = GraphHelper::getUser(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$requester,
|
||||
$requesterPassword,
|
||||
$userId
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
$this->featureContext->thenTheHTTPStatusCodeShouldBe(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupId
|
||||
* @param string|null $user
|
||||
@@ -2423,8 +2396,26 @@ class GraphContext implements Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* @When user :user unassigns the role of user :ofUser using the Graph API
|
||||
* @When user :user tries to unassign the role of user :ofUser using the Graph API
|
||||
* @param string $user
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function getAssignedRole(string $user) {
|
||||
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id') ?? $user;
|
||||
return (
|
||||
GraphHelper::getAssignedRole(
|
||||
$this->featureContext->getBAseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$this->featureContext->getAdminUsername(),
|
||||
$this->featureContext->getAdminPassword(),
|
||||
$userId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" (?:unassigns|tries to unassign) the role of user "([^"]*)" using the Graph API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $ofUser
|
||||
@@ -2437,15 +2428,7 @@ class GraphContext implements Context {
|
||||
public function theUserUnassignsTheRoleOfUserUsingTheGraphApi(string $user, string $ofUser): void {
|
||||
$userId = $this->featureContext->getAttributeOfCreatedUser($ofUser, 'id') ?? $ofUser;
|
||||
$credentials = $this->getAdminOrUserCredentials($user);
|
||||
|
||||
$response = GraphHelper::getAssignedRole(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$this->featureContext->getAdminUsername(),
|
||||
$this->featureContext->getAdminPassword(),
|
||||
$userId
|
||||
);
|
||||
$appRoleAssignmentId = $this->featureContext->getJsonDecodedResponse($response)["value"][0]["id"];
|
||||
$appRoleAssignmentId = $this->featureContext->getJsonDecodedResponse($this->getAssignedRole($ofUser))["value"][0]["id"];
|
||||
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::unassignRole(
|
||||
@@ -2470,16 +2453,7 @@ class GraphContext implements Context {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function userShouldHaveTheRoleAssigned(string $user, string $role): void {
|
||||
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id') ?? $user;
|
||||
$response = GraphHelper::getAssignedRole(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$this->featureContext->getAdminUserName(),
|
||||
$this->featureContext->getAdminPassword(),
|
||||
$userId
|
||||
);
|
||||
|
||||
$jsonDecodedResponse = $this->featureContext->getJsonDecodedResponse($response)['value'][0];
|
||||
$jsonDecodedResponse = $this->featureContext->getJsonDecodedResponse($this->getAssignedRole($user))['value'][0];
|
||||
if (empty($this->appEntity)) {
|
||||
$this->setApplicationEntity();
|
||||
}
|
||||
@@ -2502,16 +2476,7 @@ class GraphContext implements Context {
|
||||
* @throws Exception
|
||||
*/
|
||||
public function userShouldNotHaveAnyRoleAssigned(string $user): void {
|
||||
$userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id') ?? $user;
|
||||
$response = GraphHelper::getAssignedRole(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$this->featureContext->getStepLineRef(),
|
||||
$this->featureContext->getAdminUserName(),
|
||||
$this->featureContext->getAdminPassword(),
|
||||
$userId
|
||||
);
|
||||
|
||||
$jsonDecodedResponse = $this->featureContext->getJsonDecodedResponse($response)['value'];
|
||||
$jsonDecodedResponse = $this->featureContext->getJsonDecodedResponse($this->getAssignedRole($user))['value'];
|
||||
Assert::assertEmpty(
|
||||
$jsonDecodedResponse,
|
||||
__METHOD__
|
||||
|
||||
@@ -1458,96 +1458,6 @@ trait Provisioning {
|
||||
$this->manuallyAddSkeletonFilesForUser($user, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^the groupadmin "([^"]*)" sends a user creation request for user "([^"]*)" password "([^"]*)" group "([^"]*)" using the provisioning API$/
|
||||
*
|
||||
* @param string $groupadmin
|
||||
* @param string $userToCreate
|
||||
* @param string $password
|
||||
* @param string $group
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function theGroupAdminCreatesUserPasswordGroupUsingTheProvisioningApi(
|
||||
string $groupadmin,
|
||||
string $userToCreate,
|
||||
string $password,
|
||||
string $group
|
||||
):void {
|
||||
$userToCreate = $this->getActualUsername($userToCreate);
|
||||
$password = $this->getActualPassword($password);
|
||||
$email = $userToCreate . '@owncloud.com';
|
||||
$bodyTable = new TableNode(
|
||||
[
|
||||
['userid', $userToCreate],
|
||||
['password', $userToCreate],
|
||||
['username', $userToCreate],
|
||||
['email', $email],
|
||||
['groups[]', $group],
|
||||
]
|
||||
);
|
||||
$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
|
||||
$groupadmin,
|
||||
"POST",
|
||||
"/cloud/users",
|
||||
$bodyTable
|
||||
);
|
||||
$this->addUserToCreatedUsersList(
|
||||
$userToCreate,
|
||||
$password,
|
||||
null,
|
||||
$email,
|
||||
null,
|
||||
$this->theHTTPStatusCodeWasSuccess()
|
||||
);
|
||||
$this->manuallyAddSkeletonFilesForUser($userToCreate, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^the groupadmin "([^"]*)" tries to create new user "([^"]*)" password "([^"]*)" in other group "([^"]*)" using the provisioning API$/
|
||||
*
|
||||
* @param string $groupadmin
|
||||
* @param string $userToCreate
|
||||
* @param string|null $password
|
||||
* @param string $group
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function theGroupAdminCreatesUserPasswordInOtherGroupUsingTheProvisioningApi(
|
||||
string $groupadmin,
|
||||
string $userToCreate,
|
||||
?string $password,
|
||||
string $group
|
||||
):void {
|
||||
$userToCreate = $this->getActualUsername($userToCreate);
|
||||
$password = $this->getActualPassword($password);
|
||||
$email = $userToCreate . '@owncloud.com';
|
||||
$bodyTable = new TableNode(
|
||||
[
|
||||
['userid', $userToCreate],
|
||||
['password', $userToCreate],
|
||||
['username', $userToCreate],
|
||||
['email', $email],
|
||||
['groups[]', $group],
|
||||
]
|
||||
);
|
||||
$this->ocsContext->userSendsHTTPMethodToOcsApiEndpointWithBody(
|
||||
$groupadmin,
|
||||
"POST",
|
||||
"/cloud/users",
|
||||
$bodyTable
|
||||
);
|
||||
$this->addUserToCreatedUsersList(
|
||||
$userToCreate,
|
||||
$password,
|
||||
null,
|
||||
$email,
|
||||
null,
|
||||
$this->theHTTPStatusCodeWasSuccess()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @param string|null $password
|
||||
@@ -2420,25 +2330,6 @@ trait Provisioning {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^the administrator has retrieved the information of user "([^"]*)"$/
|
||||
*
|
||||
* @param string $user
|
||||
*
|
||||
* @return void
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function adminHasRetrievedTheInformationOfUserUsingTheProvisioningApi(
|
||||
string $user
|
||||
):void {
|
||||
if (OcisHelper::isTestingWithGraphApi()) {
|
||||
$this->graphContext->adminHasRetrievedUserUsingTheGraphApi($user);
|
||||
} else {
|
||||
$this->retrieveUserInformationAsAdminUsingProvisioningApi($user);
|
||||
$this->theHTTPStatusCodeShouldBeSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $requestingUser
|
||||
* @param string $targetUser
|
||||
@@ -2480,33 +2371,6 @@ trait Provisioning {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given /^user "([^"]*)" has retrieved the information of user "([^"]*)"$/
|
||||
*
|
||||
* @param string $requestingUser
|
||||
* @param string $targetUser
|
||||
*
|
||||
* @return void
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function userHasRetrievedTheInformationOfUserUsingTheProvisioningApi(
|
||||
string $requestingUser,
|
||||
string $targetUser
|
||||
):void {
|
||||
if (OcisHelper::isTestingWithGraphApi()) {
|
||||
$this->graphContext->userHasRetrievedUserUsingTheGraphApi(
|
||||
$requestingUser,
|
||||
$targetUser
|
||||
);
|
||||
} else {
|
||||
$this->userRetrieveUserInformationUsingProvisioningApi(
|
||||
$requestingUser,
|
||||
$targetUser
|
||||
);
|
||||
$this->theHTTPStatusCodeShouldBeSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^user "([^"]*)" should exist$/
|
||||
*
|
||||
|
||||
@@ -2355,20 +2355,24 @@ trait Sharing {
|
||||
}
|
||||
|
||||
/**
|
||||
* @When user :user gets all shares shared by him/her using the sharing API
|
||||
*
|
||||
* @param string $user
|
||||
* @param string|null $endpointPath
|
||||
*
|
||||
* @return void
|
||||
* @return ResponseInterface
|
||||
* @throws JsonException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function userGetsAllSharesSharedByHimUsingTheSharingApi(string $user):void {
|
||||
public function getAllShares(
|
||||
string $user,
|
||||
?string $endpointPath = null
|
||||
): ResponseInterface {
|
||||
$user = $this->getActualUsername($user);
|
||||
$this->response = OcsApiHelper::sendRequest(
|
||||
return OcsApiHelper::sendRequest(
|
||||
$this->getBaseUrl(),
|
||||
$user,
|
||||
$this->getPasswordForUser($user),
|
||||
"GET",
|
||||
$this->getSharesEndpointPath(),
|
||||
$this->getSharesEndpointPath($endpointPath),
|
||||
$this->getStepLineRef(),
|
||||
[],
|
||||
$this->ocsApiVersion
|
||||
@@ -2376,12 +2380,23 @@ trait Sharing {
|
||||
}
|
||||
|
||||
/**
|
||||
* @When the administrator gets all shares shared by him/her using the sharing API
|
||||
* @When /^user "([^"]*)" gets all shares shared by (?:him|her) using the sharing API$/
|
||||
*
|
||||
* @param string $user
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsAllSharesSharedByHimUsingTheSharingApi(string $user):void {
|
||||
$this->setResponse($this->getAllshares($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^the administrator gets all shares shared by (?:him|her) using the sharing API$/
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function theAdministratorGetsAllSharesSharedByHimUsingTheSharingApi():void {
|
||||
$this->userGetsAllSharesSharedByHimUsingTheSharingApi($this->getAdminUsername());
|
||||
$this->setResponse($this->getAllShares($this->getAdminUsername()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2393,7 +2408,6 @@ trait Sharing {
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsFilteredSharesSharedByHimUsingTheSharingApi(string $user, string $shareType):void {
|
||||
$user = $this->getActualUsername($user);
|
||||
if ($shareType === 'public link') {
|
||||
$shareType = 'public_link';
|
||||
}
|
||||
@@ -2402,16 +2416,7 @@ trait Sharing {
|
||||
} else {
|
||||
$rawShareTypes = SharingHelper::SHARE_TYPES[$shareType];
|
||||
}
|
||||
$this->response = OcsApiHelper::sendRequest(
|
||||
$this->getBaseUrl(),
|
||||
$user,
|
||||
$this->getPasswordForUser($user),
|
||||
"GET",
|
||||
$this->getSharesEndpointPath("?share_types=" . $rawShareTypes),
|
||||
$this->getStepLineRef(),
|
||||
[],
|
||||
$this->ocsApiVersion
|
||||
);
|
||||
$this->setResponse($this->getAllShares($user, "?share_types=" . $rawShareTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2423,17 +2428,7 @@ trait Sharing {
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsAllTheSharesFromTheFileUsingTheSharingApi(string $user, string $path):void {
|
||||
$user = $this->getActualUsername($user);
|
||||
$this->response = OcsApiHelper::sendRequest(
|
||||
$this->getBaseUrl(),
|
||||
$user,
|
||||
$this->getPasswordForUser($user),
|
||||
"GET",
|
||||
$this->getSharesEndpointPath("?path=$path"),
|
||||
$this->getStepLineRef(),
|
||||
[],
|
||||
$this->ocsApiVersion
|
||||
);
|
||||
$this->setResponse($this->getAllShares($user, "?path=$path"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2448,17 +2443,7 @@ trait Sharing {
|
||||
string $user,
|
||||
string $path
|
||||
):void {
|
||||
$userActual = $this->getActualUsername($user);
|
||||
$this->response = OcsApiHelper::sendRequest(
|
||||
$this->getBaseUrl(),
|
||||
$userActual,
|
||||
$this->getPasswordForUser($user),
|
||||
"GET",
|
||||
$this->getSharesEndpointPath("?reshares=true&path=$path"),
|
||||
$this->getStepLineRef(),
|
||||
[],
|
||||
$this->ocsApiVersion
|
||||
);
|
||||
$this->setResponse($this->getAllShares($user, "?reshares=true&path=$path"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2470,17 +2455,7 @@ trait Sharing {
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsAllTheSharesInsideTheFolderUsingTheSharingApi(string $user, string $path):void {
|
||||
$user = $this->getActualUsername($user);
|
||||
$this->response = OcsApiHelper::sendRequest(
|
||||
$this->getBaseUrl(),
|
||||
$user,
|
||||
$this->getPasswordForUser($user),
|
||||
"GET",
|
||||
$this->getSharesEndpointPath("?path=$path&subfiles=true"),
|
||||
$this->getStepLineRef(),
|
||||
[],
|
||||
$this->ocsApiVersion
|
||||
);
|
||||
$this->setResponse($this->getAllShares($user, "?path=$path&subfiles=true"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -591,44 +591,13 @@ class SpacesContext implements Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" (?:creates|tries to create) a space "([^"]*)" of type "([^"]*)" with quota "([^"]*)" using the Graph API$/
|
||||
* @When /^user "([^"]*)" (?:creates|tries to create) a space "([^"]*)" of type "([^"]*)" with the default quota using the Graph API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $spaceName
|
||||
* @param string $spaceType
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function theUserCreatesASpaceUsingTheGraphApi(
|
||||
string $user,
|
||||
string $spaceName,
|
||||
string $spaceType
|
||||
): void {
|
||||
$space = ["Name" => $spaceName, "driveType" => $spaceType];
|
||||
$body = json_encode($space, JSON_THROW_ON_ERROR);
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::createSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body
|
||||
)
|
||||
);
|
||||
|
||||
$this->setSpaceCreator($spaceName, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" creates a space "([^"]*)" of type "([^"]*)" with quota "([^"]*)" using the Graph API$/
|
||||
* @When /^user "([^"]*)" tries to create a space "([^"]*)" of type "([^"]*)" with quota "([^"]*)" using the Graph API$/
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $spaceName
|
||||
* @param string $spaceType
|
||||
* @param int $quota
|
||||
* @param int|null $quota
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
@@ -639,7 +608,7 @@ class SpacesContext implements Context {
|
||||
string $user,
|
||||
string $spaceName,
|
||||
string $spaceType,
|
||||
int $quota
|
||||
?int $quota = null
|
||||
): void {
|
||||
$space = ["Name" => $spaceName, "driveType" => $spaceType, "quota" => ["total" => $quota]];
|
||||
$body = json_encode($space);
|
||||
@@ -762,36 +731,6 @@ class SpacesContext implements Context {
|
||||
WebDavHelper::$SPACE_ID_FROM_OCIS = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the space "([^"]*)" should (not|)\s?contain these (?:files|entries):$/
|
||||
*
|
||||
* @param string $spaceName
|
||||
* @param string $shouldOrNot (not|)
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public function theSpaceShouldContainEntries(
|
||||
string $spaceName,
|
||||
string $shouldOrNot,
|
||||
TableNode $expectedFiles
|
||||
): void {
|
||||
$spaceCreator = $this->getSpaceCreator($spaceName);
|
||||
$space = $this->getSpaceByName($spaceCreator, $spaceName);
|
||||
$this->theUserListsTheContentOfAPersonalSpaceRootUsingTheWebDAvApi(
|
||||
$spaceCreator,
|
||||
$spaceName
|
||||
);
|
||||
WebDavHelper::$SPACE_ID_FROM_OCIS = $space['id'];
|
||||
$this->featureContext->propfindResultShouldContainEntries(
|
||||
$shouldOrNot,
|
||||
$expectedFiles,
|
||||
);
|
||||
WebDavHelper::$SPACE_ID_FROM_OCIS = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^for user "([^"]*)" the space "([^"]*)" should (not|)\s?contain these (?:files|entries):$/
|
||||
*
|
||||
@@ -1295,6 +1234,41 @@ class SpacesContext implements Context {
|
||||
$this->featureContext->uploadFileWithContent($user, $content, $destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $spaceName
|
||||
* @param array $bodyData
|
||||
* @param string $owner
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function updateSpace(
|
||||
string $user,
|
||||
string $spaceName,
|
||||
array $bodyData,
|
||||
string $owner = ''
|
||||
): ResponseInterface {
|
||||
if ($spaceName === "non-existing") {
|
||||
// check sending invalid data
|
||||
$spaceId = "39c49dd3-1f24-4687-97d1-42df43f71713";
|
||||
} else {
|
||||
$space = $this->getSpaceByName(($owner !== "") ? $owner : $user, $spaceName);
|
||||
$spaceId = $space["id"];
|
||||
}
|
||||
|
||||
$body = json_encode($bodyData, JSON_THROW_ON_ERROR);
|
||||
|
||||
return GraphHelper::updateSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body,
|
||||
$spaceId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" (?:changes|tries to change) the name of the "([^"]*)" space to "([^"]*)"$/
|
||||
* @When /^user "([^"]*)" (?:changes|tries to change) the name of the "([^"]*)" space to "([^"]*)" owned by user "([^"]*)"$/
|
||||
@@ -1314,26 +1288,8 @@ class SpacesContext implements Context {
|
||||
string $newName,
|
||||
string $owner = ''
|
||||
): void {
|
||||
if ($spaceName === "non-existing") {
|
||||
// check sending invalid data
|
||||
$spaceId = "39c49dd3-1f24-4687-97d1-42df43f71713";
|
||||
} else {
|
||||
$space = $this->getSpaceByName(($owner !== "") ? $owner : $user, $spaceName);
|
||||
$spaceId = $space["id"];
|
||||
}
|
||||
|
||||
$bodyData = ["Name" => $newName];
|
||||
$body = json_encode($bodyData, JSON_THROW_ON_ERROR);
|
||||
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::updateSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body,
|
||||
$spaceId
|
||||
)
|
||||
);
|
||||
$this->featureContext->setResponse($this->updateSpace($user, $spaceName, $bodyData, $owner));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1355,26 +1311,8 @@ class SpacesContext implements Context {
|
||||
string $newDescription,
|
||||
string $owner = ''
|
||||
): void {
|
||||
if ($spaceName === "non-existing") {
|
||||
// check sending invalid data
|
||||
$spaceId = "39c49dd3-1f24-4687-97d1-42df43f71713";
|
||||
} else {
|
||||
$space = $this->getSpaceByName(($owner !== "") ? $owner : $user, $spaceName);
|
||||
$spaceId = $space["id"];
|
||||
}
|
||||
|
||||
$bodyData = ["description" => $newDescription];
|
||||
$body = json_encode($bodyData, JSON_THROW_ON_ERROR);
|
||||
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::updateSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body,
|
||||
$spaceId
|
||||
)
|
||||
);
|
||||
$this->featureContext->setResponse($this->updateSpace($user, $spaceName, $bodyData, $owner));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1419,26 +1357,8 @@ class SpacesContext implements Context {
|
||||
int $newQuota,
|
||||
string $owner = ''
|
||||
): void {
|
||||
if ($spaceName === "non-existing") {
|
||||
// check sending invalid data
|
||||
$spaceId = "39c49dd3-1f24-4687-97d1-42df43f71713";
|
||||
} else {
|
||||
$space = $this->getSpaceByName(($owner !== "") ? $owner : $user, $spaceName);
|
||||
$spaceId = $space["id"];
|
||||
}
|
||||
|
||||
$bodyData = ["quota" => ["total" => $newQuota]];
|
||||
$body = json_encode($bodyData, JSON_THROW_ON_ERROR);
|
||||
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::updateSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body,
|
||||
$spaceId
|
||||
)
|
||||
);
|
||||
$this->featureContext->setResponse($this->updateSpace($user, $spaceName, $bodyData, $owner));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1537,8 +1457,8 @@ class SpacesContext implements Context {
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $spaceName
|
||||
* @param string $spaceType
|
||||
* @param int $quota
|
||||
* @param string|null $spaceType
|
||||
* @param int|null $quota
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
@@ -1550,15 +1470,8 @@ class SpacesContext implements Context {
|
||||
int $quota
|
||||
): void {
|
||||
$space = ["Name" => $spaceName, "driveType" => $spaceType, "quota" => ["total" => $quota]];
|
||||
$body = json_encode($space);
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::createSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body
|
||||
)
|
||||
);
|
||||
$this->featureContext->setResponse($this->createSpace($user, $space));
|
||||
$this->setSpaceCreator($spaceName, $user);
|
||||
$this->featureContext->theHTTPStatusCodeShouldBe(
|
||||
201,
|
||||
"Expected response status code should be 201 (Created)"
|
||||
@@ -1581,15 +1494,7 @@ class SpacesContext implements Context {
|
||||
string $spaceName
|
||||
): void {
|
||||
$space = ["Name" => $spaceName];
|
||||
$body = json_encode($space, JSON_THROW_ON_ERROR);
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::createSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body
|
||||
)
|
||||
);
|
||||
$this->featureContext->setResponse($this->createSpace($user, $space));
|
||||
$this->setSpaceCreator($spaceName, $user);
|
||||
$this->featureContext->theHTTPStatusCodeShouldBe(
|
||||
201,
|
||||
@@ -1597,6 +1502,27 @@ class SpacesContext implements Context {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $space
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function createSpace(
|
||||
string $user,
|
||||
array $space
|
||||
): ResponseInterface {
|
||||
$body = json_encode($space, JSON_THROW_ON_ERROR);
|
||||
return GraphHelper::createSpace(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" copies (?:file|folder) "([^"]*)" to "([^"]*)" inside space "([^"]*)" using the WebDAV API$/
|
||||
*
|
||||
@@ -2769,42 +2695,15 @@ class SpacesContext implements Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^these etags should have changed$/
|
||||
* @Then /^these etags (should|should not) have changed$/
|
||||
*
|
||||
* @param string $action
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function theseEtagsShouldHaveChanged(TableNode $table): void {
|
||||
$this->featureContext->verifyTableNodeColumns($table, ["user", "path", "space"]);
|
||||
$this->featureContext->verifyTableNodeColumnsCount($table, 3);
|
||||
$unchangedEtagCount = 0;
|
||||
$unchangedEtagMessage = __METHOD__;
|
||||
foreach ($table->getColumnsHash() as $row) {
|
||||
$user = $row['user'];
|
||||
$path = $row['path'];
|
||||
$space = $row['space'];
|
||||
$etag = $this->userGetsEtagOfElementInASpace($user, $space, $path);
|
||||
$storedEtag = $this->getStoredEtagForPathInSpaceOfAUser($user, $space, $path);
|
||||
if ($etag === $storedEtag) {
|
||||
$unchangedEtagCount++;
|
||||
$unchangedEtagMessage .= "\nExpected etag of element '$path' for user '$user' in space '$space' to change, but it did not.";
|
||||
}
|
||||
}
|
||||
|
||||
Assert::assertEquals(0, $unchangedEtagCount, $unchangedEtagMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^these etags should not have changed$/
|
||||
*
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function theseEtagsShouldNotHaveChanged(TableNode $table): void {
|
||||
public function theseEtagsShouldShouldNotHaveChanged(string $action, TableNode $table): void {
|
||||
$this->featureContext->verifyTableNodeColumns($table, ["user", "path", "space"]);
|
||||
$this->featureContext->verifyTableNodeColumnsCount($table, 3);
|
||||
$changedEtagCount = 0;
|
||||
@@ -2813,11 +2712,15 @@ class SpacesContext implements Context {
|
||||
$user = $row['user'];
|
||||
$path = $row['path'];
|
||||
$space = $row['space'];
|
||||
$actualEtag = $this->userGetsEtagOfElementInASpace($user, $space, $path);
|
||||
$etag = $this->userGetsEtagOfElementInASpace($user, $space, $path);
|
||||
$storedEtag = $this->getStoredEtagForPathInSpaceOfAUser($user, $space, $path);
|
||||
if ($actualEtag !== $storedEtag) {
|
||||
if ($action === 'should' && $etag === $storedEtag) {
|
||||
$changedEtagCount++;
|
||||
$changedEtagMessage .= "\nExpected etag of element '$path' for user '$user' in space '$space' not to change, but it did.";
|
||||
$changedEtagMessage .= "\nExpected etag of element '$path' for user '$user' in space '$space' to change, but it did not.";
|
||||
}
|
||||
if ($action === 'should not' && $etag !== $storedEtag) {
|
||||
$changedEtagCount++;
|
||||
$changedEtagMessage .= "\nExpected etag of element '$path' for user '$user' in space '$space' to change, but it did not.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3712,38 +3712,6 @@ trait WebDav {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Old style chunking upload
|
||||
*
|
||||
* @Given user :user has uploaded the following :total chunks to :file with old chunking
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $total
|
||||
* @param string $file
|
||||
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
|
||||
* content with following headings, e.g.
|
||||
* | number | content |
|
||||
* | 1 | first data |
|
||||
* | 2 | followed by second data |
|
||||
* Chunks may be numbered out-of-order if desired.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function userHasUploadedTheFollowingTotalChunksUsingOldChunking(
|
||||
string $user,
|
||||
string $total,
|
||||
string $file,
|
||||
TableNode $chunkDetails
|
||||
):void {
|
||||
$this->verifyTableNodeColumns($chunkDetails, ['number', 'content']);
|
||||
foreach ($chunkDetails->getHash() as $chunkDetail) {
|
||||
$chunkNumber = (int) $chunkDetail['number'];
|
||||
$chunkContent = $chunkDetail['content'];
|
||||
$this->userHasUploadedChunkedFile($user, $chunkNumber, (int) $total, $chunkContent, $file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Old style chunking upload
|
||||
*
|
||||
@@ -3775,37 +3743,6 @@ trait WebDav {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Old style chunking upload
|
||||
*
|
||||
* @Given user :user has uploaded the following chunks to :file with old chunking
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $file
|
||||
* @param TableNode $chunkDetails table of 2 columns, chunk number and chunk
|
||||
* content with headings, e.g.
|
||||
* | number | content |
|
||||
* | 1 | first data |
|
||||
* | 2 | followed by second data |
|
||||
* Chunks may be numbered out-of-order if desired.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function userHasUploadedTheFollowingChunksUsingOldChunking(
|
||||
string $user,
|
||||
string $file,
|
||||
TableNode $chunkDetails
|
||||
):void {
|
||||
$total = \count($chunkDetails->getRows());
|
||||
$this->userHasUploadedTheFollowingTotalChunksUsingOldChunking(
|
||||
$user,
|
||||
(string) $total,
|
||||
$file,
|
||||
$chunkDetails
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Old style chunking upload
|
||||
*
|
||||
|
||||
@@ -673,12 +673,7 @@ class WebDavPropertiesContext implements Context {
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
$xmlPart = $resXml->xpath($xpath);
|
||||
Assert::assertTrue(
|
||||
isset($xmlPart[0]),
|
||||
"Cannot find item with xpath \"$xpath\""
|
||||
);
|
||||
$value = $xmlPart[0]->__toString();
|
||||
$value = $this->getXmlItemByXpath($resXml, $xpath);
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$expectedValue = $this->featureContext->substituteInLineCodes(
|
||||
$expectedValue,
|
||||
@@ -718,12 +713,7 @@ class WebDavPropertiesContext implements Context {
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
$xmlPart = $resXml->xpath($xpath);
|
||||
Assert::assertTrue(
|
||||
isset($xmlPart[0]),
|
||||
"Cannot find item with xpath \"$xpath\""
|
||||
);
|
||||
$value = $xmlPart[0]->__toString();
|
||||
$value = $this->getXmlItemByXpath($resXml, $xpath);
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$expectedValue1 = $this->featureContext->substituteInLineCodes(
|
||||
$expectedValue1,
|
||||
@@ -744,6 +734,25 @@ class WebDavPropertiesContext implements Context {
|
||||
Assert::assertTrue($isExpectedValueInMessage, "The actual value \"$value\" is not one of the expected values: \"$expectedValue1\" or \"$expectedValue2\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <type> $xmlResponse
|
||||
* @param string $xpath
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getXmlItemByXpath(
|
||||
SimpleXMLElement $xmlResponse,
|
||||
string $xpath
|
||||
): string {
|
||||
$xmlPart = $xmlResponse->xpath($xpath);
|
||||
Assert::assertTrue(
|
||||
isset($xmlPart[0]),
|
||||
"Cannot find item with xpath \"$xpath\""
|
||||
);
|
||||
return $xmlPart[0]->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the value of the item :xpath in the response should match :value
|
||||
*
|
||||
@@ -1284,28 +1293,6 @@ class WebDavPropertiesContext implements Context {
|
||||
Assert::assertEquals(0, $changedEtagCount, $changedEtagMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the etag of element :path of user :user should not have changed
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $user
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function etagOfElementOfUserShouldNotHaveChanged(string $path, string $user):void {
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$actualEtag = $this->getCurrentEtagOfElement($path, $user);
|
||||
$storedEtag = $this->getStoredEtagOfElement($path, $user, __METHOD__);
|
||||
Assert::assertEquals(
|
||||
$storedEtag,
|
||||
$actualEtag,
|
||||
__METHOD__
|
||||
. " The etag of element '$path' of user '$user' was not expected to change."
|
||||
. " The stored etag was '$storedEtag' but got '$actualEtag' from the response"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then these etags should have changed:
|
||||
*
|
||||
@@ -1335,65 +1322,36 @@ class WebDavPropertiesContext implements Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the etag of element :path of user :user should have changed
|
||||
* @Then /^the etag of element "([^"]*)" of user "([^"]*)" (should|should not) have changed$/
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $user
|
||||
* @param string $shouldShouldNot
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function etagOfElementOfUserShouldHaveChanged(string $path, string $user):void {
|
||||
public function etagOfElementOfUserShouldOrShouldNotHaveChanged(string $path, string $user, string $shouldShouldNot):void {
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$actualEtag = $this->getCurrentEtagOfElement($path, $user);
|
||||
$storedEtag = $this->getStoredEtagOfElement($path, $user, __METHOD__);
|
||||
Assert::assertNotEquals(
|
||||
$storedEtag,
|
||||
$actualEtag,
|
||||
__METHOD__
|
||||
. " The etag of element '$path' of user '$user' was expected to change."
|
||||
. " The stored etag was '$storedEtag' and also got '$actualEtag' from the response"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the etag of element :path of user :user on server :server should have changed
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $user
|
||||
* @param string $server
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function theEtagOfElementOfUserOnServerShouldHaveChanged(
|
||||
string $path,
|
||||
string $user,
|
||||
string $server
|
||||
):void {
|
||||
$previousServer = $this->featureContext->usingServer($server);
|
||||
$this->etagOfElementOfUserShouldHaveChanged($path, $user);
|
||||
$this->featureContext->usingServer($previousServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then the etag of element :path of user :user on server :server should not have changed
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $user
|
||||
* @param string $server
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function theEtagOfElementOfUserOnServerShouldNotHaveChanged(
|
||||
string $path,
|
||||
string $user,
|
||||
string $server
|
||||
):void {
|
||||
$previousServer = $this->featureContext->usingServer($server);
|
||||
$this->etagOfElementOfUserShouldNotHaveChanged($path, $user);
|
||||
$this->featureContext->usingServer($previousServer);
|
||||
if ($shouldShouldNot === 'should not') {
|
||||
Assert::assertEquals(
|
||||
$storedEtag,
|
||||
$actualEtag,
|
||||
__METHOD__
|
||||
. " The etag of element '$path' of user '$user' was not expected to change."
|
||||
. " The stored etag was '$storedEtag' but got '$actualEtag' from the response"
|
||||
);
|
||||
} else {
|
||||
Assert::assertNotEquals(
|
||||
$storedEtag,
|
||||
$actualEtag,
|
||||
__METHOD__
|
||||
. " The etag of element '$path' of user '$user' was expected to change."
|
||||
. " The stored etag was '$storedEtag' and also got '$actualEtag' from the response"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user