def deleteObjectsByPaths(self, paths, handle_errors=True, REQUEST=None): failure = {} success = [] # use the portal for traversal in case we have relative paths portal = getToolByName(self, "portal_url").getPortalObject() traverse = portal.restrictedTraverse for path in paths: # Skip and note any errors if handle_errors: sp = transaction.savepoint(optimistic=True) try: obj = traverse(path) obj_parent = aq_parent(aq_inner(obj)) obj_parent.manage_delObjects([obj.getId()]) success.append("%s (%s)" % (obj.getId(), path)) except ConflictError: raise except LinkIntegrityNotificationException: raise except Exception, e: if handle_errors: sp.rollback() failure[path] = e log_exc() else: raise
def ensureScenariosInTarget(original, copy): my_scenarios = original.doc_extension(ELAN_APP).scenarios scen_source = original.myDocumentPool().contentconfig.scen scen = copy.myDocumentPool().contentconfig.scen new_scenarios = [] wftool = getToolByName(original, 'portal_workflow') for scenario in my_scenarios: if scen.hasObject(scenario): s = scen._getOb(scenario) if wftool.getInfoFor(s, 'review_state') == 'private': sscen = s.Substitute and s.Substitute.to_object or None if sscen and sscen.canBeAssigned(): substitute = sscen.getId() new_scenarios.append(substitute) else: new_scenarios.append(scenario) else: new_scenarios.append(scenario) else: s = scen_source._getOb(scenario) id = _copyPaste(s,scen) new_scen = scen._getOb(id) wftool = getToolByName(original, 'portal_workflow') wftool.doActionFor(new_scen, 'retract') new_scenarios.append(id) try: copy.doc_extension(ELAN_APP).scenarios = new_scenarios except Exception, e: log_exc(e)
def deleteObjectsByPaths(self, paths, handle_errors=True, REQUEST=None): log_deprecated("deleteObjectsByPaths is deprecated, you should use. " "plone.api.content.delete. This method no longer does link integrity checks") # noqa failure = {} success = [] # use the portal for traversal in case we have relative paths portal = getToolByName(self, 'portal_url').getPortalObject() traverse = portal.restrictedTraverse for path in paths: # Skip and note any errors if handle_errors: sp = transaction.savepoint(optimistic=True) try: obj = traverse(path) obj_parent = aq_parent(aq_inner(obj)) obj_parent.manage_delObjects([obj.getId()]) success.append('%s (%s)' % (obj.getId(), path)) except ConflictError: raise except Exception, e: if handle_errors: sp.rollback() failure[path] = e log_exc() else: raise
def dpAdded(self): """ """ fresh = True if self.hasObject("esd"): fresh = False # It's a reinstall copyCurrentSituation(self, fresh) transaction.commit() createBasicPortalStructure(self, fresh) transaction.commit() createContentConfig(self, fresh) transaction.commit() if fresh: self.esd.correctAllDocTypes() transaction.commit() connectTypesAndCategories(self) placeful_wf = getToolByName(self, 'portal_placeful_workflow') try: self.archive.manage_addProduct['CMFPlacefulWorkflow'].manage_addWorkflowPolicyConfig() except BadRequest, e: # print type(e) log_exc(e) config = placeful_wf.getWorkflowPolicyConfig(self.archive) placefulWfName = 'elan-archive' config.setPolicyIn(policy=placefulWfName, update_security=False) config.setPolicyBelow(policy=placefulWfName, update_security=False) createELANUsers(self) createELANGroups(self) setELANLocalRoles(self) self.reindexAll()
def _purgeDocument(self, source_brain): """ Delete utility """ from docpool.elan.config import ELAN_APP from docpool.elan.behaviors.elandocument import IELANDocument source_obj = source_brain.getObject() # determine parent folder for copy scns = None try: scns = IELANDocument(source_obj).scenarios except: # Object could have lost its ELAN behavior but that means we can potentially delete it scns = ['dummy'] if len(scns) == 1: # only the one scenario --> potential delete # Check for other applications than ELAN apps = ILocalBehaviorSupport(source_obj).local_behaviors if apps and len(apps) > 1: # There are others --> only remove ELAN behavior try: apps.remove(ELAN_APP) ILocalBehaviorSupport(source_obj).local_behaviors = list(set(apps)) except Exception, e: log_exc(e) else: # we delete p = parent(source_obj) p.manage_delObjects([source_obj.getId()])
def getSettings(site): registry = site.getSiteManager().getUtility(IRegistry) try: return registry.forInterface(ISettings) except: log_exc() raise ComponentLookupError(ISettings)
def __getitem__(self, id): # Zope's inner acquisition chain for objects returned by __getitem__ # will be: # # portal -> portal_factory -> temporary_folder -> object # # What we really want is for the inner acquisition chain to be: # # intended_parent_folder -> portal_factory -> temporary_folder -> # object # # So we need to rewrap... portal_factory = aq_parent(aq_inner(self)) intended_parent = aq_parent(portal_factory) # If the intended parent has an object with the given id, just do a # passthrough if hasattr(intended_parent, id): return getattr(intended_parent, id) # rewrap portal_factory portal_factory = aq_base(portal_factory).__of__(intended_parent) # rewrap self temp_folder = aq_base(self).__of__(portal_factory) if id in self: return (aq_base(self._getOb(id)).__of__(temp_folder)) \ .__of__(intended_parent) else: type_name = self.getId() try: # We fake an archetype tool which returns no catalogs for the # object to be indexed in to avoid it showing up in the catalog # in the first place. self.archetype_tool = FauxArchetypeTool( getToolByName(self, 'archetype_tool')) _createObjectByType(type_name, self, id) except ConflictError: raise except: # some errors from invokeFactory (AttributeError, maybe others) # get swallowed -- dump the exception to the log to make sure # developers can see what's going on log_exc(severity=logging.DEBUG) raise obj = self._getOb(id) # keep obj out of the catalog obj.unindexObject() # additionally keep it out of Archetypes UID and refs catalogs if base_hasattr(obj, '_uncatalogUID'): obj._uncatalogUID(obj) if base_hasattr(obj, '_uncatalogRefs'): obj._uncatalogRefs(obj) return (aq_base(obj).__of__(temp_folder)).__of__(intended_parent)
def connectTypesAndCategories(self): """ """ from docpool.elan.behaviors.elandoctype import IELANDocType # print self.config.dtypes.eventinformation.type_extension(ELAN_APP) try: self.config.dtypes.notification.type_extension(ELAN_APP).setCCategory('notifications') except Exception, e: log_exc(e)
def objektdatensatz(self, typ, **pkvals): """ """ klass = self.getKlass(typ) try: res = __session__.query(klass).filter_by(**pkvals).one() return res except Exception, e: log_exc(e) return None
def createActions(self): """ """ log("Creating Collaboration Folder") placeful_wf = getToolByName(self, 'portal_placeful_workflow') try: self.manage_addProduct['CMFPlacefulWorkflow'].manage_addWorkflowPolicyConfig() except BadRequest, e: log_exc(e)
def delete_handler(object, event): """ Called when an undeleteable object is removed. Only allowed for Managers. """ log("Deleting protected object " + object.getId()) try: if not object.isAdmin(): raise Unauthorized except Exception, e: log_exc(e)
def objekteImportieren(self, typ, importfile, request, context=None): """ """ try: meldung, status = imports.genericImportFromCSV(self, importfile, typ, request, context=context) log("Daten importiert.") return meldung, status except "Exception", e: log_exc("Daten konnten nicht importiert werden: %s" % importfile) return [ str(e) ], False
def createTransferArea(self, fresh): """ """ createPloneObjects(self.content, TRANSFER_AREA, fresh) # Move to first position self.content.moveObject("Transfers", 0) placeful_wf = getToolByName(self, 'portal_placeful_workflow') try: self.content.Transfers.manage_addProduct['CMFPlacefulWorkflow'].manage_addWorkflowPolicyConfig() except BadRequest, e: # print type(e) log_exc(e)
def sendEmail(self, addresses, subject, rstText, cc = None): """ Send a email to the list of addresses """ portal_url = getToolByName(self.context, 'portal_url') plone_utils = getToolByName(self.context, 'plone_utils') portal = portal_url.getPortalObject() mailHost = plone_utils.getMailHost() charset = self.charset from_address = portal.getProperty('email_from_address', '') if not from_address: logger.info('Cannot send notification email: email sender address not set') return from_name = portal.getProperty('email_from_name', '') mfrom = formataddr((from_name, from_address)) if parseaddr(mfrom)[1] != from_address: # formataddr probably got confused by special characters. mfrom - from_address # We must choose the body charset manually for body_charset in (charset, 'UTF-8', 'US-ASCII'): try: rstText = rstText.encode(body_charset) except UnicodeError: pass else: break email_msg = MIMEText(rstText, 'plain', body_charset) subject = safe_unicode(subject, charset) for address in addresses: address = safe_unicode(address, charset) if address: try: # Note that charset is only used for the headers, not # for the body text as that is a Message already. mailHost.secureSend(message = email_msg, mto = address, mfrom = mfrom, subject = subject, charset = charset) except socket.error, exc: log_exc(('Could not send email from %s to %s regarding issue ' 'in content %s\ntext is:\n%s\n') % ( mfrom, address, self.context.absolute_url(), email_msg)) log_exc("Reason: %s: %r" % (exc.__class__.__name__, str(exc))) except: raise
def deleteUsers(self): """ """ prefix = (hasattr(self, 'prefix') and self.prefix) or self.getId() prefix = str(prefix) mtool = getToolByName(self, 'portal_membership', None) # list all users for this ESD and delete them uids = mtool.listMemberIds() for uid in uids: if uid.startswith(prefix): try: mtool.deleteMembers([uid]) # also deletes the member folders except Exception, e: log_exc(e)
def eventPublished(obj, event=None): if event.__dict__['action'] == 'publish': # Update all objects for this scenario m = obj.content mpath = "/".join(m.getPhysicalPath()) args = {'portal_type': 'DPDocument', 'path': mpath} cat = getToolByName(obj, "portal_catalog") mdocs = cat(args) for doc in mdocs: try: docobj = doc.getObject() scens = docobj.scenarios # print docobj, scens if scens and obj.getId() in scens: docobj.reindexObject() # print "changed", docobj except Exception, e: log_exc(e)
def createActions(self): """ """ if shasattr(self, "myGroupFolder", acquire=True): log("Creating Private Info Folder") placeful_wf = getToolByName(self, 'portal_placeful_workflow') try: self.manage_addProduct['CMFPlacefulWorkflow'].manage_addWorkflowPolicyConfig() except BadRequest, e: log_exc(e) config = placeful_wf.getWorkflowPolicyConfig(self) placefulWfName = 'dp-private-infofolder' config.setPolicyIn(policy=placefulWfName, update_security=False) config.setPolicyBelow(policy=placefulWfName, update_security=False) self.reindexObject() self.updateSecurity() self.reindexObjectSecurity()
def _getSecurity(self, klass, context): """ """ #print klass REQUEST = self._getRequest() user = REQUEST['AUTHENTICATED_USER'] try: if IProtectedEntityClass.providedBy(klass): a = getMultiAdapter((klass, user,), IDataSecurity) else: a = DefaultSecurity(klass, user) a.setContextObj(context) return a except Exception, e: # Wenn keiner definiert ist bzw. die Interfaces fehlen, dann greift der Default log_exc(e) a = DefaultSecurity(klass, user) a.setContextObj(context) return a
def __getitem__(self, id): # Zope's inner acquisition chain for objects returned by __getitem__ will be # portal -> portal_factory -> temporary_folder -> object # What we really want is for the inner acquisition chain to be # intended_parent_folder -> portal_factory -> temporary_folder -> object # So we need to rewrap... portal_factory = aq_parent(self) intended_parent = aq_parent(portal_factory) # If the intended parent has an object with the given id, just do a passthrough if hasattr(intended_parent, id): return getattr(intended_parent, id) # rewrap portal_factory portal_factory = aq_base(portal_factory).__of__(intended_parent) # rewrap self temp_folder = aq_base(self).__of__(portal_factory) if id in self.objectIds(): return (aq_base(self._getOb(id)).__of__(temp_folder)).__of__(intended_parent) else: type_name = self.getId() try: self.invokeFactory(id=id, type_name=type_name) except ConflictError: raise except: # some errors from invokeFactory (AttributeError, maybe others) # get swallowed -- dump the exception to the log to make sure # developers can see what's going on log_exc(severity=logging.DEBUG) raise obj = self._getOb(id) # keep obj out of the catalog obj.unindexObject() # additionally keep it out of Archetypes UID and refs catalogs if base_hasattr(obj, '_uncatalogUID'): obj._uncatalogUID(obj) if base_hasattr(obj, '_uncatalogRefs'): obj._uncatalogRefs(obj) return (aq_base(obj).__of__(temp_folder)).__of__(intended_parent)
def deleteObjectsByPaths(self, paths, handle_errors=True, REQUEST=None): failure = {} success = [] # use the portal for traversal in case we have relative paths portal = getToolByName(self, 'portal_url').getPortalObject() traverse = portal.restrictedTraverse for path in paths: # Skip and note any errors if handle_errors: sp = transaction.savepoint(optimistic=True) try: # To avoid issues with the check for acquisition, # relative paths should not be part of the API anymore. # Plone core itself does not use relative paths. if not path.startswith("/".join(portal.getPhysicalPath())): msg = ( 'Path {} does not start ' 'with path to portal'.format(path) ) raise ValueError(msg) obj = traverse(path) if list(obj.getPhysicalPath()) != path.split('/'): msg = ( 'Path {} does not match ' 'traversed object physical path. ' 'This is likely an acquisition issue.'.format(path) ) raise ValueError(msg) obj_parent = aq_parent(aq_inner(obj)) obj_parent.manage_delObjects([obj.getId()]) success.append('%s (%s)' % (obj.getId(), path)) except ConflictError: raise except LinkIntegrityNotificationException: raise except Exception, e: if handle_errors: sp.rollback() failure[path] = e log_exc() else: raise
def objektSpeichern(self, request, context=None): """ """ log("objektSpeichern") typ = request.get('typ') klass = self.getKlass(typ) isMinor = int(request.get('minor', 0)) security = self._getSecurity(klass, context) data = self._extractData(request, typ) # print data obj = None kwargs = {} defs = None d = None if not request.get('create', False): log("objektAendern") pk = eval(request.get('pk', "()")) pkvals = self.getPKDict(typ, pk) # print pkvals obj = self.objektdatensatz(typ, **pkvals) # print obj d = self._diff(obj, data) if not d: # Keine Aenderungen! # print "no diff" return if IAuditing.providedBy(obj): obj.wasUpdated(self) defs = self.edit_def else: log("objektAnlegen") obj = klass kwargs['session'] = __session__ defs = self.create_def try: #print data fsobj = defs(typ, security=security)['form'].bind(obj, data = data,**kwargs) except Exception, e: log_exc(e) fsobj = defs(typ, security=security)['form'].bind(obj(), data = data,**kwargs)
def _diff(self, obj, data): """ Bestimmt die Aenderungen bei einem Objekt in Form eines Dictionarys zur Weiterverarbeitung. { fname : (old_value, new_value), ... } """ res = {} for f in data.keys(): fname = f.split('-')[-1] # Name des Feldes new_value = data[f] old_value = getattr(obj, fname) if isinstance(old_value, int): try: new_value = int(new_value) except Exception, e: log_exc(e) log_exc(fname) if new_value != old_value: if new_value or old_value: # Nicht bei Variationen von 'nichts' res[fname] = (old_value, new_value)
def createActions(self): """ We need to check if special workflows are needed depending on the user's role. """ f = self.myFolderBase() r = api.user.get_roles(obj=f, inherit=True) if "Owner" in r: return if "Reviewer" in r: log("Setting Guest Workflow on Document " + self.getId()) placeful_wf = getToolByName(self, 'portal_placeful_workflow') try: self.manage_addProduct['CMFPlacefulWorkflow'].manage_addWorkflowPolicyConfig() except BadRequest, e: log_exc(e) config = placeful_wf.getWorkflowPolicyConfig(self) placefulWfName = 'dp-guest-document' config.setPolicyIn(policy=placefulWfName, update_security=False) config.setPolicyBelow(policy=placefulWfName, update_security=False) self.reindexObject() self.reindexObjectSecurity()
def getMyImage(self, refresh=False, full=True): """ 1. the map image, otherwise 2. the representative image, otherwise 3. try to generate an image from PDF 4. Take the first image available in the doc 5. a default image @param refresh: True --> generate afresh from PDF if necessary @param full: True --> combine map & legend images @return: a tuple with an image and a filename """ alsoProvides(self.REQUEST, IDisableCSRFProtection) try: doc = self mapimg = self.getMapImageObj() if mapimg: legendimg = self.getLegendImageObj() dateiname = '%s.%s' % (mapimg.getId(), "png") if not full or not legendimg: return mapimg.image.data, dateiname else: #combine into one image if full=True and legend available images = map(Image.open, [StringIO(mapimg.image.data), StringIO(legendimg.image.data)]) w = sum(i.size[0] for i in images) mh = max(i.size[1] for i in images) result = Image.new("RGBA", (w, mh), "white") x = 0 for i in images: result.paste(i, (x, 0)) x += i.size[0] res = StringIO() result.save(res, 'PNG') return res.getvalue(), dateiname img = doc.getRepresentativeImage() if img: dateiname = '%s.%s' % (img.getId(), "png") return img.image.data, dateiname img = doc.pdfImage() if img and not refresh: dateiname = '%s.%s' % (img.getId(), "png") return img.data.data, dateiname pdf = doc.getRepresentativePDF() if pdf: execute_under_special_role(doc, "Manager", DPDocument.generatePdfImage, doc, pdf ) img = doc.pdfImage() dateiname = '%s.%s' % (img.getId(), "png") return img.data, dateiname img = doc.getFirstImageObj() if img: dateiname = '%s.%s' % (img.getId(), "png") return img.image.data, dateiname except Exception, e: log_exc(e) # TODO: Idea: support default image in DocType # Show Default image, if no other image is available img = getattr(self,'docdefaultimage.png') return img._data, 'docdefaultimage.png'
def logException(self): # Dumps most recent exception to the log. log_exc()
def sendEmail(formonline, addresses, subject, rstText, cc = None): """ Send a email to the list of addresses """ portal_url = getToolByName(formonline, 'portal_url') plone_utils = getToolByName(formonline, 'plone_utils') transforms = getToolByName(formonline, 'portal_transforms') portal = portal_url.getPortalObject() mailHost = plone_utils.getMailHost() charset = portal.getProperty('email_charset', '') if not charset: charset = plone_utils.getSiteEncoding() from_address = portal.getProperty('email_from_address', '') if not from_address: log('Cannot send notification email: email sender address not set') return from_name = portal.getProperty('email_from_name', '') mfrom = formataddr((from_name, from_address)) if parseaddr(mfrom)[1] != from_address: # formataddr probably got confused by special characters. mfrom - from_address email_msg = MIMEMultipart('alternative') email_msg.epilogue = '' # Translate the body text if PLONE3: translate = getGlobalTranslationService().translate else: translate = i18ntranslate # We must choose the body charset manually for body_charset in (charset, 'UTF-8', 'US-ASCII'): try: rstText = rstText.encode(body_charset) except UnicodeError: pass else: break # Text came from HTML text fields inside adapter, so I will convert it to simple text stream = transforms.convertTo('text/plain', rstText, mimetype='text/html') textPart = MIMEText(stream.getData().strip(), 'plain', body_charset) email_msg.attach(textPart) htmlPart = MIMEText(renderHTML(rstText, charset=body_charset), 'html', body_charset) email_msg.attach(htmlPart) subject = safe_unicode(subject, charset) for address in addresses: address = safe_unicode(address, charset) if address: try: # Note that charset is only used for the headers, not # for the body text as that is a Message already. mailHost.secureSend(message = email_msg, mto = address, mfrom = mfrom, subject = subject, charset = charset) except socket.error, exc: log_exc(('Could not send email from %s to %s regarding issue ' 'in content %s\ntext is:\n%s\n') % ( mfrom, address, formonline.absolute_url(), email_msg)) log_exc("Reason: %s: %r" % (exc.__class__.__name__, str(exc))) except:
def logException(self): """Dumps most recent exception to the log. """ log_exc()
meldung.append(u"Daten: %s" % safe_unicode(str(orig))) meldung.append(u"") neu = klass(**row) for f in pkfields: c = pkvals[f] setattr(neu, f, c) transaction.commit() inserts += 1 else: # INSERT neu = klass(**row) notify(ObjectAddedEvent(neu, tool, row, context)) transaction.commit() inserts += 1 except Exception, e: transaction.abort() fehler += 1 meldung.append(u"Fehler: %s" % str(e)) meldung.append("Daten: %s" % safe_unicode(str(orig))) meldung.append(u"") log(str(orig)) log_exc(e) summe += 1 meldung.append(u"Insgesamt behandelt: %d" % summe) meldung.append(u"Davon neu: %d" % inserts) meldung.append(u"Davon geändert: %d" % updates) if fehler: status = False meldung.append(u"Fehlerhafte Datensätze: %d" % fehler) else: meldung.append(u"Keine Fehler") return meldung, status
def sendNotificationEmail(self, addresses, subject, rstText): """ Send a notification email to the list of addresses XXX Note to self [maurits]: look at this blog post from Marius Gedminas, titled "Sending Unicode emails in Python": http://mg.pov.lt/blog/unicode-emails-in-python.html """ if not self.getSendNotificationEmailsTo() or not addresses: return portal_url = getToolByName(self, 'portal_url') plone_utils = getToolByName(self, 'plone_utils') portal = portal_url.getPortalObject() mailHost = plone_utils.getMailHost() charset = portal.getProperty('email_charset', '') if not charset: charset = plone_utils.getSiteEncoding() from_address = portal.getProperty('email_from_address', '') if not from_address: log('Cannot send notification email: email sender address not set') return from_name = portal.getProperty('email_from_name', '') mfrom = formataddr((from_name, from_address)) if parseaddr(mfrom)[1] != from_address: # formataddr probably got confused by special characters. mfrom = from_address email_msg = MIMEMultipart('alternative') email_msg.epilogue = '' # Translate the body text ts = getGlobalTranslationService() rstText = ts.translate('Poi', rstText, context=self) # We must choose the body charset manually for body_charset in 'US-ASCII', charset, 'UTF-8': try: rstText = rstText.encode(body_charset) except UnicodeError: pass else: break textPart = MIMEText(rstText, 'plain', body_charset) email_msg.attach(textPart) htmlPart = MIMEText(renderHTML(rstText, charset=body_charset), 'html', body_charset) email_msg.attach(htmlPart) # Make the subject unicode and translate it too. subject = safe_unicode(subject, charset) subject = ts.translate('Poi', subject, context=self) for address in addresses: address = safe_unicode(address, charset) if not address: # E-mail address may not be known, for example in case # of LDAP users. See: # http://plone.org/products/poi/issues/213 continue try: # Note that charset is only used for the headers, not # for the body text as that is a Message/MIMEText already. mailHost.secureSend(message=email_msg, mto=address, mfrom=mfrom, subject=subject, charset=charset) except (socket.error, SMTPException), exc: log_exc(('Could not send email from %s to %s regarding issue ' 'in tracker %s\ntext is:\n%s\n') % (mfrom, address, self.absolute_url(), email_msg)) log_exc("Reason: %s: %r" % (exc.__class__.__name__, str(exc))) except:
def eventChanged(obj, event=None): """ """ #print 'eventChanged' # write changelog old_changelog = """ <table> <thead> <tr> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> </tr> </thead> <tbody> <tr class="last"></tr> </tbody> </table> """ % ( _(u"Date"), _(u"Status"), _(u"Operation mode"), _(u"Phase"), _(u"Sectorizing sample types"), _(u"Sectorizing networks") ) if (obj.changelog): old_changelog = safe_unicode(obj.changelog.output) addLogEntry(old_changelog, obj) if obj.Status != 'active': obj.deleteEventReferences() # print obj.Substitute if obj.Substitute: sscen = obj.Substitute.to_object if not sscen.canBeAssigned(): log("Substitute can not be assigned. Not published or not active.") return # Update all objects for this scenario m = obj.content mpath = "/".join(m.getPhysicalPath()) # We now query the catalog for all documents belonging to this scenario within # the personal and group folders args = {'portal_type': 'DPDocument', 'path': mpath} cat = getToolByName(obj, "portal_catalog") mdocs = cat(args) for doc in mdocs: try: docobj = doc.getObject() scens = docobj.scenarios # print docobj, scens if scens and obj.getId() in scens: scens.remove(obj.getId()) scens.append(sscen.getId()) docobj.scenarios = scens docobj.reindexObject() # print "changed", docobj except Exception, e: log_exc(e)
def sendNotificationEmail(self, addresses, subject, rstText): """ Send a notification email to the list of addresses XXX Note to self [maurits]: look at this blog post from Marius Gedminas, titled "Sending Unicode emails in Python": http://mg.pov.lt/blog/unicode-emails-in-python.html """ if not self.getSendNotificationEmailsTo() or not addresses: return portal_url = getToolByName(self, 'portal_url') plone_utils = getToolByName(self, 'plone_utils') portal = portal_url.getPortalObject() mailHost = plone_utils.getMailHost() charset = portal.getProperty('email_charset', '') if not charset: charset = plone_utils.getSiteEncoding() from_address = portal.getProperty('email_from_address', '') if not from_address: log('Cannot send notification email: email sender address not set') return from_name = portal.getProperty('email_from_name', '') mfrom = formataddr((from_name, from_address)) if parseaddr(mfrom)[1] != from_address: # formataddr probably got confused by special characters. mfrom = from_address email_msg = MIMEMultipart('alternative') email_msg.epilogue = '' # Translate the body text ts = getGlobalTranslationService() rstText = ts.translate('Poi', rstText, context=self) # We must choose the body charset manually for body_charset in 'US-ASCII', charset, 'UTF-8': try: rstText = rstText.encode(body_charset) except UnicodeError: pass else: break textPart = MIMEText(rstText, 'plain', body_charset) email_msg.attach(textPart) htmlPart = MIMEText(renderHTML(rstText, charset=body_charset), 'html', body_charset) email_msg.attach(htmlPart) # Make the subject unicode and translate it too. subject = safe_unicode(subject, charset) subject = ts.translate('Poi', subject, context=self) for address in addresses: address = safe_unicode(address, charset) if not address: # E-mail address may not be known, for example in case # of LDAP users. See: # http://plone.org/products/poi/issues/213 continue try: # Note that charset is only used for the headers, not # for the body text as that is a Message/MIMEText already. mailHost.secureSend(message = email_msg, mto = address, mfrom = mfrom, subject = subject, charset = charset) except (socket.error, SMTPException), exc: log_exc(('Could not send email from %s to %s regarding issue ' 'in tracker %s\ntext is:\n%s\n') % ( mfrom, address, self.absolute_url(), email_msg)) log_exc("Reason: %s: %r" % (exc.__class__.__name__, str(exc))) except: