diff --git a/src/app/Http/Controllers/API/V4/Admin/UsersController.php b/src/app/Http/Controllers/API/V4/Admin/UsersController.php
--- a/src/app/Http/Controllers/API/V4/Admin/UsersController.php
+++ b/src/app/Http/Controllers/API/V4/Admin/UsersController.php
@@ -69,6 +69,54 @@
}
/**
+ * Suspend the user
+ *
+ * @param \Illuminate\Http\Request $request The API request.
+ * @params string $id User identifier
+ *
+ * @return \Illuminate\Http\JsonResponse The response
+ */
+ public function suspend(Request $request, $id)
+ {
+ $user = User::find($id);
+
+ if (empty($user)) {
+ return $this->errorResponse(404);
+ }
+
+ $user->suspend();
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => __('app.user-suspend-success'),
+ ]);
+ }
+
+ /**
+ * Un-Suspend the user
+ *
+ * @param \Illuminate\Http\Request $request The API request.
+ * @params string $id User identifier
+ *
+ * @return \Illuminate\Http\JsonResponse The response
+ */
+ public function unsuspend(Request $request, $id)
+ {
+ $user = User::find($id);
+
+ if (empty($user)) {
+ return $this->errorResponse(404);
+ }
+
+ $user->unsuspend();
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => __('app.user-unsuspend-success'),
+ ]);
+ }
+
+ /**
* Update user data.
*
* @param \Illuminate\Http\Request $request The API request.
diff --git a/src/resources/lang/en/app.php b/src/resources/lang/en/app.php
--- a/src/resources/lang/en/app.php
+++ b/src/resources/lang/en/app.php
@@ -31,9 +31,12 @@
'domain-verify-success' => 'Domain verified successfully.',
'domain-verify-error' => 'Domain ownership verification failed.',
+
'user-update-success' => 'User data updated successfully.',
'user-create-success' => 'User created successfully.',
'user-delete-success' => 'User deleted successfully.',
+ 'user-suspend-success' => 'User suspended successfully.',
+ 'user-unsuspend-success' => 'User unsuspended successfully.',
'search-foundxdomains' => ':x domains have been found.',
'search-foundxusers' => ':x user accounts have been found.',
diff --git a/src/resources/sass/_variables.scss b/src/resources/sass/_variables.scss
--- a/src/resources/sass/_variables.scss
+++ b/src/resources/sass/_variables.scss
@@ -12,7 +12,7 @@
$purple: #9561e2;
$pink: #f66d9b;
$red: #e3342f;
-$orange: #f6993f;
+$orange: #f1a539;
$yellow: #ffed4a;
$green: #38c172;
$teal: #4dc0b5;
@@ -22,4 +22,5 @@
// App colors
$menu-bg-color: $light;
-$main-color: #f1a539;
+$main-color: $orange;
+$warning: $orange;
diff --git a/src/resources/vue/Admin/User.vue b/src/resources/vue/Admin/User.vue
--- a/src/resources/vue/Admin/User.vue
+++ b/src/resources/vue/Admin/User.vue
@@ -74,6 +74,8 @@
{{ user.country }}
+
+
@@ -470,6 +472,24 @@
this.external_email = null // required because of Vue
}
})
+ },
+ suspendUser() {
+ axios.post('/api/v4/users/' + this.user.id + '/suspend', {})
+ .then(response => {
+ if (response.data.status == 'success') {
+ this.$toast.success(response.data.message)
+ this.user = Object.assign({}, this.user, { isSuspended: true })
+ }
+ })
+ },
+ unsuspendUser() {
+ axios.post('/api/v4/users/' + this.user.id + '/unsuspend', {})
+ .then(response => {
+ if (response.data.status == 'success') {
+ this.$toast.success(response.data.message)
+ this.user = Object.assign({}, this.user, { isSuspended: false })
+ }
+ })
}
}
}
diff --git a/src/routes/api.php b/src/routes/api.php
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -100,6 +100,8 @@
Route::apiResource('packages', API\V4\Admin\PackagesController::class);
Route::apiResource('skus', API\V4\Admin\SkusController::class);
Route::apiResource('users', API\V4\Admin\UsersController::class);
+ Route::post('users/{id}/suspend', 'API\V4\Admin\UsersController@suspend');
+ Route::post('users/{id}/unsuspend', 'API\V4\Admin\UsersController@unsuspend');
Route::apiResource('wallets', API\V4\Admin\WalletsController::class);
Route::apiResource('discounts', API\V4\Admin\DiscountsController::class);
}
diff --git a/src/tests/Browser/Admin/UserTest.php b/src/tests/Browser/Admin/UserTest.php
--- a/src/tests/Browser/Admin/UserTest.php
+++ b/src/tests/Browser/Admin/UserTest.php
@@ -3,6 +3,7 @@
namespace Tests\Browser\Admin;
use App\Discount;
+use App\User;
use Tests\Browser;
use Tests\Browser\Components\Dialog;
use Tests\Browser\Components\Toast;
@@ -23,6 +24,7 @@
self::useAdminUrl();
$john = $this->getTestUser('john@kolab.org');
+ $john->update(['status' => $john->status ^= User::STATUS_SUSPENDED]);
$john->setSettings([
'phone' => '+48123123123',
'external_email' => 'john.doe.external@gmail.com',
@@ -40,6 +42,7 @@
public function tearDown(): void
{
$john = $this->getTestUser('john@kolab.org');
+ $john->update(['status' => $john->status ^= User::STATUS_SUSPENDED]);
$john->setSettings([
'phone' => null,
'external_email' => 'john.doe.external@gmail.com',
@@ -407,6 +410,29 @@
}
/**
+ * Test suspending/unsuspending the user
+ */
+ public function testSuspendAndUnsuspend(): void
+ {
+ $this->browse(function (Browser $browser) {
+ $john = $this->getTestUser('john@kolab.org');
+
+ $browser->visit(new UserPage($john->id))
+ ->assertVisible('@user-info #button-suspend')
+ ->assertMissing('@user-info #button-unsuspend')
+ ->click('@user-info #button-suspend')
+ ->assertToast(Toast::TYPE_SUCCESS, 'User suspended successfully.')
+ ->assertSeeIn('@user-info #status span.text-warning', 'Suspended')
+ ->assertMissing('@user-info #button-suspend')
+ ->click('@user-info #button-unsuspend')
+ ->assertToast(Toast::TYPE_SUCCESS, 'User unsuspended successfully.')
+ ->assertSeeIn('@user-info #status span.text-success', 'Active')
+ ->assertVisible('@user-info #button-suspend')
+ ->assertMissing('@user-info #button-unsuspend');
+ });
+ }
+
+ /**
* Test editing wallet discount
*
* @depends testUserInfo2
diff --git a/src/tests/Feature/Controller/Admin/UsersTest.php b/src/tests/Feature/Controller/Admin/UsersTest.php
--- a/src/tests/Feature/Controller/Admin/UsersTest.php
+++ b/src/tests/Feature/Controller/Admin/UsersTest.php
@@ -2,6 +2,7 @@
namespace Tests\Feature\Controller\Admin;
+use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class UsersTest extends TestCase
@@ -14,6 +15,8 @@
parent::setUp();
self::useAdminUrl();
+ $this->deleteTestUser('UsersControllerTest1@userscontroller.com');
+
$jack = $this->getTestUser('jack@kolab.org');
$jack->setSetting('external_email', null);
}
@@ -23,6 +26,8 @@
*/
public function tearDown(): void
{
+ $this->deleteTestUser('UsersControllerTest1@userscontroller.com');
+
$jack = $this->getTestUser('jack@kolab.org');
$jack->setSetting('external_email', null);
@@ -142,6 +147,66 @@
}
/**
+ * Test user suspending (POST /api/v4/users//suspend)
+ */
+ public function testSuspend(): void
+ {
+ Queue::fake(); // disable jobs
+
+ $user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
+ $admin = $this->getTestUser('jeroen@jeroen.jeroen');
+
+ // Test unauthorized access to admin API
+ $response = $this->actingAs($user)->post("/api/v4/users/{$user->id}/suspend", []);
+ $response->assertStatus(403);
+
+ $this->assertFalse($user->isSuspended());
+
+ // Test suspending the user
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/suspend", []);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('success', $json['status']);
+ $this->assertSame("User suspended successfully.", $json['message']);
+ $this->assertCount(2, $json);
+
+ $this->assertTrue($user->fresh()->isSuspended());
+ }
+
+ /**
+ * Test user un-suspending (POST /api/v4/users//unsuspend)
+ */
+ public function testUnsuspend(): void
+ {
+ Queue::fake(); // disable jobs
+
+ $user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
+ $admin = $this->getTestUser('jeroen@jeroen.jeroen');
+
+ // Test unauthorized access to admin API
+ $response = $this->actingAs($user)->post("/api/v4/users/{$user->id}/unsuspend", []);
+ $response->assertStatus(403);
+
+ $this->assertFalse($user->isSuspended());
+ $user->suspend();
+ $this->assertTrue($user->isSuspended());
+
+ // Test suspending the user
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/unsuspend", []);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('success', $json['status']);
+ $this->assertSame("User unsuspended successfully.", $json['message']);
+ $this->assertCount(2, $json);
+
+ $this->assertFalse($user->fresh()->isSuspended());
+ }
+
+ /**
* Test user update (PUT /api/v4/users/)
*/
public function testUpdate(): void
@@ -150,7 +215,7 @@
$admin = $this->getTestUser('jeroen@jeroen.jeroen');
// Test unauthorized access to admin API
- $response = $this->actingAs($user)->get("/api/v4/users/{$user->id}", []);
+ $response = $this->actingAs($user)->put("/api/v4/users/{$user->id}", []);
$response->assertStatus(403);
// Test updatig the user data (empty data)