DRY Layouts For Grails Emails?

后端 未结 1 994
甜味超标
甜味超标 2021-01-28 20:39

I\'m trying to clean up some of the the email views in my grails project. My team uses the same introduction, logo, and sign off for every email. I tried to place these into the

1条回答
  •  别那么骄傲
    2021-01-28 21:03

    I was trying to figure out what you mean by DRY, I think it must be some ruby term and I guess you mean templates.

    The problem with HTML emails is actually a standard problem acrosss all languages as in when it comes to including logos (headers/footers). The standards vary and whilst it may work on some mail clients may not work on for example web mail i.e. gmail and so forth.

    The trick is to use inline images I will give you some sample stuff to work from:

    So here is a sample Controller - this is off my own code but from different parts. It is to give you an idea or should I say not very well explained specifically around multi inline images:

    class Mycontroller() { 
       pivate final static String TEMPLATE='/emails/emailTemplate'
       def emailService
       def doEmail() { 
        def images=[]
        //Where this list contains a map of photo Ids(could be macde up, the photo content type and actual photo file Names (to go and grab from uploaded folder)
        images <<[id: "uImage${photo.id}", contentType: "${photo.contentType}", file: photo.file]
        images <<[id: "uImage${photo1.id}", contentType: "${photo1.contentType}", file: photo1.file]
        emailService.sendEmail(user.email, subject, TEMPLATE, [instance: bean, domainName:domainName, fqdn:fqdn ], images)
      }
    }
    

    I have a list if images above it containts the photo id the content type and the actual file name (in text) that is sent through to emailService.sendEmail

    private void sendEmail(email,mysubject,template,templateModel,List images) throws Exception {
            List recipients = []       
            try {
                mailService.sendMail {
                    //this must be set true and at the top for inline images to work
                    multipart true
                    if (recipients) {
                        to recipients
                    }
                    else {
                        to email
                    }
                    if (config.admin.emailFrom) {
                        if (Environment.current == Environment.DEVELOPMENT && config.admin.emailFromDev ) {
                            from "${config.admin.emailFromDev}"
                        } else {
                            from "${config.admin.emailFrom}"
                        }
                    }
                    subject mysubject
                    //actual content must be html sent to fill in a grails template
                    html Holders.grailsApplication.mainContext.groovyPageRenderer.render(template: template, model: templateModel)
                    //Main Site logo
                    inline 'inlineImage', 'image/png', new File("/opt/site-stuff/myLogo.png")
                    //Additional images 
                    if (images) {
                        images?.each { a ->
                            inline "${a.id}", "${a.contentType}", new File("${a.file}")
                        }
                    }
                }
            }
            catch (e) {
                //throw new Exception(e.message)
                log.error "Problem sending email ${e.message}"
            }
        }
    

    Now the bit that you thought would just work as in using grails templates in the way you are as in layouts is not what you want instead, as above you can see it is rendering a template but the template is a typical gsp template which instead is a full on html page:


    /emails/emailTemplate

    <%@ page contentType="text/html;charset=UTF-8" %>
    
    
    
    
    
    
    
          

    You see the main problem with html emails is that CSS styles don't work very well, it works in some cases but in a lot of cases you are better off sticking to traditional tables and using style tags to properly declare your layout.

    Forget about using your actual site CSS files since so far as this process goes, this is a direct email being generated and sent. It has no awareness of your site CSS files.

    In regards to multiple images we are talking about

    here is a list of users

    usera {usera Photo} / userA Description userb {userb Photo} / userB Description

    The above solution will have all you need to add all the inline images you need to do this. This means you are attaching the images within the email so if the images are huge then you are also attaching them so the trick is to reformat the images / resize them.

    For your own site logos you can do that directly and have a separate file/folder that contains actual size to be emailed but for on the fly re-sizing you could try something like this:

       static Map getPhoto(Photos photo, int width=100,int height=100) {
            File f
            def contentType
            if (photo.status==Photos.ACTIVE) {
                def id = photo.id
                def imageSHa = photo.imageSHa
                contentType = photo.contentType
                def fileExtension = photo.fileExtension
                //remove . from fileExtension
                def noDotExtension = fileExtension.substring(1)
                def user = photo.user
                f = new File(ROOT_PATH + '/' + user.username + '/' + imageSHa);
                if (f.exists() && !f.isDirectory()) {
                    f = new File(ROOT_PATH + '/' + user.username + '/' + imageSHa+'_email');
                    if (!f.exists()) {
                        def imageStream = new FileInputStream(ROOT_PATH + '/' + user.username + '/' + imageSHa)
                        def image = FileCopyUtils.copyToByteArray(imageStream).encodeBase64().toString()
                        def caption = photo.caption
                        def position = photo.position
                        // String caption=photo.caption
                        //Lets present the image as a thumbNail
                        imageStream = new FileInputStream(ROOT_PATH + '/' + user.username + '/' + imageSHa)
                        def imageBuffer = ImageIO.read(imageStream)
                        def scaledImg = Scalr.resize(imageBuffer, Scalr.Method.QUALITY, width, height, Scalr.OP_ANTIALIAS)
                        ByteArrayOutputStream os = new ByteArrayOutputStream();
                        ImageIO.write(scaledImg, noDotExtension, os)
                        InputStream is = new ByteArrayInputStream(os.toByteArray())
    
                        def scaledImage = FileCopyUtils.copyToByteArray(is).encodeBase64().toString()
                        //imageSHa = DigestUtils.shaHex(scaledImg)
                        byte[] data = Base64.decodeBase64(scaledImage)
                        OutputStream stream = new FileOutputStream(ROOT_PATH + '/' + user.username + '/' + imageSHa+'_email')
                        stream.write(data)
                        f = new File(ROOT_PATH + '/' + user.username + '/' + imageSHa+'_email');
                    }
                    return [file:f, contentType:'img/'+fileExtension.substring(1)]
                }
    
            }
            return [:]
        }
    

    This now maps up to when I was doing the images mapping above:

     def res = PhotosBean.getPhoto(ui.attributes.profilePhoto)
                                if (res) {
                                    images << [id: "uImage${ui.id}", contentType: "${res.contentType}", file: res.file]
                                }
    

    Hope this clears up a lot of headache I had to go through to achieve html emails with as many images as required and all resized to what I want

    0 讨论(0)
提交回复
热议问题