Why can't I send CSS decorated html email via spring? - spring-boot

I have successfully sent text email and the bellow implementation sends really basic html emails - I don't get the CSS classes in any of it.
The relevant parts of my MailServiceImpl class are:
protected void prepareAndSendMail(Map<String, String> emailMap) {
try {
InternetAddress[] parsed;
try {
parsed = InternetAddress.parse(emailMap.get("to"));
} catch (AddressException e) {
throw new IllegalArgumentException("Not valid email: " + emailMap.get("to)"), e);
}
MimeMessage mailMessage = javaMailSender.createMimeMessage();
mailMessage.setSubject(emailMap.get("subject"), "UTF-8");
MimeMessageHelper helper = new MimeMessageHelper(mailMessage, true, "UTF-8");
helper.setFrom(emailMap.get("from"));
helper.setTo(parsed);
helper.setText(mailContentBuilderService.buildHTMLTemplate(emailMap.get("text")), true);
javaMailSender.send(mailMessage);
} catch (MessagingException ex) {
throw new RuntimeException(ex);
}
}
protected String buildConfirmRegistrationButton(String label, String confirmationUrl) {
StringBuilder content = new StringBuilder();
content.append("<div class=\"jumbotron\">");
content.append("<div class=\"container-fluid\">");
content.append("<div class=\"form-group row justify-content-md-center justify-content-lg-center justify-content-xl-center\">");
content.append("<label>" + label + "</label> \r\n");
content.append("<br />");
content.append("<a href=\"" + confirmationUrl + "\">");
content.append("<button class=\"btn btn-primary\"> Click here to confirm </button>");
content.append("</a>");
content.append("</div>");
content.append("</div>");
content.append("</div>");
return content.toString();
}
#Override
public void sendNewUserRegistrationToken_HTML(OnRegistrationCompleteEvent onRegistrationCompleteEvent, User user, String token) {
Locale locale = Locale.US;
final String subject = messageSource.getMessage("mail.registrationConfirmationSubject", null, locale);
final String label = messageSource.getMessage("mail.registrationConfirmationLabel", new Object[]{user.getEmail()}, locale);
final String confirmationUrl = onRegistrationCompleteEvent.getAppUrl() + "/admin/user/registration/confirm?token=" + token;
final String content = buildConfirmRegistrationButton(label, confirmationUrl);
Map<String, String> emailMap = new HashMap<>();
emailMap.put("to", user.getEmail());
emailMap.put("from", environment.getProperty("support.email"));
emailMap.put("subject", subject);
emailMap.put("text", content);
prepareAndSendMail(emailMap);
}
The prepareAndSendMail method calls mailContentBuilderService.buildHTMLTemplate(String text) and is defined as:
private TemplateEngine templateEngine;
#Autowired
public MailContentBuilderServiceImpl(TemplateEngine templateEngine) {
this.templateEngine = templateEngine;
}
#Override
public String buildHTMLTemplate(String message) {
StringBuilder html = new StringBuilder();
html.append("<!DOCTYPE html>");
html.append("<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:th=\"http://www.thymeleaf.org\"> ");
html.append("<head>");
html.append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">");
html.append("<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\" integrity=\"sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T\" crossorigin=\"anonymous\">");
html.append("<script src=\"https://code.jquery.com/jquery-3.3.1.slim.min.js\" integrity=\"sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo\" crossorigin=\"anonymous\"></script>");
html.append("<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js\" integrity=\"sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1\" crossorigin=\"anonymous\"></script>");
html.append("<script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js\" integrity=\"sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM\" crossorigin=\"anonymous\"></script>");
html.append("</head>");
html.append("<body>");
html.append("<div class=\"container\">");
html.append("<div class=\"text-center\">");
html.append(message);
html.append("</div>");
html.append("</div>");
html.append("</body>");
html.append("</html>");
return html.toString();
}
The result I get when I try to register a new user is:
How can I make it look nice, obeying the CSS it should inherit from the CDN?
I have tried using a build method as described below (pointing to the right template, obviously), still no joy.
public String build(String message) {
Context context = new Context();
context.setVariable("message", message);
return templateEngine.process("mailTemplate", context);
}

You need to understand that email does not conform to normal web standards. You cannot add scripts to email. You cannot add external stylesheets (most will get stripped; see also this answer: Can you link to a CSS file from an email?).
You must embed CSS in the <head>, i.e.:
html.append('<style type="text/css">.container {...}</style>');

Related

Loading value from json upon start up application

I want to load the values from json file upon the Spring Boot Application is started.
My code for the Configuration File is like the below:
#Configuration
#Getter
public class FedexAPIConfig {
private final static String JSON_FILE = "/static/config/fedex-api-credentials.json";
private final boolean IS_PRODUCTION = false;
private FedexAPICred apiCredentials;
public FedexAPIConfig() {
try (InputStream in = getClass().getResourceAsStream(JSON_FILE);
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
JSONObject json = new JSONObject();
// this.apiCredentials = new JSONObject(new JSONTokener(reader));
if (IS_PRODUCTION) {
json = new JSONObject(new JSONTokener(reader)).getJSONObject("production");
} else {
json = new JSONObject(new JSONTokener(reader)).getJSONObject("test");
}
System.out.println(json.toString());
this.apiCredentials = FedexAPICred.builder()
.url(json.optString("url"))
.apiKey(json.optString("api_key"))
.secretKey(json.optString("secret_key"))
.build();
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
and with this, when the application is in progress of startup, values are successfully printed on the console.Startup console log
When I tried to call this value from other ordinary class, like the below:, it brings nothing but just throws NullPointerException... What are my faults and what shall I do?
public class FedexOAuthTokenManager extends OAuthToken {
private static final String VALIDATE_TOKEN_URL = "/oauth/token";
private static final String GRANT_TYPE_CLIENT = "client_credentials";
private static final String GRANT_TYPE_CSP = "csp_credentials";
#Autowired
private FedexAPIConfig fedexApiConfig;
#Autowired
private Token token;
#Override
public void validateToken() {
// This is the part where "fedexApiConfig" is null.
FedexAPICred fedexApiCred = fedexApiConfig.getApiCredentials();
Response response = null;
try {
RequestBody body = new FormBody.Builder()
.add("grant_type", GRANT_TYPE_CLIENT)
.add("client_id", fedexApiCred.getApiKey())
.add("client_secret", fedexApiCred.getSecretKey())
.build();
response = new HttpClient().post(fedexApiCred.getUrl() + VALIDATE_TOKEN_URL, body);
if (response.code() == 200) {
JSONObject json = new JSONObject(response.body().string());
token.setAccessToken(json.optString("access_token"));
token.setTokenType(json.optString("token_type"));
token.setExpiredIn(json.optInt("expires_in"));
token.setExpiredDateTime(LocalDateTime.now().plusSeconds(json.optInt("expires_in")));
token.setScope(json.optString("scope"));
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
fedexApiConfg is null even though I autowired it in prior to call.
And this FedexOAuthTokenManager is called from other #Component class by new FedexOAuthTokenManager()
Did you try like below?
Step 1: Create one Configuration class like below
public class DemoConfig implements ApplicationListener<ApplicationPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
//Load the values from the JSON file and populate the application
//properties dynamically
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
Properties props = new Properties();
props.put("spring.datasource.url", "<my value>");
//Add more properties
environment.getPropertySources().addFirst(new PropertiesPropertySource("myProps", props));
}
To listen to a context event, a bean should implement the ApplicationListener interface which has just one method onApplicationEvent().The ApplicationPreparedEvent is invoked very early in the lifecycle of the application
Step 2: Customize in src/main/resources/META-INF/spring.factories
org.springframework.context.ApplicationListener=com.example.demo.DemoConfig
Step 3: #Value in spring boot is commonly used to inject the configuration values into the spring boot application. Access the properties as per your wish.
#Value("${spring.datasource.url}")
private String valueFromJSon;
Try this sample first in your local machine and then modify your changes accordingly.
Refer - https://www.baeldung.com/spring-value-annotation
Refer - https://www.knowledgefactory.net/2021/02/aws-secret-manager-service-as.html

Send emails to multiple email addresses, It feeds email addresses from a csv file

I wanna create an application to send emails to several recipients. It feeds email addresses from a csv file and sends an email to each recipient, and I'm getting some trouble doing this.
Could you help me please?
Here is my CSVHelper.java
#Component
public class CSVHelper
{
public static String TYPE = "text/csv";
static String[] HEADERs = { "id", "email", "dateEcheance"};
//This method is used to filter the csv file and get only the emails
public List<ContactsFile> csvToEmails() throws NumberFormatException, ParseException
{
InputStream is = null;
try (BufferedReader fileReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
CSVParser csvParser = new CSVParser(fileReader, CSVFormat.DEFAULT.withFirstRecordAsHeader().withIgnoreHeaderCase().withTrim());)
{
List<ContactsFile> emailsList = new ArrayList<>();
Iterable<CSVRecord> csvRecords = csvParser.getRecords();
for (CSVRecord csvRecord : csvRecords)
{
ContactsFile contact = new ContactsFile(csvRecord.get("email"));
emailsList.add(contact);
}
System.out.println(emailsList);
return emailsList;
}
catch (IOException e) { throw new RuntimeException("fail to get emails: " + e.getMessage()); }
}
We call csvToEmails() method in the controller to send the emails
#Autowired
private CSVHelper csvHelper;
#PostMapping("/getdetails")
public #ResponseBody EmailNotification sendMail(#RequestBody EmailNotification details) throws Exception {
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,
MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
StandardCharsets.UTF_8.name());
try {
helper.setTo((InternetAddress) csvHelper.csvToEmails());
helper.setText(details.getMessage(),true);
helper.setSubject("Test Mail");
} catch (javax.mail.MessagingException e) {
e.printStackTrace();
}
sender.send(message);
return details;
this is an example of the csv file:
id,email,dateEcheance
1,address#email.com,10/05/2021
2,address2#email.com,10/02/2021
I'm new to spring boot, and I'm in trouble completing this project.
Assuming that you've mail server configurations in place. First, you need to provide the JavaMailSender implementation. Spring does provide the implementation for the same. You've to create a bean and config the mail server configurations as follows.
#Configuration
public class MailConfig {
#Autowired
private Environment env;
#Bean
public JavaMailSender mailSender() {
try {
JavaMailSenderImpl mailSenderImpl = new JavaMailSenderImpl();
mailSenderImpl.setHost(env.getRequiredProperty("mail.smtp.host"));
mailSenderImpl.setUsername(env.getRequiredProperty("mail.smtp.username"));
mailSenderImpl.setPassword(env.getRequiredProperty("mail.smtp.password"));
mailSenderImpl.setDefaultEncoding(env.getRequiredProperty("mail.smtp.encoding"));
mailSenderImpl.setPort(Integer.parseInt(env.getRequiredProperty("mail.smtp.port")));
mailSenderImpl.setProtocol(env.getRequiredProperty("mail.transport.protocol"));
Properties p = new Properties();
p.setProperty("mail.smtp.starttls.enable", "true");
p.setProperty("mail.smtp.auth", "true");
mailSenderImpl.setJavaMailProperties(p);
return mailSenderImpl;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Configuration settings in application.properties. Add settings according to the mail server.
# SMTP mail settings
mail.smtp.host=**********
mail.smtp.username=**********
mail.smtp.password=**********
mail.smtp.port=**********
mail.smtp.socketFactory.port=**********
mail.smtp.encoding=utf-8
mail.transport.protocol=smtp
mail.smtp.auth=true
mail.smtp.timeout=10000
mail.smtp.starttls.enable=true
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
CSVHelper.java
#Component
public class CSVHelper {
// This method is used to filter the csv file and get only the emails
public List<String> csvToEmails() {
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("csvFile.csv");
try (BufferedReader fileReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
CSVParser csvParser = new CSVParser(fileReader,
CSVFormat.DEFAULT.withFirstRecordAsHeader().withIgnoreHeaderCase().withTrim());) {
List<String> emails = new ArrayList<>();
Iterable<CSVRecord> csvRecords = csvParser.getRecords();
for (CSVRecord csvRecord : csvRecords) {
String email = csvRecord.get("email");
emails.add(email);
}
return emails;
} catch (IOException e) {
throw new RuntimeException("fail to get emails: " + e.getMessage());
}
}
}
Send mail service
#Service
public class MailSenderService {
#Autowired
private CSVHelper csvHelper;
#Autowired
private JavaMailSender sender;
public void sendMail(EmailNotification details) {
try {
List<String> recipients = csvHelper.csvToEmails();
String[] to = recipients.stream().toArray(String[]::new);
JavaMailSenderImpl jms = (JavaMailSenderImpl) sender;
MimeMessage message = jms.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
// add the sender email address below
helper.setFrom("sender#mail.com");
helper.setTo(to);
helper.setText(details.getMessage(), true);
helper.setSubject("Test Mail");
sender.send(message);
} catch (javax.mail.MessagingException | NumberFormatException e) {
// action for exception case
}
}
}

how to format tables in Mails / outlook

I am sending email using Spring MVC but It is showing differently In Browser and outlook email
#Service("emailService")
public class EmailService {
#Autowired
private JavaMailSender mailSender;
StringBuilder ResponseTable=new StringBuilder();
public void sendMail(String emailid,String subject,int
tracking_id,String classification_type){
MimeMessage message=mailSender.createMimeMessage();
MimeMessageHelper helper=new MimeMessageHelper(message,true,"UTF- 8");
helper.setTo(emailid);
helper.setText(ResponseTable.toString(),true);
helper.setSubject(subject);
mailSender.send(message);
}
}
You can use a velocity Engine Template to create a custom template :
create your template mailTemplate.vm
create a methode where you can insert you data in a model
create a methode to insert this model in your template
1.mailTemplate.vm
<html>
<head></head>
<body>
<p>hello,</p>
<p>${variable}</p>
</body>
</html>
Create Model to send the Notification Mail
public Boolean NotificationMail(String sender, String receiver, String url,
String subject,String footer) {
LOG.info(" ... Sending new Subscription Notification Mail ");
try {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(mailHost);
mailSender.setPort(port);
mailSender.setJavaMailProperties(setMailProperties());
Map<String, Object> model = new HashMap<String, Object>();
model.put("variable", "hello this is the text will be display in the mail");
String body = getVelocityTemplate(model);
LOG.info("Mail host {} , Port {} , url {} , sender {} , receiver {} , subject {} , body {} ",
mailHost, port, url, sender, receiver, subject, body);
Boolean sent = mailService.sendMailWithImg(sender, receiver, subject, body);
return true;
} catch (IllegalArgumentException e) {
LOG.error("IllegalArgumentException , Missing configuration", e);
} catch (Exception e) {
LOG.error("General Exception :{}", e);
}
return false;
}
insert model in the Template
protected String getVelocityTemplateContent(Map<String, Object> model) {
StringWriter stringWriter = new StringWriter();
StringBuilder builder = new StringBuilder();
builder.append("/template/mailTemplate.vm");
try {
VelocityEngineUtils.mergeTemplate(velocityEngine, builder.toString(), "UTF8", model,
stringWriter);
return stringWriter.toString();
} catch (Exception e) {
LOG.error("Error",e);
}
return null;
}
}

While uploading image to server (error while uploading)

In my app I m sending 3 parameters to server latitude,longitude and image.Earlier i was using volley for sending the parameter, but since i have a image also I had to use Multipart in my code.But i m getting an error while uploadind. In the notification bar the uploading starts but after some times it says error in uploading
Below is the code for MultiPart:
public void send() {
try {
String uploadId = UUID.randomUUID().toString();
//Creating a multi part request
new MultipartUploadRequest(this, uploadId, REGISTER_URL)
.setMethod("POST")
.addParameter("action", "location")
.addFileToUpload(imagePath, "data")//Adding file
//.addParameter("name", name) //Adding text parameter to the request
.setNotificationConfig(new UploadNotificationConfig())
.setMaxRetries(5)
.startUpload(); //Starting the upload
} catch (Exception exc) {
Toast.makeText(this, exc.getMessage(), Toast.LENGTH_SHORT).show();
}
}
Below is my volley code:
final String latitudee = String.valueOf(latitude);
final String longitudee =String.valueOf(longitude);
final String datae = imagePath;
//getting the actual path of the image
StringRequest stringRequest = new StringRequest(Request.Method.POST, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(MapsActivity.this,response,Toast.LENGTH_LONG).show();
System.out.println(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MapsActivity.this,error.toString(),Toast.LENGTH_LONG).show();
}
}){
#Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<String, String>();
params.put("action","location");
params.put("latitude",latitudee);
params.put("longitude",longitudee);
send();
// params.put("data", datae);
//Uploading code
return params;}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
Please help me where I'm going wrong
You can send your others parameters through Multipart request library too. just add "add parameter" to send more parameters.

java.lang.NullPointerException in textview inside asyncTask

If I use the following code, I will have no error, but there will be a freeze time.
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
setContentView(R.layout.profilepic);
initialize();
Bundle bundle = getIntent().getExtras();
email = bundle.getString("Email");
ArrayList<NameValuePair> postParameters;
String response = null;
postParameters = new ArrayList<NameValuePair>();
postParameters.add(new BasicNameValuePair("emaillog", email));
try {
response = CustomHttpClient.executeHttpPost("http://whatstherex.info/checkU.php", postParameters);
res = response.toString();
res = res.replaceAll("null", "");
username = res.toString();
tvProfilePic.setText("Hi " + username + ", you are encourage to add a profile picture.");
}catch(Exception e){
res = e.toString();
tvProfilePic.setText(res);
}
}
But if I use this code with asyncTask and progressDialog like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
setContentView(R.layout.profilepic);
initialize();
getUsername();
}
private AsyncTask<String, Void, String> task;
public void getUsername(){
Bundle bundle = getIntent().getExtras();
email = bundle.getString("Email");
task = new AsyncTask<String, Void, String>() {
ProgressDialog dialog;
ArrayList<NameValuePair> postParameters;
String response = null;
#Override
protected void onPreExecute() {
//
ArrayList<NameValuePair> postParameters = new ArrayList<NameValuePair>();
postParameters.add(new BasicNameValuePair("emaillog", email));
dialog = new ProgressDialog(Profilepic.this, ProgressDialog.STYLE_SPINNER);
dialog.setMessage("Loading Data...");
dialog.show();
}
#Override
protected String doInBackground(String... params) {
//
try {
response = CustomHttpClient.executeHttpPost("http://whatstherex.info/checkU.php", postParameters);
res = response.toString();
res = res.replaceAll("null", "");
username = res.toString();
return username;
}catch(Exception e){
res = e.toString();
return res;
}
}
#Override
protected void onPostExecute(String result) {
if(result.length()< 25){
username = result;
tvProfilePic.setText("Hi " + result + ", you are encourage to add a profile picture.");
dialog.dismiss();
}else{
tvProfilePic.setText(result);
dialog.dismiss();
}
}
};
task.execute();
}
I get java.lang.NullPointerException in the textview.
What's the problem? Can anyone help me how to resolve this issue with NullPointerException appearing?
I see that you use tvProfilePic but I don't see where you define it.
In case you are not defining it, it should be done in a similar way as
tvProfilePic = findViewById(R.id.profile_pic)
Are you doing this?
Note that 'profile_pic' is the name in the layout XML document.
You are using tvProfilePic as a textview but i can't see where you defined it.
if you are not defining it then define it using something like
tvProfilePic = findViewById(R.id.profile_pic)
You are getting nullpointerexception because your tvProfilePic is null

Resources