--- name: Wheels Email Generator description: Generate email functionality including mailer controllers, email templates, and configuration. Use when sending emails, creating notifications, or implementing transactional emails. Ensures proper email structure, layouts, and testing. --- # Wheels Email Generator ## When to Use This Skill Activate automatically when: - User wants to send emails - User mentions: email, mailer, sendMail, notification - User needs password reset emails - User wants welcome emails or transactional emails - User asks about email templates or configuration ## Email Directory Structure ``` /app/ ├── controllers/ │ └── Mailer.cfc # Email controller ├── views/ │ └── mailer/ # Email templates │ ├── layouts/ │ │ ├── email.cfm # HTML layout │ │ └── email.txt.cfm # Plain text layout │ ├── welcome.cfm # HTML version │ ├── welcome.txt.cfm # Text version │ ├── resetPassword.cfm │ └── resetPassword.txt.cfm └── /config/ └── settings.cfm # Email configuration ``` ## Mailer Controller Template ```cfm component extends="Controller" { function config() { // Configure email defaults set( functionName = "sendEmail", from = "noreply@yourapp.com", layout = "email", detectMultipart = true ); } /** * Send welcome email to new user */ function welcome(required user) { sendEmail( to = arguments.user.email, subject = "Welcome to Our App!", template = "mailer/welcome", user = arguments.user ); } /** * Send password reset email */ function resetPassword(required user, required token) { local.resetUrl = URLFor( route = "passwordReset", token = arguments.token, onlyPath = false ); sendEmail( to = arguments.user.email, subject = "Reset Your Password", template = "mailer/resetPassword", user = arguments.user, resetUrl = local.resetUrl ); } /** * Send order confirmation email */ function orderConfirmation(required order) { sendEmail( to = arguments.order.customerEmail, subject = "Order Confirmation - ##" & arguments.order.id, template = "mailer/orderConfirmation", order = arguments.order ); } /** * Send notification to admin */ function adminNotification(required subject, required message) { sendEmail( to = application.adminEmail, subject = arguments.subject, template = "mailer/adminNotification", message = arguments.message ); } } ``` ## Email Layout (HTML) - views/mailer/layouts/email.cfm ```cfm

Your App Name

##contentForLayout()##
``` ## Email Layout (Plain Text) - views/mailer/layouts/email.txt.cfm ```cfm ======================================== YOUR APP NAME ======================================== ##contentForLayout()## ======================================== © #Year(Now())# Your Company All rights reserved. Unsubscribe: [URL] Privacy: [URL] ======================================== ``` ## Email Template Examples ### Welcome Email (HTML) - views/mailer/welcome.cfm ```cfm

Welcome, ##user.firstName##!

Thank you for joining our community. We're excited to have you on board.

Here's what you can do next:

Get Started

If you have any questions, feel free to reply to this email.

Best regards,
The Team

``` ### Welcome Email (Plain Text) - views/mailer/welcome.txt.cfm ```cfm Welcome, ##user.firstName##! Thank you for joining our community. We're excited to have you on board. Here's what you can do next: - Complete your profile - Explore our features - Connect with other users Get Started: ##URLFor(route='dashboard', onlyPath=false)## If you have any questions, feel free to reply to this email. Best regards, The Team ``` ### Password Reset Email - views/mailer/resetPassword.cfm ```cfm

Reset Your Password

Hi ##user.firstName##,

We received a request to reset your password. Click the button below to create a new password:

Reset Password

Or copy and paste this link into your browser:

##resetUrl##

This link will expire in 24 hours.

If you didn't request a password reset, you can safely ignore this email.

Thanks,
The Security Team

``` ## Email Configuration - config/settings.cfm ```cfm // Email Server Configuration set( functionName = "sendEmail", server = "smtp.gmail.com", port = 587, username = "your-email@gmail.com", password = "your-app-password", useTLS = true, useSSL = false, from = "noreply@yourapp.com", type = "html", charset = "utf-8" ); // Environment-Specific Email Settings if (application.wheels.environment == "development") { // Log emails instead of sending set(functionName = "sendEmail", debug = true, deliver = false); } if (application.wheels.environment == "testing") { // Send all emails to test account set(functionName = "sendEmail", to = "test@yourapp.com"); } if (application.wheels.environment == "production") { // Production settings set( functionName = "sendEmail", server = getEnv("SMTP_SERVER"), username = getEnv("SMTP_USERNAME"), password = getEnv("SMTP_PASSWORD"), deliver = true ); } ``` ## Using the Mailer ### In Controllers ```cfm component extends="Controller" { function create() { user = model("User").new(params.user); if (user.save()) { // Send welcome email controller("Mailer").welcome(user); redirectTo(route="home", success="Account created! Check your email."); } else { renderView(action="new"); } } function forgotPassword() { user = model("User").findOne(where="email='#params.email#'"); if (isObject(user)) { // Generate reset token token = createUUID(); user.update(resetToken=token, resetTokenExpiry=dateAdd("h", 24, now())); // Send reset email controller("Mailer").resetPassword(user=user, token=token); flashInsert(success="Password reset instructions sent to your email."); } redirectTo(action="login"); } } ``` ### Direct Usage ```cfm // Simple email sendEmail( to = "user@example.com", from = "noreply@yourapp.com", subject = "Test Email", body = "This is a test email." ); // Email with template sendEmail( to = "user@example.com", subject = "Custom Email", template = "mailer/custom", customVariable = "value" ); // Email with attachments sendEmail( to = "user@example.com", subject = "Invoice", template = "mailer/invoice", file = expandPath("./uploads/invoice.pdf"), fileName = "invoice-##123##.pdf" ); ``` ## Email with File Attachments ```cfm function sendInvoice(required order) { local.pdfPath = expandPath("./temp/invoice-##arguments.order.id##.pdf"); // Generate PDF invoice generateInvoicePDF(arguments.order, local.pdfPath); // Send email with attachment sendEmail( to = arguments.order.customerEmail, subject = "Your Invoice - Order ##arguments.order.id##", template = "mailer/invoice", order = arguments.order, file = local.pdfPath, fileName = "invoice-##arguments.order.id##.pdf" ); // Clean up temp file fileDelete(local.pdfPath); } ``` ## Email with Multiple Recipients ```cfm function sendNewsletter(required subject, required template) { // Get all subscribed users subscribers = model("User").findAll(where="subscribed=1"); // Send to each subscriber for (local.subscriber in subscribers) { sendEmail( to = local.subscriber.email, subject = arguments.subject, template = arguments.template, user = local.subscriber ); } } ``` ## Testing Emails ### Email Test ```cfm component extends="wheels.Test" { function testWelcomeEmailSent() { // Create test user user = model("User").create( email = "test@example.com", firstName = "Test" ); // Call mailer controller("Mailer").welcome(user); // Verify email was queued assert("application.wheels.emailQueue.len() > 0"); } function testPasswordResetEmail() { user = model("User").findByKey(1); token = createUUID(); controller("Mailer").resetPassword(user=user, token=token); // Check email contains reset link lastEmail = application.wheels.emailQueue[1]; assert("findNoCase('reset', lastEmail.body) > 0"); assert("findNoCase(token, lastEmail.body) > 0"); } } ``` ### Manual Testing ```cfm // Test in browser - add route .get(name="testEmail", pattern="/test/email", to="tests##testEmail") // Test controller function testEmail() { user = model("User").findByKey(1); controller("Mailer").welcome(user); renderText("Email sent! Check server logs."); } ``` ## Email Best Practices ### ✅ DO: - Always provide both HTML and plain text versions - Use responsive email layouts (max-width: 600px) - Include unsubscribe links - Test emails across different clients - Use descriptive subject lines - Handle email failures gracefully - Queue emails for bulk sending - Use environment-specific settings ### ❌ DON'T: - Send emails synchronously in production - Hardcode email addresses - Use complex CSS (limited support) - Forget error handling - Send without user consent - Include sensitive data in emails - Use JavaScript in emails ## Common Email Patterns ### 1. Transactional Emails ```cfm // Order confirmation, password resets, account verification - Time-sensitive - User-triggered - High priority ``` ### 2. Notification Emails ```cfm // Activity updates, mentions, reminders - Event-driven - May be batched - User preferences apply ``` ### 3. Marketing Emails ```cfm // Newsletters, promotions - Bulk sending - Unsubscribe required - Scheduled ``` ## Email Queue Pattern ```cfm // Queue email for background processing function queueEmail(required struct emailData) { model("EmailQueue").create( recipient = arguments.emailData.to, subject = arguments.emailData.subject, template = arguments.emailData.template, data = serializeJSON(arguments.emailData), status = "pending" ); } // Process queue (run via scheduled task) function processEmailQueue() { pending = model("EmailQueue").findAll( where = "status='pending'", order = "createdAt", maxRows = 50 ); for (local.email in pending) { try { local.data = deserializeJSON(local.email.data); sendEmail(argumentCollection=local.data); local.email.update(status="sent", sentAt=now()); } catch (any e) { local.email.update( status = "failed", errorMessage = e.message ); } } } ``` ## SMTP Providers ### Gmail ```cfm server = "smtp.gmail.com" port = 587 useTLS = true // Note: Use app password, not account password ``` ### SendGrid ```cfm server = "smtp.sendgrid.net" port = 587 username = "apikey" password = "YOUR_API_KEY" useTLS = true ``` ### Mailgun ```cfm server = "smtp.mailgun.org" port = 587 username = "postmaster@yourdomain.mailgun.org" password = "YOUR_PASSWORD" useTLS = true ``` ### AWS SES ```cfm server = "email-smtp.us-east-1.amazonaws.com" port = 587 username = "YOUR_SMTP_USERNAME" password = "YOUR_SMTP_PASSWORD" useTLS = true ``` ## Related Skills - **wheels-controller-generator**: Create mailer controllers - **wheels-view-generator**: Create email templates - **wheels-auth-generator**: Password reset emails - **wheels-test-generator**: Test email functionality --- **Generated by:** Wheels Email Generator Skill v1.0