changes and release cut for 0.2.3 #31
9 changed files with 84 additions and 11 deletions
send a notification when a review causes a submission to be published
commit
26f82691f7
35
src/email.rs
35
src/email.rs
|
|
@ -36,7 +36,7 @@ fn user_agent() -> UserAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub(crate) struct EmailData {
|
pub(crate) struct ConfirmationEmailData {
|
||||||
pub(crate) confirmation_link: Url,
|
pub(crate) confirmation_link: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,12 +44,12 @@ pub(crate) async fn send_confirmation_email(
|
||||||
hb: &Handlebars<'_>,
|
hb: &Handlebars<'_>,
|
||||||
contact_address: Address,
|
contact_address: Address,
|
||||||
email_config: &EmailConfig,
|
email_config: &EmailConfig,
|
||||||
email_data: &EmailData,
|
email_data: &ConfirmationEmailData,
|
||||||
) -> actix_web::Result<(), EmailError> {
|
) -> actix_web::Result<(), EmailError> {
|
||||||
// receiver of the confirmation e-mail
|
// receiver of the confirmation e-mail
|
||||||
let to_mailbox = Mailbox::new(None, contact_address);
|
let to_mailbox = Mailbox::new(None, contact_address);
|
||||||
|
|
||||||
let email_body = hb.render(template::EMAIL_PLAIN, &email_data)?;
|
let email_body = hb.render(template::EMAIL_CONFIRMATION_PLAIN, email_data)?;
|
||||||
|
|
||||||
let message = lettre::Message::builder()
|
let message = lettre::Message::builder()
|
||||||
.from(email_config.from.to_owned())
|
.from(email_config.from.to_owned())
|
||||||
|
|
@ -89,3 +89,32 @@ pub(crate) async fn send_reviewer_notice(email_config: &EmailConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub(crate) struct PublishNoticeEmailData {
|
||||||
|
pub(crate) highlight_link: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn send_publish_notice(
|
||||||
|
hb: &Handlebars<'_>,
|
||||||
|
contact_address: Address,
|
||||||
|
email_config: &EmailConfig,
|
||||||
|
email_data: &PublishNoticeEmailData,
|
||||||
|
) -> actix_web::Result<(), EmailError> {
|
||||||
|
// receiver of the publish notice e-mail
|
||||||
|
let to_mailbox = Mailbox::new(None, contact_address);
|
||||||
|
|
||||||
|
let email_body = hb.render(template::EMAIL_PUBLISH_NOTICE_PLAIN, &email_data)?;
|
||||||
|
|
||||||
|
let message = lettre::Message::builder()
|
||||||
|
.from(email_config.from.to_owned())
|
||||||
|
.to(to_mailbox)
|
||||||
|
.subject(&email_config.subject)
|
||||||
|
.header(user_agent())
|
||||||
|
.header(AutoGeneratedHeader::default())
|
||||||
|
.singlepart(SinglePart::plain(email_body))?;
|
||||||
|
|
||||||
|
lettre::AsyncSendmailTransport::new().send(message).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use view::{JobOfferActions, JobOfferViewData};
|
||||||
use better_toml_datetime::Date;
|
use better_toml_datetime::Date;
|
||||||
|
|
||||||
use crate::job_offers::view::JobOfferEditData;
|
use crate::job_offers::view::JobOfferEditData;
|
||||||
|
use crate::route::JOBOFFER_OVERVIEW_ROUTE;
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::User,
|
auth::User,
|
||||||
error::PresentationError,
|
error::PresentationError,
|
||||||
|
|
@ -486,6 +487,16 @@ impl JobOffer<PathBuf> {
|
||||||
links: self.links.clone(),
|
links: self.links.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn highlight_link(
|
||||||
|
&self,
|
||||||
|
id: &JobOfferId,
|
||||||
|
req: &HttpRequest,
|
||||||
|
) -> Result<Url, UrlGenerationError> {
|
||||||
|
let mut url = req.url_for_static(JOBOFFER_OVERVIEW_ROUTE)?;
|
||||||
|
url.set_fragment(Some(&format!("joboffer-{}", id)));
|
||||||
|
Ok(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct JobOffers {
|
pub struct JobOffers {
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,11 @@ impl ResponseError for SaveResponseError {
|
||||||
}
|
}
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub(crate) enum EmailError {
|
pub(crate) enum EmailError {
|
||||||
#[error("Construction of confirmation E-mail failed!")]
|
#[error("Construction of E-mail failed!")]
|
||||||
Email(#[from] lettre::error::Error),
|
Email(#[from] lettre::error::Error),
|
||||||
#[error("Sending of confirmation E-mail failed!")]
|
#[error("Sending of E-mail failed!")]
|
||||||
SendMail(#[from] lettre::transport::sendmail::Error),
|
SendMail(#[from] lettre::transport::sendmail::Error),
|
||||||
#[error("Error in Confirmation E-Mail template!")]
|
#[error("Error in E-Mail template!")]
|
||||||
Template(#[from] handlebars::RenderError),
|
Template(#[from] handlebars::RenderError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use serde_json::json;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
use crate::auth::User;
|
use crate::auth::User;
|
||||||
use crate::email::EmailData;
|
use crate::email::ConfirmationEmailData;
|
||||||
use crate::error::{LoginRequired, PresentationError};
|
use crate::error::{LoginRequired, PresentationError};
|
||||||
use crate::job_offers::error::SaveResponseError;
|
use crate::job_offers::error::SaveResponseError;
|
||||||
use crate::job_offers::lease::SubmissionLimiter;
|
use crate::job_offers::lease::SubmissionLimiter;
|
||||||
|
|
@ -260,7 +260,7 @@ pub(crate) async fn create_job_offer<'data, 'config>(
|
||||||
hb,
|
hb,
|
||||||
job_offer_form.contact,
|
job_offer_form.contact,
|
||||||
email_config,
|
email_config,
|
||||||
&EmailData {
|
&ConfirmationEmailData {
|
||||||
confirmation_link: confirm_url.into(),
|
confirmation_link: confirm_url.into(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use multipart_helper::MultipartFieldError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::error::{default_error_response, LoginRequired, PresentationError};
|
use crate::error::{default_error_response, LoginRequired, PresentationError};
|
||||||
use crate::job_offers::error::{DeleteError, SaveError, SaveResponseError};
|
use crate::job_offers::error::{DeleteError, EmailError, SaveError, SaveResponseError};
|
||||||
use crate::job_offers::JobofferLoadError;
|
use crate::job_offers::JobofferLoadError;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -104,6 +104,8 @@ pub(crate) enum StateChangeResponseError {
|
||||||
Login(#[from] LoginRequired),
|
Login(#[from] LoginRequired),
|
||||||
#[error("Rendering the requests response failed: {0}")]
|
#[error("Rendering the requests response failed: {0}")]
|
||||||
Presentation(#[from] PresentationError),
|
Presentation(#[from] PresentationError),
|
||||||
|
#[error("Failed to send email: {0}")]
|
||||||
|
Email(#[from] EmailError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RenderError> for StateChangeResponseError {
|
impl From<RenderError> for StateChangeResponseError {
|
||||||
|
|
@ -124,11 +126,14 @@ impl ResponseError for StateChangeResponseError {
|
||||||
StateChangeResponseError::Save(inner) => inner.as_status_code(),
|
StateChangeResponseError::Save(inner) => inner.as_status_code(),
|
||||||
StateChangeResponseError::Login(_inner) => StatusCode::UNAUTHORIZED,
|
StateChangeResponseError::Login(_inner) => StatusCode::UNAUTHORIZED,
|
||||||
StateChangeResponseError::Presentation(inner) => inner.status_code(),
|
StateChangeResponseError::Presentation(inner) => inner.status_code(),
|
||||||
|
StateChangeResponseError::Email(_inner) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
match self {
|
match self {
|
||||||
StateChangeResponseError::Login(_) | StateChangeResponseError::Save(_) => {
|
StateChangeResponseError::Login(_)
|
||||||
|
| StateChangeResponseError::Save(_)
|
||||||
|
| StateChangeResponseError::Email(_) => {
|
||||||
default_error_response(self, self.status_code())
|
default_error_response(self, self.status_code())
|
||||||
}
|
}
|
||||||
StateChangeResponseError::Presentation(inner) => inner.error_response(),
|
StateChangeResponseError::Presentation(inner) => inner.error_response(),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use actix_session::Session;
|
use actix_session::Session;
|
||||||
use actix_web::{http, post, web, HttpRequest, HttpResponse};
|
use actix_web::{http, post, web, HttpRequest, HttpResponse};
|
||||||
|
use handlebars::Handlebars;
|
||||||
|
|
||||||
|
use crate::email::PublishNoticeEmailData;
|
||||||
use crate::route::job_offer::error::StateChangeResponseError;
|
use crate::route::job_offer::error::StateChangeResponseError;
|
||||||
use crate::route::JOBOFFER_OVERVIEW_ROUTE;
|
use crate::route::JOBOFFER_OVERVIEW_ROUTE;
|
||||||
use crate::{auth, JobOffers, ServerConfig};
|
use crate::{auth, JobOffers, ServerConfig};
|
||||||
|
|
@ -11,6 +13,7 @@ pub(crate) const JOBOFFER_PUBLISH_ROUTE: &str = "review_offer";
|
||||||
pub(crate) async fn review_joboffer(
|
pub(crate) async fn review_joboffer(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
|
hb: web::Data<Handlebars<'_>>,
|
||||||
session: Session,
|
session: Session,
|
||||||
config: web::Data<ServerConfig>,
|
config: web::Data<ServerConfig>,
|
||||||
offers: web::Data<JobOffers>,
|
offers: web::Data<JobOffers>,
|
||||||
|
|
@ -22,8 +25,23 @@ pub(crate) async fn review_joboffer(
|
||||||
let id = path.into_inner();
|
let id = path.into_inner();
|
||||||
|
|
||||||
if let Some(mut entry) = offers.get_offer_mut(&id, &config).await {
|
if let Some(mut entry) = offers.get_offer_mut(&id, &config).await {
|
||||||
|
let requires_review = entry.requires_review();
|
||||||
entry.mark_as_reviewed();
|
entry.mark_as_reviewed();
|
||||||
entry.try_clean().await?;
|
entry.try_clean().await?;
|
||||||
|
if requires_review && entry.is_published() {
|
||||||
|
if let Some(email_config) = &config.config.email {
|
||||||
|
let data = PublishNoticeEmailData {
|
||||||
|
highlight_link: entry.highlight_link(entry.id(), &req)?,
|
||||||
|
};
|
||||||
|
crate::email::send_publish_notice(
|
||||||
|
&hb,
|
||||||
|
entry.contact_info.clone(),
|
||||||
|
email_config,
|
||||||
|
&data,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dest = req.url_for_static(JOBOFFER_OVERVIEW_ROUTE)?;
|
let dest = req.url_for_static(JOBOFFER_OVERVIEW_ROUTE)?;
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,5 @@ pub(crate) const JOBOFFER_FAQ: &str = "job_offer/faq";
|
||||||
pub(crate) const AUTH_LOGIN: &str = "auth/login";
|
pub(crate) const AUTH_LOGIN: &str = "auth/login";
|
||||||
pub(crate) const LICENCES: &str = "licenses";
|
pub(crate) const LICENCES: &str = "licenses";
|
||||||
|
|
||||||
pub const EMAIL_PLAIN: &str = "email/plaintext";
|
pub const EMAIL_CONFIRMATION_PLAIN: &str = "email/confirmation_plaintext";
|
||||||
|
pub const EMAIL_PUBLISH_NOTICE_PLAIN: &str = "email/publish_notice_plaintext";
|
||||||
|
|
|
||||||
9
templates/email/publish_notice_plaintext.hb
Normal file
9
templates/email/publish_notice_plaintext.hb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
Moin,
|
||||||
|
|
||||||
|
Es wurde eine Stellenausschreibung mit dieser E-Mail Address als Kontakt-Address in unserer Jobbörse veröffentlicht.
|
||||||
|
|
||||||
|
{{{highlight_link}}}
|
||||||
|
|
||||||
|
Freundliche Grüße
|
||||||
|
|
||||||
|
Das Team der FS-InfMath Jobbörse
|
||||||
Loading…
Add table
Add a link
Reference in a new issue