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)]
|
||||
pub(crate) struct EmailData {
|
||||
pub(crate) struct ConfirmationEmailData {
|
||||
pub(crate) confirmation_link: Url,
|
||||
}
|
||||
|
||||
|
|
@ -44,12 +44,12 @@ pub(crate) async fn send_confirmation_email(
|
|||
hb: &Handlebars<'_>,
|
||||
contact_address: Address,
|
||||
email_config: &EmailConfig,
|
||||
email_data: &EmailData,
|
||||
email_data: &ConfirmationEmailData,
|
||||
) -> actix_web::Result<(), EmailError> {
|
||||
// receiver of the confirmation e-mail
|
||||
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()
|
||||
.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 crate::job_offers::view::JobOfferEditData;
|
||||
use crate::route::JOBOFFER_OVERVIEW_ROUTE;
|
||||
use crate::{
|
||||
auth::User,
|
||||
error::PresentationError,
|
||||
|
|
@ -486,6 +487,16 @@ impl JobOffer<PathBuf> {
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -74,11 +74,11 @@ impl ResponseError for SaveResponseError {
|
|||
}
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub(crate) enum EmailError {
|
||||
#[error("Construction of confirmation E-mail failed!")]
|
||||
#[error("Construction of E-mail failed!")]
|
||||
Email(#[from] lettre::error::Error),
|
||||
#[error("Sending of confirmation E-mail failed!")]
|
||||
#[error("Sending of E-mail failed!")]
|
||||
SendMail(#[from] lettre::transport::sendmail::Error),
|
||||
#[error("Error in Confirmation E-Mail template!")]
|
||||
#[error("Error in E-Mail template!")]
|
||||
Template(#[from] handlebars::RenderError),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use serde_json::json;
|
|||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::auth::User;
|
||||
use crate::email::EmailData;
|
||||
use crate::email::ConfirmationEmailData;
|
||||
use crate::error::{LoginRequired, PresentationError};
|
||||
use crate::job_offers::error::SaveResponseError;
|
||||
use crate::job_offers::lease::SubmissionLimiter;
|
||||
|
|
@ -260,7 +260,7 @@ pub(crate) async fn create_job_offer<'data, 'config>(
|
|||
hb,
|
||||
job_offer_form.contact,
|
||||
email_config,
|
||||
&EmailData {
|
||||
&ConfirmationEmailData {
|
||||
confirmation_link: confirm_url.into(),
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use multipart_helper::MultipartFieldError;
|
|||
use thiserror::Error;
|
||||
|
||||
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;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -104,6 +104,8 @@ pub(crate) enum StateChangeResponseError {
|
|||
Login(#[from] LoginRequired),
|
||||
#[error("Rendering the requests response failed: {0}")]
|
||||
Presentation(#[from] PresentationError),
|
||||
#[error("Failed to send email: {0}")]
|
||||
Email(#[from] EmailError),
|
||||
}
|
||||
|
||||
impl From<RenderError> for StateChangeResponseError {
|
||||
|
|
@ -124,11 +126,14 @@ impl ResponseError for StateChangeResponseError {
|
|||
StateChangeResponseError::Save(inner) => inner.as_status_code(),
|
||||
StateChangeResponseError::Login(_inner) => StatusCode::UNAUTHORIZED,
|
||||
StateChangeResponseError::Presentation(inner) => inner.status_code(),
|
||||
StateChangeResponseError::Email(_inner) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||
match self {
|
||||
StateChangeResponseError::Login(_) | StateChangeResponseError::Save(_) => {
|
||||
StateChangeResponseError::Login(_)
|
||||
| StateChangeResponseError::Save(_)
|
||||
| StateChangeResponseError::Email(_) => {
|
||||
default_error_response(self, self.status_code())
|
||||
}
|
||||
StateChangeResponseError::Presentation(inner) => inner.error_response(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use actix_session::Session;
|
||||
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::JOBOFFER_OVERVIEW_ROUTE;
|
||||
use crate::{auth, JobOffers, ServerConfig};
|
||||
|
|
@ -11,6 +13,7 @@ pub(crate) const JOBOFFER_PUBLISH_ROUTE: &str = "review_offer";
|
|||
pub(crate) async fn review_joboffer(
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
hb: web::Data<Handlebars<'_>>,
|
||||
session: Session,
|
||||
config: web::Data<ServerConfig>,
|
||||
offers: web::Data<JobOffers>,
|
||||
|
|
@ -22,8 +25,23 @@ pub(crate) async fn review_joboffer(
|
|||
let id = path.into_inner();
|
||||
|
||||
if let Some(mut entry) = offers.get_offer_mut(&id, &config).await {
|
||||
let requires_review = entry.requires_review();
|
||||
entry.mark_as_reviewed();
|
||||
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)?;
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ pub(crate) const JOBOFFER_FAQ: &str = "job_offer/faq";
|
|||
pub(crate) const AUTH_LOGIN: &str = "auth/login";
|
||||
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