def test_add_messages(self): mailinglist = Mailinglist(self.env, emailaddress="LIST1", name="Sample List 1", private=True, postperm="OPEN") mailinglist.insert() mailinglist = Mailinglist(self.env, emailaddress="list2", name="Sample List 2", private=True, postperm="OPEN") mailinglist.insert() for rawmsg in rawmsgs: for listname in ("list1", "list2"): bytes = rawmsg % dict(sender="Jack Sparrow", email="*****@*****.**", list=listname, domain="example.com", subject="Boats", asctime=time.asctime(), id="asdfasdf", body="Need boats.") mailinglist = Mailinglist.select_by_address(self.env, "*****@*****.**" % listname) message = mailinglist.insert_raw_email(bytes) assert len(list(Mailinglist.select(self.env))) == 2 for mailinglist in Mailinglist.select(self.env): for conversation in mailinglist.conversations(): assert conversation.get_first() is not None for message in conversation.messages(): assert message #for attachment in Attachment.select(self.env, 'mailinglistmessage', message.id): # assert attachment mailinglist.delete() assert len(list(Mailinglist.select(self.env))) == 0
def get_timeline_events(self, req, start, stop, filters): if 'mailinglist' in filters: mailinglist_realm = Resource('mailinglist') lists = {} for mailinglist in Mailinglist.select(self.env): if "MAILINGLIST_VIEW" in req.perm(mailinglist.resource): lists[mailinglist.id] = mailinglist if not lists: self.log.debug("This user can't view any lists, so not listing timeline events.") return self.log.debug("Searching for timeline events in %s", lists) db = self.env.get_read_db() cursor = db.cursor() cursor.execute("SELECT id, subject, body, from_name, from_email, date, list, conversation " "FROM mailinglistmessages " "WHERE date>=%%s AND date<=%%s AND list IN (%s)" % ",".join(map(str,lists.keys())), (to_timestamp(start), to_timestamp(stop))) # for mid, subject, body, from_name, from_email, date, mlist, conversation in cursor: # build resource ourself to speed things up m = mailinglist_realm(id="%s/%d/%d" % (lists[mlist].emailaddress, conversation, mid)) if 'MAILINGLIST_VIEW' in req.perm(m): yield ('mailinglist', datetime.fromtimestamp(date, utc), "%s" % (from_name,), (mid, subject, body.lstrip()[:200], lists[mlist].name, lists[mlist].emailaddress, conversation)) # Attachments for event in AttachmentModule(self.env).get_timeline_events( req, mailinglist_realm, start, stop): yield event
def get_search_results(self, req, terms, filters): if not 'mailinglist' in filters: return mailinglist_realm = Resource('mailinglist') lists = {} for mailinglist in Mailinglist.select(self.env): if "MAILINGLIST_VIEW" in req.perm(mailinglist.resource): lists[mailinglist.id] = mailinglist if not lists: self.log.debug("This user can't view any lists, so not searching.") return db = self.env.get_read_db() sql, args = search_to_sql(db, ['subject','body','from_email','from_name'], terms) cursor = db.cursor() query = """ SELECT id, subject, body, from_name, from_email, date, list, conversation FROM mailinglistmessages WHERE list IN (%s) AND %s """ % (",".join(map(str,lists.keys())), sql,) self.log.debug("Search query: %s", query) cursor.execute(query, args) for mid, subject, body, from_name, from_email, date, mlist, conversation in cursor: # build resource ourself to speed things up m = mailinglist_realm(id="%s/%d/%d" % (lists[mlist].emailaddress, conversation, mid)) if 'MAILINGLIST_VIEW' in req.perm(m): yield (req.href.mailinglist(m.id), tag("%s: %s" % (lists[mlist].name, subject)), datetime.fromtimestamp(date, utc), "%s <%s>" % (from_name, from_email), shorten_result(body, terms)) # Attachments for result in AttachmentModule(self.env).get_search_results( req, mailinglist_realm, terms): yield result
def export_mailinglists(self, template_path): """Exports project mailing lists into mailinglist.xml""" # a list to return to the template with info about transaction successful_exports = list() self.log.info("Creating mailing list XML file for template archive") root = ET.Element("lists", project=self.env.project_name, date=datetime.date.today().isoformat()) for ml in Mailinglist.select(self.env): ET.SubElement(root, "list_info", name=ml.name, email=ml.emailaddress, private=str(ml.private), postperm=ml.postperm, replyto=ml.replyto).text = ml.description successful_exports.append(ml.name) # save the xml file filename = os.path.join(template_path, 'mailinglist.xml') ET.ElementTree(root).write(filename) self.log.info("File %s has been created at %s" % (filename, template_path)) return successful_exports
def render_macro(self, req, name, content): args,kwargs = parse_args(content) if len(args) == 0: ul = tag.ul(class_="mailinglistlist") for mailinglist in Mailinglist.select(self.env): if "MAILINGLIST_VIEW" in req.perm(mailinglist.resource): ul.append(tag.li(tag.a(mailinglist.name, href=get_resource_url(self.env, mailinglist.resource, req.href)))) return ul if kwargs.has_key('limit'): limit = int(kwargs['limit']) elif len(args) > 1: limit = int(args[1]) else: limit = 10 resource = Resource("mailinglist",args[0]) instance = MailinglistSystem(self.env).get_instance_for_resource(resource) if isinstance(instance, Mailinglist): if not req.perm(instance.resource).has_permission('MAILINGLIST_VIEW'): return system_message("Permission denied viewing mailinglist: %s" % instance.name) ul = tag.ul() for message in instance.messages(limit=limit, insubject=kwargs.get('insubject', None),desc=True): ul.append(tag.li( tag.a(tag.span(message.subject, class_="messagesubject"), href=get_resource_url(self.env, message.resource, req.href)), " (", dateinfo(message.date), ")", )) ul.append(tag.li(tag.a("(%d messages...)" % instance.count_messages(insubject=kwargs.get('insubject', None)), href=get_resource_url(self.env, instance.resource, req.href)))) return tag.div("Mailinglist: ", tag.a(instance.name, href=get_resource_url(self.env, instance.resource, req.href)), ul, class_="mailinglistfeed") elif isinstance(instance, MailinglistMessage): if not req.perm(instance.resource).has_permission('MAILINGLIST_VIEW'): return system_message("Permission denied viewing mail.") else: limit = None text = wrap_and_quote(instance.body, 78)[0] if limit: text = "\n".join(text.split("\n")[0:limit]) textelement = tag.pre(text) + tag.a(tag.pre("(More...)"), href=get_resource_url(self.env, instance.resource, req.href)) else: textelement = tag.pre(text) return tag.div( tag.div("Mailinglist: ", tag.a(instance.conversation.mailinglist.name, href=get_resource_url(self.env, instance.conversation.mailinglist.resource, req.href))), tag.div("Subject: ", tag.a(instance.subject, href=get_resource_url(self.env, instance.resource, req.href))), tag.div("From: ", tag.a(instance.from_name, href="mailto:%s" % instance.from_email)), tag.div("To: ", instance.to_header), tag.div("Date: ", dateinfo(instance.date)), tag.div(textelement), class_="mailinglistmessage") return system_message("Unknown Mailinglist: %s" % content)
def render_admin_panel(self, req, cat, page, mailinglist_emailpart): req.perm.require('MAILINGLIST_ADMIN') if mailinglist_emailpart: mailinglist = Mailinglist.select_by_address(self.env, mailinglist_emailpart, localpart=True) if req.method == 'POST': if req.args.get('save'): mailinglist.name = req.args.get('name') mailinglist.private = req.args.get('private') == 'PRIVATE' mailinglist.postperm = req.args.get('postperm') mailinglist.replyto = req.args.get('replyto') mailinglist.description = req.args.get('description') if 'TRAC_ADMIN' in req.perm: mailinglist.emailaddress = req.args.get('emailaddress') mailinglist.save_changes() add_notice(req, _('Your changes have been saved.')) req.redirect(req.href.admin(cat, page)) elif req.args.get('cancel'): req.redirect(req.href.admin(cat, page)) elif req.args.get('subscribeuser'): @self.env.with_transaction() def do_subscribe(db): mailinglist = Mailinglist.select_by_address(self.env, mailinglist_emailpart, localpart=True, db=db) mailinglist.subscribe(user=req.args['username'], db=db) add_notice(req, _('The user %s has been subscribed.') % req.args['username']) req.redirect(req.href.admin(cat, page, mailinglist_emailpart)) elif req.args.get('removeusers'): sel = req.args.get('sel') if not sel: raise TracError(_('No users selected')) if not isinstance(sel, list): sel = [sel] @self.env.with_transaction() def do_remove(db): mailinglist = Mailinglist.select_by_address(self.env, mailinglist_emailpart, localpart=True, db=db) for username in sel: mailinglist.unsubscribe(user=username, db=db) add_notice(req, _('The selected users have been unsubscribed.')) req.redirect(req.href.admin(cat, page, mailinglist_emailpart)) elif req.args.get('subscribegroup'): @self.env.with_transaction() def do_subscribe(db): mailinglist = Mailinglist.select_by_address(self.env, mailinglist_emailpart, localpart=True, db=db) mailinglist.subscribe(group=req.args['groupname'], db=db) add_notice(req, _('The group %s has been subscribed.') % req.args['groupname']) req.redirect(req.href.admin(cat, page, mailinglist_emailpart)) elif req.args.get('removegroups'): sel = req.args.get('sel') if not sel: raise TracError(_('No groups selected')) if not isinstance(sel, list): sel = [sel] @self.env.with_transaction() def do_remove(db): mailinglist = Mailinglist.select_by_address(self.env, mailinglist_emailpart, localpart=True, db=db) for username in sel: mailinglist.unsubscribe(group=username, db=db) add_notice(req, _('The selected groups have been unsubscribed.')) req.redirect(req.href.admin(cat, page, mailinglist_emailpart)) elif req.args.get('updatepostergroups') or req.args.get('updateposterusers'): sel = req.args.get('sel') if not sel: sel = [] if not isinstance(sel, list): sel = [sel] @self.env.with_transaction() def do_update(db): mailinglist = Mailinglist.select_by_address(self.env, mailinglist_emailpart, localpart=True, db=db) if req.args.get('updatepostergroups'): current_statuses = mailinglist.groups() else: current_statuses = mailinglist.individuals() for subname, poster in current_statuses: if req.args.get('updatepostergroups'): updater = partial(mailinglist.update_poster, group=subname) else: updater = partial(mailinglist.update_poster, user=subname) if poster and subname not in sel: updater(poster=False) elif not poster and subname in sel: updater(poster=True) add_notice(req, _('Posters have been updated.')) req.redirect(req.href.admin(cat, page, mailinglist_emailpart)) Chrome(self.env).add_wiki_toolbars(req) if self.env.is_component_enabled('simplifiedpermissionsadminplugin.simplifiedpermissions.SimplifiedPermissions'): from simplifiedpermissionsadminplugin.simplifiedpermissions import SimplifiedPermissions # groups is used for subscription, so it should not have subscribed groups in it groups = set(SimplifiedPermissions(self.env).groups) - set([subscribed_group for subscribed_group, group_poster in mailinglist.groups()]) else: groups = None data = {'view': 'detail', 'mailinglist': mailinglist, 'groups': groups, 'email_domain': MailinglistSystem(self.env).email_domain} else: if req.method == 'POST': if req.args.get('add') and req.args.get('emailaddress'): emailaddress = req.args['emailaddress'].lower() try: mailinglist = Mailinglist.select_by_address(self.env, emailaddress, localpart=True) except ResourceNotFound: mailinglist = Mailinglist(self.env, name=req.args['name']) mailinglist.private = req.args.get('private') == 'PRIVATE' mailinglist.postperm = req.args.get('postperm') mailinglist.replyto = req.args.get('replyto') mailinglist.emailaddress = req.args.get('emailaddress') mailinglist.insert() add_notice(req, _('The mailinglist "%(addr)s" has been ' 'added.', addr=mailinglist.addr())) req.redirect(req.href.admin(cat, page)) else: raise TracError(_('Mailinglist with email address %(emailaddress)s already exists.', emailaddress=emailaddress)) # Remove mailinglists elif req.args.get('remove'): req.perm.require('TRAC_ADMIN') sel = req.args.get('sel') if not sel: raise TracError(_('No mailinglist selected')) if not isinstance(sel, list): sel = [sel] @self.env.with_transaction() def do_remove(db): for email in sel: mailinglist = Mailinglist.select_by_address(self.env, email, localpart=True, db=db) mailinglist.delete(db=db) add_notice(req, _('The selected mailinglists have been ' 'removed.')) req.redirect(req.href.admin(cat, page)) mailinglists = Mailinglist.select(self.env) data = {'view': 'list', 'mailinglists': mailinglists, 'email_domain': MailinglistSystem(self.env).email_domain} return ('mailinglist_admin.html', data)
def process_request(self, req): offset = req.args.get("offset",0) page = req.args.get('page', 1) try: offset = int(offset) except: raise TracError(_('Invalid offset used: %(offset)s', offset=offset)) try: page = int(page) except: raise TracError(_('Invalid page used: %(page)s', page=page)) offset = (page - 1) * self.limit add_stylesheet(req, 'mailinglist/css/mailinglist.css') add_javascript(req, 'mailinglist/mailinglist.js') mailinglists = [m for m in Mailinglist.select(self.env) if "MAILINGLIST_VIEW" in req.perm(m.resource)] data = {"mailinglists": mailinglists, "offset": offset, "limit": self.limit} if req.method == 'POST': if 'subscribe' in req.args: subscribe = True unsubscribe = False mailinglist_email = req.args.get('subscribe') elif 'unsubscribe' in req.args: subscribe = False unsubscribe = True mailinglist_email = req.args.get('unsubscribe') else: # at the moment we only post subscription info to # mailing list page - so if there is none in req.args we # can just redirect to mailing list page req.redirect(req.href.mailinglist()) # get mailing list object and check permissions mailinglist = Mailinglist.select_by_address(self.env, mailinglist_email, localpart=True) req.perm(mailinglist.resource).require("MAILINGLIST_VIEW") if subscribe: mailinglist.subscribe(user=req.authname) # subscribe does not return a value to indicate if it # was successful, so we have to explicitly check if mailinglist.is_subscribed(req.authname): add_notice(req, _('You have been subscribed to %s.' % mailinglist.name)) else: add_notice(req, _('Unable to subscribe to %s.' % mailinglist.name)) elif unsubscribe: mailinglist.unsubscribe(user=req.authname) # unsubscribe does not return a value to indicate if it # was successful, so we have to explicitly check if not mailinglist.is_subscribed(req.authname): add_notice(req, _('You have been unsubscribed from %s.' % mailinglist.name)) else: add_notice(req, _('Unable to unsubscribe from %s.' % mailinglist.name)) if req.path_info.endswith('/mailinglist'): # overview mailing list page req.redirect(req.href.mailinglist()) elif 'conversationid' in req.args: # individual mailing list conversation log req.redirect(req.href.mailinglist(mailinglist_email, req.args['conversationid'])) else: # individual mailing list homepage req.redirect(req.href.mailinglist(mailinglist_email)) #for mailinglist in mailinglists: # add_ctxtnav(req, # _("List: %s") % mailinglist.name, # req.href.mailinglist(mailinglist.emailaddress)) if 'messageid' in req.args: message = MailinglistMessage(self.env, req.args['messageid']) # leaks the subject of the email in the error, wonder if # that's a problem... req.perm(message.resource).require("MAILINGLIST_VIEW") if req.args.get('format') == "raw": req.send_header('Content-Disposition', 'attachment') req.send_response(200) content = message.raw.bytes req.send_header('Content-Type', 'application/mbox') req.send_header('Content-Length', len(content)) req.end_headers() if req.method != 'HEAD': req.write(content) return context = Context.from_request(req, message.resource) data['message'] = message data['attachments'] = AttachmentModule(self.env).attachment_data(context) add_link(req, 'up', get_resource_url(self.env, message.conversation.resource, req.href, offset=data['offset']), _("Back to conversation")) prevnext_nav(req, _("Newer message"), _("Older message"), _("Back to conversation")) raw_href = get_resource_url(self.env, message.resource, req.href, format='raw') add_link(req, 'alternate', raw_href, _('mbox'), "application/mbox") if 'MAILINGLIST_ADMIN' in req.perm: add_ctxtnav(req, tag.a(tag.i(class_="fa fa-cog"), ' Manage List', href=req.href.admin('mailinglist', 'lists', message.conversation.mailinglist.emailaddress), title='Manage and subscribe users to the %s mailing list' % message.conversation.mailinglist.name)) return 'mailinglist_message.html', data, None if 'conversationid' in req.args: conversation = MailinglistConversation(self.env, req.args['conversationid']) # also leaks the subject of the first email in the error message req.perm(conversation.resource).require("MAILINGLIST_VIEW") data['conversation'] = conversation data['attachmentselect'] = partial(Attachment.select, self.env) results = Paginator(conversation.messages(), page - 1, self.limit) if results.has_next_page: next_href = get_resource_url(self.env, conversation.resource, req.href, page=page + 1) add_link(req, 'next', next_href, _('Next Page')) if results.has_previous_page: prev_href = get_resource_url(self.env, conversation.resource, req.href, page=page - 1) add_link(req, 'prev', prev_href, _('Previous Page')) shown_pages = results.get_shown_pages() pagedata = [{'href': get_resource_url(self.env, conversation.resource, req.href, page=page), 'class': None, 'string': str(page), 'title': _('Page %(num)d', num=page)} for page in shown_pages] results.shown_pages = pagedata results.current_page = {'href': None, 'class': 'current', 'string': str(results.page + 1), 'title': None} data['paginator'] = results add_link(req, 'up', get_resource_url(self.env, conversation.mailinglist.resource, req.href, offset=data['offset']), _("List of conversations")) prevnext_nav(req, _("Newer conversation"), _("Older conversation"), _("Back to list of conversations")) if 'MAILINGLIST_ADMIN' in req.perm: add_ctxtnav(req, tag.a(tag.i(class_="fa fa-cog"), ' Manage List', href=req.href.admin('mailinglist', 'lists', conversation.mailinglist.emailaddress), title='Manage and subscribe users to the %s mailing list' % conversation.mailinglist.name)) # Check if user is already subscribed to mailing list # and add the appropriate subscribe / unsubscribe ribbon option if conversation.mailinglist.is_subscribed(req.authname): add_ctxtnav(req, tag.form(tag.input(tag.a(tag.i(class_='fa fa-eye-slash'), ' Unsubscribe', title='Unsubscribe from the %s mailing list' % conversation.mailinglist.name, id='subscribe-link'), name='unsubscribe', value=conversation.mailinglist.emailaddress, class_='hidden'), method_='post', action='', id='subscribe-form', class_='hidden')) else: add_ctxtnav(req, tag.form(tag.input(tag.a(tag.i(class_='fa fa-eye'), ' Subscribe', title='Subscribe to the %s mailing list' % conversation.mailinglist.name, id='subscribe-link'), name='subscribe', value=conversation.mailinglist.emailaddress, class_='hidden'), method_='post', action='', id='subscribe-form', class_='hidden')) return 'mailinglist_conversation.html', data, None elif 'listname' in req.args: mailinglist = Mailinglist.select_by_address(self.env, req.args['listname'], localpart=True) # leaks the name of the mailinglist req.perm(mailinglist.resource).require("MAILINGLIST_VIEW") data['mailinglist'] = mailinglist results = Paginator(mailinglist.conversations(), page - 1, self.limit) if results.has_next_page: next_href = get_resource_url(self.env, mailinglist.resource, req.href, page=page + 1) add_link(req, 'next', next_href, _('Next Page')) if results.has_previous_page: prev_href = get_resource_url(self.env, mailinglist.resource, req.href, page=page - 1) add_link(req, 'prev', prev_href, _('Previous Page')) shown_pages = results.get_shown_pages() pagedata = [{'href': get_resource_url(self.env, mailinglist.resource, req.href, page=page), 'class': None, 'string': str(page), 'title': _('Page %(num)d', num=page)} for page in shown_pages] results.shown_pages = pagedata results.current_page = {'href': None, 'class': 'current', 'string': str(results.page + 1), 'title': None} data['paginator'] = results if data['offset'] + data['limit'] < mailinglist.count_conversations(): add_link(req, 'next', get_resource_url(self.env, mailinglist.resource, req.href, offset=data['offset']+data['limit']), _("Older conversations")) if offset > 0: add_link(req, 'prev', get_resource_url(self.env, mailinglist.resource, req.href, offset=data['offset']-data['limit']), _("Newer conversations")) add_link(req, 'up', req.href.mailinglist(), _("List of mailinglists")) prevnext_nav(req, _("Newer conversations"), _("Older conversations"), ("Back to Mailinglists")) if 'MAILINGLIST_ADMIN' in req.perm: add_ctxtnav(req, tag.a(tag.i(class_="fa fa-cog"), ' Manage List', href=req.href.admin('mailinglist', 'lists', mailinglist.emailaddress), title='Manage and subscribe users to the %s mailing list' % mailinglist.name)) # Check if user is already subscribed to mailing list # and add the appropriate subscribe / unsubscribe ribbon option if mailinglist.is_subscribed(req.authname): add_ctxtnav(req, tag.form(tag.input(tag.a(tag.i(class_='fa fa-eye-slash'), ' Unsubscribe', title='Unsubscribe from the %s mailing list' % mailinglist.name, id='subscribe-link'), name='unsubscribe', value=mailinglist.emailaddress, class_='hidden'), method_='post', action='', id='subscribe-form', class_='hidden')) else: add_ctxtnav(req, tag.form(tag.input(tag.a(tag.i(class_='fa fa-eye'), ' Subscribe', title='Subscribe to the %s mailing list' % mailinglist.name, id='subscribe-link'), name='subscribe', value=mailinglist.emailaddress, class_='hidden'), method_='post', action='', id='subscribe-form', class_='hidden')) return 'mailinglist_conversations.html', data, None else: return 'mailinglist_list.html', data, None