reledit: stuff the value into its own div and properly hide it when necessary (but dont lump it with the landingzone div for it switches the form on when one clicks on a value to traverse it)
"""Common utilies to format / semd emails.:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"frombase64importb64encode,b64decodefromitertoolsimportrepeatfromtimeimporttimefromemail.MIMEMultipartimportMIMEMultipartfromemail.MIMETextimportMIMETextfromemail.MIMEImageimportMIMEImagefromemail.HeaderimportHeadertry:fromsocketimportgethostnameexceptImportError:defgethostname():# gaereturn'XXX'fromcubicweb.viewimportEntityViewfromcubicweb.entityimportEntitydefheader(ustring):returnHeader(ustring.encode('UTF-8'),'UTF-8')defaddrheader(uaddr,uname=None):# even if an email address should be ascii, encode it using utf8 since# automatic tests may generate non ascii email addressaddr=uaddr.encode('UTF-8')ifuname:return'%s <%s>'%(header(uname).encode(),addr)returnaddrdefconstruct_message_id(appid,eid,withtimestamp=True):ifwithtimestamp:addrpart='eid=%s×tamp=%.10f'%(eid,time())else:addrpart='eid=%s'%eid# we don't want any equal sign nor trailing newlinesleftpart=b64encode(addrpart,'.-').rstrip().rstrip('=')return'<%s@%s.%s>'%(leftpart,appid,gethostname())defparse_message_id(msgid,appid):ifmsgid[0]=='<':msgid=msgid[1:]ifmsgid[-1]=='>':msgid=msgid[:-1]try:values,qualif=msgid.split('@')padding=len(values)%4values=b64decode(str(values+'='*padding),'.-')values=dict(v.split('=')forvinvalues.split('&'))fromappid,host=qualif.split('.',1)except:returnNoneifappid!=fromappidorhost!=gethostname():returnNonereturnvaluesdefformat_mail(uinfo,to_addrs,content,subject="",cc_addrs=(),msgid=None,references=(),config=None):"""Sends an Email to 'e_addr' with content 'content', and subject 'subject' to_addrs and cc_addrs are expected to be a list of email address without name """asserttype(content)isunicode,repr(content)msg=MIMEText(content.encode('UTF-8'),'plain','UTF-8')# safety: keep only the first newlinesubject=subject.splitlines()[0]msg['Subject']=header(subject)ifuinfo.get('email'):email=uinfo['email']elifconfigandconfig['sender-addr']:email=unicode(config['sender-addr'])else:email=u''ifuinfo.get('name'):name=uinfo['name']elifconfigandconfig['sender-addr']:name=unicode(config['sender-name'])else:name=u''msg['From']=addrheader(email,name)ifconfigandconfig['sender-addr']andconfig['sender-addr']!=email:appaddr=addrheader(config['sender-addr'],config['sender-name'])msg['Reply-to']='%s, %s'%(msg['From'],appaddr)elifemail:msg['Reply-to']=msg['From']ifconfigisnotNone:msg['X-CW']=config.appidunique_addrs=lambdaaddrs:sorted(set(addrforaddrinaddrsifaddrisnotNone))msg['To']=', '.join(addrheader(addr)foraddrinunique_addrs(to_addrs))ifcc_addrs:msg['Cc']=', '.join(addrheader(addr)foraddrinunique_addrs(cc_addrs))ifmsgid:msg['Message-id']=msgidifreferences:msg['References']=', '.join(references)returnmsgclassHtmlEmail(MIMEMultipart):def__init__(self,subject,textcontent,htmlcontent,sendermail=None,sendername=None,recipients=None,ccrecipients=None):MIMEMultipart.__init__(self,'related')self['Subject']=header(subject)self.preamble='This is a multi-part message in MIME format.'# Attach alternative text messagealternative=MIMEMultipart('alternative')self.attach(alternative)msgtext=MIMEText(textcontent.encode('UTF-8'),'plain','UTF-8')alternative.attach(msgtext)# Attach html messagemsghtml=MIMEText(htmlcontent.encode('UTF-8'),'html','UTF-8')alternative.attach(msghtml)ifsendermailorsendername:self['From']=addrheader(sendermail,sendername)ifrecipients:self['To']=', '.join(addrheader(addr)foraddrinrecipientsifaddrisnotNone)ifccrecipients:self['Cc']=', '.join(addrheader(addr)foraddrinccrecipientsifaddrisnotNone)defattach_image(self,data,htmlId):image=MIMEImage(data)image.add_header('Content-ID','<%s>'%htmlId)self.attach(image)classNotificationView(EntityView):"""abstract view implementing the "email" API (eg to simplify sending notification) """# XXX refactor this class to work with len(rset) > 1msgid_timestamp=True# this is usually the method to calldefrender_and_send(self,**kwargs):"""generate and send an email message for this view"""delayed=kwargs.pop('delay_to_commit',None)forrecipients,msginself.render_emails(**kwargs):ifdelayedisNone:self.send(recipients,msg)elifdelayed:self.send_on_commit(recipients,msg)else:self.send_now(recipients,msg)defcell_call(self,row,col=0,**kwargs):self.w(self.req._(self.content)%self.context(**kwargs))defrender_emails(self,**kwargs):"""generate and send emails for this view (one per recipient)"""self._kwargs=kwargsrecipients=self.recipients()ifnotrecipients:self.info('skipping %s notification, no recipients',self.id)returnifself.rsetisnotNone:entity=self.entity(self.rowor0,self.color0)# if the view is using timestamp in message ids, no way to reference# previous emailifnotself.msgid_timestamp:refs=[self.construct_message_id(eid)foreidinentity.notification_references(self)]else:refs=()msgid=self.construct_message_id(entity.eid)else:refs=()msgid=Nonereq=self.reqself.user_data=req.user_data()origlang=req.langforsomethinginrecipients:ifisinstance(something,Entity):# hi-jack self.req to get a session for the returned userself.req=self.req.hijack_user(something)emailaddr=something.get_email()else:emailaddr,lang=somethingself.req.set_language(lang)# since the same view (eg self) may be called multiple time and we# need a fresh stream at each iteration, reset it explicitlyself.w=None# XXX call render before subject to set .row/.col attributes on the# viewtry:content=self.render(row=0,col=0,**kwargs)subject=self.subject()exceptSkipEmail:continuemsg=format_mail(self.user_data,[emailaddr],content,subject,config=self.config,msgid=msgid,references=refs)yield[emailaddr],msg# restore languagereq.set_language(origlang)# recipients / email sending ###############################################defrecipients(self):"""return a list of either 2-uple (email, language) or user entity to who this email should be sent """# use super_session when available, we don't want to consider security# when selecting recipients_findertry:req=self.req.super_sessionexceptAttributeError:req=self.reqfinder=self.vreg['components'].select('recipients_finder',req,rset=self.rset,row=self.rowor0,col=self.color0)returnfinder.recipients()defsend_now(self,recipients,msg):self.config.sendmails([(msg,recipients)])defsend_on_commit(self,recipients,msg):raiseNotImplementedErrorsend=send_now# email generation helpers #################################################defconstruct_message_id(self,eid):returnconstruct_message_id(self.config.appid,eid,self.msgid_timestamp)defformat_field(self,attr,value):return':%(attr)s: %(value)s'%{'attr':attr,'value':value}defformat_section(self,attr,value):return'%(attr)s\n%(ul)s\n%(value)s\n'%{'attr':attr,'ul':'-'*len(attr),'value':value}defsubject(self):entity=self.entity(self.rowor0,self.color0)subject=self.req._(self.message)etype=entity.dc_type()eid=entity.eidlogin=self.user_data['login']returnself.req._('%(subject)s%(etype)s #%(eid)s (%(login)s)')%locals()defcontext(self,**kwargs):entity=self.entity(self.rowor0,self.color0)forkey,valinkwargs.iteritems():ifvalandisinstance(val,unicode)andval.strip():kwargs[key]=self.req._(val)kwargs.update({'user':self.user_data['login'],'eid':entity.eid,'etype':entity.dc_type(),'url':entity.absolute_url(),'title':entity.dc_long_title(),})returnkwargsclassSkipEmail(Exception):"""raise this if you decide to skip an email during its generation"""