Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117479328
D2876.1774843970.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
23 KB
Referenced Files
None
Subscribers
None
D2876.1774843970.diff
View Options
diff --git a/src/app/Http/Controllers/API/AuthController.php b/src/app/Http/Controllers/API/AuthController.php
--- a/src/app/Http/Controllers/API/AuthController.php
+++ b/src/app/Http/Controllers/API/AuthController.php
@@ -82,7 +82,7 @@
$user = \App\User::where('email', $request->email)->first();
if (!$user) {
- return response()->json(['status' => 'error', 'message' => __('auth.failed')], 401);
+ return response()->json(['status' => 'error', 'message' => \trans('auth.failed')], 401);
}
return self::logonResponse($user, $request->password, $request->secondfactor);
@@ -107,7 +107,7 @@
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
return response()->json([
'status' => 'success',
- 'message' => __('auth.logoutsuccess')
+ 'message' => \trans('auth.logoutsuccess')
]);
}
@@ -161,7 +161,7 @@
return response()->json(['status' => 'error', 'errors' => $errors], 422);
}
- return response()->json(['status' => 'error', 'message' => __('auth.failed')], 401);
+ return response()->json(['status' => 'error', 'message' => \trans('auth.failed')], 401);
}
$response['access_token'] = $data->access_token;
diff --git a/src/app/Http/Controllers/API/PasswordResetController.php b/src/app/Http/Controllers/API/PasswordResetController.php
--- a/src/app/Http/Controllers/API/PasswordResetController.php
+++ b/src/app/Http/Controllers/API/PasswordResetController.php
@@ -41,12 +41,12 @@
$user = User::findByEmail($request->email);
if (!$user) {
- $errors = ['email' => __('validation.usernotexists')];
+ $errors = ['email' => \trans('validation.usernotexists')];
return response()->json(['status' => 'error', 'errors' => $errors], 422);
}
if (!$user->getSetting('external_email')) {
- $errors = ['email' => __('validation.noextemail')];
+ $errors = ['email' => \trans('validation.noextemail')];
return response()->json(['status' => 'error', 'errors' => $errors], 422);
}
diff --git a/src/app/Http/Controllers/API/SignupController.php b/src/app/Http/Controllers/API/SignupController.php
--- a/src/app/Http/Controllers/API/SignupController.php
+++ b/src/app/Http/Controllers/API/SignupController.php
@@ -48,7 +48,7 @@
$plans[] = [
'title' => $plan->title,
'name' => $plan->name,
- 'button' => __('app.planbutton', ['plan' => $plan->name]),
+ 'button' => \trans('app.planbutton', ['plan' => $plan->name]),
'description' => $plan->description,
];
});
diff --git a/src/app/Http/Controllers/API/V4/Admin/DomainsController.php b/src/app/Http/Controllers/API/V4/Admin/DomainsController.php
--- a/src/app/Http/Controllers/API/V4/Admin/DomainsController.php
+++ b/src/app/Http/Controllers/API/V4/Admin/DomainsController.php
@@ -74,7 +74,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.domain-suspend-success'),
+ 'message' => \trans('app.domain-suspend-success'),
]);
}
@@ -98,7 +98,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.domain-unsuspend-success'),
+ 'message' => \trans('app.domain-unsuspend-success'),
]);
}
}
diff --git a/src/app/Http/Controllers/API/V4/Admin/GroupsController.php b/src/app/Http/Controllers/API/V4/Admin/GroupsController.php
--- a/src/app/Http/Controllers/API/V4/Admin/GroupsController.php
+++ b/src/app/Http/Controllers/API/V4/Admin/GroupsController.php
@@ -88,7 +88,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.distlist-suspend-success'),
+ 'message' => \trans('app.distlist-suspend-success'),
]);
}
@@ -112,7 +112,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.distlist-unsuspend-success'),
+ 'message' => \trans('app.distlist-unsuspend-success'),
]);
}
}
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
@@ -173,7 +173,57 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-reset-2fa-success'),
+ 'message' => \trans('app.user-reset-2fa-success'),
+ ]);
+ }
+
+ /**
+ * Set/Add a SKU for the user
+ *
+ * @param \Illuminate\Http\Request $request The API request.
+ * @param string $id User identifier
+ * @param string $sku SKU title
+ *
+ * @return \Illuminate\Http\JsonResponse The response
+ */
+ public function setSku(Request $request, $id, $sku)
+ {
+ // For now we allow adding the 'beta' SKU only
+ if ($sku != 'beta') {
+ return $this->errorResponse(404);
+ }
+
+ $user = User::find($id);
+
+ if (!$this->checkTenant($user)) {
+ return $this->errorResponse(404);
+ }
+
+ if (!$this->guard()->user()->canUpdate($user)) {
+ return $this->errorResponse(403);
+ }
+
+ $sku = Sku::withObjectTenantContext($user)->where('title', $sku)->first();
+
+ if (!$sku) {
+ return $this->errorResponse(404);
+ }
+
+ if ($user->entitlements()->where('sku_id', $sku->id)->first()) {
+ return $this->errorResponse(422, \trans('app.user-set-sku-already-exists'));
+ }
+
+ $user->assignSku($sku);
+ $entitlement = $user->entitlements()->where('sku_id', $sku->id)->first();
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => \trans('app.user-set-sku-success'),
+ 'sku' => [
+ 'cost' => $entitlement->cost,
+ 'name' => $sku->name,
+ 'id' => $sku->id,
+ ]
]);
}
@@ -251,7 +301,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-suspend-success'),
+ 'message' => \trans('app.user-suspend-success'),
]);
}
@@ -279,7 +329,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-unsuspend-success'),
+ 'message' => \trans('app.user-unsuspend-success'),
]);
}
@@ -327,7 +377,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-update-success'),
+ 'message' => \trans('app.user-update-success'),
]);
}
}
diff --git a/src/app/Http/Controllers/API/V4/GroupsController.php b/src/app/Http/Controllers/API/V4/GroupsController.php
--- a/src/app/Http/Controllers/API/V4/GroupsController.php
+++ b/src/app/Http/Controllers/API/V4/GroupsController.php
@@ -46,7 +46,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.distlist-delete-success'),
+ 'message' => \trans('app.distlist-delete-success'),
]);
}
@@ -294,7 +294,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.distlist-create-success'),
+ 'message' => \trans('app.distlist-create-success'),
]);
}
@@ -352,7 +352,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.distlist-update-success'),
+ 'message' => \trans('app.distlist-update-success'),
]);
}
diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php
--- a/src/app/Http/Controllers/API/V4/UsersController.php
+++ b/src/app/Http/Controllers/API/V4/UsersController.php
@@ -62,7 +62,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-delete-success'),
+ 'message' => \trans('app.user-delete-success'),
]);
}
@@ -113,7 +113,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-setconfig-success'),
+ 'message' => \trans('app.user-setconfig-success'),
]);
}
@@ -359,7 +359,7 @@
return response()->json([
'status' => 'success',
- 'message' => __('app.user-create-success'),
+ 'message' => \trans('app.user-create-success'),
]);
}
@@ -420,7 +420,7 @@
$response = [
'status' => 'success',
- 'message' => __('app.user-update-success'),
+ 'message' => \trans('app.user-update-success'),
];
// For self-update refresh the statusInfo in the UI
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
@@ -54,6 +54,8 @@
'user-unsuspend-success' => 'User unsuspended successfully.',
'user-reset-2fa-success' => '2-Factor authentication reset successfully.',
'user-setconfig-success' => 'User settings updated successfully.',
+ 'user-set-sku-success' => 'The subscription added successfully.',
+ 'user-set-sku-already-exists' => 'The subscription already exists.',
'search-foundxdomains' => ':x domains have been found.',
'search-foundxgroups' => ':x distribution lists have been found.',
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
@@ -322,6 +322,7 @@
'user' => [
'2fa-hint1' => "This will remove 2-Factor Authentication entitlement as well as the user-configured factors.",
'2fa-hint2' => "Please, make sure to confirm the user identity properly.",
+ 'add-beta' => "Enable beta program",
'address' => "Address",
'aliases' => "Aliases",
'aliases-email' => "Email Aliases",
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
@@ -224,6 +224,9 @@
<button type="button" class="btn btn-danger" id="reset2fa" v-if="has2FA" @click="reset2FADialog">
{{ $t('user.reset-2fa') }}
</button>
+ <button type="button" class="btn btn-secondary" id="addbetasku" v-if="!hasBeta" @click="addBetaSku">
+ {{ $t('user.add-beta') }}
+ </button>
</div>
</div>
</div>
@@ -451,6 +454,7 @@
discounts: [],
external_email: '',
has2FA: false,
+ hasBeta: false,
wallet: {},
walletReload: false,
distlists: [],
@@ -529,6 +533,8 @@
if (sku.handler == 'auth2f') {
this.has2FA = true
this.sku2FA = sku.id
+ } else if (sku.handler == 'beta') {
+ this.hasBeta = true
}
}
})
@@ -559,6 +565,22 @@
$(this.$el).find('ul.nav-tabs a').on('click', this.$root.tab)
},
methods: {
+ addBetaSku() {
+ axios.post('/api/v4/users/' + this.user.id + '/skus/beta')
+ .then(response => {
+ if (response.data.status == 'success') {
+ this.$toast.success(response.data.message)
+ this.hasBeta = true
+ const sku = response.data.sku
+ this.skus.push({
+ id: sku.id,
+ name: sku.name,
+ cost: sku.cost,
+ price: this.$root.priceLabel(sku.cost, this.discount)
+ })
+ }
+ })
+ },
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
},
diff --git a/src/routes/api.php b/src/routes/api.php
--- a/src/routes/api.php
+++ b/src/routes/api.php
@@ -188,6 +188,7 @@
Route::get('users/{id}/discounts', 'API\V4\Reseller\DiscountsController@userDiscounts');
Route::post('users/{id}/reset2FA', 'API\V4\Admin\UsersController@reset2FA');
Route::get('users/{id}/skus', 'API\V4\Admin\SkusController@userSkus');
+ Route::post('users/{id}/skus/{sku}', 'API\V4\Admin\UsersController@setSku');
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);
@@ -232,6 +233,7 @@
Route::get('users/{id}/discounts', 'API\V4\Reseller\DiscountsController@userDiscounts');
Route::post('users/{id}/reset2FA', 'API\V4\Reseller\UsersController@reset2FA');
Route::get('users/{id}/skus', 'API\V4\Reseller\SkusController@userSkus');
+ Route::post('users/{id}/skus/{sku}', 'API\V4\Admin\UsersController@setSku');
Route::post('users/{id}/suspend', 'API\V4\Reseller\UsersController@suspend');
Route::post('users/{id}/unsuspend', 'API\V4\Reseller\UsersController@unsuspend');
Route::apiResource('wallets', API\V4\Reseller\WalletsController::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
@@ -368,7 +368,8 @@
->assertSeeIn('table tbody tr:nth-child(6) td:last-child', '45,09 CHF/month¹')
->assertMissing('table tfoot')
->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher')
- ->assertSeeIn('#reset2fa', 'Reset 2-Factor Auth');
+ ->assertSeeIn('#reset2fa', 'Reset 2-Factor Auth')
+ ->assertMissing('#addbetasku');
});
// We don't expect John's domains here
@@ -515,4 +516,29 @@
->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)');
});
}
+
+ /**
+ * Test adding the beta SKU for the user
+ */
+ public function testAddBetaSku(): void
+ {
+ $this->browse(function (Browser $browser) {
+ $this->deleteTestUser('userstest1@kolabnow.com');
+ $user = $this->getTestUser('userstest1@kolabnow.com');
+ $sku = Sku::withEnvTenantContext()->where('title', 'beta')->first();
+
+ $browser->visit(new UserPage($user->id))
+ ->click('@nav #tab-subscriptions')
+ ->waitFor('@user-subscriptions #addbetasku')
+ ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)')
+ ->assertSeeIn('#addbetasku', 'Enable beta program')
+ ->click('#addbetasku')
+ ->assertToast(Toast::TYPE_SUCCESS, 'The subscription added successfully.')
+ ->waitFor('#sku' . $sku->id)
+ ->assertSeeIn("#sku{$sku->id} td:first-child", 'Private Beta (invitation only)')
+ ->assertSeeIn("#sku{$sku->id} td:last-child", '0,00 CHF/month')
+ ->assertMissing('#addbetasku')
+ ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (1)');
+ });
+ }
}
diff --git a/src/tests/Browser/Reseller/UserTest.php b/src/tests/Browser/Reseller/UserTest.php
--- a/src/tests/Browser/Reseller/UserTest.php
+++ b/src/tests/Browser/Reseller/UserTest.php
@@ -480,4 +480,29 @@
->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)');
});
}
+
+ /**
+ * Test adding the beta SKU for the user
+ */
+ public function testAddBetaSku(): void
+ {
+ $this->browse(function (Browser $browser) {
+ $this->deleteTestUser('userstest1@kolabnow.com');
+ $user = $this->getTestUser('userstest1@kolabnow.com');
+ $sku = Sku::withEnvTenantContext()->where('title', 'beta')->first();
+
+ $browser->visit(new UserPage($user->id))
+ ->click('@nav #tab-subscriptions')
+ ->waitFor('@user-subscriptions #addbetasku')
+ ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)')
+ ->assertSeeIn('#addbetasku', 'Enable beta program')
+ ->click('#addbetasku')
+ ->assertToast(Toast::TYPE_SUCCESS, 'The subscription added successfully.')
+ ->waitFor('#sku' . $sku->id)
+ ->assertSeeIn("#sku{$sku->id} td:first-child", 'Private Beta (invitation only)')
+ ->assertSeeIn("#sku{$sku->id} td:last-child", '0,00 CHF/month')
+ ->assertMissing('#addbetasku')
+ ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (1)');
+ });
+ }
}
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
@@ -282,6 +282,8 @@
*/
public function testReset2FA(): void
{
+ Queue::fake();
+
$user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
$admin = $this->getTestUser('jeroen@jeroen.jeroen');
@@ -317,6 +319,58 @@
}
/**
+ * Test adding beta SKU (POST /api/v4/users/<user-id>/skus/beta)
+ */
+ public function testAddBetaSku(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('UsersControllerTest1@userscontroller.com');
+ $admin = $this->getTestUser('jeroen@jeroen.jeroen');
+ $sku = Sku::withEnvTenantContext()->where(['title' => 'beta'])->first();
+
+ // Test unauthorized access to admin API
+ $response = $this->actingAs($user)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(403);
+
+ // For now we allow only the beta sku
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/skus/mailbox", []);
+ $response->assertStatus(404);
+
+ $entitlements = $user->entitlements()->where('sku_id', $sku->id)->get();
+ $this->assertCount(0, $entitlements);
+
+ // Test adding the beta sku
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('success', $json['status']);
+ $this->assertSame("The subscription added successfully.", $json['message']);
+ $this->assertSame(0, $json['sku']['cost']);
+ $this->assertSame($sku->id, $json['sku']['id']);
+ $this->assertSame($sku->name, $json['sku']['name']);
+ $this->assertCount(3, $json);
+
+ $entitlements = $user->entitlements()->where('sku_id', $sku->id)->get();
+ $this->assertCount(1, $entitlements);
+
+ // Test adding the beta sku again, expect an error
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(422);
+
+ $json = $response->json();
+
+ $this->assertSame('error', $json['status']);
+ $this->assertSame("The subscription already exists.", $json['message']);
+ $this->assertCount(2, $json);
+
+ $entitlements = $user->entitlements()->where('sku_id', $sku->id)->get();
+ $this->assertCount(1, $entitlements);
+ }
+
+ /**
* Test user creation (POST /api/v4/users)
*/
public function testStore(): 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
@@ -300,6 +300,70 @@
}
/**
+ * Test adding beta SKU (POST /api/v4/users/<user-id>/skus/beta)
+ */
+ public function testAddBetaSku(): void
+ {
+ Queue::fake(); // disable jobs
+
+ $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');
+ $sku = Sku::withEnvTenantContext()->where(['title' => 'beta'])->first();
+
+ // Test unauthorized access to reseller API
+ $response = $this->actingAs($user)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(403);
+
+ $response = $this->actingAs($admin)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(403);
+
+ $response = $this->actingAs($reseller2)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(404);
+
+ // Touching admins is forbidden
+ $response = $this->actingAs($reseller1)->post("/api/v4/users/{$admin->id}/skus/beta", []);
+ $response->assertStatus(403);
+
+ // For now we allow only the beta sku
+ $response = $this->actingAs($reseller1)->post("/api/v4/users/{$user->id}/skus/mailbox", []);
+ $response->assertStatus(404);
+
+ $entitlements = $user->entitlements()->where('sku_id', $sku->id)->get();
+ $this->assertCount(0, $entitlements);
+
+ // Test adding the beta sku
+ $response = $this->actingAs($reseller1)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('success', $json['status']);
+ $this->assertSame("The subscription added successfully.", $json['message']);
+ $this->assertSame(0, $json['sku']['cost']);
+ $this->assertSame($sku->id, $json['sku']['id']);
+ $this->assertSame($sku->name, $json['sku']['name']);
+ $this->assertCount(3, $json);
+
+ $entitlements = $user->entitlements()->where('sku_id', $sku->id)->get();
+ $this->assertCount(1, $entitlements);
+
+ // Test adding the beta sku again, expect an error
+ $response = $this->actingAs($reseller1)->post("/api/v4/users/{$user->id}/skus/beta", []);
+ $response->assertStatus(422);
+
+ $json = $response->json();
+
+ $this->assertSame('error', $json['status']);
+ $this->assertSame("The subscription already exists.", $json['message']);
+ $this->assertCount(2, $json);
+
+ $entitlements = $user->entitlements()->where('sku_id', $sku->id)->get();
+ $this->assertCount(1, $entitlements);
+ }
+
+ /**
* Test user creation (POST /api/v4/users)
*/
public function testStore(): void
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 30, 4:12 AM (6 d, 16 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18791916
Default Alt Text
D2876.1774843970.diff (23 KB)
Attached To
Mode
D2876: Add "Enable beta program" button for admins/resellers
Attached
Detach File
Event Timeline