Page MenuHomePhorge

D5122.1775436865.diff
No OneTemporary

Authored By
Unknown
Size
29 KB
Referenced Files
None
Subscribers
None

D5122.1775436865.diff

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
@@ -17,6 +17,7 @@
use App\Rules\UserEmailLocal;
use App\SignupCode;
use App\SignupInvitation;
+use App\Tenant;
use App\User;
use App\Utils;
use App\VatRate;
@@ -124,15 +125,13 @@
'status' => 'success',
'code' => $code->code,
'mode' => $plan->mode ?: 'email',
+ 'domains' => Domain::getPublicDomains(),
];
if ($plan->mode == Plan::MODE_TOKEN) {
// Token verification, jump to the last step
- $has_domain = $plan->hasDomain();
-
$response['short_code'] = $code->short_code;
- $response['is_domain'] = $has_domain;
- $response['domains'] = $has_domain ? [] : Domain::getPublicDomains();
+ $response['is_domain'] = $plan->hasDomain();
} else {
// External email verification, send an email message
SignupVerificationJob::dispatch($code);
@@ -156,18 +155,15 @@
return $this->errorResponse(404);
}
- $has_domain = $this->getPlan()->hasDomain();
-
$result = [
'id' => $id,
- 'is_domain' => $has_domain,
- 'domains' => $has_domain ? [] : Domain::getPublicDomains(),
+ 'is_domain' => $this->getPlan()->hasDomain(),
+ 'domains' => Domain::getPublicDomains(),
];
return response()->json($result);
}
-
/**
* Validation of the verification code.
*
@@ -212,8 +208,6 @@
$code->save();
}
- $has_domain = $this->getPlan()->hasDomain();
-
// Return user name and email/phone/voucher from the codes database,
// domains list for selection and "plan type" flag
return response()->json([
@@ -222,8 +216,8 @@
'first_name' => $code->first_name,
'last_name' => $code->last_name,
'voucher' => $code->voucher,
- 'is_domain' => $has_domain,
- 'domains' => $has_domain ? [] : Domain::getPublicDomains(),
+ 'is_domain' => $this->getPlan()->hasDomain(),
+ 'domains' => Domain::getPublicDomains(),
]);
}
@@ -391,7 +385,7 @@
DB::beginTransaction();
// Create domain record
- if ($is_domain) {
+ if ($is_domain && !Domain::withTrashed()->where('namespace', $domain_name)->exists()) {
$domain = Domain::create([
'namespace' => $domain_name,
'type' => Domain::TYPE_EXTERNAL,
@@ -508,7 +502,7 @@
$mandate = [
'currency' => $wallet->currency,
- 'description' => \App\Tenant::getConfig($user->tenant_id, 'app.name')
+ 'description' => Tenant::getConfig($user->tenant_id, 'app.name')
. ' ' . self::trans('app.mandate-description-suffix'),
'methodId' => PaymentProvider::METHOD_CREDITCARD,
@@ -592,8 +586,12 @@
// ...otherwise use the default plan
if (empty($plan)) {
- // TODO: Get default plan title from config
- $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $default = Tenant::getConfig(\config('app.tenant_id'), 'app.default_plan');
+ $plan = Plan::withEnvTenantContext()->where('title', $default)->first();
+
+ if (empty($plan)) {
+ throw new \Exception("No default plan defined for signup");
+ }
}
$request->plan = $plan;
@@ -605,13 +603,13 @@
/**
* Login (kolab identity) validation
*
- * @param string $login Login (local part of an email address)
- * @param string $domain Domain name
- * @param bool $external Enables additional checks for domain part
+ * @param string $login Login (local part of an email address)
+ * @param string $namespace Domain name
+ * @param bool $external Enable signup for a custom domain
*
* @return array Error messages on validation error
*/
- protected static function validateLogin($login, $domain, $external = false): ?array
+ protected static function validateLogin($login, $namespace, $external = false): ?array
{
// Validate login part alone
$v = Validator::make(
@@ -623,29 +621,35 @@
return ['login' => $v->errors()->toArray()['login'][0]];
}
- $domains = $external ? null : Domain::getPublicDomains();
-
- // Validate the domain
- $v = Validator::make(
- ['domain' => $domain],
- ['domain' => ['required', 'string', new UserEmailDomain($domains)]]
- );
-
- if ($v->fails()) {
- return ['domain' => $v->errors()->toArray()['domain'][0]];
+ $domain = null;
+ if (is_string($namespace)) {
+ $namespace = Str::lower($namespace);
+ $domain = Domain::withTrashed()->where('namespace', $namespace)->first();
}
- $domain = Str::lower($domain);
+ if ($domain && $domain->isPublic() && !$domain->trashed()) {
+ // no error, everyone can signup for an existing public domain
+ } elseif ($domain) {
+ // domain exists and is not public (or is deleted)
+ return ['domain' => self::trans('validation.domainnotavailable')];
+ } else {
+ // non-existing custom domain
+ if (!$external) {
+ return ['domain' => self::trans('validation.domaininvalid')];
+ }
+
+ $v = Validator::make(
+ ['domain' => $namespace],
+ ['domain' => ['required', 'string', new UserEmailDomain()]]
+ );
- // Check if domain is already registered with us
- if ($external) {
- if (Domain::withTrashed()->where('namespace', $domain)->exists()) {
- return ['domain' => self::trans('validation.domainexists')];
+ if ($v->fails()) {
+ return ['domain' => $v->errors()->toArray()['domain'][0]];
}
}
// Check if user with specified login already exists
- $email = $login . '@' . $domain;
+ $email = $login . '@' . $namespace;
if (User::emailExists($email) || User::aliasExists($email) || \App\Group::emailExists($email)) {
return ['login' => self::trans('validation.loginexists')];
}
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
@@ -7,7 +7,6 @@
use App\License;
use App\Plan;
use App\Rules\Password;
-use App\Rules\UserEmailDomain;
use App\Rules\UserEmailLocal;
use App\Sku;
use App\User;
diff --git a/src/app/Observers/EntitlementObserver.php b/src/app/Observers/EntitlementObserver.php
--- a/src/app/Observers/EntitlementObserver.php
+++ b/src/app/Observers/EntitlementObserver.php
@@ -33,13 +33,11 @@
return false;
}
- $sku = \App\Sku::find($entitlement->sku_id);
-
- if (!$sku) {
+ if (empty($entitlement->sku)) {
return false;
}
- $result = $sku->handler_class::preReq($entitlement, $wallet->owner);
+ $result = $entitlement->sku->handler_class::preReq($entitlement, $wallet->owner);
if (!$result) {
return false;
diff --git a/src/app/Plan.php b/src/app/Plan.php
--- a/src/app/Plan.php
+++ b/src/app/Plan.php
@@ -23,7 +23,6 @@
* @property string $id
* @property string $mode Plan signup mode (Plan::MODE_*)
* @property string $name
- * @property \App\Package[] $packages
* @property datetime $promo_from
* @property datetime $promo_to
* @property ?int $tenant_id
diff --git a/src/app/User.php b/src/app/User.php
--- a/src/app/User.php
+++ b/src/app/User.php
@@ -150,22 +150,32 @@
* @param \App\Domain $domain Optional domain object
*
* @return \App\User Self
+ * @throws \Exception
*/
public function assignPlan($plan, $domain = null): User
{
- $this->setSetting('plan_id', $plan->id);
+ $domain_packages = $plan->packages->filter(function ($package) {
+ return $package->isDomain();
+ });
- foreach ($plan->packages as $package) {
- if ($package->isDomain()) {
- if (!$domain) {
- throw new \Exception("Attempted to assign a domain package without passing a domain.");
- }
+ // Before we do anything let's make sure that a custom domain can be assigned only
+ // to a plan with a domain package
+ if ($domain && $domain_packages->isEmpty()) {
+ throw new \Exception("Custom domain requires a plan with a domain SKU");
+ }
+
+ foreach ($plan->packages->diff($domain_packages) as $package) {
+ $this->assignPackage($package);
+ }
+
+ if ($domain) {
+ foreach ($domain_packages as $package) {
$domain->assignPackage($package, $this);
- } else {
- $this->assignPackage($package);
}
}
+ $this->setSetting('plan_id', $plan->id);
+
return $this;
}
diff --git a/src/config/app.php b/src/config/app.php
--- a/src/config/app.php
+++ b/src/config/app.php
@@ -226,6 +226,8 @@
'copyright' => 'Apheleia IT AG',
],
+ 'default_plan' => env('DEFAULT_PLAN') ?: 'group',
+
'storage' => [
'min_qty' => (int) env('STORAGE_MIN_QTY', 5), // in GB
],
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
@@ -436,6 +436,7 @@
'step2' => "We sent out a confirmation code to your email address. Enter the code we sent you, or click the link in the message.",
'step3' => "Create your {app} identity (you can choose additional addresses later).",
'created' => "The account is about to be created!",
+ 'owndomain' => "Use your own domain",
'token' => "Signup authorization token",
'voucher' => "Voucher Code",
],
diff --git a/src/resources/vue/Signup.vue b/src/resources/vue/Signup.vue
--- a/src/resources/vue/Signup.vue
+++ b/src/resources/vue/Signup.vue
@@ -39,7 +39,7 @@
<label for="signup_email" class="visually-hidden">{{ $t('signup.email') }}</label>
<input type="text" class="form-control" id="signup_email" :placeholder="$t('signup.email')" required v-model="email">
</div>
- <btn class="btn-secondary" @click="stepBack">{{ $t('btn.back') }}</btn>
+ <btn class="btn-secondary" @click="stepBack" v-if="plans.length > 1">{{ $t('btn.back') }}</btn>
<btn class="btn-primary ms-2" type="submit" icon="check">{{ $t('btn.continue') }}</btn>
</form>
</div>
@@ -78,10 +78,14 @@
</div>
<div class="mb-3">
<label for="signup_login" class="visually-hidden"></label>
+ <div v-if="is_domain" class="form-check float-end text-secondary">
+ <input class="form-check-input" type="checkbox" value="1" id="custom_domain" @change="useCustomDomain">
+ <label class="form-check-label" for="custom_domain">{{ $t('signup.owndomain') }}</label>
+ </div>
<div class="input-group">
<input type="text" class="form-control" id="signup_login" required v-model="login" :placeholder="$t('signup.login')">
<span class="input-group-text">@</span>
- <input v-if="is_domain" type="text" class="form-control rounded-end" id="signup_domain" required v-model="domain" :placeholder="$t('form.domain')">
+ <input v-if="use_custom" type="text" class="form-control rounded-end" id="signup_domain" required :placeholder="$t('form.domain')" v-model="domain">
<select v-else class="form-select rounded-end" id="signup_domain" required v-model="domain">
<option v-for="_domain in domains" :key="_domain" :value="_domain">{{ _domain }}</option>
</select>
@@ -176,7 +180,9 @@
},
plans: [],
referral: '',
+ selectedDomain: '',
token: '',
+ use_custom: false,
voucher: ''
}
},
@@ -247,29 +253,20 @@
this.plan = title
this.mode = plan.mode
this.is_domain = plan.isDomain
- this.domain = ''
-
- let step = 1
-
- if (plan.mode == 'mandate') {
- step = 3
- if (!plan.isDomain || !this.domains.length) {
- axios.get('/api/auth/signup/domains')
- .then(response => {
- this.displayForm(step, true)
- this.setDomain(response.data)
- })
- return
- }
- }
- this.displayForm(step, true)
+ this.displayForm(plan.mode == 'mandate' ? 3 : 1, true)
}
},
step0(plan) {
if (!this.plans.length) {
axios.get('/api/auth/signup/plans', { loader: true }).then(response => {
this.plans = response.data.plans
+ if (this.plans.length == 1) {
+ if (!plan || plan != this.plans[0].title) {
+ plan = this.plans[0].title
+ }
+ }
+
this.selectPlanByTitle(plan)
})
.catch(error => {
@@ -291,12 +288,8 @@
this.short_code = response.data.short_code
this.mode = response.data.mode
this.is_domain = response.data.is_domain
+ this.setDomain(response.data)
this.displayForm(this.mode == 'token' ? 3 : 2, true)
-
- // Fill the domain selector with available domains
- if (!this.is_domain) {
- this.setDomain(response.data)
- }
})
},
// Submits the code to the API for verification
@@ -318,12 +311,7 @@
this.email = response.data.email
this.is_domain = response.data.is_domain
this.voucher = response.data.voucher
- this.domain = ''
-
- // Fill the domain selector with available domains
- if (!this.is_domain) {
- this.setDomain(response.data)
- }
+ this.setDomain(response.data)
})
.catch(error => {
if (bylink === true) {
@@ -394,10 +382,19 @@
return this.step0()
}
+ if (step > 2 && !this.domains.length) {
+ axios.get('/api/auth/signup/domains')
+ .then(response => {
+ this.setDomain(response.data)
+ this.displayForm(step, true)
+ })
+ return
+ }
+
$('#step' + step).removeClass('d-none').find('form')[0].reset()
if (focus) {
- $('#step' + step).find('input').first().focus()
+ $('#step' + step).find('input:not([type=checkbox])').first().focus()
}
},
lastStepPostData() {
@@ -430,6 +427,12 @@
this.domain = this.domains[0]
}
}
+
+ this.selectedDomain = this.domain
+ },
+ useCustomDomain(event) {
+ this.use_custom = event.target.checked
+ this.domain = this.use_custom ? '' : this.selectedDomain
}
}
}
diff --git a/src/tests/Browser/SignupTest.php b/src/tests/Browser/SignupTest.php
--- a/src/tests/Browser/SignupTest.php
+++ b/src/tests/Browser/SignupTest.php
@@ -34,6 +34,7 @@
$this->deleteTestDomain('user-domain-signup.com');
Plan::whereNot('mode', Plan::MODE_EMAIL)->update(['mode' => Plan::MODE_EMAIL]);
+ Plan::withEnvTenantContext()->where('title', 'individual')->update(['hidden' => false]);
SignupToken::truncate();
ReferralProgram::query()->delete();
}
@@ -49,6 +50,7 @@
SignupInvitation::truncate();
Plan::whereNot('mode', Plan::MODE_EMAIL)->update(['mode' => Plan::MODE_EMAIL]);
+ Plan::withEnvTenantContext()->where('title', 'individual')->update(['hidden' => false]);
SignupToken::truncate();
ReferralProgram::query()->delete();
@@ -323,6 +325,8 @@
->assertVisible('#signup_password')
->assertVisible('#signup_password_confirmation')
->assertVisible('select#signup_domain')
+ ->assertMissing('#custom_domain')
+ ->assertMissing('input#signup_domain')
->assertElementsCount('select#signup_domain option', $domains_count, false)
->assertText('select#signup_domain option:nth-child(1)', $domains[0])
->assertValue('select#signup_domain option:nth-child(1)', $domains[0])
@@ -456,15 +460,28 @@
->click('[type=submit]');
});
- // Here we expect 4 text inputs, Back and Continue buttons
+ // Here we expect text inputs, as well as domain selector, and buttons
$browser->whenAvailable('@step3', function ($step) {
+ $domains = Domain::getPublicDomains();
+ $domains_count = count($domains);
+
$step->assertVisible('#signup_login')
->assertVisible('#signup_password')
->assertVisible('#signup_password_confirmation')
- ->assertVisible('input#signup_domain')
+ ->assertVisible('select#signup_domain')
+ ->assertMissing('input#signup_domain')
+ ->assertVisible('#custom_domain')
+ ->assertElementsCount('select#signup_domain option', $domains_count, false)
+ ->assertText('select#signup_domain option:nth-child(1)', $domains[0])
+ ->assertValue('select#signup_domain option:nth-child(1)', $domains[0])
+ ->assertText('select#signup_domain option:nth-child(2)', $domains[1])
+ ->assertValue('select#signup_domain option:nth-child(2)', $domains[1])
->assertVisible('[type=button]')
->assertVisible('[type=submit]')
->assertFocused('#signup_login')
+ ->click('#custom_domain + label')
+ ->assertVisible('input#signup_domain')
+ ->assertMissing('select#signup_domain')
->assertValue('input#signup_domain', '')
->assertValue('#signup_login', '')
->assertValue('#signup_password', '')
@@ -473,8 +490,7 @@
// Submit invalid login and password data
$browser->with('@step3', function ($step) {
- $step->assertFocused('#signup_login')
- ->type('#signup_login', '*')
+ $step->type('#signup_login', '*')
->type('#signup_domain', 'test.com')
->type('#signup_password', '12345678')
->type('#signup_password_confirmation', '123456789')
@@ -524,6 +540,68 @@
});
}
+ /**
+ * Test more signup cases
+ */
+ public function testSignupGroupWithPublicDomain(): void
+ {
+ // We also test skipping plan selection if there's only one plan active
+ $plan = Plan::withEnvTenantContext()->where('title', 'individual')->first();
+ $plan->hidden = true;
+ $plan->save();
+
+ $this->browse(function (Browser $browser) {
+ $browser->visit(new Signup());
+
+ // Submit valid data
+ $browser->whenAvailable('@step1', function ($step) {
+ $step->type('#signup_first_name', 'Test')
+ ->type('#signup_last_name', 'User')
+ ->type('#signup_email', 'BrowserSignupTestUser1@kolab.org')
+ ->click('[type=submit]');
+ });
+
+ // Submit valid code
+ $browser->whenAvailable('@step2', function ($step) {
+ // Get the code and short_code from database
+ $code = $step->value('#signup_code');
+ $code = SignupCode::find($code);
+
+ $step->type('#signup_short_code', $code->short_code)
+ ->click('[type=submit]');
+ });
+
+ // Here we expect text inputs, as well as domain selector, and buttons
+ $browser->whenAvailable('@step3', function ($step) {
+ $step->assertVisible('#signup_login')
+ ->assertVisible('#signup_password')
+ ->assertVisible('#signup_password_confirmation')
+ ->assertVisible('select#signup_domain')
+ ->assertMissing('input#signup_domain')
+ ->assertVisible('#custom_domain')
+ ->assertValue('select#signup_domain option:checked', \config('app.domain'))
+ ->type('#signup_login', 'signuptestdusk')
+ ->type('#signup_password', '12345678')
+ ->type('#signup_password_confirmation', '12345678')
+ ->click('[type=submit]');
+ });
+
+ // At this point we should be auto-logged-in to dashboard
+ $browser->waitUntilMissing('@step3')
+ ->waitUntilMissing('.app-loader')
+ ->on(new Dashboard())
+ ->assertUser('signuptestdusk@' . \config('app.domain'))
+ ->assertVisible('@links a.link-settings')
+ //->assertVisible('@links a.link-domains')
+ ->assertVisible('@links a.link-users')
+ ->assertVisible('@links a.link-wallet');
+
+ $browser->within(new Menu(), function ($browser) {
+ $browser->clickMenuItem('logout');
+ });
+ });
+ }
+
/**
* Test signup with a mandate plan, also the UI lock
*
@@ -558,6 +636,7 @@
$domains_count = count($domains);
$browser->assertMissing('.card-title')
+ ->waitFor('select#signup_domain option:checked')
->assertElementsCount('select#signup_domain option', $domains_count, false)
->assertText('select#signup_domain option:nth-child(1)', $domains[0])
->assertValue('select#signup_domain option:nth-child(1)', $domains[0])
@@ -585,7 +664,6 @@
})
->on(new PaymentMollie())
->assertSeeIn('@title', 'Auto-Payment Setup')
- ->assertMissing('@amount')
->submitPayment('open')
->on(new PaymentStatus())
->assertSeeIn('@lock-alert', 'The account is locked')
@@ -755,6 +833,7 @@
// Step 2
->whenAvailable('@step3', function ($browser) {
$browser->assertSeeIn('.card-title', 'Sign Up - Step 2/2')
+ ->click('#custom_domain')
->type('input#signup_domain', 'user-domain-signup.com')
->type('#signup_login', 'admin')
->type('#signup_password', '12345678')
diff --git a/src/tests/Feature/Controller/SignupTest.php b/src/tests/Feature/Controller/SignupTest.php
--- a/src/tests/Feature/Controller/SignupTest.php
+++ b/src/tests/Feature/Controller/SignupTest.php
@@ -163,6 +163,8 @@
$json = $response->json();
$this->assertSame($invitation->id, $json['id']);
+ $this->assertTrue($json['is_domain']); // depends on which plan is default
+ $this->assertSame(Domain::getPublicDomains(), $json['domains']);
// Test non-existing invitation
$response = $this->get("/api/auth/signup/invitations/abc");
@@ -334,10 +336,11 @@
$response->assertStatus(200);
- $this->assertCount(3, $json);
+ $this->assertCount(4, $json);
$this->assertSame('success', $json['status']);
$this->assertSame('email', $json['mode']);
$this->assertNotEmpty($json['code']);
+ $this->assertSame($all_domains = Domain::getPublicDomains(), $json['domains']);
$code = SignupCode::find($json['code']);
@@ -366,10 +369,11 @@
$json = $response->json();
$response->assertStatus(200);
- $this->assertCount(3, $json);
+ $this->assertCount(4, $json);
$this->assertSame('success', $json['status']);
$this->assertSame('email', $json['mode']);
$this->assertNotEmpty($json['code']);
+ $this->assertSame($all_domains, $json['domains']);
// Assert the job has proper data assigned
Queue::assertPushed(\App\Jobs\Mail\SignupVerificationJob::class, function ($job) use ($data, $json) {
@@ -719,10 +723,11 @@
$json = $response->json();
$response->assertStatus(200);
- $this->assertCount(3, $json);
+ $this->assertCount(4, $json);
$this->assertSame('success', $json['status']);
$this->assertSame('email', $json['mode']);
$this->assertNotEmpty($json['code']);
+ $this->assertSame($all_domains = Domain::getPublicDomains(), $json['domains']);
// Assert the email sending job was pushed once
Queue::assertPushed(\App\Jobs\Mail\SignupVerificationJob::class, 1);
@@ -756,7 +761,7 @@
$this->assertSame($user_data['last_name'], $result['last_name']);
$this->assertSame(null, $result['voucher']);
$this->assertSame(true, $result['is_domain']);
- $this->assertSame([], $result['domains']);
+ $this->assertSame($all_domains, $json['domains']);
// Final signup request
$login = 'admin';
@@ -1182,6 +1187,7 @@
['test.test', $domain, false, null],
['test_test', $domain, false, null],
['test-test', $domain, false, null],
+ ['test-test', 'kolab.org', false, ['domain' => 'The specified domain is not available.']],
['admin', $domain, false, ['login' => 'The specified login is not available.']],
['administrator', $domain, false, ['login' => 'The specified login is not available.']],
['sales', $domain, false, ['login' => 'The specified login is not available.']],
@@ -1189,6 +1195,7 @@
// Domain account
['admin', 'kolabsys.com', true, null],
+ ['testsystemdomain', $domain, true, null],
['testnonsystemdomain', 'invalid', true, ['domain' => 'The specified domain is invalid.']],
['testnonsystemdomain', '.com', true, ['domain' => 'The specified domain is invalid.']],
];
diff --git a/src/tests/Feature/UserTest.php b/src/tests/Feature/UserTest.php
--- a/src/tests/Feature/UserTest.php
+++ b/src/tests/Feature/UserTest.php
@@ -7,6 +7,7 @@
use App\Group;
use App\Package;
use App\PackageSku;
+use App\Plan;
use App\Sku;
use App\User;
use App\Auth\Utils as AuthUtils;
@@ -115,7 +116,37 @@
*/
public function testAssignPlan(): void
{
- $this->markTestIncomplete();
+ $domain = $this->getTestDomain('useraccount.com', [
+ 'status' => Domain::STATUS_NEW | Domain::STATUS_ACTIVE,
+ 'type' => Domain::TYPE_EXTERNAL,
+ ]);
+ $user = $this->getTestUser('useraccounta@' . $domain->namespace);
+ $plan = Plan::withObjectTenantContext($user)->where('title', 'group')->first();
+
+ // Group plan without domain
+ $user->assignPlan($plan);
+
+ $this->assertSame((string) $plan->id, $user->getSetting('plan_id'));
+ $this->assertSame(7, $user->entitlements()->count()); // 5 storage + 1 mailbox + 1 groupware
+
+ $user = $this->getTestUser('useraccountb@' . $domain->namespace);
+
+ // Group plan with a domain
+ $user->assignPlan($plan, $domain);
+
+ $this->assertSame((string) $plan->id, $user->getSetting('plan_id'));
+ $this->assertSame(7, $user->entitlements()->count()); // 5 storage + 1 mailbox + 1 groupware
+ $this->assertSame(1, $domain->entitlements()->count());
+
+ // Individual plan (domain is not allowed)
+ $user = $this->getTestUser('user-test@' . \config('app.domain'));
+ $plan = Plan::withObjectTenantContext($user)->where('title', 'individual')->first();
+
+ $this->expectException(\Exception::class);
+ $user->assignPlan($plan, $domain);
+
+ $this->assertNull($user->getSetting('plan_id'));
+ $this->assertSame(0, $user->entitlements()->count());
}
/**

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 6, 12:54 AM (8 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18823673
Default Alt Text
D5122.1775436865.diff (29 KB)

Event Timeline