diff --git a/README.md b/README.md index 3b79d06a7..a92e3ca93 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ See docker-compose.yml for details. Swagger UI (see https://springdoc.org/) and OpenAPI v3 description (JSON). You can use the Swagger UI to send requests, if you copy your bearer token over from the browser. So, + 1. Log in to Haitaton. 2. Open the Network tab from developer tools. 3. Open e.g. Omat Hankkeet in Haitaton. @@ -164,6 +165,13 @@ cd haitaton-backend When running locally, the system emails are sent to smtp4dev, which is started as part of the Docker Compose setup. You can access the sent emails by opening http://localhost:3003. +Creation of new emails is done with [mjml.io](https://mjml.io/). Either IntelliJ or Visual Studio Code plugin MJML is +needed. Mjml templates are located in email/. The output email content (html) is in +hanke-service/resources/email/template. + +Once the Mjml template is done, it is converted to html. For example in Visual Studio, type >MJML:Copy HTML. The html +output is the actual email content. + ## File scan Haitaton supports uploading of attachment files. Files are validated with ClamAV anti-malware tool. diff --git a/email/common/attributes.mjml b/email/common/attributes.mjml new file mode 100644 index 000000000..a53df1056 --- /dev/null +++ b/email/common/attributes.mjml @@ -0,0 +1,5 @@ + + + + + diff --git a/email/common/footer-content.mjml b/email/common/footer-content.mjml new file mode 100644 index 000000000..d6c4b2191 --- /dev/null +++ b/email/common/footer-content.mjml @@ -0,0 +1,26 @@ + + + +
+
+ + + + +
+
+ Haitaton +
+
+
+ + © Helsingin + kaupunki 2023 + +
+
diff --git a/email/common/header-content.mjml b/email/common/header-content.mjml new file mode 100644 index 000000000..7a21c746e --- /dev/null +++ b/email/common/header-content.mjml @@ -0,0 +1,22 @@ + + + +
+
+ + + + +
+
+ Haitaton +
+
+
+
+
diff --git a/email/johtoselvitys-valmis.mjml b/email/johtoselvitys-valmis.mjml new file mode 100644 index 000000000..fd7a63902 --- /dev/null +++ b/email/johtoselvitys-valmis.mjml @@ -0,0 +1,109 @@ + + + + + + + + + Haitaton: Johtoselvitys + {{applicationIdentifier}} + + + Hakemanne johtoselvitys {{applicationIdentifier}} on käsitelty. Lataa selvitys Haitattomasta {{baseUrl}}/fi/hakemus/{{applicationId}} + ja tutustu sen sisältöön huolellisesti. + + + Miten onnistuimme? Kerro mielipiteesi + täällä. + + + Ystävällisin terveisin, +
+
+ Helsingin kaupunkiympäristön asukas- ja yrityspalvelut +
+ Alueidenkäyttö ja -valvonta +
+ Johtotietopalvelu +
+ Puh. 09 310 31940 +
+ johtotietopalvelu@hel.fi +
+
+
+ + + + Haitaton: Ledningsutredning {{applicationIdentifier}} + + + Ledningsutredningen {{applicationIdentifier}} ni ansökt om har behandlats. Ladda ned utredningen + från Haitaton + + {{baseUrl}}/sv/ansokan/{{applicationId}} + + och läs + innehållet i den noggrannt. + + + Hur lyckades vi? Ge din åsikt + här. + + + Med vänlig hälsning, +
+
+ Helsingfors stadsmiljös boende- och företagstjänster +
+ Användning och tillsyn av områden +
+ Ledningstjänsten +
+ Tfn 09 310 31940 +
+ johtotietopalvelu@hel.fi +
+
+
+ + + + Haitaton: Cable report {{applicationIdentifier}} + + + Cable report {{applicationIdentifier}} that you requested has been processed. Download the + report + via Haitaton + {{baseUrl}}/en/application/{{applicationId}} + and read it carefully. + + + How did we do? Please share your opinion + here. + + + Kind regards, +
+
+ Helsinki Urban Environment Resident and Business Services +
+ Land Use and Monitoring +
+ Cable Location Service +
+ Tel. +358 9 310 31940 +
+ johtotietopalvelu@hel.fi +
+
+
+
+ +
+
diff --git a/email/kayttaja-lisatty-hakemus.mjml b/email/kayttaja-lisatty-hakemus.mjml new file mode 100644 index 000000000..891aa8ba2 --- /dev/null +++ b/email/kayttaja-lisatty-hakemus.mjml @@ -0,0 +1,44 @@ + + + + + + + + + + Haitaton: Sinut on lisätty hakemukselle {{applicationIdentifier}} + + + {{inviterInfo}} on tehnyt {{applicationType}} ({{applicationIdentifier}}) hankkeella + {{hankeTunnus}}, ja lähettänyt sen käsittelyyn. Sinut on määritetty hakemuksella rooliin + {{recipientRole}}. Tarkastele hakemusta Haitattomassa + {{baseUrl}} + + + Löydät hakemuksesi siirtymällä etusivulta Omiin hankkeisiin ja valitsemalla sieltä oikean + hankkeen. + Avaa listanäkymässä hankkeen tiedot ja valitse "Näytä hankkeen hakemukset". + + + Haitaton on Helsingin kaupungin asiointijärjestelmä yleisille alueille sijoittuvien hankkeiden + edistämiseen ja haittojen hallintaan. + + + Tämä on automaattinen sähköposti – älä vastaa tähän viestiin. + + + Ystävällisin terveisin, +
+
+ Helsingin kaupungin kaupunkiympäristön toimiala +
+ Haitaton-asiointi +
+ haitaton@hel.fi +
+
+
+ +
+
diff --git a/email/kayttaja-lisatty-hanke.mjml b/email/kayttaja-lisatty-hanke.mjml new file mode 100644 index 000000000..c71973d34 --- /dev/null +++ b/email/kayttaja-lisatty-hanke.mjml @@ -0,0 +1,43 @@ + + + + + + + + + + Haitaton: Sinut on lisätty hankkeelle {{hankeTunnus}} + + + {{inviterInfo}} lisäsi sinut hankkeelle {{hankeNimi}} ({{hankeTunnus}}). Tunnistaudu + Haitattomaan alla olevan linkin kautta. + + + Haitattomaan + + + Löydät hankkeesi Haitattoman etusivun Omat hankkeet -osion alta hankkeen nimellä. + + + Haitaton on Helsingin kaupungin asiointijärjestelmä yleisille alueille sijoittuvien hankkeiden + edistämiseen ja haittojen hallintaan. + + + Tämä on automaattinen sähköposti – älä vastaa tähän viestiin. + + + Ystävällisin terveisin, +
+
+ Helsingin kaupungin kaupunkiympäristön toimiala +
+ Haitaton-asiointi +
+ haitaton@hel.fi +
+
+
+ +
+
diff --git a/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/email/EmailSenderServiceITest.kt b/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/email/EmailSenderServiceITest.kt index 405b3ee6a..5e1961bb3 100644 --- a/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/email/EmailSenderServiceITest.kt +++ b/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/email/EmailSenderServiceITest.kt @@ -10,10 +10,13 @@ import assertk.assertions.startsWith import com.icegreen.greenmail.configuration.GreenMailConfiguration import com.icegreen.greenmail.junit5.GreenMailExtension import com.icegreen.greenmail.util.ServerSetupTest +import fi.hel.haitaton.hanke.ContactType import fi.hel.haitaton.hanke.DatabaseTest +import fi.hel.haitaton.hanke.application.ApplicationType import fi.hel.haitaton.hanke.firstReceivedMessage import jakarta.mail.internet.MimeMessage import jakarta.mail.internet.MimeMultipart +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import org.springframework.beans.factory.annotation.Autowired @@ -22,7 +25,9 @@ import org.springframework.test.context.ActiveProfiles import org.testcontainers.junit.jupiter.Testcontainers private const val TEST_EMAIL = "test@test.test" +private const val HAITATON_NO_REPLY = "no-reply@hel.fi" private const val APPLICATION_IDENTIFIER = "JS2300001" +private const val DEFAULT_INVITER_NAME = "Kalle Kutsuja" @Testcontainers @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -39,54 +44,202 @@ class EmailSenderServiceITest : DatabaseTest() { @Autowired lateinit var emailSenderService: EmailSenderService - @Test - fun `sendJohtoselvitysCompleteEmail sends email with correct recipient`() { - emailSenderService.sendJohtoselvitysCompleteEmail(TEST_EMAIL, 13L, APPLICATION_IDENTIFIER) + @Nested + inner class JohtoSelvitysComplete { + @Test + fun `sendJohtoselvitysCompleteEmail sends email with correct recipient`() { + emailSenderService.sendJohtoselvitysCompleteEmail( + TEST_EMAIL, + 13L, + APPLICATION_IDENTIFIER + ) - val email = greenMail.firstReceivedMessage() - assertThat(email.allRecipients).hasSize(1) - assertThat(email.allRecipients[0].toString()).isEqualTo(TEST_EMAIL) - } + val email = greenMail.firstReceivedMessage() + assertThat(email.allRecipients).hasSize(1) + assertThat(email.allRecipients[0].toString()).isEqualTo(TEST_EMAIL) + } - @Test - fun `sendJohtoselvitysCompleteEmail sends email with sender from properties`() { - emailSenderService.sendJohtoselvitysCompleteEmail(TEST_EMAIL, 13L, APPLICATION_IDENTIFIER) + @Test + fun `sendJohtoselvitysCompleteEmail sends email with sender from properties`() { + emailSenderService.sendJohtoselvitysCompleteEmail( + TEST_EMAIL, + 13L, + APPLICATION_IDENTIFIER + ) - val email = greenMail.firstReceivedMessage() - assertThat(email.from).hasSize(1) - assertThat(email.from[0].toString()).isEqualTo("no-reply@hel.fi") - } + val email = greenMail.firstReceivedMessage() + assertThat(email.from).hasSize(1) + assertThat(email.from[0].toString()).isEqualTo(HAITATON_NO_REPLY) + } - @Test - fun `sendJohtoselvitysCompleteEmail sends email with correct subject`() { - emailSenderService.sendJohtoselvitysCompleteEmail(TEST_EMAIL, 13L, APPLICATION_IDENTIFIER) + @Test + fun `sendJohtoselvitysCompleteEmail sends email with correct subject`() { + emailSenderService.sendJohtoselvitysCompleteEmail( + TEST_EMAIL, + 13L, + APPLICATION_IDENTIFIER + ) + + val email = greenMail.firstReceivedMessage() + assertThat(email.subject) + .isEqualTo( + "Johtoselvitys JS2300001 / Ledningsutredning JS2300001 / Cable report JS2300001" + ) + } - val email = greenMail.firstReceivedMessage() - assertThat(email.subject) - .isEqualTo( - "Johtoselvitys JS2300001 / Ledningsutredning JS2300001 / Cable report JS2300001" + @Test + fun `sendJohtoselvitysCompleteEmail sends email with parametrized hybrid body`() { + emailSenderService.sendJohtoselvitysCompleteEmail( + TEST_EMAIL, + 13L, + APPLICATION_IDENTIFIER ) + + val email = greenMail.firstReceivedMessage() + val (textBody, htmlBody) = getBodiesFromHybridEmail(email) + assertThat(textBody).all { + contains(APPLICATION_IDENTIFIER) + contains("http://localhost:3001/fi/hakemus/13") + contains("http://localhost:3001/sv/ansokan/13") + contains("http://localhost:3001/en/application/13") + } + // Compress all whitespace into single spaces so that they don't interfere with + // matching. + val squashedHtmlBody = htmlBody.replace("\\s+".toRegex(), " ") + assertThat(squashedHtmlBody).all { + contains(APPLICATION_IDENTIFIER) + contains("""""") + contains("""""") + contains("""""") + } + } } - @Test - fun `sendJohtoselvitysCompleteEmail sends email with parametrized hybrid body`() { - emailSenderService.sendJohtoselvitysCompleteEmail(TEST_EMAIL, 13L, APPLICATION_IDENTIFIER) - - val email = greenMail.firstReceivedMessage() - val (textBody, htmlBody) = getBodiesFromHybridEmail(email) - assertThat(textBody).all { - contains(APPLICATION_IDENTIFIER) - contains("http://localhost:3001/fi/hakemus/13") - contains("http://localhost:3001/sv/ansokan/13") - contains("http://localhost:3001/en/application/13") + @Nested + inner class HankeInvitation { + + @Test + fun `sendHankeInvitationEmail sends email with correct recipient`() { + emailSenderService.sendHankeInvitationEmail(hankeInvitationData()) + + val email = greenMail.firstReceivedMessage() + assertThat(email.allRecipients).hasSize(1) + assertThat(email.allRecipients[0].toString()).isEqualTo(TEST_EMAIL) + } + + @Test + fun `sendHankeInvitationEmail sends email with sender from properties`() { + emailSenderService.sendHankeInvitationEmail(hankeInvitationData()) + + val email = greenMail.firstReceivedMessage() + assertThat(email.from).hasSize(1) + assertThat(email.from[0].toString()).isEqualTo(HAITATON_NO_REPLY) + } + + @Test + fun `sendHankeInvitationEmail sends email with correct subject`() { + emailSenderService.sendHankeInvitationEmail(hankeInvitationData()) + + val email = greenMail.firstReceivedMessage() + assertThat(email.subject).isEqualTo("Sinut on lisätty hankkeelle HAI24-1") } - // Compress all whitespace into single spaces so that they don't interfere with matching. - val squashedHtmlBody = htmlBody.replace("\\s+".toRegex(), " ") - assertThat(squashedHtmlBody).all { - contains(APPLICATION_IDENTIFIER) - contains("""""") - contains("""""") - contains("""""") + + @Test + fun `sendHankeInvitationEmail sends email with parametrized hybrid body`() { + val data = hankeInvitationData() + + emailSenderService.sendHankeInvitationEmail(data) + + val email = greenMail.firstReceivedMessage() + val (textBody, htmlBody) = getBodiesFromHybridEmail(email) + assertThat(textBody).all { + contains("${data.inviterName} (${data.inviterEmail}) lisäsi sinut") + contains("hankkeelle ${data.hankeNimi} (${data.hankeTunnus}).") + contains("http://localhost:3001/${data.invitationToken}") + } + assertThat(htmlBody).all { + contains("${data.inviterName} (${data.inviterEmail})") + contains("hankkeelle ${data.hankeNimi} (${data.hankeTunnus}).") + contains("""""") + } + } + + @Test + fun `sendHankeInvitationEmail handles input without inviter name`() { + val data = hankeInvitationData(inviterName = null) + + emailSenderService.sendHankeInvitationEmail(data) + + val email = greenMail.firstReceivedMessage() + val (textBody, htmlBody) = getBodiesFromHybridEmail(email) + assertThat(textBody).contains("Asioija ${data.inviterEmail}") + assertThat(htmlBody).contains("Asioija ${data.inviterEmail}") + } + } + + @Nested + inner class ApplicationInvitation { + @Test + fun `sendApplicationInvitationEmail sends email with correct recipient`() { + emailSenderService.sendApplicationInvitationEmail(applicationInvitationData()) + + val email = greenMail.firstReceivedMessage() + assertThat(email.allRecipients).hasSize(1) + assertThat(email.allRecipients[0].toString()).isEqualTo(TEST_EMAIL) + } + + @Test + fun `sendApplicationInvitationEmail sends email with sender from properties`() { + emailSenderService.sendApplicationInvitationEmail(applicationInvitationData()) + + val email = greenMail.firstReceivedMessage() + assertThat(email.from).hasSize(1) + assertThat(email.from[0].toString()).isEqualTo(HAITATON_NO_REPLY) + } + + @Test + fun `sendApplicationInvitationEmail sends email with correct subject`() { + val data = applicationInvitationData() + emailSenderService.sendApplicationInvitationEmail(data) + + val email = greenMail.firstReceivedMessage() + assertThat(email.subject) + .isEqualTo("Sinut on lisätty hakemukselle ${data.applicationIdentifier}") + } + + @Test + fun `sendApplicationInvitationEmail sends email with parametrized hybrid body`() { + val data = applicationInvitationData() + + emailSenderService.sendApplicationInvitationEmail(data) + + val email = greenMail.firstReceivedMessage() + val (textBody, htmlBody) = getBodiesFromHybridEmail(email) + assertThat(textBody).all { + contains("${data.inviterName} (${data.inviterEmail}) on") + contains("tehnyt johtoselvityshakemuksen (${data.applicationIdentifier})") + contains("hankkeella ${data.hankeTunnus}") + contains("rooliin ${data.roleType.text()}.") + contains("Tarkastele hakemusta Haitattomassa: http://localhost:3001") + } + assertThat(htmlBody).all { + contains("${data.inviterName} (${data.inviterEmail})") + contains("johtoselvityshakemuksen (${data.applicationIdentifier})") + contains("rooliin ${data.roleType.text()}") + contains("""""") + } + } + + @Test + fun `sendApplicationInvitationEmail handles input without inviter name`() { + val data = applicationInvitationData(inviterName = null) + + emailSenderService.sendApplicationInvitationEmail(data) + + val email = greenMail.firstReceivedMessage() + val (textBody, htmlBody) = getBodiesFromHybridEmail(email) + assertThat(textBody).contains("Asioija ${data.inviterEmail} on tehnyt") + assertThat(htmlBody).contains("Asioija ${data.inviterEmail} on tehnyt") } } @@ -116,4 +269,27 @@ class EmailSenderServiceITest : DatabaseTest() { .map { i -> mp3.getBodyPart(i).content.toString() } return Pair(bodies[0], bodies[1]) } + + private fun hankeInvitationData(inviterName: String? = DEFAULT_INVITER_NAME) = + HankeInvitationData( + inviterName = inviterName, + inviterEmail = "kalle.kutsuja@test.fi", + recipientEmail = TEST_EMAIL, + hankeTunnus = "HAI24-1", + hankeNimi = "Mannerheimintien liikenneuudistus", + invitationToken = "MgtzRbcPsvoKQamnaSxCnmW7", + ) + + private fun applicationInvitationData(inviterName: String? = DEFAULT_INVITER_NAME) = + ApplicationInvitationData( + inviterName = inviterName, + inviterEmail = "kalle.kutsuja@test.fi", + recipientEmail = TEST_EMAIL, + applicationType = ApplicationType.CABLE_REPORT, + applicationIdentifier = APPLICATION_IDENTIFIER, + hankeTunnus = "HAI24-1", + roleType = ContactType.RAKENNUTTAJA, + ) + + private fun ContactType.text() = toString().lowercase() } diff --git a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/email/EmailSenderService.kt b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/email/EmailSenderService.kt index ad57777d4..88d6ed3d5 100644 --- a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/email/EmailSenderService.kt +++ b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/email/EmailSenderService.kt @@ -1,5 +1,7 @@ package fi.hel.haitaton.hanke.email +import fi.hel.haitaton.hanke.ContactType +import fi.hel.haitaton.hanke.application.ApplicationType import fi.hel.haitaton.hanke.getResource import jakarta.mail.internet.MimeMessage import mu.KotlinLogging @@ -24,6 +26,31 @@ data class EmailFilterProperties( @Delimiter(";") val allowList: List, ) +data class ApplicationInvitationData( + val inviterName: String?, + val inviterEmail: String, + val recipientEmail: String, + val applicationType: ApplicationType, + val applicationIdentifier: String, + val hankeTunnus: String, + val roleType: ContactType, +) + +data class HankeInvitationData( + val inviterName: String?, + val inviterEmail: String, + val recipientEmail: String, + val hankeTunnus: String, + val hankeNimi: String, + val invitationToken: String, +) + +enum class EmailTemplate(val value: String) { + CABLE_REPORT_DONE("johtoselvitys-valmis"), + INVITATION_HANKE("kayttaja-lisatty-hanke"), + INVITATION_APPLICATION("kayttaja-lisatty-hakemus") +} + @Service class EmailSenderService( private val mailSender: JavaMailSender, @@ -36,24 +63,59 @@ class EmailSenderService( applicationIdentifier: String, ) { logger.info { "Sending email for completed johtoselvitys $applicationIdentifier" } + val templateData = mapOf( "baseUrl" to emailConfig.baseUrl, "applicationId" to applicationId.toString(), "applicationIdentifier" to applicationIdentifier, ) - sendHybridEmail(to, "johtoselvitys-valmis", templateData) + + sendHybridEmail(to, EmailTemplate.CABLE_REPORT_DONE, templateData) + } + + fun sendHankeInvitationEmail(data: HankeInvitationData) { + logger.info { "Sending invitation email for Hanke" } + + val templateData = + mapOf( + "baseUrl" to emailConfig.baseUrl, + "inviterInfo" to defineInviterInfo(data.inviterName, data.inviterEmail), + "hankeTunnus" to data.hankeTunnus, + "hankeNimi" to data.hankeNimi, + "invitationToken" to data.invitationToken, + ) + + sendHybridEmail(data.recipientEmail, EmailTemplate.INVITATION_HANKE, templateData) + } + + fun sendApplicationInvitationEmail(data: ApplicationInvitationData) { + logger.info { "Sending invitation email for application" } + + val applicationTypeText = convertApplicationTypeFinnish(data.applicationType) + + val templateData = + mapOf( + "baseUrl" to emailConfig.baseUrl, + "inviterInfo" to defineInviterInfo(data.inviterName, data.inviterEmail), + "applicationType" to applicationTypeText, + "applicationIdentifier" to data.applicationIdentifier, + "hankeTunnus" to data.hankeTunnus, + "recipientRole" to data.roleType.toString().lowercase(), + ) + + sendHybridEmail(data.recipientEmail, EmailTemplate.INVITATION_APPLICATION, templateData) } - private fun sendHybridEmail(to: String, template: String, templateData: Map) { + private fun sendHybridEmail(to: String, context: EmailTemplate, data: Map) { if (emailConfig.filter.use && emailNotAllowed(to)) { logger.info { "Email recipient not allowed, ignoring email." } return } - val textBody = parseTemplate("/email/template/$template.text.mustache", templateData) - val htmlBody = parseTemplate("/email/template/$template.html.mustache", templateData) - val subject = - parseTemplate("/email/template/$template.subject.mustache", templateData).trimEnd() + val basePath = "/email/template" + val textBody = parseTemplate("$basePath/${context.value}.text.mustache", data) + val htmlBody = parseTemplate("$basePath/${context.value}.html.mustache", data) + val subject = parseTemplate("$basePath/${context.value}.subject.mustache", data).trimEnd() val mimeMessage: MimeMessage = mailSender.createMimeMessage() val helper = MimeMessageHelper(mimeMessage, true, "utf-8") @@ -65,6 +127,14 @@ class EmailSenderService( mailSender.send(mimeMessage) } + private fun defineInviterInfo(name: String?, email: String): String = + if (name.isNullOrBlank()) "Asioija $email" else "$name ($email)" + + private fun convertApplicationTypeFinnish(type: ApplicationType): String = + when (type) { + ApplicationType.CABLE_REPORT -> "johtoselvityshakemuksen" + } + private fun emailNotAllowed(email: String) = !emailConfig.filter.allowList.contains(email) private fun parseTemplate(path: String, contextObject: Any): String = diff --git a/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.html.mustache b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.html.mustache new file mode 100644 index 000000000..c0256c48b --- /dev/null +++ b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.html.mustache @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+
+
+ + +
+
+ Haitaton +
+
+ + + +
+
+
+
+ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+ Haitaton: Sinut on lisätty hakemukselle {{applicationIdentifier}} +
+
+ {{inviterInfo}} on tehnyt {{applicationType}} ({{applicationIdentifier}}) hankkeella {{hankeTunnus}}, ja lähettänyt sen käsittelyyn. Sinut on määritetty hakemuksella rooliin {{recipientRole}}. Tarkastele hakemusta Haitattomassa {{baseUrl}} +
+
+ Löydät hakemuksesi siirtymällä etusivulta Omiin hankkeisiin ja valitsemalla sieltä oikean hankkeen. Avaa listanäkymässä hankkeen tiedot ja valitse "Näytä hankkeen hakemukset". +
+
+ Haitaton on Helsingin kaupungin asiointijärjestelmä yleisille alueille sijoittuvien hankkeiden edistämiseen ja haittojen hallintaan. +
+
+ Tämä on automaattinen sähköposti – älä vastaa tähän viestiin. +
+
+ Ystävällisin terveisin,

Helsingin kaupungin kaupunkiympäristön toimiala
Haitaton-asiointi
haitaton@hel.fi +
+
+
+
+ + + + + + +
+
+
+
+ + +
+
+ Haitaton +
+
+ + + + + + + + + +

+
+ © Helsingin kaupunki 2023 +
+
+
+
+ + diff --git a/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.subject.mustache b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.subject.mustache new file mode 100644 index 000000000..849b678c6 --- /dev/null +++ b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.subject.mustache @@ -0,0 +1 @@ +Sinut on lisätty hakemukselle {{applicationIdentifier}} diff --git a/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.text.mustache b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.text.mustache new file mode 100644 index 000000000..0ac1350ba --- /dev/null +++ b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hakemus.text.mustache @@ -0,0 +1,18 @@ +Haitaton: Sinut on lisätty hakemukselle {{applicationIdentifier}} + +{{inviterInfo}} on tehnyt {{applicationType}} ({{applicationIdentifier}}) hankkeella {{hankeTunnus}}, ja lähettänyt sen käsittelyyn. Sinut on määritetty hakemuksella rooliin {{recipientRole}}. Tarkastele hakemusta Haitattomassa: {{baseUrl}} + +Löydät hakemuksesi siirtymällä etusivulta Omiin hankkeisiin ja valitsemalla sieltä oikean hankkeen. Avaa listanäkymässä hankkeen tiedot ja valitse “Näytä hankkeen hakemukset”. + +Haitaton on Helsingin kaupungin asiointijärjestelmä yleisille alueille sijoittuvien hankkeiden edistämiseen ja haittojen hallintaan. + +Tämä on automaattinen sähköposti – älä vastaa tähän viestiin. + + +Ystävällisin terveisin, + +Helsingin kaupungin kaupunkiympäristön toimiala + +Haitaton-asiointi + +haitaton@hel.fi diff --git a/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.html.mustache b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.html.mustache new file mode 100644 index 000000000..8eb44fbc1 --- /dev/null +++ b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.html.mustache @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+
+
+ + +
+
+ Haitaton +
+
+ + + +
+
+
+
+ + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Haitaton: Sinut on lisätty hankkeelle {{hankeTunnus}} +
+
+ {{inviterInfo}} lisäsi sinut hankkeelle {{hankeNimi}} ({{hankeTunnus}}). Tunnistaudu Haitattomaan alla olevan linkin kautta. +
+
+
+ Löydät hankkeesi Haitattoman etusivun Omat hankkeet -osion alta hankkeen nimellä. +
+
+ Haitaton on Helsingin kaupungin asiointijärjestelmä yleisille alueille sijoittuvien hankkeiden edistämiseen ja haittojen hallintaan. +
+
+ Tämä on automaattinen sähköposti – älä vastaa tähän viestiin. +
+
+ Ystävällisin terveisin,

Helsingin kaupungin kaupunkiympäristön toimiala
Haitaton-asiointi
haitaton@hel.fi +
+
+
+
+ + + + + + +
+
+
+
+ + +
+
+ Haitaton +
+
+ + + + + + + + + +

+
+ © Helsingin kaupunki 2023 +
+
+
+
+ + diff --git a/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.subject.mustache b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.subject.mustache new file mode 100644 index 000000000..75becad00 --- /dev/null +++ b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.subject.mustache @@ -0,0 +1 @@ +Sinut on lisätty hankkeelle {{hankeTunnus}} diff --git a/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.text.mustache b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.text.mustache new file mode 100644 index 000000000..f099722c6 --- /dev/null +++ b/services/hanke-service/src/main/resources/email/template/kayttaja-lisatty-hanke.text.mustache @@ -0,0 +1,15 @@ +Haitaton: Sinut on lisätty hankkeelle {{hankeTunnus}} + +{{inviterInfo}} lisäsi sinut hankkeelle {{hankeNimi}} ({{hankeTunnus}}). Tunnistaudu Haitattomaan alla olevan linkin kautta. +{{baseUrl}}/{{invitationToken}} + +Löydät hankkeesi Haitattoman etusivun Omat hankkeet -osion alta hankkeen nimellä. + +Haitaton on Helsingin kaupungin asiointijärjestelmä yleisille alueille sijoittuvien hankkeiden edistämiseen ja haittojen hallintaan. + +Tämä on automaattinen sähköposti – älä vastaa tähän viestiin. + +Ystävällisin terveisin, +Helsingin kaupungin kaupunkiympäristön toimiala +Haitaton-asiointi +haitaton@hel.fi