diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailEventConsumer.cs b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailEventConsumer.cs index 01211fcd2..35fa0e12f 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailEventConsumer.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailEventConsumer.cs @@ -60,7 +60,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation return; } - if (@event.Payload is AppContributorAssigned appContributorAssigned && appContributorAssigned.Actor.IsSubject) + if (@event.Payload is AppContributorAssigned appContributorAssigned && + appContributorAssigned.IsCreated && + appContributorAssigned.Actor.IsSubject) { var assignerId = appContributorAssigned.Actor.Identifier; var assigneeId = appContributorAssigned.ContributorId; diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailSender.cs b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailSender.cs index 867111fb8..c4ad43977 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailSender.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEmailSender.cs @@ -45,12 +45,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation public Task SendExistingUserEmailAsync(IUser assigner, IUser assignee, string appName) { - return SendEmailAsync(texts.ExistingUserSubject, texts.ExistingUserBody, assigner, assignee, appName); + return SendEmailAsync(texts.ExistingUserBody, texts.ExistingUserSubject, assigner, assignee, appName); } public Task SendNewUserEmailAsync(IUser assigner, IUser assignee, string appName) { - return SendEmailAsync(texts.NewUserBody, texts.NewUserSubject, assigner, assignee, appName); + return SendEmailAsync(texts.NewUserSubject, texts.NewUserBody, assigner, assignee, appName); } private async Task SendEmailAsync(string emailSubj, string emailBody, IUser assigner, IUser assignee, string appName) diff --git a/src/Squidex.Domain.Users/DefaultUserResolver.cs b/src/Squidex.Domain.Users/DefaultUserResolver.cs index ac67f7827..49723af01 100644 --- a/src/Squidex.Domain.Users/DefaultUserResolver.cs +++ b/src/Squidex.Domain.Users/DefaultUserResolver.cs @@ -28,7 +28,7 @@ namespace Squidex.Domain.Users this.userFactory = userFactory; } - public async Task CreateUserIfNotExists(string email) + public async Task CreateUserIfNotExists(string email, bool invited) { var user = userFactory.Create(email); @@ -38,7 +38,9 @@ namespace Squidex.Domain.Users if (result.Succeeded) { - await userManager.UpdateAsync(user, new UserValues { DisplayName = email }); + var values = new UserValues { DisplayName = email, Invited = invited }; + + await userManager.UpdateAsync(user, values); } return result.Succeeded; diff --git a/src/Squidex.Domain.Users/UserValues.cs b/src/Squidex.Domain.Users/UserValues.cs index e4ce18662..2dc1baac5 100644 --- a/src/Squidex.Domain.Users/UserValues.cs +++ b/src/Squidex.Domain.Users/UserValues.cs @@ -22,6 +22,8 @@ namespace Squidex.Domain.Users public string Email { get; set; } + public bool? Invited { get; set; } + public bool? Consent { get; set; } public bool? ConsentForEmails { get; set; } @@ -47,6 +49,11 @@ namespace Squidex.Domain.Users yield return new Claim(SquidexClaimTypes.Hidden, Hidden.ToString()); } + if (Invited.HasValue) + { + yield return new Claim(SquidexClaimTypes.Invited, Invited.ToString()); + } + if (Consent.HasValue) { yield return new Claim(SquidexClaimTypes.Consent, Consent.ToString()); diff --git a/src/Squidex.Shared/Identity/SquidexClaimTypes.cs b/src/Squidex.Shared/Identity/SquidexClaimTypes.cs index 32ba4f90c..7112350b8 100644 --- a/src/Squidex.Shared/Identity/SquidexClaimTypes.cs +++ b/src/Squidex.Shared/Identity/SquidexClaimTypes.cs @@ -19,6 +19,8 @@ namespace Squidex.Shared.Identity public static readonly string Hidden = "urn:squidex:hidden"; + public static readonly string Invited = "urn:squidex:invited"; + public static readonly string Permissions = "urn:squidex:permissions"; public static readonly string PermissionsClient = "client_urn:squidex:permissions"; diff --git a/src/Squidex.Shared/Users/IUserResolver.cs b/src/Squidex.Shared/Users/IUserResolver.cs index dd86bb5d1..29f3b541a 100644 --- a/src/Squidex.Shared/Users/IUserResolver.cs +++ b/src/Squidex.Shared/Users/IUserResolver.cs @@ -12,7 +12,7 @@ namespace Squidex.Shared.Users { public interface IUserResolver { - Task CreateUserIfNotExists(string email); + Task CreateUserIfNotExists(string email, bool invited = false); Task FindByIdOrEmailAsync(string idOrEmail); diff --git a/src/Squidex.Shared/Users/UserExtensions.cs b/src/Squidex.Shared/Users/UserExtensions.cs index 52584f671..52f5a5de5 100644 --- a/src/Squidex.Shared/Users/UserExtensions.cs +++ b/src/Squidex.Shared/Users/UserExtensions.cs @@ -19,6 +19,11 @@ namespace Squidex.Shared.Users return new PermissionSet(user.GetClaimValues(SquidexClaimTypes.Permissions).Select(x => new Permission(x))); } + public static bool IsInvited(this IUser user) + { + return user.HasClaimValue(SquidexClaimTypes.Invited, "true"); + } + public static bool IsHidden(this IUser user) { return user.HasClaimValue(SquidexClaimTypes.Hidden, "true"); diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index df0d018fd..28f6665f2 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -150,7 +150,7 @@ namespace Squidex.Config.Domain return result; }); - var emailOptions = config.GetValue("email:smtp"); + var emailOptions = config.GetSection("email:smtp").Get(); if (emailOptions.IsConfigured()) { diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts index b540f1b75..61d77c7b8 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts @@ -99,10 +99,10 @@ export class ContributorsPageComponent implements OnInit { const requestDto = new AssignContributorDto(user, 'Editor', true); this.contributorsState.assign(requestDto) - .subscribe(wasInvited => { + .subscribe(isCreated => { this.assignContributorForm.submitCompleted({}); - if (wasInvited) { + if (isCreated) { this.dialogs.notifyInfo('A new user with the entered email address has been created and assigned as contributor.'); } }, error => { diff --git a/src/Squidex/app/shared/services/app-contributors.service.spec.ts b/src/Squidex/app/shared/services/app-contributors.service.spec.ts index 76106a334..fda899774 100644 --- a/src/Squidex/app/shared/services/app-contributors.service.spec.ts +++ b/src/Squidex/app/shared/services/app-contributors.service.spec.ts @@ -94,7 +94,7 @@ describe('AppContributorsService', () => { expect(req.request.method).toEqual('POST'); expect(req.request.headers.get('If-Match')).toEqual(version.value); - req.flush({ contributorId: '123', wasInvited: true }); + req.flush({ contributorId: '123', isCreated: true }); expect(contributorAssignedDto!.contributorId).toEqual('123'); })); diff --git a/src/Squidex/app/shared/services/app-contributors.service.ts b/src/Squidex/app/shared/services/app-contributors.service.ts index 766d8c08e..c67966b79 100644 --- a/src/Squidex/app/shared/services/app-contributors.service.ts +++ b/src/Squidex/app/shared/services/app-contributors.service.ts @@ -52,7 +52,7 @@ export class AppContributorDto extends Model { export class ContributorAssignedDto { constructor( public readonly contributorId: string, - public readonly wasInvited: boolean + public readonly isCreated: boolean ) { } } @@ -93,7 +93,7 @@ export class AppContributorsService { map(response => { const body: any = response.payload.body; - const result = new ContributorAssignedDto(body.contributorId, body.wasInvited); + const result = new ContributorAssignedDto(body.contributorId, body.isCreated); return new Versioned(response.version, result); }), diff --git a/src/Squidex/app/shared/state/contributors.state.ts b/src/Squidex/app/shared/state/contributors.state.ts index 1531598e6..58034970b 100644 --- a/src/Squidex/app/shared/state/contributors.state.ts +++ b/src/Squidex/app/shared/state/contributors.state.ts @@ -115,7 +115,7 @@ export class ContributorsState extends State { this.replaceContributors(contributors, dto.version); - return dto.payload.wasInvited; + return dto.payload.isCreated; }), catchError(error => { if (Types.is(error, ErrorDto) && error.statusCode === 404) { diff --git a/src/Squidex/appsettings.json b/src/Squidex/appsettings.json index afb699604..cc836887c 100644 --- a/src/Squidex/appsettings.json +++ b/src/Squidex/appsettings.json @@ -96,23 +96,23 @@ */ "port": 465 }, - "invitiations": { + "invitations": { /* * The email subject when a new user is added as contributor. */ - "newUserSubject": "Welcome to Squidex, you have been invited to app $APP_NAME", + "newUserSubject": "Welcome to Squidex, you have been invited to join Project $APP_NAME", /* * The email body when a new user is added as contributor. */ - "newUserBody": "You have been invited to join an app at Squidex CMS\n\nWelcome to Squidex\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join app $APP_NAME at Squidex headless CMS.\nLogin with your Github, Google or Microsoft account to create a new user account and start editing content now.\n\nThank you very much,\nThe Squidex Team\n<> [$UI_URL]", + "newUserBody": "You have been invited to join a Project at Squidex CMS\n\nWelcome to Squidex\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join app $APP_NAME at Squidex Headless CMS.\nLogin with your Github, Google or Microsoft credentials to create a new user account and start editing content now.\n\nThank you very much,\nThe Squidex Team\n<> [$UI_URL]", /* * The email subject when an existing user is added as contributor. */ - "existingUserSubject": "[Squidex CMS] You have been invited to app $APP_NAME", + "existingUserSubject": "[Squidex CMS] You have been invited to App $APP_NAME", /* * The email body when an existing user is added as contributor. */ - "existingUserBody": "You have been invited to join an app at Squidex CMS\n\nWelcome to Squidex\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join app $APP_NAME at Squidex headless CMS.\nLogin or reload the Management UI to see the app.\n\nThank you very much,\nThe Squidex Team\n<> [$UI_URL]" + "existingUserBody": "You have been invited to join an App at Squidex CMS\n\nWelcome to Squidex\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join app $APP_NAME at Squidex Headless CMS.\nLogin or reload the Management UI to see the app.\n\nThank you very much,\nThe Squidex Team\n<> [$UI_URL]" } }, diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs index 3e71cfee5..24e01634a 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Invitation/InviteUserCommandMiddlewareTests.cs @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation var command = new AssignContributor { ContributorId = "me@email.com", IsInviting = true }; var context = new CommandContext(command, commandBus); - A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com")) + A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com", true)) .Returns(true); var result = EntityCreatedResult.Create("13", 13L); @@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation Assert.Same(context.Result().Id, result); - A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com")) + A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com", true)) .MustHaveHappened(); } @@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation var command = new AssignContributor { ContributorId = "me@email.com", IsInviting = true }; var context = new CommandContext(command, commandBus); - A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com")) + A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com", true)) .Returns(false); var result = EntityCreatedResult.Create("13", 13L); @@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation Assert.Same(context.Result>(), result); - A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com")) + A.CallTo(() => userResolver.CreateUserIfNotExists("me@email.com", true)) .MustHaveHappened(); } @@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation await sut.HandleAsync(context); - A.CallTo(() => userResolver.CreateUserIfNotExists(A.Ignored)) + A.CallTo(() => userResolver.CreateUserIfNotExists(A.Ignored, A.Ignored)) .MustNotHaveHappened(); } }