Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Ticket #244 (closed enhancement: fixed)

Opened 13 years ago

Last modified 12 years ago

email templates in HTML

Reported by: Alvin Wang Owned by: anonymous
Priority: normal Milestone: 1.0
Component: TurboGears Version:
Severity: normal Keywords:
Cc:

Description

This is very simple to use

Automatic subject line generation Automatic text conversion

Config in dev.cfg email.smtp_server ="YOUR SMTP SERVER" email.smtp_user = "YOUR SMTP USER ID" email.smtp_password ="YOUR SMTP PASSWORD" email.smtp_fromaddr ="YOUR Default From Address"

Usage import turbogears.email

sendkidemail( 'email@…', 'project.templates.welcome', dict_args)

email.py

import turbogears
import cherrypy
import smtplib


# Chunks of code taken from Python Cookbook - Art Gillespie then updated to 2.4 with SMTP authentication
#
# email.smtp_server ="YOUR SMTP SERVER"
# email.smtp_user = "YOUR SMTP USER ID"
# email.smtp_password ="YOUR SMTP PASSWORD"
# email.smtp_fromaddr ="YOUR Default From Address"

class Email:

	def testmail(self, to_addr, from_addr=None):
		html = "<html><body><h1>It works</h1></body></html>"
		text = "Text Message - It Works"
		subject = "TurboEmail Test"

		if from_addr ==None:
			from_addr = cherrypy.config.get('email.from_addr')
		self.sendemail( to_addr, html, text, subject, from_addr)
		return

	def sendkidemail(self, to_addr, kid_template, dict_args, from_addr=None, subject=None ):
		import kid
		import time
		import turbogears.controllers

		# set defaults

		html = turbogears.controllers._process_output('html', dict_args, kid_template)
		self.sendemail( to_addr, html, from_addr)
		return

	def sendemail(self, to_addr, html, from_addr=None, text=None, subject=None, ):
		# set defaults
		smtp_server = cherrypy.config.get('email.smtp_server', 'localhost')
		smtp_user = cherrypy.config.get('email.smtp_user')
		smtp_password = cherrypy.config.get('email.smtp_password')

		if from_addr ==None:
			from_addr = cherrypy.config.get('email.from_addr')

		if text == None:
			text = self.html2text(html)

		if subject == None:
			subject = self.get_title(html)

		msg = self.createhtmlmail(from_addr, to_addr, html, text, subject)
		s = smtplib.SMTP(smtp_server)
# Remove to get more debug messages
#		s.set_debuglevel(1)

# None - not tested, my smtp server has a password
		if smtp_user != None:
			s.login(smtp_user, smtp_password)
		s.sendmail( from_addr, to_addr, msg)
		s.close()
		return


# returns a message to send
	def createhtmlmail(self, from_addr, to_addr, html, text, subject):
   		import MimeWriter
		import mimetools
		import cStringIO

		out = cStringIO.StringIO() # output buffer for our message
		htmlin = cStringIO.StringIO(html)
	 	txtin = cStringIO.StringIO(text)

		writer = MimeWriter.MimeWriter(out)
		#
		# set up some basic headers... we put subject here
		# because smtplib.sendmail expects it to be in the
		# message body
		#
		writer.addheader("Subject", subject)
		writer.addheader("To", to_addr)
		writer.addheader("From", from_addr)
		writer.addheader("MIME-Version", "1.0")
		#
		# start the multipart section of the message
		# multipart/alternative seems to work better
		# on some MUAs than multipart/mixed
		#
		writer.startmultipartbody("alternative")
		writer.flushheaders()
		#
		# the plain text section
		#
		subpart = writer.nextpart()
		subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
		pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
		mimetools.encode(txtin, pout, 'quoted-printable')
		txtin.close()
		#
		# start the html subpart of the message
		#
		subpart = writer.nextpart()
		subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
		#
		# returns us a file-ish object we can write to
		#
		pout = subpart.startbody("text/html", [("charset", 'ISO-8859-1')])
		mimetools.encode(htmlin, pout, 'quoted-printable')
		htmlin.close()
		#
		# Now that we're done, close our writer and
		# return the message body
		#
		writer.lastpart()
		msg = out.getvalue()
		out.close()
		return msg

	def html2text(self, html):
		import formatter
		import htmllib
		from cStringIO import StringIO

		outstream = StringIO()
		w = formatter.DumbWriter(outstream)
		f = formatter.AbstractFormatter(w)
		p = htmllib.HTMLParser(f)
		p.feed(html)
		p.close()
		text = outstream.getvalue()
		outstream.close()
		return text

# use regular expressions to extract the title for subject line
	def get_title(self, html):
		import re

		p = re.compile('<title[^>]*>(.*?)</title>', re.IGNORECASE)
		m = p.search(html)
		if m:
		    p = re.compile( '<[^>]*>')
		    title = p.sub('', m.group(0))
		    return title
		# set your own default
		return "Email from Turbogears"

Change History

comment:1 Changed 13 years ago by anonymous

Requested enhancements

  • send text email
  • text text from Kid
  • move default subject line to a config statement

Changed classname to TurboEmail? to avoid conflict with email package. Plus this is useless without TurboGears anyway.

4 useful functions and 1 not so useful one testmail(self, to_addr, from_addr=None) sendKIDemail(self, to_addr, kid_template, dict_args, from_addr=None, subject=None ) sendHTMLemail(self, to_addr, html, from_addr=None, text=None, subject=None ) sendTEXTemail(self, to_addr, text, subject=None, from_addr=None) sendKIDTEXTemail(self, to_addr, kid_template, dict_args, from_addr=None, subject=None )

import turbogears
import cherrypy
import smtplib


# Chunks of code taken from Python Cookbook - Art Gillespie then updated to 2.4 with SMTP authentication
#
# email.smtp_server ="YOUR SMTP SERVER"
# email.smtp_user = "YOUR SMTP USER ID"
# email.smtp_password ="YOUR SMTP PASSWORD"
# email.smtp_fromaddr ="YOUR Default From Address"
# email.default_subject ="YOUR Default Subject"

class TurboEmail:

	def testmail(self, to_addr, from_addr=None):
		html = "<html><body><h1>It works</h1></body></html>"
		text = "Text Message - It Works"
		subject = "TurboEmail Test"

		if from_addr ==None:
			from_addr = cherrypy.config.get('email.from_addr')
		self.sendemail( to_addr, html, text, subject, from_addr)
		return

	def sendKIDemail(self, to_addr, kid_template, dict_args, from_addr=None, subject=None ):
		import turbogears.controllers

		# set defaults

		html = turbogears.controllers._process_output('html', dict_args, kid_template)
		self.sendHTMLemail( to_addr, html, from_addr)
		return

	def sendHTMLemail(self, to_addr, html, from_addr=None, text=None, subject=None ):
		# set defaults
		smtp_server = cherrypy.config.get('email.smtp_server', 'localhost')
		smtp_user = cherrypy.config.get('email.smtp_user')
		smtp_password = cherrypy.config.get('email.smtp_password')

		if from_addr ==None:
			from_addr = cherrypy.config.get('email.from_addr')

		if text == None:
			text = self.html2text(html)

		if subject == None:
			subject = self.get_title(html)

		msg = self.createHTMLmail(from_addr, to_addr, html, text, subject)
		s = smtplib.SMTP(smtp_server)
# Remove to get more debug messages
#		s.set_debuglevel(1)

# None - not tested, my smtp server has a password
		if smtp_user != None:
			s.login(smtp_user, smtp_password)
		s.sendmail( from_addr, to_addr, msg)
		s.close()
		return


# returns a message to send
	def createHTMLmail(self, from_addr, to_addr, html, text, subject):
   		import MimeWriter
		import mimetools
		import cStringIO

		out = cStringIO.StringIO() # output buffer for our message
		htmlin = cStringIO.StringIO(html)
	 	txtin = cStringIO.StringIO(text)

		writer = MimeWriter.MimeWriter(out)
		#
		# set up some basic headers... we put subject here
		# because smtplib.sendmail expects it to be in the
		# message body
		#
		writer.addheader("Subject", subject)
		writer.addheader("To", to_addr)
		writer.addheader("From", from_addr)
		writer.addheader("MIME-Version", "1.0")
		#
		# start the multipart section of the message
		# multipart/alternative seems to work better
		# on some MUAs than multipart/mixed
		#
		writer.startmultipartbody("alternative")
		writer.flushheaders()
		#
		# the plain text section
		#
		subpart = writer.nextpart()
		subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
		pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
		mimetools.encode(txtin, pout, 'quoted-printable')
		txtin.close()
		#
		# start the html subpart of the message
		#
		subpart = writer.nextpart()
		subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
		#
		# returns us a file-ish object we can write to
		#
		pout = subpart.startbody("text/html", [("charset", 'ISO-8859-1')])
		mimetools.encode(htmlin, pout, 'quoted-printable')
		htmlin.close()
		#
		# Now that we're done, close our writer and
		# return the message body
		#
		writer.lastpart()
		msg = out.getvalue()
		out.close()
		return msg

	def sendKIDTEXTemail(self, to_addr, kid_template, dict_args, from_addr=None, subject=None ):
		import turbogears.controllers

		# set defaults
		html = turbogears.controllers._process_output('html', dict_args, kid_template)
		subject = self.get_title(html)
		text = self.html2text(html)
		self.sendTEXTemail( to_addr, text, subject, from_addr)
		return

	def sendTEXTemail(self, to_addr, text, subject=None, from_addr=None):
		# set defaults
		smtp_server = cherrypy.config.get('email.smtp_server', 'localhost')
		smtp_user = cherrypy.config.get('email.smtp_user')
		smtp_password = cherrypy.config.get('email.smtp_password')

		if from_addr ==None:
			from_addr = cherrypy.config.get('email.from_addr')

		if subject == None:
			subject = cherrypy.config.get('email.default_subject', 'Email from Turbogears')

		msg = self.createTEXTmail(from_addr, to_addr, text, subject)
		s = smtplib.SMTP(smtp_server)
# Remove to get more debug messages
#		s.set_debuglevel(1)

		if smtp_user != None:
			s.login(smtp_user, smtp_password)
		s.sendmail( from_addr, to_addr, msg)
		s.close()
		return

# returns a message to send
	def createTEXTmail(self, from_addr, to_addr, text, subject=None):

		if subject == None:
			subject = self.get_title(html)

		msg = "Subject: " + subject +"\n"
		msg += "From: " + from_addr +"\n"
  		msg += "To: " + to_addr + "\n\n"
		msg += text
		return msg

	def html2text(self, html):
		import formatter
		import htmllib
		from cStringIO import StringIO

		outstream = StringIO()
		w = formatter.DumbWriter(outstream)
		f = formatter.AbstractFormatter(w)
		p = htmllib.HTMLParser(f)
		p.feed(html)
		p.close()
		text = outstream.getvalue()
		outstream.close()
		return text

# use regular expressions to extract the title for subject line
	def get_title(self, html):
		import re

		p = re.compile('<title[^>]*>(.*?)</title>', re.IGNORECASE)
		m = p.search(html)
		if m:
		    p = re.compile( '<[^>]*>')
		    title = p.sub('', m.group(0))
		    return title
		return cherrypy.config.get('email.default_subject', 'Email from Turbogears')

comment:2 Changed 13 years ago by anonymous

How about, since there's a lot of Linux TurboGears users, there's an option to use sendmail? That would be very nice :).

comment:3 Changed 13 years ago by ischenko@…

I believe this will work poorly outside US-ASCII environment: charsets are hardcoded and no text conversion is done.

As an example, here are some routines I used:

def Header(text):
    "A wrapper over L{email.Header} which auto-encodes unicode strings in UTF-8."
    if type(text) == type(u''):
        return _Header(text.encode('utf8'), 'UTF-8')
    return _Header(text)

def MIMEText(content, _subtype='plain'):
    content, charset = get_text_mime(content)
    return _MIMEText(content, _subtype=_subtype, _charset=charset)

def get_text_mime(text):
    """
    Coerces text to UTF-8 and returns proper charset name.

    Original implementation used UTF-8 only for Unicode input, and used us-ascii
    for plain strings. Unfortunately, the resulting messages sometimes were
    displayed incorrecty in Outlook Express and then I decided to _always_ use UTF-8
    encoding.
    """
    charset = 'UTF-8'
    if type(text) == type(u''):
        text = text.encode(charset)
    else:
        text = unicode(text, charset, 'strict').encode(charset)
    return text, charset

comment:4 Changed 13 years ago by kevin

  • Milestone set to 1.0

comment:5 Changed 13 years ago by kevin

  • Component changed from CherryPy to TurboGears
  • Summary changed from [PATCH] email templates in HTML to email templates in HTML

This code is a start. It needs better i18n, PEP8-style names and should probably change from a class to functions.

comment:6 Changed 13 years ago by godoy

Kevin, I believe this could be closed and it should be some kind of widget or plugin for TG (I'd go with "widget"). This, IMVHO, is very application specific and can be easily automated as of today with lynx, links, w3m, and other text browsers and the scheduler of the operating system (what makes it more "postponable") with the benefit of being able to send both HTML and text email.

Besides that, it would free us from having to worry with email authentication, remote servers, etc. in the core of TG...

comment:7 Changed 13 years ago by jvanasco@…

FYI, chances in the 962 branch to turbogrears.contollers. _process_output()

was:

def _process_output(tg_format, output, template, fragment=False):

now:

def _process_output(output, template, format, content_type, mapping, fragment=False):

broke this patch.

its probably broken from other changes too. haven't foud them yet.

comment:8 Changed 13 years ago by jorge.vargas

I agree with godoy, I don't see why turboemail should be inside the framework. although I think a plugin will be better then a widget since you may want to use this without a GUI frontend, like a confirmation email or some kind of email reminder.

comment:9 Changed 12 years ago by alberto

  • Status changed from new to closed
  • Resolution set to fixed

 TurboMail now provides this functionallity and more... I'll close this.

Note: See TracTickets for help on using tickets.