diff --git a/.env.testing b/.env.testing index d0361048..2191bfc0 100644 --- a/.env.testing +++ b/.env.testing @@ -1,6 +1,6 @@ APP_NAME=Laravel APP_ENV=local -APP_KEY=base64:47yorkyoH3qCrKKO4eG6LpZUogoTC51qey5vYq/O3AM= +APP_KEY=base64:XP4FSfZLrj3n2MbhbOVWp4ldCbU0Ew+bhiEpHyOpxVw= APP_DEBUG=true APP_URL=http://localhost APP_DEMO=false diff --git a/config/content.php b/config/content.php index 97f0e2ad..ed54275b 100644 --- a/config/content.php +++ b/config/content.php @@ -95,6 +95,10 @@ return [ 'name' => 'allowed_adsense', 'value' => 0, ], + [ + 'name' => 'allowed_recaptcha', + 'value' => 0, + ], ], 'extended' => [ [ @@ -177,6 +181,10 @@ return [ 'name' => 'allowed_adsense', 'value' => 0, ], + [ + 'name' => 'allowed_recaptcha', + 'value' => 0, + ], // Subscription [ diff --git a/database/migrations/2021_08_24_080638_create_team_folder_invitations_table.php b/database/migrations/2021_08_24_080638_create_team_folder_invitations_table.php index fc540d11..27e8e06a 100644 --- a/database/migrations/2021_08_24_080638_create_team_folder_invitations_table.php +++ b/database/migrations/2021_08_24_080638_create_team_folder_invitations_table.php @@ -20,7 +20,7 @@ class CreateTeamFolderInvitationsTable extends Migration $table->text('email'); $table->string('color')->nullable(); $table->enum('permission', ['can-edit', 'can-view', 'can-view-and-download']); - $table->enum('status', ['pending', 'accepted', 'rejected'])->default('pending'); + $table->enum('status', ['pending', 'accepted', 'waiting-for-registration', 'rejected'])->default('pending'); $table->timestamps(); $table->charset = 'utf8mb4'; $table->collation = 'utf8mb4_unicode_ci'; diff --git a/resources/js/views/Teams/Invitation.vue b/resources/js/views/Teams/Invitation.vue index 8237e41a..87cc28e8 100644 --- a/resources/js/views/Teams/Invitation.vue +++ b/resources/js/views/Teams/Invitation.vue @@ -6,22 +6,37 @@ v-if="invitation" :title="$t('Invitation To Join Team Folder')" :description=" - $t('{name} invite you to join with his team into shared team folder', { + $t('Jane invite you to join with his team into shared team folder', { name: invitation.data.relationships.inviter.data.attributes.name, }) " >
- +
+

+ @@ -37,10 +52,17 @@ - + - + - + - + - + - + - + @@ -106,6 +146,11 @@ export default { }, computed: { ...mapGetters(['config']), + acceptButton() { + return this.invitation && this.invitation.data.attributes.isExistedUser + ? this.$t('Accept Invitation') + : this.$t('Accept and Register Account') + }, }, data() { return { @@ -121,7 +166,12 @@ export default { axios .put(`/api/teams/invitations/${this.$router.currentRoute.params.id}`) .then(() => { - this.goToAuthPage('accepted') + + if (this.invitation.data.attributes.isExistedUser) { + this.goToAuthPage('accepted') + } else { + this.$router.push({name: 'SignUp'}) + } }) .catch(() => { this.$isSomethingWrong() diff --git a/src/App/Console/Commands/SetupDevEnvironment.php b/src/App/Console/Commands/SetupDevEnvironment.php index 941a5b47..dd5abab5 100644 --- a/src/App/Console/Commands/SetupDevEnvironment.php +++ b/src/App/Console/Commands/SetupDevEnvironment.php @@ -1090,6 +1090,10 @@ class SetupDevEnvironment extends Command 'name' => 'subscription_type', 'value' => 'fixed', ], + [ + 'name' => 'allowed_recaptcha', + 'value' => 0, + ], ])->each(function ($col) { Setting::updateOrCreate([ 'name' => $col['name'], diff --git a/src/App/Console/Commands/SetupProdEnvironment.php b/src/App/Console/Commands/SetupProdEnvironment.php index 96e4822c..78407829 100644 --- a/src/App/Console/Commands/SetupProdEnvironment.php +++ b/src/App/Console/Commands/SetupProdEnvironment.php @@ -187,6 +187,10 @@ class SetupProdEnvironment extends Command 'name' => 'billing_vat_number', 'value' => null, ], + [ + 'name' => 'allowed_recaptcha', + 'value' => 0, + ], ])->each(function ($col) { Setting::forceCreate([ 'name' => $col['name'], diff --git a/src/App/Users/Actions/CreateNewUserAction.php b/src/App/Users/Actions/CreateNewUserAction.php index d52ecfae..7d7162fe 100644 --- a/src/App/Users/Actions/CreateNewUserAction.php +++ b/src/App/Users/Actions/CreateNewUserAction.php @@ -1,17 +1,19 @@ $data->avatar, ]); + // Join to previously accepted team folder invitations + TeamFolderInvitation::where('email', $user->email) + ->where('status', 'waiting-for-registration') + ->cursor() + ->each(function ($invitation) use ($user) { + TeamFolderMember::create([ + 'user_id' => $user->id, + 'parent_id' => $invitation->parent_id, + 'permission' => $invitation->permission, + ]); + + $invitation->accept(); + }); + // Subscribe user for metered billing if ($settings['subscription_type'] === 'metered') { ($this->autoSubscribeForMeteredBilling)($user); } // Mark as verified if verification is disabled - if (! $data->password || ! intval($settings['user_verification'])) { + if (!$data->password || !intval($settings['user_verification'])) { $user->markEmailAsVerified(); } diff --git a/src/App/Users/Requests/RegisterUserRequest.php b/src/App/Users/Requests/RegisterUserRequest.php index f4ff59d3..8b13f59b 100644 --- a/src/App/Users/Requests/RegisterUserRequest.php +++ b/src/App/Users/Requests/RegisterUserRequest.php @@ -32,7 +32,7 @@ class RegisterUserRequest extends FormRequest 'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email', new EmailProvider], 'name' => 'required|string|max:255', 'password' => $this->passwordRules(), - 'reCaptcha' => [new RequiredIf(get_settings('allowed_recaptcha') == 1), 'string', app(ReCaptchaRules::class)], + 'reCaptcha' => [new RequiredIf(get_settings('allowed_recaptcha') == 1), 'string', 'nullable', app(ReCaptchaRules::class)], ]; } } diff --git a/src/Domain/SetupWizard/Controllers/StoreEnvironmentSettingsController.php b/src/Domain/SetupWizard/Controllers/StoreEnvironmentSettingsController.php index 22e97982..88bebb5d 100644 --- a/src/Domain/SetupWizard/Controllers/StoreEnvironmentSettingsController.php +++ b/src/Domain/SetupWizard/Controllers/StoreEnvironmentSettingsController.php @@ -81,8 +81,9 @@ class StoreEnvironmentSettingsController extends Controller 'APP_DEBUG' => 'false', ], 'local' => [ - 'APP_ENV' => 'local', - 'APP_DEBUG' => 'true', + 'APP_ENV' => 'local', + 'APP_DEBUG' => 'true', + 'QUEUE_CONNECTION' => 'sync', ], ]; diff --git a/src/Domain/Teams/Controllers/InvitationsController.php b/src/Domain/Teams/Controllers/InvitationsController.php index e0b106fc..1192dfff 100644 --- a/src/Domain/Teams/Controllers/InvitationsController.php +++ b/src/Domain/Teams/Controllers/InvitationsController.php @@ -2,6 +2,7 @@ namespace Domain\Teams\Controllers; use App\Users\Models\User; +use Domain\Teams\Models\TeamFolderMember; use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; use App\Http\Controllers\Controller; @@ -13,7 +14,7 @@ class InvitationsController extends Controller { public function show(TeamFolderInvitation $invitation) { - if ($invitation->status === 'accepted') { + if ($invitation->status !== 'pending') { abort(410); } @@ -23,19 +24,24 @@ class InvitationsController extends Controller public function update( TeamFolderInvitation $invitation ): ResponseFactory | Response { - $user = User::where('email', $invitation->email) - ->firstOrFail(); + $user = User::where('email', $invitation->email); - $invitation->update([ - 'status' => 'accepted', - ]); + if ($user->exists()) { + $invitation->accept(); - DB::table('team_folder_members') - ->insert([ + // Store team member + TeamFolderMember::create([ + 'user_id' => $user->first()->id, 'parent_id' => $invitation->parent_id, - 'user_id' => $user->id, - 'permission' => 'can-edit', + 'permission' => $invitation->permission, ]); + } + + if ($user->doesntExist()) { + $invitation->update([ + 'status' => 'waiting-for-registration', + ]); + } return response('Done', 204); } @@ -43,9 +49,7 @@ class InvitationsController extends Controller public function destroy( TeamFolderInvitation $invitation ): ResponseFactory | Response { - $invitation->update([ - 'status' => 'rejected', - ]); + $invitation->reject(); return response('Done', 204); } diff --git a/src/Domain/Teams/Models/TeamFolderInvitation.php b/src/Domain/Teams/Models/TeamFolderInvitation.php index 4a2cca69..9537893b 100644 --- a/src/Domain/Teams/Models/TeamFolderInvitation.php +++ b/src/Domain/Teams/Models/TeamFolderInvitation.php @@ -31,6 +31,18 @@ class TeamFolderInvitation extends Model protected $keyType = 'string'; + public function accept() { + $this->update([ + 'status' => 'accepted', + ]); + } + + public function reject() { + $this->update([ + 'status' => 'rejected', + ]); + } + protected static function newFactory(): TeamFolderInvitationFactory { return TeamFolderInvitationFactory::new(); diff --git a/src/Domain/Teams/Resources/TeamInvitationResource.php b/src/Domain/Teams/Resources/TeamInvitationResource.php index b09f5c07..df8d0504 100644 --- a/src/Domain/Teams/Resources/TeamInvitationResource.php +++ b/src/Domain/Teams/Resources/TeamInvitationResource.php @@ -1,6 +1,7 @@ $this->color, 'status' => $this->status, 'permission' => $this->permission, + 'isExistedUser' => User::where('email', $this->email)->exists(), ], 'relationships' => [ $this->mergeWhen($this->inviter, fn () => [ diff --git a/tests/Domain/Homepage/HomepageTest.php b/tests/Domain/Homepage/HomepageTest.php index 2aff3976..0eac15e9 100644 --- a/tests/Domain/Homepage/HomepageTest.php +++ b/tests/Domain/Homepage/HomepageTest.php @@ -41,7 +41,7 @@ class HomepageTest extends TestCase { $this->get('/') ->assertStatus(200) - ->assertSee('setup-disclaimer') + ->assertSee('installation-needed') ->assertSee('VueFileManager'); } diff --git a/tests/Domain/Settings/SettingsTest.php b/tests/Domain/Settings/SettingsTest.php index 0387f5cf..4b50d2cf 100644 --- a/tests/Domain/Settings/SettingsTest.php +++ b/tests/Domain/Settings/SettingsTest.php @@ -190,12 +190,12 @@ class SettingsTest extends TestCase $this ->actingAs($admin) ->postJson('/api/admin/settings/email', [ - 'driver' => 'smtp', - 'host' => 'smtp.email.com', - 'port' => 25, - 'username' => 'john@doe.com', - 'password' => 'secret', - 'encryption' => 'tls', + 'mailDriver' => 'smtp', + 'smtp.host' => 'smtp.email.com', + 'smtp.port' => 25, + 'smtp.username' => 'john@doe.com', + 'smtp.password' => 'secret', + 'smtp.encryption' => 'tls', ])->assertStatus(204); } } diff --git a/tests/Domain/SetupWizard/SetupWizardTest.php b/tests/Domain/SetupWizard/SetupWizardTest.php index dd3100d9..15f66de7 100644 --- a/tests/Domain/SetupWizard/SetupWizardTest.php +++ b/tests/Domain/SetupWizard/SetupWizardTest.php @@ -21,7 +21,7 @@ class SetupWizardTest extends TestCase $this->postJson('/api/setup/purchase-code', [ 'purchaseCode' => '8624194e-3156-4cd0-944e-3440fcecdacb', - ])->assertStatus(204); + ])->assertStatus(201); } /** diff --git a/tests/Domain/Sharing/VisitorBrowseTest.php b/tests/Domain/Sharing/VisitorBrowseTest.php index 710f56c9..cb2c5ff3 100644 --- a/tests/Domain/Sharing/VisitorBrowseTest.php +++ b/tests/Domain/Sharing/VisitorBrowseTest.php @@ -1,4 +1,5 @@ getJson("/api/browse/folders/$root->id/$share->token") ->assertStatus(200) ->assertJsonFragment([ @@ -260,10 +261,11 @@ class VisitorBrowseTest extends TestCase $tree = [ [ - 'id' => $share->item_id, - 'name' => 'Home', - 'location' => 'public', - 'folders' => [ + 'name' => 'Home', + 'location' => 'public', + 'isMovable' => true, + 'isOpen' => true, + 'folders' => [ [ 'id' => $folder_level_2->id, 'parent_id' => $folder_level_1->id, @@ -308,7 +310,7 @@ class VisitorBrowseTest extends TestCase } // Check public shared item - if (! $is_protected) { + if (!$is_protected) { $this->getJson("/api/browse/navigation/$share->token") ->assertStatus(200) ->assertExactJson($tree); @@ -360,7 +362,7 @@ class VisitorBrowseTest extends TestCase } // Check public shared item - if (! $is_protected) { + if (!$is_protected) { $this->getJson("/api/browse/search/$share->token?query=doc") ->assertStatus(200) ->assertJsonFragment([ @@ -411,7 +413,7 @@ class VisitorBrowseTest extends TestCase } // Check public shared item - if (! $is_protected) { + if (!$is_protected) { $this->getJson("/api/browse/search/$share->token?query=doc") ->assertStatus(200) ->assertJsonFragment([]); @@ -458,7 +460,7 @@ class VisitorBrowseTest extends TestCase } // Check public shared item - if (! $is_protected) { + if (!$is_protected) { $this->getJson("/api/browse/file/$share->token") ->assertStatus(200) ->assertJsonFragment([ diff --git a/tests/Domain/Sharing/VisitorManipulatingTest.php b/tests/Domain/Sharing/VisitorManipulatingTest.php index c741d3e9..0cf3c903 100644 --- a/tests/Domain/Sharing/VisitorManipulatingTest.php +++ b/tests/Domain/Sharing/VisitorManipulatingTest.php @@ -300,6 +300,7 @@ class VisitorManipulatingTest extends TestCase collect([true, false]) ->each(function ($is_protected) { $user = User::factory() + ->hasSettings() ->create(); $folder = Folder::factory(Folder::class) diff --git a/tests/Domain/Teams/TeamManagementTest.php b/tests/Domain/Teams/TeamManagementTest.php index 79c07bed..b13f7c73 100644 --- a/tests/Domain/Teams/TeamManagementTest.php +++ b/tests/Domain/Teams/TeamManagementTest.php @@ -44,7 +44,7 @@ class TeamManagementTest extends TestCase /** * @test */ - public function it_accept_team_folder_invite() + public function it_accept_team_folder_invite_as_registered_user() { $member = User::factory() ->create([ @@ -59,7 +59,7 @@ class TeamManagementTest extends TestCase 'parent_id' => $folder->id, 'email' => $member->email, 'status' => 'pending', - 'permission' => 'can-edit', + 'permission' => 'can-view', ]); $this @@ -75,8 +75,69 @@ class TeamManagementTest extends TestCase ->assertDatabaseHas('team_folder_members', [ 'parent_id' => $folder->id, 'user_id' => $member->id, + 'permission' => 'can-view', + ]); + } + + /** + * @test + */ + public function it_accept_team_folder_invite_as_guest_user() + { + $folder = Folder::factory() + ->create(); + + $invitation = TeamFolderInvitation::factory() + ->create([ + 'parent_id' => $folder->id, + 'email' => 'howdy@hi5ve.digital', + 'status' => 'pending', 'permission' => 'can-edit', ]); + + $this + ->putJson("/api/teams/invitations/{$invitation->id}") + ->assertNoContent(); + + $this + ->assertDatabaseHas('team_folder_invitations', [ + 'parent_id' => $folder->id, + 'status' => 'waiting-for-registration', + ]) + ->assertDatabaseMissing('team_folder_members', [ + 'parent_id' => $folder->id, + 'permission' => 'can-edit', + ]); + } + + /** + * @test + */ + public function it_apply_accepted_invitation_after_user_registration() + { + $invitation = TeamFolderInvitation::factory() + ->create([ + 'email' => 'john@doe.com', + 'status' => 'waiting-for-registration', + ]); + + $this->postJson('api/register', [ + 'email' => 'john@doe.com', + 'password' => 'SecretPassword', + 'password_confirmation' => 'SecretPassword', + 'name' => 'John Doe', + ])->assertStatus(201); + + $this + ->assertDatabaseHas('team_folder_invitations', [ + 'parent_id' => $invitation->parent_id, + 'status' => 'accepted', + ]) + ->assertDatabaseHas('team_folder_members', [ + 'parent_id' => $invitation->parent_id, + 'user_id' => User::first()->id, + 'permission' => $invitation->permission, + ]); } /**