Page MenuHomePhorge

D4241.1774896020.diff
No OneTemporary

Authored By
Unknown
Size
11 KB
Referenced Files
None
Subscribers
None

D4241.1774896020.diff

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
@@ -207,6 +207,37 @@
}
/**
+ * Resync the user
+ *
+ * @param \Illuminate\Http\Request $request The API request.
+ * @param string $id User identifier
+ *
+ * @return \Illuminate\Http\JsonResponse The response
+ */
+ public function resync(Request $request, $id)
+ {
+ $user = User::find($id);
+
+ if (!$this->checkTenant($user)) {
+ return $this->errorResponse(404);
+ }
+
+ if (!$this->guard()->user()->canUpdate($user)) {
+ return $this->errorResponse(403);
+ }
+
+ if (\Artisan::call('user:resync', ['user' => $user->id])) {
+ return $this->errorResponse(500);
+ }
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => self::trans('app.user-resync-success'),
+ ]);
+ }
+
+
+ /**
* Set/Add a SKU for the user
*
* @param \Illuminate\Http\Request $request The API request.
diff --git a/src/app/Utils.php b/src/app/Utils.php
--- a/src/app/Utils.php
+++ b/src/app/Utils.php
@@ -606,12 +606,12 @@
*
* @return string String representation, e.g. "0 %", "7.7 %"
*/
- public static function percent(int|float $amount, $locale = 'de_DE'): string
+ public static function percent(int|float $percent, $locale = 'de_DE'): string
{
$nf = new \NumberFormatter($locale, \NumberFormatter::PERCENT);
$sep = $nf->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
- $result = sprintf('%.2f', $amount);
+ $result = sprintf('%.2f', $percent);
$result = preg_replace('/\.00/', '', $result);
$result = preg_replace('/(\.[0-9])0/', '\\1', $result);
$result = str_replace('.', $sep, $result);
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
@@ -111,6 +111,7 @@
'user-update-success' => 'User data updated successfully.',
'user-create-success' => 'User created successfully.',
'user-delete-success' => 'User deleted successfully.',
+ 'user-resync-success' => 'User synchronization have been started.',
'user-suspend-success' => 'User suspended successfully.',
'user-unsuspend-success' => 'User unsuspended successfully.',
'user-reset-2fa-success' => '2-Factor authentication reset successfully.',
diff --git a/src/resources/lang/en/ui.php b/src/resources/lang/en/ui.php
--- a/src/resources/lang/en/ui.php
+++ b/src/resources/lang/en/ui.php
@@ -29,6 +29,7 @@
'refresh' => "Refresh",
'reset' => "Reset",
'resend' => "Resend",
+ 'resync' => "Resync",
'save' => "Save",
'search' => "Search",
'share' => "Share",
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
@@ -77,11 +77,11 @@
</div>
</form>
<div class="mt-2 buttons">
- <btn v-if="!user.isSuspended" id="button-suspend" class="btn-warning" @click="suspendUser">
- {{ $t('btn.suspend') }}
+ <btn :id="'button-' + (user.isSuspended ? 'unsuspend' : 'suspend')" class="btn-outline-primary" @click="setSuspendState">
+ {{ $t(user.isSuspended ? 'btn.unsuspend' : 'btn.suspend') }}
</btn>
- <btn v-if="user.isSuspended" id="button-unsuspend" class="btn-warning" @click="unsuspendUser">
- {{ $t('btn.unsuspend') }}
+ <btn id="button-resync" class="btn-outline-primary" @click="resyncUser">
+ {{ $t('btn.resync') }}
</btn>
</div>
</div>
@@ -567,6 +567,14 @@
}
})
},
+ resyncUser() {
+ axios.post('/api/v4/users/' + this.user.id + '/resync')
+ .then(response => {
+ if (response.data.status == 'success') {
+ this.$toast.success(response.data.message)
+ }
+ })
+ },
submitDiscount() {
this.$refs.discountDialog.hide()
@@ -624,21 +632,12 @@
}
})
},
- 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')
+ setSuspendState() {
+ axios.post('/api/v4/users/' + this.user.id + '/' + (this.user.isSuspended ? 'unsuspend' : 'suspend'))
.then(response => {
if (response.data.status == 'success') {
this.$toast.success(response.data.message)
- this.user = Object.assign({}, this.user, { isSuspended: false })
+ this.user = Object.assign({}, this.user, { isSuspended: !this.user.isSuspended })
}
})
}
diff --git a/src/routes/api.php b/src/routes/api.php
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -229,6 +229,7 @@
Route::get('users/{id}/discounts', [API\V4\Reseller\DiscountsController::class, 'userDiscounts']);
Route::post('users/{id}/reset2FA', [API\V4\Admin\UsersController::class, 'reset2FA']);
Route::post('users/{id}/resetGeoLock', [API\V4\Admin\UsersController::class, 'resetGeoLock']);
+ Route::post('users/{id}/resync', [API\V4\Admin\UsersController::class, 'resync']);
Route::get('users/{id}/skus', [API\V4\Admin\UsersController::class, 'skus']);
Route::post('users/{id}/skus/{sku}', [API\V4\Admin\UsersController::class, 'setSku']);
Route::post('users/{id}/suspend', [API\V4\Admin\UsersController::class, 'suspend']);
@@ -280,6 +281,7 @@
Route::get('users/{id}/discounts', [API\V4\Reseller\DiscountsController::class, 'userDiscounts']);
Route::post('users/{id}/reset2FA', [API\V4\Reseller\UsersController::class, 'reset2FA']);
Route::post('users/{id}/resetGeoLock', [API\V4\Reseller\UsersController::class, 'resetGeoLock']);
+ Route::post('users/{id}/resync', [API\V4\Reseller\UsersController::class, 'resync']);
Route::get('users/{id}/skus', [API\V4\Reseller\UsersController::class, 'skus']);
Route::post('users/{id}/skus/{sku}', [API\V4\Admin\UsersController::class, 'setSku']);
Route::post('users/{id}/suspend', [API\V4\Reseller\UsersController::class, 'suspend']);
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
@@ -555,6 +555,21 @@
}
/**
+ * Test the Resync button
+ */
+ public function testResync(): void
+ {
+ $this->browse(function (Browser $browser) {
+ $john = $this->getTestUser('john@kolab.org');
+
+ $browser->visit(new UserPage($john->id))
+ ->assertSeeIn('@user-info #button-resync', 'Resync')
+ ->click('@user-info #button-resync')
+ ->assertToast(Toast::TYPE_SUCCESS, "User synchronization have been started.");
+ });
+ }
+
+ /**
* Test resetting 2FA for the user
*/
public function testReset2FA(): void
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
@@ -388,6 +388,33 @@
}
/**
+ * Test resync (POST /api/v4/users/<user-id>/resync)
+ */
+ public function testResync(): void
+ {
+ Queue::fake();
+
+ $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}/resync", []);
+ $response->assertStatus(403);
+
+ // Test resync
+ \Artisan::shouldReceive('call')->once()->with('user:resync', ['user' => $user->id]);
+
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/resync", []);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertCount(2, $json);
+ $this->assertSame('success', $json['status']);
+ $this->assertSame("User synchronization have been started.", $json['message']);
+ }
+
+ /**
* Test adding beta SKU (POST /api/v4/users/<user-id>/skus/beta)
*/
public function testAddBetaSku(): void
diff --git a/src/tests/Feature/Controller/Reseller/UsersTest.php b/src/tests/Feature/Controller/Reseller/UsersTest.php
--- a/src/tests/Feature/Controller/Reseller/UsersTest.php
+++ b/src/tests/Feature/Controller/Reseller/UsersTest.php
@@ -366,6 +366,46 @@
}
/**
+ * Test resync (POST /api/v4/users/<user-id>/resync)
+ */
+ public function testResync(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
+ $admin = $this->getTestUser('jeroen@jeroen.jeroen');
+ $reseller1 = $this->getTestUser('reseller@' . \config('app.domain'));
+ $reseller2 = $this->getTestUser('reseller@sample-tenant.dev-local');
+
+ // Test unauthorized access to admin API
+ // Test unauthorized access
+ $response = $this->actingAs($user)->post("/api/v4/users/{$user->id}/resync", []);
+ $response->assertStatus(403);
+
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/resync", []);
+ $response->assertStatus(403);
+
+ $response = $this->actingAs($reseller2)->post("/api/v4/users/{$user->id}/resync", []);
+ $response->assertStatus(404);
+
+ // Touching admins is forbidden
+ $response = $this->actingAs($reseller1)->post("/api/v4/users/{$admin->id}/resync", []);
+ $response->assertStatus(403);
+
+ // Test resync
+ \Artisan::shouldReceive('call')->once()->with('user:resync', ['user' => $user->id]);
+
+ $response = $this->actingAs($reseller1)->post("/api/v4/users/{$user->id}/resync", []);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertCount(2, $json);
+ $this->assertSame('success', $json['status']);
+ $this->assertSame("User synchronization have been started.", $json['message']);
+ }
+
+ /**
* Test adding beta SKU (POST /api/v4/users/<user-id>/skus/beta)
*/
public function testAddBetaSku(): void

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 30, 6:40 PM (5 d, 2 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18818931
Default Alt Text
D4241.1774896020.diff (11 KB)

Event Timeline