def render_preference_panel(self, req, panel): if req.method == 'POST': if Locale and \ req.args.get('language') != req.session.get('language'): # reactivate translations with new language setting # when changed del req.locale # for re-negotiating locale deactivate() make_activable(lambda: req.locale, self.env.path) _do_save(req, panel, self._form_fields) data = { 'timezones': all_timezones, 'timezone': get_timezone, 'localtz': localtz, 'has_babel': False, } if Locale: locale_ids = get_available_locales() locales = [Locale.parse(locale) for locale in locale_ids] # use locale identifiers from get_available_locales() instead # of str(locale) to prevent storing expanded locale identifier # to session, e.g. zh_Hans_CN and zh_Hant_TW, since Babel 1.0. # see #11258. languages = sorted((id_, locale.display_name) for id_, locale in zip(locale_ids, locales)) data['locales'] = locales data['languages'] = languages data['has_babel'] = True return 'prefs_localization.html', data
def notify(self, tickets, new_values, comment, action, author): """Send batch ticket change notification e-mail (untranslated)""" t = deactivate() try: self._notify(tickets, new_values, comment, action, author) finally: reactivate(t)
def _format_body(self): stream = self.template.generate(**self.data) # don't translate the e-mail stream t = deactivate() try: return stream.render('text', encoding='utf-8') finally: reactivate(t)
def format(self, tickets, new_values, comment, action, author): """Format batch ticket change notification e-mail (untranslated)""" t = deactivate() try: self._prepare_body(tickets, new_values, comment, action, author) return self._format_body() finally: reactivate(t)
def notify(self, ticket, newticket=True, modtime=None): """Send ticket change notification e-mail (untranslated)""" t = deactivate() translated_fields = ticket.fields try: ticket.fields = TicketSystem(self.env).get_ticket_fields() self._notify(ticket, newticket, modtime) finally: ticket.fields = translated_fields reactivate(t)
def _translation_deactivated(ticket=None): t = deactivate() if ticket is not None: ts = TicketSystem(ticket.env) translated_fields = ticket.fields ticket.fields = ts.get_ticket_fields() try: yield finally: if ticket is not None: ticket.fields = translated_fields reactivate(t)
def translation_deactivated(ticket=None): t = deactivate() if ticket is not None: ts = TicketSystem(ticket.env) translated_fields = ticket.fields ticket.fields = ts.get_ticket_fields() try: yield finally: if ticket is not None: ticket.fields = translated_fields reactivate(t)
def test_error_with_lazy_translation(self): self._create_env() os.remove(self.db_path) env = Environment(self.env_path) chrome = Chrome(env) dispatcher = RequestDispatcher(env) req = self._create_req(cookie='trac_auth=1234567890') req.callbacks.update({'authname': dispatcher.authenticate, 'chrome': chrome.prepare_request, 'session': dispatcher._get_session, 'locale': dispatcher._get_locale}) translation.make_activable(lambda: req.locale, env.path) try: self._db_query(env) self.fail('ConfigurationError not raised') except ConfigurationError as e: message = unicode(e) self.assertIn('Database "', message) self.assertIn('" not found.', message) finally: translation.deactivate()
def notify_attachment(self, ticket, author, filename, modtime, body): """Send ticket attachment notification (untranslated)""" t = deactivate() translated_fields = ticket.fields try: ticket.fields = TicketSystem(self.env).get_ticket_fields() self.ticket = ticket self.modtime = modtime self.newticket = False self.reporter = '' self.owner = '' link = self.env.abs_href.ticket(ticket.id) summary = self.ticket['summary'] ticket_values = ticket.values.copy() ticket_values['id'] = ticket.id wrap_kargs = { 'initial_indent': ' ', 'subsequent_indent': ' ', 'linesep': CRLF } if 'ambiwidth' in getargspec(wrap)[0]: wrap_kargs['ambiwidth'] = self.ambiwidth ticket_values['description'] = wrap( ticket_values.get('description', ''), self.COLS, **wrap_kargs ) ticket_values['new'] = self.newticket ticket_values['link'] = link subject = 'Re: ' + self.format_subj(summary) author = obfuscate_email_address(author) change = { 'author': author } self.data.update({ 'ticket_props': self.format_props(), 'ticket_body_hdr': self.format_hdr(), 'subject': subject, 'ticket': ticket_values, 'change': change, 'changes_body': body, }) NotifyEmail.notify(self, ticket.id, subject) finally: ticket.fields = translated_fields reactivate(t)
def _do_save(self, req): language = req.session.get('language') for field in self._form_fields: val = req.args.get(field, '').strip() if val: if field == 'tz' and 'tz' in req.session and \ val not in all_timezones: del req.session['tz'] elif field == 'newsid': req.session.change_sid(val) elif field == 'accesskeys': req.session[field] = '1' else: req.session[field] = val elif field in req.session and (field in req.args or field + '_cb' in req.args): del req.session[field] if Locale and req.session.get('language') != language: # reactivate translations with new language setting when changed del req.locale # for re-negotiating locale deactivate() make_activable(lambda: req.locale, self.env.path) add_notice(req, _("Your preferences have been saved."))
def substitute_message(self, message, ignore_exc=True): try: chrome = Chrome(self.env) req = self._create_request() t = deactivate() try: make_activable(lambda: req.locale, self.env.path) return self._substitute_message(chrome, req, message) finally: reactivate(t) except: self.log.warn('Caught exception while substituting message', exc_info=True) if ignore_exc: return message raise
def _internal_render(self, req, name, content, render_args=None): if not name == 'latex': return self._show_err('Unknown macro %s' % (name)) label = None for line in content.split("\n"): m = reLABEL.search(content) if m: label = m.group(1) # We have to remove unnecessary ending lines because it seems otherwise preview LaTeX package # improperly crops the PDF -- if there is an empty line bettween the end of the content # and \end{preview} content = content.strip() key = sha1( content.encode('utf-8') + self.template_digest + str(self.png_resolution)).hexdigest() imgname = key + '.png' imgpath = os.path.join(self.cache_dir, imgname) if not os.path.exists(imgpath): errmsg = self._validate(content) if errmsg: return self._show_err(errmsg) texname = key + '.tex' texpath = os.path.join(self.cache_dir, texname) # Don't translate tex file t = deactivate() try: f = open(texpath, mode='w') self.template.generate(content=content).render( encoding='utf-8', out=f) f.close() except Exception, e: reactivate(t) return self._show_err("Problem creating tex file: %s" % (e)) finally:
def _internal_render(self, req, name, content, render_args=None): if not name == 'latex': return self._show_err('Unknown macro %s' % (name)) label = None for line in content.split("\n"): m = reLABEL.search(content) if m: label = m.group(1) # We have to remove unnecessary ending lines because it seems otherwise preview LaTeX package # improperly crops the PDF -- if there is an empty line bettween the end of the content # and \end{preview} content = content.strip() key = sha1(content.encode('utf-8') + self.template_digest + str(self.png_resolution)).hexdigest() imgname = key + '.png' imgpath = os.path.join(self.cache_dir, imgname) if not os.path.exists(imgpath): errmsg = self._validate(content) if errmsg: return self._show_err(errmsg) texname = key + '.tex' texpath = os.path.join(self.cache_dir, texname) # Don't translate tex file t = deactivate() try: f = open(texpath, mode='w') self.template.generate(content=content).render(encoding='utf-8', out=f) f.close() except Exception, e: reactivate(t) return self._show_err("Problem creating tex file: %s" % (e)) finally:
def tearDown(self): translation.deactivate() self.env.reset_db() shutil.rmtree(self.env.path)
def send(self, torcpts, ccrcpts, mime_headers={}): from email.MIMEText import MIMEText from email.Utils import formatdate stream = self.template.generate(**self.data) # don't translate the e-mail stream t = deactivate() try: body = stream.render('text', encoding='utf-8') finally: reactivate(t) public_cc = self.config.getbool('notification', 'use_public_cc') headers = { 'X-Mailer': 'Trac %s, by Edgewall Software' % __version__, 'X-Trac-Version': __version__, 'X-Trac-Project': self.env.project_name, 'X-URL': self.env.project_url, 'Precedence': 'bulk', 'Auto-Submitted': 'auto-generated', 'Subject': self.subject, 'From': (self.from_name, self.from_email) if self.from_name else self.from_email, 'Reply-To': self.replyto_email } def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, [self.get_smtp_address(addr) for addr in rcpts]) def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if not rcpt in all: tmp.append(rcpt) all.append(rcpt) return tmp, all notify_sys = NotificationSystem(self.env) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) accaddrs = notify_sys.smtp_always_cc_list bccaddrs = notify_sys.smtp_always_bcc_list recipients = [] toaddrs, recipients = remove_dup(toaddrs, recipients) ccaddrs, recipients = remove_dup(ccaddrs, recipients) accaddrs, recipients = remove_dup(accaddrs, recipients) bccaddrs, recipients = remove_dup(bccaddrs, recipients) # if there is not valid recipient, leave immediately if len(recipients) < 1: self.env.log.info("no recipient for a ticket notification") return pcc = accaddrs if public_cc: pcc += ccaddrs if toaddrs: headers['To'] = ', '.join(toaddrs) if pcc: headers['Cc'] = ', '.join(pcc) headers['Date'] = formatdate() msg = MIMEText(body, 'plain') # Message class computes the wrong type from MIMEText constructor, # which does not take a Charset object as initializer. Reset the # encoding type to force a new, valid evaluation del msg['Content-Transfer-Encoding'] msg.set_charset(self._charset) self.add_headers(msg, headers) self.add_headers(msg, mime_headers) NotificationSystem(self.env).send_email(self.from_email, recipients, msg.as_string())
def send(self, torcpts, ccrcpts, mime_headers={}): from email.MIMEText import MIMEText from email.Utils import formatdate attach_diff = self.config.getbool('wiki-notification', 'attach_diff') if attach_diff: from email.MIMEMultipart import MIMEMultipart self.data["wikidiff"] = None charset = str(self._charset) stream = self.template.generate(**self.data) # don't translate the e-mail stream t = deactivate() try: body = stream.render('text', encoding=charset) finally: reactivate(t) # self.env.log.debug('Email Contents: %s', body) public_cc = self.config.getbool('wiki-notification', 'use_public_cc') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = self.env.project_name headers['X-URL'] = self.env.project_url headers['Precedence'] = 'bulk' headers['Auto-Submitted'] = 'auto-generated' headers['Subject'] = self.subject headers['From'] = ( self.from_name, self.from_email) if self.from_name else self.from_email headers['Reply-To'] = self.replyto_email def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, [self.get_smtp_address(addr) for addr in rcpts]) blocked_addresses = [] def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if rcpt in self.banned_addresses: self.env.log.debug("Banned Address: %s", rcpt) blocked_addresses.append(rcpt) elif not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) accaddrs = build_addresses(self.config.getlist( 'wiki-notification', 'smtp_always_cc', [])) bccaddrs = build_addresses(self.config.getlist( 'wiki-notification', 'smtp_always_bcc', [])) recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) (accaddrs, recipients) = remove_dup(accaddrs, recipients) (bccaddrs, recipients) = remove_dup(bccaddrs, recipients) self.env.log.debug("Not notifying the following addresses: %s", ', '.join(blocked_addresses)) # if there is not valid recipient, leave immediately if len(recipients) < 1: self.env.log.info('no recipient for a wiki notification') return dest = self.change_author or 'anonymous' headers['X-Trac-Wiki-URL'] = self.data['link'] pcc = accaddrs if public_cc: pcc += ccaddrs if toaddrs: headers['To'] = ', '.join(toaddrs) if pcc: headers['Cc'] = ', '.join(pcc) headers['Date'] = formatdate() if attach_diff: # With MIMEMultipart the charset has to be set before any parts # are added. msg = MIMEMultipart('mixed', None, [], charset=charset) msg.preamble = 'This is a multi-part message in MIME format.' # The text Message mail = MIMEText(body, 'plain', charset) mail.add_header('Content-Disposition', 'inline', filename="message.txt") msg.attach(mail) try: # The Diff Attachment attach = MIMEText(self.wikidiff.encode( 'utf-8'), 'x-patch', charset) attach.add_header('Content-Disposition', 'inline', filename=self.page.name + '.diff') msg.attach(attach) except AttributeError: # We don't have a wikidiff to attach pass else: msg = MIMEText(body, 'plain', charset) self.add_headers(msg, headers) self.add_headers(msg, mime_headers) self.env.log.info("Sending notification to %s" % (recipients,)) try: NotificationSystem(self.env).send_email( self.from_email, recipients, msg.as_string()) except Exception, err: self.env.log.debug('Notification could not be sent: %r', err)
def send(self, torcpts, ccrcpts, mime_headers={}): from email.MIMEText import MIMEText from email.Utils import formatdate stream = self.template.generate(**self.data) # don't translate the e-mail stream t = deactivate() try: body = stream.render('text', encoding='utf-8') finally: reactivate(t) public_cc = self.config.getbool('notification', 'use_public_cc') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = self.env.project_name headers['X-URL'] = self.env.project_url headers['Precedence'] = 'bulk' headers['Auto-Submitted'] = 'auto-generated' headers['Subject'] = self.subject headers['From'] = (self.from_name, self.from_email) if self.from_name \ else self.from_email headers['Reply-To'] = self.replyto_email def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, \ [self.get_smtp_address(addr) for addr in rcpts]) def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) accparam = self.config.get('notification', 'smtp_always_cc') accaddrs = accparam and \ build_addresses(accparam.replace(',', ' ').split()) or [] bccparam = self.config.get('notification', 'smtp_always_bcc') bccaddrs = bccparam and \ build_addresses(bccparam.replace(',', ' ').split()) or [] recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) (accaddrs, recipients) = remove_dup(accaddrs, recipients) (bccaddrs, recipients) = remove_dup(bccaddrs, recipients) # if there is not valid recipient, leave immediately if len(recipients) < 1: self.env.log.info('no recipient for a ticket notification') return pcc = accaddrs if public_cc: pcc += ccaddrs if toaddrs: headers['To'] = ', '.join(toaddrs) if pcc: headers['Cc'] = ', '.join(pcc) headers['Date'] = formatdate() msg = MIMEText(body, 'plain') # Message class computes the wrong type from MIMEText constructor, # which does not take a Charset object as initializer. Reset the # encoding type to force a new, valid evaluation del msg['Content-Transfer-Encoding'] msg.set_charset(self._charset) self.add_headers(msg, headers) self.add_headers(msg, mime_headers) NotificationSystem(self.env).send_email(self.from_email, recipients, msg.as_string())
environ.update({ 'trac.web.frontend': 'mod_wsgi', 'trac.web.version': mod_wsgi_version}) env.webfrontend = environ.get('trac.web.frontend') if env.webfrontend: env.systeminfo.append((env.webfrontend, environ['trac.web.version'])) except Exception, e: env_error = e req = RequestWithSession(environ, start_response) translation.make_activable(lambda: req.locale, env.path if env else None) try: return _dispatch_request(req, env, env_error) finally: translation.deactivate() if env and not run_once: env.shutdown(threading._get_ident()) # Now it's a good time to do some clean-ups # # Note: enable the '##' lines as soon as there's a suspicion # of memory leak due to uncollectable objects (typically # objects with a __del__ method caught in a cycle) # ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE) unreachable = gc.collect() ##env.log.debug("%d unreachable objects found.", unreachable) ##uncollectable = len(gc.garbage) ##if uncollectable: ## del gc.garbage[:] ## env.log.warn("%d uncollectable objects found.", uncollectable)
def test_error_with_lazy_translation(self): self._create_env() os.chmod(self.db_path, 0444) env = Environment(self.env_path) chrome = Chrome(env) dispatcher = RequestDispatcher(env) req = self._create_req(cookie='trac_auth=1234567890') req.callbacks.update({'authname': dispatcher.authenticate, 'chrome': chrome.prepare_request, 'session': dispatcher._get_session, 'locale': dispatcher._get_locale}) translation.make_activable(lambda: req.locale, env.path) try: self._db_query(env) self.fail('ConfigurationError not raised') except ConfigurationError, e: self.assertIn('requires read _and_ write permissions', unicode(e)) finally: translation.deactivate() def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DatabaseFileTestCase)) return suite if __name__ == '__main__': unittest.main(defaultTest='suite')
def tearDown(self): translation.deactivate() self.env.reset_db_and_disk()
def dispatch_request(environ, start_response): """Main entry point for the Trac web interface. :param environ: the WSGI environment dict :param start_response: the WSGI callback for starting the response """ global _warn_setuptools if _warn_setuptools is False: _warn_setuptools = True warn_setuptools_issue(out=environ.get('wsgi.errors')) # SCRIPT_URL is an Apache var containing the URL before URL rewriting # has been applied, so we can use it to reconstruct logical SCRIPT_NAME script_url = environ.get('SCRIPT_URL') if script_url is not None: path_info = environ.get('PATH_INFO') if not path_info: environ['SCRIPT_NAME'] = script_url else: # mod_wsgi squashes slashes in PATH_INFO (!) script_url = _slashes_re.sub('/', script_url) path_info = _slashes_re.sub('/', path_info) if script_url.endswith(path_info): environ['SCRIPT_NAME'] = script_url[:-len(path_info)] # If the expected configuration keys aren't found in the WSGI environment, # try looking them up in the process environment variables environ.setdefault('trac.env_path', os.getenv('TRAC_ENV')) environ.setdefault('trac.env_parent_dir', os.getenv('TRAC_ENV_PARENT_DIR')) environ.setdefault('trac.env_index_template', os.getenv('TRAC_ENV_INDEX_TEMPLATE')) environ.setdefault('trac.template_vars', os.getenv('TRAC_TEMPLATE_VARS')) environ.setdefault('trac.locale', '') environ.setdefault('trac.base_url', os.getenv('TRAC_BASE_URL')) locale.setlocale(locale.LC_ALL, environ['trac.locale']) # Determine the environment env_path = environ.get('trac.env_path') if not env_path: env_parent_dir = environ.get('trac.env_parent_dir') env_paths = environ.get('trac.env_paths') if env_parent_dir or env_paths: # The first component of the path is the base name of the # environment path_info = environ.get('PATH_INFO', '').lstrip('/').split('/') env_name = path_info.pop(0) if not env_name: # No specific environment requested, so render an environment # index page send_project_index(environ, start_response, env_parent_dir, env_paths) return [] errmsg = None # To make the matching patterns of request handlers work, we append # the environment name to the `SCRIPT_NAME` variable, and keep only # the remaining path in the `PATH_INFO` variable. script_name = environ.get('SCRIPT_NAME', '') try: script_name = unicode(script_name, 'utf-8') # (as Href expects unicode parameters) environ['SCRIPT_NAME'] = Href(script_name)(env_name) environ['PATH_INFO'] = '/' + '/'.join(path_info) if env_parent_dir: env_path = os.path.join(env_parent_dir, env_name) else: env_path = get_environments(environ).get(env_name) if not env_path or not os.path.isdir(env_path): errmsg = 'Environment not found' except UnicodeDecodeError: errmsg = 'Invalid URL encoding (was %r)' % script_name if errmsg: start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(errmsg)))]) return [errmsg] if not env_path: raise EnvironmentError('The environment options "TRAC_ENV" or ' '"TRAC_ENV_PARENT_DIR" or the mod_python ' 'options "TracEnv" or "TracEnvParentDir" are ' 'missing. Trac requires one of these options ' 'to locate the Trac environment(s).') run_once = environ['wsgi.run_once'] env = env_error = None try: env = open_environment(env_path, use_cache=not run_once) if env.base_url_for_redirect: environ['trac.base_url'] = env.base_url # Web front-end type and version information if not hasattr(env, 'webfrontend'): mod_wsgi_version = environ.get('mod_wsgi.version') if mod_wsgi_version: mod_wsgi_version = ( "%s (WSGIProcessGroup %s WSGIApplicationGroup %s)" % ('.'.join([str(x) for x in mod_wsgi_version]), environ.get('mod_wsgi.process_group'), environ.get('mod_wsgi.application_group') or '%{GLOBAL}')) environ.update({ 'trac.web.frontend': 'mod_wsgi', 'trac.web.version': mod_wsgi_version}) env.webfrontend = environ.get('trac.web.frontend') if env.webfrontend: env.webfrontend_version = environ['trac.web.version'] except Exception as e: env_error = e req = RequestWithSession(environ, start_response) translation.make_activable(lambda: req.locale, env.path if env else None) try: return _dispatch_request(req, env, env_error) finally: translation.deactivate() if env and not run_once: env.shutdown(threading._get_ident()) # Now it's a good time to do some clean-ups # # Note: enable the '##' lines as soon as there's a suspicion # of memory leak due to uncollectable objects (typically # objects with a __del__ method caught in a cycle) # ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE) unreachable = gc.collect()
def send(self, torcpts, ccrcpts, mime_headers={}): from email.MIMEText import MIMEText from email.Utils import formatdate attach_diff = self.config.getbool('wiki-notification', 'attach_diff') if attach_diff: from email.MIMEMultipart import MIMEMultipart self.data["wikidiff"] = None charset = str(self._charset) stream = self.template.generate(**self.data) # don't translate the e-mail stream t = deactivate() try: body = stream.render('text', encoding=charset) finally: reactivate(t) # self.env.log.debug('Email Contents: %s', body) public_cc = self.config.getbool('wiki-notification', 'use_public_cc') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = self.env.project_name headers['X-URL'] = self.env.project_url headers['Precedence'] = 'bulk' headers['Auto-Submitted'] = 'auto-generated' headers['Subject'] = self.subject headers['From'] = ( self.from_name, self.from_email) if self.from_name else self.from_email headers['Reply-To'] = self.replyto_email def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, [self.get_smtp_address(addr) for addr in rcpts]) blocked_addresses = [] def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if rcpt in self.banned_addresses: self.env.log.debug("Banned Address: %s", rcpt) blocked_addresses.append(rcpt) elif not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) accaddrs = build_addresses( self.config.getlist('wiki-notification', 'smtp_always_cc', [])) bccaddrs = build_addresses( self.config.getlist('wiki-notification', 'smtp_always_bcc', [])) recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) (accaddrs, recipients) = remove_dup(accaddrs, recipients) (bccaddrs, recipients) = remove_dup(bccaddrs, recipients) self.env.log.debug("Not notifying the following addresses: %s", ', '.join(blocked_addresses)) # if there is not valid recipient, leave immediately if len(recipients) < 1: self.env.log.info('no recipient for a wiki notification') return dest = self.change_author or 'anonymous' headers['X-Trac-Wiki-URL'] = self.data['link'] pcc = accaddrs if public_cc: pcc += ccaddrs if toaddrs: headers['To'] = ', '.join(toaddrs) if pcc: headers['Cc'] = ', '.join(pcc) headers['Date'] = formatdate() if attach_diff: # With MIMEMultipart the charset has to be set before any parts # are added. msg = MIMEMultipart('mixed', None, [], charset=charset) msg.preamble = 'This is a multi-part message in MIME format.' # The text Message mail = MIMEText(body, 'plain', charset) mail.add_header('Content-Disposition', 'inline', filename="message.txt") msg.attach(mail) try: # The Diff Attachment attach = MIMEText(self.wikidiff.encode('utf-8'), 'x-patch', charset) attach.add_header('Content-Disposition', 'inline', filename=self.page.name + '.diff') msg.attach(attach) except AttributeError: # We don't have a wikidiff to attach pass else: msg = MIMEText(body, 'plain', charset) self.add_headers(msg, headers) self.add_headers(msg, mime_headers) self.env.log.info("Sending notification to %s" % (recipients, )) try: NotificationSystem(self.env).send_email(self.from_email, recipients, msg.as_string()) except Exception, err: self.env.log.debug('Notification could not be sent: %r', err)
def dispatch_request(environ, start_response): """Main entry point for the Trac web interface. :param environ: the WSGI environment dict :param start_response: the WSGI callback for starting the response """ global _warn_setuptools if _warn_setuptools is False: _warn_setuptools = True warn_setuptools_issue(out=environ.get('wsgi.errors')) # SCRIPT_URL is an Apache var containing the URL before URL rewriting # has been applied, so we can use it to reconstruct logical SCRIPT_NAME script_url = environ.get('SCRIPT_URL') if script_url is not None: path_info = environ.get('PATH_INFO') if not path_info: environ['SCRIPT_NAME'] = script_url else: # mod_wsgi squashes slashes in PATH_INFO (!) script_url = _slashes_re.sub('/', script_url) path_info = _slashes_re.sub('/', path_info) if script_url.endswith(path_info): environ['SCRIPT_NAME'] = script_url[:-len(path_info)] # If the expected configuration keys aren't found in the WSGI environment, # try looking them up in the process environment variables environ.setdefault('trac.env_path', os.getenv('TRAC_ENV')) environ.setdefault('trac.env_parent_dir', os.getenv('TRAC_ENV_PARENT_DIR')) environ.setdefault('trac.env_index_template', os.getenv('TRAC_ENV_INDEX_TEMPLATE')) environ.setdefault('trac.template_vars', os.getenv('TRAC_TEMPLATE_VARS')) environ.setdefault('trac.locale', '') environ.setdefault('trac.base_url', os.getenv('TRAC_BASE_URL')) locale.setlocale(locale.LC_ALL, environ['trac.locale']) # Determine the environment env_path = environ.get('trac.env_path') if not env_path: env_parent_dir = environ.get('trac.env_parent_dir') env_paths = environ.get('trac.env_paths') if env_parent_dir or env_paths: # The first component of the path is the base name of the # environment path_info = environ.get('PATH_INFO', '').lstrip('/').split('/') env_name = path_info.pop(0) if not env_name: # No specific environment requested, so render an environment # index page send_project_index(environ, start_response, env_parent_dir, env_paths) return [] errmsg = None # To make the matching patterns of request handlers work, we append # the environment name to the `SCRIPT_NAME` variable, and keep only # the remaining path in the `PATH_INFO` variable. script_name = environ.get('SCRIPT_NAME', '') try: script_name = unicode(script_name, 'utf-8') # (as Href expects unicode parameters) environ['SCRIPT_NAME'] = Href(script_name)(env_name) environ['PATH_INFO'] = '/' + '/'.join(path_info) if env_parent_dir: env_path = os.path.join(env_parent_dir, env_name) else: env_path = get_environments(environ).get(env_name) if not env_path or not os.path.isdir(env_path): errmsg = 'Environment not found' except UnicodeDecodeError: errmsg = 'Invalid URL encoding (was %r)' % script_name if errmsg: start_response('404 Not Found', [('Content-Type', 'text/plain'), ('Content-Length', str(len(errmsg)))]) return [errmsg] if not env_path: raise EnvironmentError('The environment options "TRAC_ENV" or ' '"TRAC_ENV_PARENT_DIR" or the mod_python ' 'options "TracEnv" or "TracEnvParentDir" are ' 'missing. Trac requires one of these options ' 'to locate the Trac environment(s).') run_once = environ['wsgi.run_once'] env = env_error = None try: env = open_environment(env_path, use_cache=not run_once) if env.base_url_for_redirect: environ['trac.base_url'] = env.base_url # Web front-end type and version information if not hasattr(env, 'webfrontend'): mod_wsgi_version = environ.get('mod_wsgi.version') if mod_wsgi_version: mod_wsgi_version = ( "%s (WSGIProcessGroup %s WSGIApplicationGroup %s)" % ('.'.join([str(x) for x in mod_wsgi_version ]), environ.get('mod_wsgi.process_group'), environ.get('mod_wsgi.application_group') or '%{GLOBAL}')) environ.update({ 'trac.web.frontend': 'mod_wsgi', 'trac.web.version': mod_wsgi_version }) env.webfrontend = environ.get('trac.web.frontend') if env.webfrontend: env.webfrontend_version = environ['trac.web.version'] except Exception as e: env_error = e req = RequestWithSession(environ, start_response) translation.make_activable(lambda: req.locale, env.path if env else None) try: return _dispatch_request(req, env, env_error) finally: translation.deactivate() if env and not run_once: env.shutdown(threading._get_ident()) # Now it's a good time to do some clean-ups # # Note: enable the '##' lines as soon as there's a suspicion # of memory leak due to uncollectable objects (typically # objects with a __del__ method caught in a cycle) # ##gc.set_debug(gc.DEBUG_UNCOLLECTABLE) unreachable = gc.collect()
def send(self, torcpts, ccrcpts, mime_headers={}): """ this method is based NotifyEmail in trac/notification.py As the default trac NotifyEmail class assumes alot, and will overwrite headers we do not call our ancestor class method here, but send the mail direct """ from email.MIMEText import MIMEText from email.Utils import formatdate stream = self.template.generate(**self.data) # don't translate the e-mail stream t = deactivate() try: body = stream.render('text') finally: reactivate(t) projname = self.env.project_name public_cc = self.config.getbool('notification', 'use_public_cc') headers = {} headers['X-Mailer'] = 'Trac %s, by Edgewall Software' % __version__ headers['X-Trac-Version'] = __version__ headers['X-Trac-Project'] = projname headers['X-URL'] = self.env.project_url headers['Precedence'] = 'bulk' headers['Auto-Submitted'] = 'auto-generated' headers['Subject'] = self.subject headers['From'] = (self.from_name or projname, self.from_email) headers['Reply-To'] = self.reply_to_email # add Message-ID and In-Reply-To for threaded mail clients if self.action == 'post_created': headers['Message-ID'] = self.get_message_id( projname, self.blog.name) else: headers['Message-ID'] = self.get_message_id( projname, self.blog.name, self.time) headers['In-Reply-To'] = headers[ 'References'] = self.get_message_id(projname, self.blog.name) def build_addresses(rcpts): """Format and remove invalid addresses""" return filter(lambda x: x, [self.get_smtp_address(addr) for addr in rcpts]) def remove_dup(rcpts, all): """Remove duplicates""" tmp = [] for rcpt in rcpts: if not rcpt in all: tmp.append(rcpt) all.append(rcpt) return (tmp, all) toaddrs = build_addresses(torcpts) ccaddrs = build_addresses(ccrcpts) accparam = self.config.getlist('fullblog-notification', 'smtp_always_cc') accaddrs = accparam and build_addresses(accparam) or [] recipients = [] (toaddrs, recipients) = remove_dup(toaddrs, recipients) (ccaddrs, recipients) = remove_dup(ccaddrs, recipients) (accaddrs, recipients) = remove_dup(accaddrs, recipients) # if there is not valid recipient, leave immediately if len(recipients) < 1: self.env.log.info('no recipient for a fullblog notification') return cc = accaddrs + ccaddrs if cc: headers['Cc'] = ', '.join(cc) if toaddrs: headers['To'] = ', '.join(toaddrs) headers['Date'] = formatdate() msg = MIMEText(body, 'plain') # Message class computes the wrong type from MIMEText constructor, # which does not take a Charset object as initializer. Reset the # encoding type to force a new, valid evaluation del msg['Content-Transfer-Encoding'] msg.set_charset(self._charset) self.add_headers(msg, headers) self.add_headers(msg, mime_headers) NotificationSystem(self.env).send_email(self.from_email, recipients, msg.as_string())