Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use orga and project owner emails to notify #7561

Merged
merged 7 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Updated antd UI library from version 4.24.8 to 4.24.15. [#7505](https://github.com/scalableminds/webknossos/pull/7505)
- Changed the default dataset search mode to also search in subfolders. [#7539](https://github.com/scalableminds/webknossos/pull/7539)
- When clicking a segment in the viewport, it is automatically focused in the segment list. A corresponding context menu entry was added as well. [#7512](https://github.com/scalableminds/webknossos/pull/7512)
- Updated the isValidName route in the API to return 200 for valid and invalid names. With this, the API version was bumped up to 6. [#7550](https://github.com/scalableminds/webknossos/pull/7550)
- Upgraded to Play 3. [#7562](https://github.com/scalableminds/webknossos/pull/7562)
- Updated the isValidName route in the API to return 200 for valid and invalid names. With this, the API version was bumped up to 6. [#7550](https://github.com/scalableminds/webknossos/pull/7550)
- When no Email Address for New-User Notifications is configured, the organization owner will be notified. For overtime notifications, the project owner and the organization owner will be notified. [#7561](https://github.com/scalableminds/webknossos/pull/7561)
- The metadata for ND datasets and their annotation has changed: upper bound of additionalAxes is now stored as an exclusive value, called "end" in the NML format. [#7547](https://github.com/scalableminds/webknossos/pull/7547)
- Added support for the *index_location* parameter in sharded Zarr 3 datasets. [#7553](https://github.com/scalableminds/webknossos/pull/7553)

Expand Down
17 changes: 14 additions & 3 deletions app/controllers/AuthenticationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,19 @@ class AuthenticationController @Inject()(
_ <- Fox.runIf(inviteBox.isDefined)(Fox.runOptional(inviteBox.toOption)(i =>
inviteService.deactivateUsedInvite(i)(GlobalAccessContext)))
brainDBResult <- Fox.runIf(registerBrainDB)(brainTracing.registerIfNeeded(user, password.getOrElse("")))
newUserEmailRecipient <- organizationService.newUserMailRecipient(organization)(GlobalAccessContext)
_ = if (conf.Features.isWkorgInstance) {
mailchimpClient.registerUser(user, multiUser, tag = MailchimpTag.RegisteredAsUser)
} else {
Mailer ! Send(defaultMails.newUserMail(user.name, email, brainDBResult.flatten, autoActivate))
}
_ = Mailer ! Send(
defaultMails.registerAdminNotifyerMail(user.name, email, brainDBResult.flatten, organization, autoActivate))
defaultMails.registerAdminNotifierMail(user.name,
email,
brainDBResult.flatten,
organization,
autoActivate,
newUserEmailRecipient))
} yield {
user
}
Expand Down Expand Up @@ -356,9 +362,14 @@ class AuthenticationController @Inject()(
_ <- userService.joinOrganization(request.identity, organization._id, autoActivate = invite.autoActivate)
_ = analyticsService.track(JoinOrganizationEvent(request.identity, organization))
userEmail <- userService.emailFor(request.identity)
newUserEmailRecipient <- organizationService.newUserMailRecipient(organization)
_ = Mailer ! Send(
defaultMails
.registerAdminNotifyerMail(request.identity.name, userEmail, None, organization, invite.autoActivate))
defaultMails.registerAdminNotifierMail(request.identity.name,
userEmail,
None,
organization,
invite.autoActivate,
newUserEmailRecipient))
_ <- inviteService.deactivateUsedInvite(invite)(GlobalAccessContext)
} yield Ok
}
Expand Down
31 changes: 16 additions & 15 deletions app/mail/DefaultMails.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,59 @@ class DefaultMails @Inject()(conf: WkConf) {
private val defaultSender = conf.Mail.defaultSender
private val newOrganizationMailingList = conf.WebKnossos.newOrganizationMailingList

def registerAdminNotifyerMail(name: String,
def registerAdminNotifierMail(name: String,
email: String,
brainDBResult: Option[String],
organization: Organization,
autoActivate: Boolean): Mail =
autoActivate: Boolean,
recipient: String): Mail =
Mail(
from = defaultSender,
subject =
s"WEBKNOSSOS | A new user ($name, $email) registered on $uri for ${organization.displayName} (${organization.name})",
bodyHtml = html.mail.notifyAdminNewUser(name, brainDBResult, uri, autoActivate).body,
recipients = List(organization.newUserMailingList)
recipients = List(recipient)
)

def overLimitMail(user: User,
projectName: String,
taskId: String,
annotationId: String,
organization: Organization): Mail =
recipients: List[String]): Mail =
Mail(
from = defaultSender,
subject = s"WEBKNOSSOS | Time limit reached. ${user.abbreviatedName} in $projectName",
bodyHtml = html.mail.notifyAdminTimeLimit(user.name, projectName, taskId, annotationId, uri).body,
recipients = List(organization.overTimeMailingList)
recipients = recipients
)

def newUserMail(name: String, receiver: String, brainDBresult: Option[String], enableAutoVerify: Boolean)(
def newUserMail(name: String, recipient: String, brainDBresult: Option[String], enableAutoVerify: Boolean)(
implicit mp: MessagesProvider): Mail =
Mail(
from = defaultSender,
subject = "Welcome to WEBKNOSSOS",
bodyHtml = html.mail.newUser(name, brainDBresult.map(Messages(_)), enableAutoVerify).body,
recipients = List(receiver)
recipients = List(recipient)
)

def activatedMail(name: String, receiver: String): Mail =
def activatedMail(name: String, recipient: String): Mail =
Mail(from = defaultSender,
subject = "WEBKNOSSOS | Account activated",
bodyHtml = html.mail.validateUser(name, uri).body,
recipients = List(receiver))
recipients = List(recipient))

def changePasswordMail(name: String, receiver: String): Mail =
def changePasswordMail(name: String, recipient: String): Mail =
Mail(from = defaultSender,
subject = "WEBKNOSSOS | Password changed",
bodyHtml = html.mail.passwordChanged(name, uri).body,
recipients = List(receiver))
recipients = List(recipient))

def resetPasswordMail(name: String, receiver: String, token: String): Mail =
def resetPasswordMail(name: String, recipient: String, token: String): Mail =
Mail(
from = defaultSender,
subject = "WEBKNOSSOS | Password Reset",
bodyHtml = html.mail.resetPassword(name, uri, token).body,
recipients = List(receiver)
recipients = List(recipient)
)

def newOrganizationMail(organizationDisplayName: String, creatorEmail: String, domain: String): Mail =
Expand All @@ -78,7 +79,7 @@ class DefaultMails @Inject()(conf: WkConf) {
recipients = List(newOrganizationMailingList)
)

def inviteMail(receiver: String,
def inviteMail(recipient: String,
inviteTokenValue: String,
autoVerify: Boolean,
organizationDisplayName: String,
Expand All @@ -88,7 +89,7 @@ class DefaultMails @Inject()(conf: WkConf) {
from = defaultSender,
subject = s"$senderName invited you to join their WEBKNOSSOS organization at $host",
bodyHtml = html.mail.invite(senderName, organizationDisplayName, inviteTokenValue, uri, autoVerify).body,
recipients = List(receiver)
recipients = List(recipient)
)
}

Expand Down
21 changes: 20 additions & 1 deletion app/models/organization/OrganizationService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import javax.inject.Inject
import models.dataset.{DataStore, DataStoreDAO}
import models.folder.{Folder, FolderDAO, FolderService}
import models.team.{PricingPlan, Team, TeamDAO}
import models.user.{Invite, MultiUserDAO, User, UserDAO}
import models.user.{Invite, MultiUserDAO, User, UserDAO, UserService}
import play.api.libs.json.{JsObject, Json}
import utils.{ObjectId, WkConf}

Expand All @@ -22,6 +22,7 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
dataStoreDAO: DataStoreDAO,
folderDAO: FolderDAO,
folderService: FolderService,
userService: UserService,
rpc: RPC,
conf: WkConf,
)(implicit ec: ExecutionContext)
Expand Down Expand Up @@ -144,4 +145,22 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
_ <- Fox.runOptional(organization.includedUsers)(includedUsers =>
bool2Fox(userCount + usersToAddCount <= includedUsers))
} yield ()

private def fallbackOnOwnerEmail(possiblyEmptyEmail: String, organization: Organization)(
implicit ctx: DBAccessContext): Fox[String] =
if (possiblyEmptyEmail.nonEmpty) {
Fox.successful(possiblyEmptyEmail)
} else {
for {
owner <- userDAO.findOwnerByOrg(organization._id)
ownerEmail <- userService.emailFor(owner)
} yield ownerEmail
}

def overTimeMailRecipient(organization: Organization)(implicit ctx: DBAccessContext): Fox[String] =
fallbackOnOwnerEmail(organization.overTimeMailingList, organization)

def newUserMailRecipient(organization: Organization)(implicit ctx: DBAccessContext): Fox[String] =
fallbackOnOwnerEmail(organization.newUserMailingList, organization)

}
10 changes: 7 additions & 3 deletions app/models/user/time/TimeSpanService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import mail.{DefaultMails, Send}

import javax.inject.Inject
import models.annotation._
import models.organization.OrganizationDAO
import models.organization.{OrganizationDAO, OrganizationService}
import models.project.ProjectDAO
import models.task.TaskDAO
import models.user.{User, UserService}
Expand All @@ -25,6 +25,7 @@ class TimeSpanService @Inject()(annotationDAO: AnnotationDAO,
taskDAO: TaskDAO,
brainTracing: BrainTracing,
annotationService: AnnotationService,
organizationService: OrganizationService,
projectDAO: ProjectDAO,
organizationDAO: OrganizationDAO,
timeSpanDAO: TimeSpanDAO,
Expand Down Expand Up @@ -174,10 +175,13 @@ class TimeSpanService @Inject()(annotationDAO: AnnotationDAO,
annotationTime <- annotation.tracingTime ?~> "no annotation.tracingTime"
timeLimit <- project.expectedTime ?~> "no project.expectedTime"
organization <- organizationDAO.findOne(user._organization)(GlobalAccessContext)
projectOwner <- userService.findOneCached(project._owner)(GlobalAccessContext)
projectOwnerEmail <- userService.emailFor(projectOwner)(GlobalAccessContext)
mailRecipient <- organizationService.overTimeMailRecipient(organization)(GlobalAccessContext)
} yield {
if (annotationTime >= timeLimit && annotationTime - time.toMillis < timeLimit) {
brainTracing.Mailer ! Send(
defaultMails.overLimitMail(user, project.name, task._id.toString, annotation.id, organization))
brainTracing.Mailer ! Send(defaultMails
.overLimitMail(user, project.name, task._id.toString, annotation.id, List(mailRecipient, projectOwnerEmail)))
}
}

Expand Down