ตอนนี้คุณมีเว็บไซต์ที่ใช้ระบบไฟล์แบน (flat file system) แล้ว และอยากได้ฟีดแบ็กจากผู้ใช้ การเพิ่ม Disqus ง่ายเพราะเป็นแค่ JavaScript แทรกในหน้า แต่ไม่ใช่สิ่งที่คุณต้องการ คุณอยากให้พวกเขาส่งอีเมลมาโดยตรง เพื่อให้คุณตอบกลับได้เฉพาะตัว
คุณอาจสร้างระบบ JavaScript ส่งอีเมลจากเครื่องผู้ใช้โดยตรง แต่จะทำให้อีเมลของคุณโดนสแปมเมอร์ดึงจากโค้ดไปขายให้สแปมเมอร์อื่น ดังนั้นคุณต้องซ่อนอีเมลไว้บนเซิร์ฟเวอร์
บทช่วยสอนนี้เกี่ยวกับการเพิ่มระบบส่งข้อความอีเมลไปยัง PressCMS ใหม่ของคุณ (คือ phpPress, rubyPress, nodePress, และ goPress) ผมจะเริ่มจาก front-end แล้วค่อยไปที่ back-end ของแต่ละระบบ สมมติว่าคุณมีเซิร์ฟเวอร์เหล่านี้บนระบบแล้ว
วิธีสร้างฟอร์มในเบราว์เซอร์
โค้ด front-end จะเหมือนกันทุกเซิร์ฟเวอร์ คุณต้องคัดลอกไฟล์ใหม่ๆ ไปยังไดเรกทอรีของแต่ละเซิร์ฟเวอร์ ผมจะอ้างอิง path จากไดเรกทอรีเซิร์ฟเวอร์
แทนที่จะเพิ่มสไตล์ฟอร์มในธีม สคริปต์ฟอร์มนี้รวมทุกอย่างไว้ที่เดียว สร้างไฟล์ questions.html ในไดเรกทอรี site/parts ของเว็บไซต์ เนื้อหาดังนี้:
<!-- Styling for the form -->
<style>
form { margin: 20px auto; width: 400px; padding: 1em; border: 1px solid #f0d5ad; border-radius: 1em; }
form div + div { margin-top: 1em; }
label { display: inline-block; width: 90px; text-align: right; }
input, textarea { width: 300px; -moz-box-sizing: border-box; }
input:focus, textarea:focus { border-color: #000; }
textarea { vertical-align: top; height: 5em; resize: vertical; }
button { margin-top: 10px; margin-left: 8em; }
</style>
<!-- HTML for the Form -->
<form action="/api/message" method="post">
<div>
<label for="Name">Name:</label>
<input id="Name" name="Name" type="text" value="" pattern="[a-zA-Z]{3,3} [a-zA-Z]{3,3}" title="First and Last name." autofocus required />
</div>
<div>
<label for="Email">Email:</label>
<input id="Email" name="Email" type="email" pattern="[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}" title="Valid Email only." value="" required />
</div>
<div>
<label for="Message">Message:</label>
<textarea id="Message" name="Message" type="text" value="" required></textarea>
</div>
<button type="submit">Send Message</button>
</form>
ฟอร์มขอข้อมูล: ชื่อ-นามสกุล (first and last name), อีเมล, และข้อความ ใช้ regular expressions ตรวจสอบความถูกต้องของชื่อและอีเมล หากผู้ใช้กรอกไม่ตรง pattern ใน pattern ฟอร์มจะไม่ส่ง และโชว์ popup ขอให้กรอกให้ถูกต้องตามข้อความใน title แต่ละฟิลด์มี required ป้องกันการส่งฟอร์มว่าง นี่คือ validation ขั้นพื้นฐานขั้นต่ำบน front-end
action ใน form บอกเบราว์เซอร์ว่าส่งข้อมูลไปที่ URL ไหน method บอกว่าเป็น post ข้อมูลฟอร์มจะอยู่ใน URL ของ post request — เรียกว่า Query String เซิร์ฟเวอร์จะประมวลผลข้อมูลใน query string นี้
ในไดเรกทอรี site/pages สร้างไฟล์ contact.md ใส่โค้ดนี้:
### Contact Us
This is a simple contact form. Please fill in your name, first and last, email address, and message. I will get back to you as soon as possible. Thanks.
{{{question}}}
บันทึกแล้วลองเปิดในเบราว์เซอร์: https://localhost:8081/contact
หน้า Contact Us จะเห็นแบบรูปข้างบน สังเกตช่อง Name ถูก focus อัตโนมัติตอนโหลด ด้วย autofocus — UX ที่ดีควร focus ช่องแรกที่ผู้ใช้ต้องพิมพ์
หลังส่งข้อความ ควรมีหน้ายืนยัน ใน site/pages สร้าง messagesent.md ใส่:
### Message was sent Thank you so much for taking time to send me a message. I will reply as soon as possible.
ข้อความง่ายๆ แจ้งว่าส่งสำเร็จ ขยายได้ตามใจชอบ
ประมวลผลฟอร์มด้วย goPress
เพื่อทำความสะอาดข้อความจากผู้ใช้ ใช้ไลบรารี Blue Monday ติดตั้งด้วยคำสั่ง:
go get github.com/microcosm-cc/bluemonday
นี่คือไลบรารี non-standard 唯一ที่ต้องการ
เปิด goPressServer.go เพิ่มใน import ():
"fmt" "github.com/hoisie/web" "net/smtp" "github.com/microcosm-cc/bluemonday"
หลังบรรทัด goPress.DefaultRoutes( เพิ่ม:
//
// Setup special route for our form processing.
//
goPress.SetPostRoute('/api/message', postMessage)
ตั้งค่า post route /api/message ให้รันฟังก์ชัน postMessage() ที่ท้ายไฟล์เพิ่ม:
//
// Function: postMessage
//
// Description: This function will send
// the message from them
// the website to the owner
// of the site.
//
// Inputs:
// ctx The web server
// context.
//
func postMessage(ctx *web.Context) string {
//
// Get the post information and send the
// email.
//
name := ctx.Params["Name"]
from := ctx.Params["Email"]
p := bluemonday.UGCPolicy()
message := p.Sanitize(ctx.Params["Message"])
to := "<your email address>"
subject := "Message from User:" + name + " of CustomCT.com"
sendEmail(to, from, subject, message)
//
// Get the messagesent page contents and
// process it.
//
pgloc := goPress.SiteData.Sitebase + "pages/messagesent"
return goPress.RenderPageContents(ctx, goPress.GetPageContents(pgloc), pgloc)
}
//
// Function: sendEmail
//
// Description: This function sends an
// email message.
//
// Inputs:
// to The email address
// to send the
// message
// from The email address
// of the person
// sending the
// message
// subject The subject of the
// message
// message The message of the
// email
//
func sendEmail(to string, from string, subject string, message string) {
body := fmt.Sprintf("To: %s\r\nSubject: %s\r\n\r\n%s", to, subject, message)
auth := smtp.PlainAuth("", "<your email address>", "<your password>", "smtp.gmail.com")
err := smtp.SendMail("smtp.gmail.com:587", auth, from, []string{to}, []byte(body))
if err != nil {
//
// Process the error. Currently, assuming there
// isn't a problem.
//
}
}
ฟังก์ชันสองตัวนี้จัดการส่งอีเมลจากเบราว์เซอร์ Route /api/message เรียก postMessage() ดึงข้อมูลจากฟอร์ม sanitize ด้วย BlueMonday ส่งอีเมลให้เจ้าของเว็บด้วย sendEmail() แทนที่ <your email address> และ <password> ด้วยของคุณ
ใน goPress.go เพิ่มฟังก์ชันหลัง SetGetRoute():
//
// Function: SetPostRoute
//
// Description: This function gives an
// easy access to the
// web variable setup in
// this library.
//
// Inputs:
// route Route to setup
// handler Function to run that
// route.
//
func SetPostRoute(route string, handler interface{}) {
web.Post(route, handler)
}
เหมือน SetGetRoute() แตกต่างแค่ใช้ web.Post() ตอนนี้ goPress ส่งอีเมลจากผู้ใช้ได้แล้ว
ประมวลผลฟอร์มด้วย nodePress
ส่งอีเมลจาก Node ต้องติดตั้ง nodemailer และ body-parser:
npm install -save nodemailer npm install -save body-parser
ที่บน nodePress.js หลังโหลดไลบรารีล่าสุด เพิ่ม:
var nodemailer = require('nodemailer'); // https://nodemailer.com/
var bodyParser = require('body-parser'); // https://github.com/expressjs/body-parser
//
// create reusable transporter object using
// the default SMTP transport
//
var transporter = nodemailer.createTransport('smtps://<your email name%40<your email domain>:<your password>@smtp.gmail.com');
โหลด nodemailer และตั้งค่า reusable component ส่งอีเมล แทนที่ <your email name> (ชื่อก่อน @), <your email domain> (โดเมน เช่น gmail.com), <your password> (รหัสผ่าน)
หลัง init nodePress variable เพิ่ม:
//
// Configure the body parser library.
//
nodePress.use(bodyParser.urlencoded({ extended: true }));
หลัง nodePress.get() ตัวสุดท้าย เพิ่ม:
nodePress.post('/api/message', function(request, response) {
//
// setup e-mail data
//
var mailOptions = {
from: request.body.Email,
to: '<your email address>',
subject: 'Message from ' + request.body.Name + ' on contact form.',
text: request.body.Message,
html: request.body.Message
};
//
// Send the email.
//
transporter.sendMail(mailOptions, function(error, info){
if(error){
return console.log(error);
}
//
// Send the user to the message was sent okay page.
//
response.send(page("messagesent"));
});
});
Handler สำหรับ /api/message ดึงข้อมูลจากฟอร์ม สร้างอีเมล ส่งไป <your email address> แล้ว redirect ไป /messagesent body-parser เก็บ URL parameters ไว้ใน request.body พร้อม sanitized แล้ว
โค้ดนี้ทำงานกับ Gmail ไม่มี two-factor authentication ถ้ามี 2FA อ่าน docs ของ Nodemailer
ประมวลผลฟอร์มด้วย rubyPress
ส่งอีเมลใน Ruby ต้องติดตั้ง ruby-gmail:
gem install ruby-gmail
อาจต้อง sudo ขึ้นอยู่กับ setup Ruby ที่บน rubyPress.rb เพิ่ม:
require 'gmail' # https://github.com/dcparker/ruby-gmail
หลัง get definitions ทั้งหมด เพิ่ม:
post '/api/message' do
#
# Get the parameters from the form.
#
name = params[:Name]
email = params[:Email]
message = params[:Message]
#
# Create and send the email.
#
Gmail.new('<your email address>', '<your password>') do |gmail|
gmail.deliver do
to "<your email address>"
from email
subject "Message from " + name
text_part do
body message
end
end
end
#
# Send the user to the message sent page.
#
page 'messagesent'
end
เพิ่มแล้ว rubyPress ประมวลผลฟอร์มอีเมลได้ แค่เปลี่ยน <your email address> และ <your password> ให้ตรงกับอีเมลเซิร์ฟเวอร์ของคุณ
ประมวลผลฟอร์มด้วย phpPress
เซิร์ฟเวอร์สุดท้ายคือ phpPress ติดตั้ง phpmailer — ไลบรารี PHP ยอดนิยมสำหรับอีเมล ในไดเรกทอรี phpPress รัน:
composer update composer require phpmailer/phpmailer
เสียดาย composer update จะอัปเดต LightnCandy เร็วกว่าและง่ายกว่า แต่ทำให้โค้ดเซิร์ฟเวอร์พัง ใน index.php หา ProcessPage() แทนที่ด้วย:
//
// Function: ProcessPage
//
// Description: This function will process
// a page into the template,
// process all Mustache
// macros, and process all
// shortcodes.
//
// Inputs:
// $layout The layout for
// the page
// $page The pages main
// contents
//
function ProcessPage( $layout, $page ) {
global $site, $parts, $helpers;
//
// Get the page contents.
//
$parts['content'] = figurePage($page);
//
// First pass on Handlebars.
//
$phpStr = LightnCandy::compile($layout, $helpers);
$renderer = LightnCandy::prepare($phpStr);
$page = $renderer($parts);
//
// Process the shortcodes.
//
$pageShort = processShortcodes($page);
//
// Second pass Handlebars.
//
$phpStr = LightnCandy::compile($pageShort, $helpers);
$renderer = LightnCandy::prepare($phpStr);
$page = $renderer($parts);
//
// Return the results.
//
return($page);
}
เปรียบเทียบกับโค้ดเก่า ไม่ต้องใช้ temporary file ทำใน memory เร็วกว่ามาก ที่บน index.php หลัง Jade library เพิ่ม:
// // PHP Mailer: https://github.com/PHPMailer/PHPMailer // require 'vendor/phpmailer/phpmailer/PHPMailerAutoload.php';
โหลด phpmailer หลัง $app->get() ตัวสุดท้าย เพิ่ม:
//
// This route is for processing the post request from the
// email form on the website.
//
$app->post('/api/message', function(Request $request, Response $response) {
global $_POST;
//
// Get the post variables.
//
$Name = $_POST['Name'];
$Email = $_POST['Email'];
$Message = $_POST['Message'];
//
// Create the email message and send it.
//
$mail = new PHPMailer;
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.gmail.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = '<your email address>'; // SMTP username
$mail->Password = '<your password>'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587; // TCP port to connect to
$mail->setFrom($Email, $Name);
$mail->addAddress('<your email>', '<your name>'); // Add a recipient
$mail->Subject = "Message from $Name";
$mail->Body = $Message;
if(!$mail->send()) {
echo 'Message could not be sent.';
echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
$newResponse = SetBasicHeader($response);
$newResponse->getBody()->write(page('messagesent'));
return($newResponse);
}
});
Post request handler สำหรับ /api/message ดึงข้อมูลฟอร์ม สร้างอีเมล ส่ง PHP ใส่ URL parameters ใน global array $_POST อัตโนมัติ
แทนที่ <your email address>, <your password>, <your name> ให้ตรงกับอีเมลของคุณ ถ้าไม่ใช่ Gmail SMTP เปลี่ยน $mail->Host ตามนั้น
สรุป
ผมแสดงวิธีเพิ่มฟอร์มอีเมลไปยัง pressCMS อย่างง่าย ไฟล์ดาวน์โหลดมีเซิร์ฟเวอร์ทั้งหมดพร้อมแก้ไขแล้ว ดาวน์โหลดไปเลยแทนการพิมพ์ ผมทำ error handling นิดหน่อย ส่วนที่เหลือเป็นการบ้าน
วิธีนี้ส่งข้อมูลฟอร์มผ่าน URL (query string) เว็บสมัยใหม่มักใช้ REST API พร้อม JSON ใน body แทน โร틴เหล่านี้ปรับใช้ได้ง่าย แต่เป็นการบ้าน (หรือ tutorial หน้า) ตอนนี้คุณรู้วิธีนี้แล้ว ลองเพิ่มฟอร์มของตัวเองไป การปรับแต่งแบบนี้สนุกมาก ขีดจำกัดคือจินตนาการของคุณ