| 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
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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: ##333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background: ##007bff;
color: white;
padding: 20px;
text-align: center;
}
.content {
background: ##f9f9f9;
padding: 30px;
border: 1px solid ##ddd;
}
.button {
display: inline-block;
padding: 12px 24px;
background: ##007bff;
color: white;
text-decoration: none;
border-radius: 4px;
margin: 20px 0;
}
.footer {
text-align: center;
padding: 20px;
color: ##666;
font-size: 12px;
}
</style>
</head>
<body>
<div class="header">
<h1>Your App Name</h1>
</div>
<div class="content">
<cfoutput>##contentForLayout()##</cfoutput>
</div>
<div class="footer">
<p>© #Year(Now())# Your Company. All rights reserved.</p>
<p>
<a href="##">Unsubscribe</a> |
<a href="##">Privacy Policy</a> |
<a href="##">Contact Us</a>
</p>
</div>
</body>
</html>
Email Layout (Plain Text) - views/mailer/layouts/email.txt.cfm
<cfoutput>
========================================
YOUR APP NAME
========================================
##contentForLayout()##
========================================
© #Year(Now())# Your Company
All rights reserved.
Unsubscribe: [URL]
Privacy: [URL]
========================================
</cfoutput>
Email Template Examples
Welcome Email (HTML) - views/mailer/welcome.cfm
<cfparam name="user">
<cfoutput>
<h2>Welcome, ##user.firstName##!</h2>
<p>Thank you for joining our community. We're excited to have you on board.</p>
<p>Here's what you can do next:</p>
<ul>
<li>Complete your profile</li>
<li>Explore our features</li>
<li>Connect with other users</li>
</ul>
<p>
<a href="##URLFor(route='dashboard', onlyPath=false)##" class="button">
Get Started
</a>
</p>
<p>If you have any questions, feel free to reply to this email.</p>
<p>Best regards,<br>
The Team</p>
</cfoutput>
Welcome Email (Plain Text) - views/mailer/welcome.txt.cfm
<cfparam name="user">
<cfoutput>
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
</cfoutput>
Password Reset Email - views/mailer/resetPassword.cfm
<cfparam name="user">
<cfparam name="resetUrl">
<cfoutput>
<h2>Reset Your Password</h2>
<p>Hi ##user.firstName##,</p>
<p>We received a request to reset your password. Click the button below to create a new password:</p>
<p>
<a href="##resetUrl##" class="button">Reset Password</a>
</p>
<p>Or copy and paste this link into your browser:</p>
<p>##resetUrl##</p>
<p><strong>This link will expire in 24 hours.</strong></p>
<p>If you didn't request a password reset, you can safely ignore this email.</p>
<p>Thanks,<br>
The Security Team</p>
</cfoutput>
Email Configuration - config/settings.cfm
<cfscript>
// 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
);
}
</cfscript>
Using the Mailer
In Controllers
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
// 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
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
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
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
// 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
// Order confirmation, password resets, account verification
- Time-sensitive
- User-triggered
- High priority
2. Notification Emails
// Activity updates, mentions, reminders
- Event-driven
- May be batched
- User preferences apply
3. Marketing Emails
// Newsletters, promotions
- Bulk sending
- Unsubscribe required
- Scheduled
Email Queue Pattern
// 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
server = "smtp.gmail.com"
port = 587
useTLS = true
// Note: Use app password, not account password
SendGrid
server = "smtp.sendgrid.net"
port = 587
username = "apikey"
password = "YOUR_API_KEY"
useTLS = true
Mailgun
server = "smtp.mailgun.org"
port = 587
username = "postmaster@yourdomain.mailgun.org"
password = "YOUR_PASSWORD"
useTLS = true
AWS SES
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