Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117766185
D5607.1775232020.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
12 KB
Referenced Files
None
Subscribers
None
D5607.1775232020.diff
View Options
diff --git a/src/app/Console/Commands/Data/Import/SignupTokensCommand.php b/src/app/Console/Commands/Data/Import/SignupTokensCommand.php
--- a/src/app/Console/Commands/Data/Import/SignupTokensCommand.php
+++ b/src/app/Console/Commands/Data/Import/SignupTokensCommand.php
@@ -14,7 +14,7 @@
*
* @var string
*/
- protected $signature = 'data:import:signup-tokens {plan} {file} {--tenant=}';
+ protected $signature = 'data:import:signup-tokens {file} {plan*} {--tenant=}';
/**
* The console command description.
@@ -35,16 +35,22 @@
{
parent::handle();
- $plan = $this->getObject(Plan::class, $this->argument('plan'), 'title', false);
+ $plans = [];
- if (!$plan) {
- $this->error("Plan not found");
- return 1;
- }
+ foreach ($this->argument('plan') as $p) {
+ $plan = $this->getObject(Plan::class, $p, 'title', false);
- if ($plan->mode != Plan::MODE_TOKEN) {
- $this->error("The plan is not for tokens");
- return 1;
+ if (!$plan) {
+ $this->error("Plan '{$p}' not found");
+ return 1;
+ }
+
+ if ($plan->mode != Plan::MODE_TOKEN) {
+ $this->error("Plan '{$p}' is not for tokens");
+ return 1;
+ }
+
+ $plans[] = $plan->id;
}
$file = $this->argument('file');
@@ -98,8 +104,9 @@
// Import tokens
foreach ($list as $token) {
- $plan->signupTokens()->create([
+ SignupToken::create([
'id' => $token,
+ 'plans' => $plans,
// This allows us to update counter when importing old tokens in migration.
// It can be removed later
'counter' => UserSetting::where('key', 'signup_token')
diff --git a/src/app/Plan.php b/src/app/Plan.php
--- a/src/app/Plan.php
+++ b/src/app/Plan.php
@@ -6,7 +6,6 @@
use App\Traits\UuidStrKeyTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Translatable\HasTranslations;
/**
@@ -135,14 +134,4 @@
return false;
}
-
- /**
- * The relationship to signup tokens.
- *
- * @return HasMany<SignupToken, $this>
- */
- public function signupTokens()
- {
- return $this->hasMany(SignupToken::class);
- }
}
diff --git a/src/app/Rules/SignupToken.php b/src/app/Rules/SignupToken.php
--- a/src/app/Rules/SignupToken.php
+++ b/src/app/Rules/SignupToken.php
@@ -3,6 +3,7 @@
namespace App\Rules;
use App\Plan;
+use App\SignupToken as Token;
use Illuminate\Contracts\Validation\Rule;
class SignupToken implements Rule
@@ -40,8 +41,9 @@
return false;
}
- // Check the token existence
- if (!$this->plan->signupTokens()->find($token)) {
+ // Check the token existence and its plan set
+ $signup_token = Token::find($token);
+ if (!$signup_token || !in_array($this->plan->id, $signup_token->plans)) {
$this->message = \trans('validation.signuptokeninvalid');
return false;
}
diff --git a/src/app/SignupToken.php b/src/app/SignupToken.php
--- a/src/app/SignupToken.php
+++ b/src/app/SignupToken.php
@@ -4,7 +4,6 @@
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* The eloquent definition of a SignupToken.
@@ -12,7 +11,7 @@
* @property Carbon $created_at The creation timestamp
* @property int $counter Count of signups on this token
* @property ?string $id Token
- * @property ?string $plan_id Plan identifier
+ * @property array $plans Plan identifiers
*/
class SignupToken extends Model
{
@@ -24,7 +23,7 @@
/** @var list<string> The attributes that are mass assignable */
protected $fillable = [
- 'plan_id',
+ 'plans',
'id',
'counter',
];
@@ -33,18 +32,9 @@
protected $casts = [
'created_at' => 'datetime:Y-m-d H:i:s',
'counter' => 'integer',
+ 'plans' => 'array',
];
/** @var bool Indicates if the model should be timestamped. */
public $timestamps = false;
-
- /**
- * The plan this token applies to
- *
- * @return BelongsTo<Plan, $this>
- */
- public function plan()
- {
- return $this->belongsTo(Plan::class);
- }
}
diff --git a/src/database/migrations/2025_09_15_100000_signup_tokens_plans.php b/src/database/migrations/2025_09_15_100000_signup_tokens_plans.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2025_09_15_100000_signup_tokens_plans.php
@@ -0,0 +1,57 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration {
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::table(
+ 'signup_tokens',
+ static function (Blueprint $table) {
+ $table->text('plans');
+ }
+ );
+
+ DB::table('signup_tokens')->update(['plans' => DB::raw("concat('[\"', `plan_id`, '\"]')")]);
+
+ Schema::table(
+ 'signup_tokens',
+ static function (Blueprint $table) {
+ $table->dropColumn('plan_id');
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table(
+ 'signup_tokens',
+ static function (Blueprint $table) {
+ $table->string('plan_id', 36);
+ }
+ );
+
+ DB::table('signup_tokens')->select('id', 'plans')->get()
+ ->each(static function ($token) {
+ $plan_id = json_decode($token->plans, true)[0];
+ DB::table('signup_tokens')->where('id', $token->id)->update(['plan_id' => $plan_id]);
+ });
+
+ Schema::table(
+ 'signup_tokens',
+ static function (Blueprint $table) {
+ $table->dropColumn('plans');
+ $table->index('plan_id');
+ }
+ );
+ }
+};
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
@@ -752,7 +752,7 @@
$plan->update(['mode' => Plan::MODE_TOKEN]);
// Register a valid token
- $plan->signupTokens()->create(['id' => '1234567890']);
+ SignupToken::create(['id' => '1234567890', 'plans' => [$plan->id]]);
$this->browse(static function (Browser $browser) {
$browser->visit(new Signup())
@@ -804,7 +804,7 @@
$plan->update(['mode' => Plan::MODE_TOKEN]);
// Register a valid token
- $plan->signupTokens()->create(['id' => 'abcdefghijk']);
+ SignupToken::create(['id' => 'abcdefghijk', 'plans' => [$plan->id]]);
$this->browse(static function (Browser $browser) {
$browser->visit(new Signup())
diff --git a/src/tests/Feature/Console/Data/Import/SignupTokensTest.php b/src/tests/Feature/Console/Data/Import/SignupTokensTest.php
--- a/src/tests/Feature/Console/Data/Import/SignupTokensTest.php
+++ b/src/tests/Feature/Console/Data/Import/SignupTokensTest.php
@@ -12,13 +12,15 @@
{
parent::setUp();
- Plan::where('title', 'test')->delete();
+ Plan::where('title', 'test1')->delete();
+ Plan::where('title', 'test2')->delete();
SignupToken::truncate();
}
protected function tearDown(): void
{
- Plan::where('title', 'test')->delete();
+ Plan::where('title', 'test1')->delete();
+ Plan::where('title', 'test2')->delete();
SignupToken::truncate();
@unlink(storage_path('test-tokens.txt'));
@@ -35,35 +37,42 @@
file_put_contents($file, '');
// Unknown plan
- $code = \Artisan::call("data:import:signup-tokens unknown {$file}");
+ $code = \Artisan::call("data:import:signup-tokens {$file} unknown");
$output = trim(\Artisan::output());
$this->assertSame(1, $code);
- $this->assertSame("Plan not found", $output);
+ $this->assertSame("Plan 'unknown' not found", $output);
// Plan not for tokens
- $code = \Artisan::call("data:import:signup-tokens individual {$file}");
+ $code = \Artisan::call("data:import:signup-tokens {$file} individual");
$output = trim(\Artisan::output());
$this->assertSame(1, $code);
- $this->assertSame("The plan is not for tokens", $output);
+ $this->assertSame("Plan 'individual' is not for tokens", $output);
- $plan = Plan::create([
- 'title' => 'test',
- 'name' => 'Test Account',
+ $plan1 = Plan::create([
+ 'title' => 'test1',
+ 'name' => 'Test Account 1',
+ 'description' => 'Test',
+ 'mode' => Plan::MODE_TOKEN,
+ ]);
+
+ $plan2 = Plan::create([
+ 'title' => 'test2',
+ 'name' => 'Test Account 2',
'description' => 'Test',
'mode' => Plan::MODE_TOKEN,
]);
// Non-existent input file
- $code = \Artisan::call("data:import:signup-tokens {$plan->title} nofile.txt");
+ $code = \Artisan::call("data:import:signup-tokens nofile.txt {$plan1->title}");
$output = trim(\Artisan::output());
$this->assertSame(1, $code);
$this->assertSame("File 'nofile.txt' does not exist", $output);
// Empty input file
- $code = \Artisan::call("data:import:signup-tokens {$plan->title} {$file}");
+ $code = \Artisan::call("data:import:signup-tokens {$file} {$plan1->title}");
$output = trim(\Artisan::output());
$this->assertSame(1, $code);
@@ -71,16 +80,21 @@
// Valid tokens
file_put_contents($file, "12345\r\nabcde");
- $code = \Artisan::call("data:import:signup-tokens {$plan->id} {$file}");
+ $code = \Artisan::call("data:import:signup-tokens {$file} {$plan1->id} {$plan2->title}");
$output = trim(\Artisan::output());
$this->assertSame(0, $code);
$this->assertStringContainsString("Validating tokens... DONE", $output);
$this->assertStringContainsString("Importing tokens... DONE", $output);
- $this->assertSame(['12345', 'ABCDE'], $plan->signupTokens()->orderBy('id')->pluck('id')->all());
+ $tokens = SignupToken::orderBy('id')->get();
+ $this->assertCount(2, $tokens);
+ $this->assertSame('12345', $tokens[0]->id);
+ $this->assertSame([$plan1->id, $plan2->id], $tokens[0]->plans);
+ $this->assertSame('ABCDE', $tokens[1]->id);
+ $this->assertSame([$plan1->id, $plan2->id], $tokens[1]->plans);
// Attempt the same tokens again
- $code = \Artisan::call("data:import:signup-tokens {$plan->id} {$file}");
+ $code = \Artisan::call("data:import:signup-tokens {$file} {$plan1->id}");
$output = trim(\Artisan::output());
$this->assertSame(0, $code);
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
@@ -1022,7 +1022,7 @@
$this->assertSame(['token' => ["The signup token is invalid."]], $json['errors']);
// Test valid token
- $plan->signupTokens()->create(['id' => 'abc']);
+ $token = SignupToken::create(['id' => 'abc', 'plans' => [$plan->id]]);
$post['plan'] = $plan->title;
$response = $this->post('/api/auth/signup', $post);
$response->assertStatus(200);
@@ -1036,11 +1036,11 @@
$user = User::where('email', 'test-inv@kolabnow.com')->first();
$this->assertNotEmpty($user);
$this->assertSame($plan->id, $user->getSetting('plan_id'));
- $this->assertSame($plan->signupTokens()->first()->id, $user->getSetting('signup_token'));
+ $this->assertSame($token->id, $user->getSetting('signup_token'));
$this->assertNull($user->getSetting('external_email'));
// Token's counter bumped up
- $this->assertSame(1, $plan->signupTokens()->first()->counter);
+ $this->assertSame(1, $token->fresh()->counter);
}
/**
diff --git a/src/tests/Unit/Rules/SignupTokenTest.php b/src/tests/Unit/Rules/SignupTokenTest.php
--- a/src/tests/Unit/Rules/SignupTokenTest.php
+++ b/src/tests/Unit/Rules/SignupTokenTest.php
@@ -41,8 +41,8 @@
'mode' => Plan::MODE_TOKEN,
]);
- $plan->signupTokens()->create(['id' => $tokens[0]]);
- $tokenPlan->signupTokens()->create(['id' => $tokens[1]]);
+ SignupToken::create(['id' => $tokens[0], 'plans' => [$plan->id]]);
+ SignupToken::create(['id' => $tokens[1], 'plans' => [$tokenPlan->id]]);
$rules = ['token' => [new SignupTokenRule(null)]];
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 4:00 PM (2 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18824615
Default Alt Text
D5607.1775232020.diff (12 KB)
Attached To
Mode
D5607: Support multi-plan signup tokens
Attached
Detach File
Event Timeline