@@ -0,0 +1,42 @@ | |||
package com.ffii.core.utils; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.springframework.context.i18n.LocaleContextHolder; | |||
import java.util.Locale; | |||
/** this utils follow "-" standard ("zh-TW", no "zh_TW") */ | |||
public abstract class LocaleUtils { | |||
public static Locale getLocale() { | |||
return LocaleContextHolder.getLocale(); | |||
} | |||
public static String getLocaleStr() { | |||
return toLocaleStr(LocaleContextHolder.getLocale()); | |||
} | |||
public static String toLocaleStr(Locale locale) { | |||
String language = locale.getLanguage(); | |||
String country = locale.getCountry(); | |||
if (StringUtils.isNotBlank(country)) { | |||
return language + "-" + country; | |||
} else { | |||
return language; | |||
} | |||
} | |||
/** | |||
* @param localeStr | |||
* e.g. zh-TW | |||
*/ | |||
public static Locale from(String localeStr) { | |||
String[] localeArr = localeStr.split("-"); | |||
if (localeArr.length == 1) { | |||
return new Locale(localeArr[0]); | |||
} else { | |||
return new Locale(localeArr[0], localeArr[1]); | |||
} | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.ffii.tsms.modules.common | |||
import com.ffii.tsms.modules.settings.service.SettingsService | |||
import org.apache.commons.lang3.StringUtils | |||
open class MailSMTP(settingsService: SettingsService) { | |||
open var host: String? = null | |||
open var port: Int? = null | |||
open var username: String? = null | |||
open var password: String? = null | |||
open var auth: Boolean? = null | |||
init { | |||
this.host = settingsService.getString(SettingNames.MAIL_SMTP_HOST) | |||
this.port = settingsService.getInt(SettingNames.MAIL_SMTP_PORT) | |||
this.username = settingsService.getString(SettingNames.MAIL_SMTP_USERNAME) | |||
this.password = settingsService.getString(SettingNames.MAIL_SMTP_PASSWORD) | |||
this.auth = settingsService.getBoolean(SettingNames.MAIL_SMTP_AUTH) | |||
} | |||
final override fun equals(other: Any?): Boolean { | |||
if (other == null || other !is MailSMTP) return false | |||
val o = other as MailSMTP | |||
if (StringUtils.equals( | |||
this.host, | |||
o.host | |||
) && this.port == o.port && | |||
StringUtils.equals(this.username, o.username) && | |||
StringUtils.equals(this.password, o.password) | |||
) { | |||
return true | |||
} | |||
return false | |||
} | |||
} |
@@ -40,6 +40,14 @@ public abstract class SettingNames { | |||
public static final String MAIL_SMTP_AUTH = "MAIL.smtp.auth"; | |||
public static final String TIMESHEET_MAIL_CC = "TIMESHEET.mail.cc"; | |||
public static final String TIMESHEET_MAIL_BCC = "TIMESHEET.mail.bcc"; | |||
public static final String TIMESHEET_MAIL_SUBJECT = "TIMESHEET.mail.subject"; | |||
public static final String TIMESHEET_MAIL_TEMPLATE = "TIMESHEET.mail.template"; | |||
public static final String JS_VERSION = "JS.version"; | |||
public static final String REPORT_DAILYMAINT_RECIPIENTS_MECH = "REPORT.dailyMaint.recipients.mech"; | |||
@@ -0,0 +1,29 @@ | |||
package com.ffii.tsms.modules.common.holiday.models | |||
import com.squareup.moshi.Json | |||
import com.squareup.moshi.JsonClass | |||
@JsonClass(generateAdapter = true) | |||
data class VCalendarRequest( | |||
val vcalendar: List<VCalendar> | |||
) | |||
@JsonClass(generateAdapter = true) | |||
data class VCalendar( | |||
val prodid: String, | |||
val version: String, | |||
val calscale: String, | |||
@Json(name = "x-wr-timezone") val xWrTimezone: String, | |||
@Json(name = "x-wr-calname") val xWrCalname: String, | |||
@Json(name = "x-wr-caldesc") val xWrCaldesc: String, | |||
val vevent: List<VEvent> | |||
) | |||
@JsonClass(generateAdapter = true) | |||
data class VEvent( | |||
val dtstart: List<Any>, // Can be a String or a Map | |||
val dtend: List<Any>, // Can be a String or a Map | |||
val transp: String, | |||
val uid: String, | |||
val summary: String | |||
) |
@@ -0,0 +1,8 @@ | |||
package com.ffii.tsms.modules.common.holiday.models | |||
import java.time.LocalDate | |||
data class CommonHolidayResponse( | |||
val date: LocalDate, | |||
val name: String, | |||
) |
@@ -0,0 +1,60 @@ | |||
package com.ffii.tsms.modules.common.holiday.service | |||
import com.ffii.tsms.modules.common.holiday.models.CommonHolidayResponse | |||
import com.ffii.tsms.modules.common.holiday.models.VCalendarRequest | |||
import com.squareup.moshi.Moshi | |||
import com.squareup.moshi.adapter | |||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory | |||
import okhttp3.Request | |||
import okhttp3.OkHttpClient | |||
import okhttp3.ResponseBody | |||
import org.apache.commons.logging.Log | |||
import org.apache.commons.logging.LogFactory | |||
import org.springframework.stereotype.Service | |||
import java.io.IOException | |||
import java.time.LocalDate | |||
import java.time.format.DateTimeFormatter | |||
@Service | |||
open class HolidayService() { | |||
private val logger: Log = LogFactory.getLog(javaClass) | |||
private val HOLIDAY_URL = "https://www.1823.gov.hk/common/ical/tc.json" | |||
open fun commonHolidayList(): List<CommonHolidayResponse> { | |||
val client = OkHttpClient() | |||
val request = Request.Builder() | |||
.url(HOLIDAY_URL) | |||
.build() | |||
client.newCall(request).execute().use { response -> | |||
if (!response.isSuccessful) throw IOException("Unexpected code: $response") | |||
// logger.info(response.body.toString()) | |||
val moshi = Moshi.Builder() | |||
.add(KotlinJsonAdapterFactory()) | |||
.build() | |||
val jsonAdapter = moshi.adapter(VCalendarRequest::class.java) | |||
val vCalendarResponse = jsonAdapter.fromJson(response.body?.string() ?: "") | |||
val vCalendar = vCalendarResponse?.vcalendar?.get(0) | |||
if (vCalendar != null) { | |||
val commonHolidayResponse = mutableListOf<CommonHolidayResponse>() | |||
val formatter = DateTimeFormatter.ofPattern("yyyyMMdd") | |||
vCalendar.vevent.forEach {vevent -> | |||
commonHolidayResponse.add( | |||
CommonHolidayResponse( | |||
date = LocalDate.parse(vevent.dtstart[0] as String, formatter), | |||
name = vevent.summary | |||
) | |||
) | |||
} | |||
return commonHolidayResponse | |||
} else { | |||
return emptyList() | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
package com.ffii.tsms.modules.common.holiday.web | |||
import com.ffii.tsms.modules.common.holiday.models.CommonHolidayResponse | |||
import com.ffii.tsms.modules.common.holiday.service.HolidayService | |||
import org.springframework.web.bind.annotation.GetMapping | |||
import org.springframework.web.bind.annotation.RequestMapping | |||
import org.springframework.web.bind.annotation.RestController | |||
@RestController | |||
@RequestMapping("/holiday") | |||
class HolidayController(private val holidayService: HolidayService) { | |||
@GetMapping("/common") | |||
fun commonHolidays(): List<CommonHolidayResponse>{ | |||
return holidayService.commonHolidayList() | |||
} | |||
} |
@@ -0,0 +1,235 @@ | |||
package com.ffii.tsms.modules.common.mail.pojo | |||
import jakarta.mail.internet.InternetAddress | |||
open class MailRequest() { | |||
val PRIORITY_HIGHEST: Int = 1 | |||
val PRIORITY_HIGH: Int = 2 | |||
val PRIORITY_NORMAL: Int = 3 | |||
val PRIORITY_LOW: Int = 4 | |||
val PRIORITY_LOWEST: Int = 5 | |||
open var from: InternetAddress? = null | |||
open var to: List<InternetAddress> = mutableListOf() | |||
open var subject: String? = null | |||
open var template: String? = null | |||
open var templateContent: String? = null | |||
open var args: Map<String, Any?> = mapOf() | |||
open var priority: Int? = null | |||
open var replyTo: InternetAddress? = null | |||
open var cc: List<InternetAddress> = mutableListOf() | |||
open var bcc: List<InternetAddress> = mutableListOf() | |||
open var attachments: Map<String, ByteArray> = mapOf() | |||
open fun builder(): Builder { | |||
return Builder() | |||
} | |||
open fun addAttachment(attachmentFilename: String, byteArray: ByteArray) { | |||
this.attachments += Pair(attachmentFilename, byteArray) | |||
} | |||
open fun addTo(to: InternetAddress) { | |||
this.to += to | |||
} | |||
open fun addCc(cc: InternetAddress) { | |||
this.cc += cc | |||
} | |||
open fun addBcc(bcc: InternetAddress) { | |||
this.bcc += bcc | |||
} | |||
open class Builder() { | |||
open var mailRequest:MailRequest = MailRequest() | |||
init { | |||
this.mailRequest = MailRequest() | |||
} | |||
open fun build(): MailRequest { | |||
return this.mailRequest | |||
} | |||
open fun addAttachment(attachmentFilename: String, byteArray: ByteArray): Builder { | |||
mailRequest.addAttachment(attachmentFilename, byteArray) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addToInternetAddress") | |||
open fun addTo(to: InternetAddress): Builder { | |||
mailRequest.addTo(to) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addToString") | |||
open fun addTo(to: String): Builder { | |||
mailRequest.addTo(InternetAddress(to)) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addCcInternetAddress") | |||
open fun addCc(cc: InternetAddress): Builder { | |||
mailRequest.addCc(cc) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addCcString") | |||
open fun addCc(cc: String): Builder { | |||
mailRequest.addCc(InternetAddress(cc)) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addCcStringList") | |||
open fun addCc(cc: List<String>): Builder { | |||
cc.forEach { | |||
if (it.isNotEmpty()) { | |||
mailRequest.addCc(InternetAddress(it)) | |||
} | |||
} | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addBccInternetAddress") | |||
open fun addBcc(bcc: InternetAddress): Builder { | |||
mailRequest.addBcc(bcc) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addBccString") | |||
open fun addBcc(bcc: String): Builder { | |||
mailRequest.addBcc(InternetAddress(bcc)) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("addBccStringList") | |||
open fun addBcc(bcc: List<String>): Builder { | |||
bcc.forEach { | |||
if (it.isNotEmpty()) { | |||
mailRequest.addBcc(InternetAddress(it)) | |||
} | |||
} | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("fromInternetAddress") | |||
open fun from(from: InternetAddress): Builder { | |||
mailRequest.from = from | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("fromString") | |||
open fun from(from: String): Builder { | |||
mailRequest.from = InternetAddress(from) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("toListInternetAddress") | |||
open fun to(to: List<InternetAddress>): Builder { | |||
mailRequest.to = to | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("toListString") | |||
open fun to(to: List<String>): Builder { | |||
mailRequest.to = to.map { InternetAddress(it) } | |||
return this | |||
} | |||
open fun subject(subject: String?): Builder { | |||
mailRequest.subject = subject | |||
return this | |||
} | |||
open fun template(template: String?): Builder { | |||
mailRequest.template = template | |||
return this | |||
} | |||
open fun templateContent(templateContent: String?): Builder { | |||
mailRequest.templateContent = templateContent | |||
return this | |||
} | |||
open fun args(args: Map<String, Any?>): Builder { | |||
mailRequest.args = args | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("replyToInternetAddress") | |||
open fun replyTo(replyTo: InternetAddress?): Builder { | |||
mailRequest.replyTo = replyTo | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("replyToString") | |||
open fun replyTo(replyTo: String?): Builder { | |||
mailRequest.replyTo = InternetAddress(replyTo) | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("ccListInternetAddress") | |||
open fun cc(cc: List<InternetAddress>): Builder { | |||
mailRequest.cc = cc | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("ccListString") | |||
open fun cc(cc: List<String>): Builder { | |||
mailRequest.cc = cc.map { InternetAddress(it) } | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("bccListInternetAddress") | |||
open fun bcc(bcc: List<InternetAddress>): Builder { | |||
mailRequest.bcc = bcc | |||
return this | |||
} | |||
@Suppress("INAPPLICABLE_JVM_NAME") | |||
@JvmName("bccListString") | |||
open fun bcc(bcc: List<String>): Builder { | |||
mailRequest.bcc = bcc.map { InternetAddress(it) } | |||
return this | |||
} | |||
open fun attachments(attachments: Map<String, ByteArray>): Builder { | |||
mailRequest.attachments = attachments | |||
return this | |||
} | |||
open fun priority(priority: Int?): Builder { | |||
mailRequest.priority = priority | |||
return this | |||
} | |||
} | |||
} |
@@ -0,0 +1,75 @@ | |||
package com.ffii.tsms.modules.common.mail.service | |||
import com.ffii.tsms.modules.common.SecurityUtils | |||
import com.ffii.tsms.modules.common.SettingNames | |||
import com.ffii.tsms.modules.common.mail.pojo.MailRequest | |||
import com.ffii.tsms.modules.settings.service.SettingsService | |||
import com.ffii.tsms.modules.user.service.UserService | |||
import jakarta.mail.internet.InternetAddress | |||
import org.apache.commons.logging.Log | |||
import org.apache.commons.logging.LogFactory | |||
import org.springframework.stereotype.Service | |||
import java.time.LocalDate | |||
import java.time.format.DateTimeFormatter | |||
@Service | |||
open class MailReminderService ( | |||
val mailService: MailService, | |||
val userService: UserService, | |||
val settingsService: SettingsService, | |||
) { | |||
protected val logger: Log = LogFactory.getLog(javaClass) | |||
private val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") | |||
// @Scheduled(cron = "0 0 6 * * ?") // Runs at 06:00 AM every day | |||
open fun sendTimesheetReminder() { | |||
val inputDate = LocalDate.now().minusDays(4).format(dateFormat) | |||
val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value | |||
val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value | |||
val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") | |||
val bcc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_BCC).orElseThrow().value.split(",") | |||
val mailRequest = MailRequest.Builder() | |||
.subject(subject) | |||
.template("mail/TimesheetNotification") | |||
.args(mapOf( | |||
Pair("date", inputDate), | |||
Pair("template", template) | |||
)) | |||
.addTo(InternetAddress("[email protected]")) | |||
.addCc(cc) | |||
.addBcc(bcc) | |||
.build() | |||
val mailRequestList = mutableListOf<MailRequest>() | |||
mailRequestList += mailRequest | |||
mailService.send(mailRequestList) | |||
} | |||
open fun sendTimesheetReminderTest() { | |||
val inputDate = LocalDate.now().minusDays(4).format(dateFormat) | |||
val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value | |||
val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value | |||
val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") | |||
logger.info(cc) | |||
val bcc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_BCC).orElseThrow().value.split(",") | |||
val mailRequest = MailRequest.Builder() | |||
.subject(subject) | |||
// .template("mail/TimesheetNotification") | |||
.templateContent(template) | |||
.args(mapOf( | |||
Pair("date", inputDate), | |||
// Pair("template", template) | |||
)) | |||
.addTo(InternetAddress(SecurityUtils.getUser().orElseThrow().email)) | |||
.addCc(cc) | |||
.addBcc(bcc) | |||
.build() | |||
val mailRequestList = mutableListOf<MailRequest>() | |||
mailRequestList += mailRequest | |||
mailService.send(mailRequestList) | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
package com.ffii.tsms.modules.common.mail.service | |||
import com.ffii.tsms.modules.common.MailSMTP | |||
import com.ffii.tsms.modules.settings.service.SettingsService | |||
import org.springframework.mail.javamail.JavaMailSender | |||
import org.springframework.mail.javamail.JavaMailSenderImpl | |||
import org.springframework.stereotype.Service | |||
import java.util.* | |||
@Service | |||
open class MailSenderService(private val settingsService: SettingsService) { | |||
private var sender: JavaMailSender? = null | |||
private var mailConfigCachs: MailSMTP? = null | |||
open fun get(): JavaMailSender { | |||
val config = MailSMTP(settingsService) | |||
if (this.sender == null || (mailConfigCachs == null) || !config.equals(this.mailConfigCachs)) { | |||
this.mailConfigCachs = config | |||
val sender = JavaMailSenderImpl() | |||
val props = Properties() | |||
val auth = config.auth ?: false | |||
if (auth) { | |||
props["mail.smtp.timeout"] = "20000" | |||
props["mail.smtp.connectiontimeout"] = "10000" | |||
} | |||
props["mail.smtp.auth"] = auth | |||
props["mail.smtp.starttls.enable"] = "true" | |||
sender.host = config.host | |||
sender.port = config.port!! | |||
if (auth) { | |||
sender.username = config.username | |||
sender.password = config.password | |||
} | |||
sender.javaMailProperties = props | |||
this.sender = sender | |||
} | |||
return this.sender ?: JavaMailSenderImpl() | |||
} | |||
} |
@@ -0,0 +1,207 @@ | |||
package com.ffii.tsms.modules.common.mail.service | |||
import com.ffii.core.exception.InternalServerErrorException | |||
import com.ffii.core.utils.LocaleUtils | |||
import com.ffii.tsms.modules.common.ErrorCodes | |||
import com.ffii.tsms.modules.common.SettingNames | |||
import com.ffii.tsms.modules.common.mail.pojo.MailRequest | |||
import com.ffii.tsms.modules.common.mail.web.models.MailSave | |||
import com.ffii.tsms.modules.settings.entity.Settings | |||
import com.ffii.tsms.modules.settings.entity.SettingsRepository | |||
import com.ffii.tsms.modules.settings.service.SettingsService | |||
import freemarker.cache.StringTemplateLoader | |||
import freemarker.template.* | |||
import jakarta.mail.MessagingException | |||
import org.apache.commons.logging.Log | |||
import org.apache.commons.logging.LogFactory | |||
import org.springframework.core.io.ByteArrayResource | |||
import org.springframework.mail.javamail.MimeMessageHelper | |||
import org.springframework.scheduling.annotation.Async | |||
import org.springframework.security.crypto.password.PasswordEncoder | |||
import org.springframework.stereotype.Service | |||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils | |||
import java.io.IOException | |||
import java.text.ParseException | |||
import java.util.* | |||
@Service | |||
open class MailService( | |||
private val mailSenderService: MailSenderService, | |||
private val freemarkerConfig: Configuration, | |||
private val settingsService: SettingsService, | |||
private val passwordEncoder: PasswordEncoder, | |||
private val settingsRepository: SettingsRepository, | |||
) { | |||
protected val logger: Log = LogFactory.getLog(javaClass) | |||
@Throws( | |||
MessagingException::class, | |||
TemplateNotFoundException::class, | |||
MalformedTemplateNameException::class, | |||
ParseException::class, | |||
IOException::class, | |||
TemplateException::class | |||
) | |||
open fun doSend(mailRequests: List<MailRequest>, locale: Locale) { | |||
val sender = mailSenderService.get() | |||
for (mailRequest in mailRequests) { | |||
val mimeMessage = sender.createMimeMessage() | |||
val helper = MimeMessageHelper(mimeMessage, true, "UTF-8") | |||
mailRequest.subject?.let { helper.setSubject(it) } | |||
var template: Template? = null | |||
if (mailRequest.template != null) { | |||
template = try { | |||
freemarkerConfig | |||
.getTemplate((mailRequest.template + "_" + LocaleUtils.toLocaleStr(locale)) + ".ftl") | |||
} catch (e: TemplateNotFoundException) { | |||
freemarkerConfig.getTemplate(mailRequest.template + ".ftl") | |||
} | |||
} else { | |||
// Create a FreeMarker configuration | |||
val configuration = Configuration(Configuration.VERSION_2_3_32) | |||
// Create a template loader and add the template content | |||
val templateLoader = StringTemplateLoader() | |||
templateLoader.putTemplate("myTemplate", mailRequest.templateContent) | |||
configuration.templateLoader = templateLoader | |||
try { | |||
// Get the template by name | |||
template = configuration.getTemplate("myTemplate") | |||
} catch (e: IOException) { | |||
// Handle any IO exceptions | |||
} | |||
} | |||
helper.setText( | |||
FreeMarkerTemplateUtils.processTemplateIntoString(template!!, mailRequest.args), | |||
true | |||
) | |||
if (mailRequest.from != null) { | |||
helper.setFrom(mailRequest.from!!) | |||
} else { | |||
helper.setFrom(settingsService.getString(SettingNames.MAIL_SMTP_USERNAME)) | |||
} | |||
if (mailRequest.priority != null) helper.setPriority(mailRequest.priority!!) | |||
if (mailRequest.replyTo != null) helper.setReplyTo(mailRequest.replyTo!!) | |||
if (mailRequest.to.isNotEmpty()) helper.setTo( | |||
mailRequest.to.toTypedArray() | |||
) | |||
if (mailRequest.cc.isNotEmpty()) helper.setCc( | |||
mailRequest.cc.toTypedArray() | |||
) | |||
if (mailRequest.bcc.isNotEmpty()) helper.setBcc( | |||
mailRequest.bcc.toTypedArray() | |||
) | |||
if (mailRequest.attachments.isNotEmpty()) { | |||
for ((key, value) in mailRequest.attachments.entries) { | |||
helper.addAttachment(key, ByteArrayResource(value)) | |||
} | |||
} | |||
sender.send(mimeMessage) | |||
} | |||
} | |||
@Throws(ParseException::class) | |||
fun send(mailRequests: List<MailRequest>, locale: Locale) { | |||
try { | |||
doSend(mailRequests, locale) | |||
} catch (e: MessagingException) { | |||
throw InternalServerErrorException(ErrorCodes.SEND_EMAIL_ERROR, e) | |||
} catch (e: IOException) { | |||
throw InternalServerErrorException(ErrorCodes.SEND_EMAIL_ERROR, e) | |||
} catch (e: TemplateException) { | |||
throw InternalServerErrorException(ErrorCodes.SEND_EMAIL_ERROR, e) | |||
} | |||
} | |||
@Throws(ParseException::class) | |||
fun send(mailRequests: List<MailRequest>) { | |||
send(mailRequests, LocaleUtils.getLocale()) | |||
} | |||
@Throws(ParseException::class) | |||
fun send(mailRequest: MailRequest, locale: Locale) { | |||
send(mutableListOf(mailRequest), locale) | |||
} | |||
@Async | |||
@Throws(ParseException::class) | |||
open fun asyncSend(mailRequests: List<MailRequest>, locale: Locale) { | |||
try { | |||
doSend(mailRequests, locale) | |||
} catch (e: MessagingException) { | |||
logger.error("send email error", e) | |||
} catch (e: IOException) { | |||
logger.error("send email error", e) | |||
} catch (e: TemplateException) { | |||
logger.error("send email error", e) | |||
} | |||
} | |||
@Async | |||
@Throws(ParseException::class) | |||
open fun asyncSend(mailRequest: MailRequest, locale: Locale) { | |||
asyncSend(mutableListOf(mailRequest), locale) | |||
} | |||
@Async | |||
@Throws(ParseException::class) | |||
open fun asyncSend(mailRequests: List<MailRequest>) { | |||
asyncSend(mailRequests, LocaleUtils.getLocale()) | |||
} | |||
@Async | |||
@Throws(ParseException::class) | |||
open fun asyncSend(mailRequest: MailRequest) { | |||
asyncSend(mutableListOf(mailRequest)) | |||
} | |||
open fun saveMail(mailSave: MailSave): List<Settings> { | |||
// ------------------ save mail settings ------------------ // | |||
val settings = mutableListOf<Settings>() | |||
mailSave.settings.forEach { setting -> | |||
val tempSetting = settingsRepository.findById(setting.id).orElseThrow() | |||
settings += tempSetting.apply { | |||
value = setting.value | |||
} | |||
} | |||
// ------------------ save timesheet mail details ------------------ // | |||
val mailCc = settingsRepository.findByName("TIMESHEET.mail.cc").orElseThrow() | |||
settings += mailCc.apply { | |||
value = mailSave.template.cc | |||
} | |||
val mailBcc = settingsRepository.findByName("TIMESHEET.mail.bcc").orElseThrow() | |||
settings += mailBcc.apply { | |||
value = mailSave.template.bcc | |||
} | |||
val mailSubject = settingsRepository.findByName("TIMESHEET.mail.subject").orElseThrow() | |||
settings += mailSubject.apply { | |||
value = mailSave.template.subject | |||
} | |||
val mailTemplate = settingsRepository.findByName("TIMESHEET.mail.template").orElseThrow() | |||
settings += mailTemplate.apply { | |||
value = mailSave.template.template | |||
} | |||
// ------------------ save all ------------------ // | |||
settingsRepository.saveAll(settings) | |||
return settings | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
package com.ffii.tsms.modules.common.mail.web | |||
import com.ffii.tsms.modules.common.mail.service.MailReminderService | |||
import com.ffii.tsms.modules.common.mail.service.MailService | |||
import com.ffii.tsms.modules.common.mail.web.models.MailSave | |||
import com.ffii.tsms.modules.settings.entity.Settings | |||
import com.ffii.tsms.modules.settings.entity.SettingsRepository | |||
import com.ffii.tsms.modules.settings.service.SettingsService | |||
import jakarta.validation.Valid | |||
import org.springframework.web.bind.annotation.* | |||
@RestController | |||
@RequestMapping("/mails") | |||
class MailController( | |||
private val settingsService: SettingsService, | |||
private val mailService: MailService, | |||
private val mailReminderService: MailReminderService, | |||
) { | |||
@GetMapping("/setting") | |||
fun mailSetting(): List<Settings> { | |||
return settingsService.findAllByCategory("MAIL") | |||
} | |||
@GetMapping("/timesheet-template") | |||
fun mailTimesheetTemplate(): List<Settings> { | |||
return settingsService.findAllByCategory("TIMESHEET") | |||
} | |||
@PostMapping("/save") | |||
fun saveMail(@Valid @RequestBody mailSave: MailSave): List<Settings> { | |||
return mailService.saveMail(mailSave) | |||
} | |||
@GetMapping("/test") | |||
fun testMail() { | |||
mailReminderService.sendTimesheetReminderTest() | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.ffii.tsms.modules.common.mail.web.models | |||
data class Setting ( | |||
val id: Long, | |||
val name: String, | |||
val value: String, | |||
val category: String, | |||
val type: String, | |||
) | |||
data class Template ( | |||
val cc: String?, | |||
val bcc: String?, | |||
val subject: String, | |||
val template: String?, | |||
) | |||
data class MailSave ( | |||
val settings: List<Setting>, | |||
val template: Template | |||
) |
@@ -3,7 +3,7 @@ package com.ffii.tsms.modules.timesheet.web | |||
import com.ffii.core.exception.BadRequestException | |||
import com.ffii.tsms.modules.timesheet.entity.LeaveType | |||
import com.ffii.tsms.modules.timesheet.service.LeaveService | |||
//import com.ffii.tsms.modules.timesheet.service.MailReminderService | |||
//import com.ffii.tsms.modules.common.mail.service.MailReminderService | |||
import com.ffii.tsms.modules.timesheet.service.TimesheetsService | |||
import com.ffii.tsms.modules.timesheet.web.models.* | |||
import jakarta.servlet.http.HttpServletRequest | |||
@@ -0,0 +1,21 @@ | |||
-- liquibase formatted sql | |||
-- changeset cyril:settings | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (7,'MAIL.smtp.host','smtp.office365.com','MAIL','string'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (8,'MAIL.smtp.port','587','MAIL','integer'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (9,'MAIL.smtp.username','','MAIL','string'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (10,'MAIL.smtp.password','','MAIL','string'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (11,'MAIL.smtp.auth','false','MAIL','boolean'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (12,'TIMESHEET.mail.cc','','TIMESHEET','string'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (13,'TIMESHEET.mail.bcc','','TIMESHEET','string'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (14,'TIMESHEET.mail.subject','','TIMESHEET','string'); | |||
INSERT INTO settings (id,name,value,category,`type`) | |||
VALUES (15,'TIMESHEET.mail.template','','TIMESHEET','string'); |
@@ -0,0 +1,12 @@ | |||
-- liquibase formatted sql | |||
-- changeset cyril:authority | |||
INSERT INTO authority (authority,name) | |||
VALUES ('MAINTAIN_MAIL','Maintain Mail in Master Data'); | |||
INSERT INTO authority (authority,name) | |||
VALUES ('VIEW_MAIL','View Mail in Master Data'); | |||
INSERT INTO user_authority (userId,authId) | |||
VALUES (1,53); | |||
INSERT INTO user_authority (userId,authId) | |||
VALUES (1,54); |
@@ -0,0 +1,28 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |||
</head> | |||
<style> | |||
body { | |||
margin: 0; | |||
} | |||
.content { | |||
padding-top: 2rem; | |||
display: flex; | |||
} | |||
.container { | |||
padding: 2px 16px; | |||
} | |||
</style> | |||
<body> | |||
<main class="content"> | |||
<div class="container"> | |||
${template} | |||
</div> | |||
</main> | |||
</body> | |||
</html> |