source: trunk/plugins/mailmanplugin/mailinglists/admin.py @ 816

Revision 816, 10.5 KB checked in by rcorsaro, 3 years ago (diff)

adds fallback to announcer default domain

fixes #78

Line 
1# -*- coding: iso-8859-1 -*-
2#
3# Copyright (C) 2007 Optaros, Inc.
4#
5
6import os
7
8from genshi.builder import tag
9
10from trac.admin.api import IAdminPanelProvider
11from trac.core import *
12from trac.config import Option
13from trac.notification import NotifyEmail
14from trac.perm import IPermissionRequestor
15from trac.util.translation import _
16from trac.web.chrome import ITemplateProvider
17
18# Local imports
19from mailinglists import errors
20from mailinglists.api import MailingListAPI
21
22class MailingListAdminPanel(Component):
23    implements(IPermissionRequestor, ITemplateProvider, IAdminPanelProvider)
24
25    _listcreator_pwd = Option('mailing_lists', 'listcreator_pwd', 
26                              doc="""Password for mailman listcreator""")
27   
28    def __init__(self):
29        self._mlapi = MailingListAPI(self.env)
30
31    # IPermissionRequestor methods
32    def get_permission_actions(self):
33        return ['MAIL_ADMIN']
34
35    # ITemplateProvider methods
36    def get_htdocs_dirs(self):
37        return []
38
39    def get_templates_dirs(self):
40        from pkg_resources import resource_filename
41        return [resource_filename(__name__, 'templates')]
42
43    # IAdminPanelProvider
44    def get_admin_panels(self, req):
45        if req.perm.has_permission('MAIL_ADMIN'):
46            yield ('maillist', _('Mailing Lists'), 'lists', _('Lists'))
47
48    def render_admin_panel(self, req, cat_id, panel_id, path_info):
49
50        req.perm.require('MAIL_ADMIN')
51
52        errors = None
53        if req.method == 'POST':
54            if 'newlist' in req.args:
55                errors=self._do_newlist(req, path_info)
56            elif 'add_user' in req.args:
57                errors=self._do_add(req, path_info)
58            elif 'manage_users' in req.args:
59                errors=self._do_manage(req, path_info)
60            if not errors:
61                req.redirect(req.href.admin(cat_id, panel_id, path_info))
62            self.log.warning(errors)
63
64        return self._render_view(req, path_info, errors)
65       
66    def _render_view(self, req, email_list, errors):
67
68        error_msg = []
69        if errors:
70            error_msg.append(errors)
71        if not self._listcreator_pwd:
72            error_msg.append('The list-creator password is not configured.  Please contact your system administrator.')
73
74        # get the mailing lists from the configuration
75        data = {'lists': self.config.getlist('mailing_lists', 'lists'),
76                'project_shortname': os.path.basename(self.env.path),
77                'email_list': email_list,
78                'email_host': self._mlapi.get_email_host(),
79                'mm_script_url': self._mlapi.script_url('admin'),
80                'errors': error_msg }
81        if email_list:
82            userlist = self._get_userlist(email_list)
83            data['userlist']= userlist.values()
84
85        return 'mailinglists_admin.html', data
86
87    def _do_newlist(self, req, pathinfo):
88        # always convert request arguments into pure strings since they get passed back as unicode
89        list_name = str(req.args.get('new_list_name', None))
90        list_password = str(req.args.get('list_password', None))
91        conf_list_password = str(req.args.get('conf_list_password', None))
92
93        error_msg=None
94        # now check for possible error conditions
95        if not list_name:
96            error_msg = "'List Name' cannot be empty."
97        elif not list_password:
98            error_msg = "'List Password' cannot be empty."
99        elif list_password != conf_list_password:
100            error_msg = "Password does not match confirmation."
101
102        user_name = str(req.authname)
103        user_email = self._get_user_email(req)
104        project_name = self.config.get('project','name')
105       
106        if not error_msg:
107            # we have no error condition, so we can call the api
108            error = self._mlapi.create_new_mailing_list(list_name, user_name, user_email, list_password, self._listcreator_pwd, project_name)
109            if error:
110                self.log.error(error)
111                error_msg = error
112            else:
113                mailing_lists=self.config.getlist('mailing_lists', 'lists')
114                mailing_lists.append(list_name)
115                self.config.set('mailing_lists', 'lists', ','.join(mailing_lists))
116                self.config.save()
117        return error_msg
118               
119    def _do_add(self, req, email_list):
120
121        newuser_email = str(req.args.get('newuser_email'))
122        if len(newuser_email) > 0:
123            error = self._mlapi.verify_email(newuser_email)
124            if error:
125                self.log.error(error)
126                raise TracError(error)
127            project_name = self.config.get('project', 'name')
128            subscribe = [{'email':newuser_email, 'name':str(req.args.get('newuser_name',''))}]
129            error = self._mlapi.subscribe_list_members(email_list, subscribe, project_name, req.args.has_key('notify_flag'))
130            if error:
131                self.log.error(error)
132                raise TracError(error)
133       
134    def _do_manage(self, req, email_list):
135       
136        subscribe = []
137        unsubscribe = []
138        sel = req.args.getlist('sel_user')
139       
140        userlist = self._get_userlist(email_list)
141       
142        # let's process the new subscriptions first
143        selected_map = {}
144        for user in sel:
145            # only process the list if it is different from 'None'
146            if user:
147                user = str(user)
148                # add the selected users to a map for later use
149                selected_map[user] = user
150                user_profile = userlist[user]
151                # only subscribe users that are not yet subscribed
152                if not user_profile['subscribed']:
153                    subscribe.append( {'email':user, 'name': user_profile['name']} )
154
155        subscribe = [ {'email':user, 'name': userlist[user]['name']} 
156                      for user in sel
157                      if not userlist[user]['subscribed']]
158        # self.log.warning(userlist)
159        unsubcribe = [ {'email':user, 'name': userlist[user]['name']} 
160                      for user, user_profile in userlist.items() if user_profile['subscribed'] and not user in sel]
161        # get a list of the keys (email addresses) from our userlist
162        allusers = userlist.keys()
163        # now loop over it and determine who has been unselected
164        for user in allusers:
165            user_profile = userlist[user]
166            if user_profile['subscribed']:
167                if not selected_map.has_key(user):
168                    # the user has been unselected in the list,
169                    # so mark for unsubscription
170                    unsubscribe.append( {'email':user, 'name': user_profile['name']} )
171                   
172        # we are ready now to process the subscriptions
173        project_name = self.config.get('project', 'name')
174        if len(subscribe) > 0:
175            error = self._mlapi.subscribe_list_members(email_list, subscribe, project_name, req.args.has_key('notify_flag'))
176            if error:
177                self.log.error(error)
178                raise TracError(error)
179        # and the un-subscriptions, but only we did not get an error message before
180        if len(unsubscribe) > 0:
181            error = self._mlapi.unsubscribe_list_members(email_list, unsubscribe, project_name, req.args.has_key('notify_flag'))
182            if error:
183                self.log.error(error)
184                raise TracError(error)
185
186    # method that will return a data structure containing the team of this project
187    # currently, it will return the "known user', but
188    # in the future, we will interface with the team roster plugin
189    def _get_team(self):
190        userlist = []
191        try:
192            for username, name, email in self.env.get_known_users():   
193                # we consider any user that has a username and email address here
194                # but we need at least an email address,
195                # otherwise we could not subscribe this user to a mail list
196               
197                if username and email:
198                    userlist.append( {'username':username, 'name':name, 'email':email} )
199
200        except:
201            userlist.append( dict( username='error',name='error', email='error'))
202        return userlist
203
204    def _get_userlist(self, email_list):
205        userlist={}
206        # and get the member list from Mailman
207        mail_subscribers = self._mlapi.get_list_members(email_list)
208       
209        if mail_subscribers.has_key('error'):
210            error_msg = mail_subscribers['error']
211            # TODO: this is currently failing silently
212            self.log.error("Problem getting member list for mailing list %s : %s"%(email_list,error_msg))
213            raise TracError(error_msg)
214       
215        # now we need to merge the two lists without having duplicates
216        # first iterate over the trac user list and add them to a dict with the email as key
217        for tracuser in self._get_team():
218            email=tracuser.get('email')
219            # only add it if we have an email address
220            if email:
221                # now create a dict with the email as key
222                # but only add it once in case we have the same email address twice
223                email=email.lower()
224                if not userlist.has_key(email):
225                    userlist[email] = {'username':str(tracuser.get('username','')),
226                                       'name':tracuser.get('name',''),
227                                       'email':email,
228                                       'subscribed':0 }
229               
230        # now iterate over the subscriber list from Mailman
231        for mailuser in mail_subscribers['memberlist']:
232            # DG: how would a mailman member not have an email?
233            if mailuser.has_key('email'):
234                email = mailuser['email']
235                # check if we have this user already in trac -> set to subscribed and leave unchanged
236                if userlist.has_key(email):
237                    userlist[email]['subscribed']=1
238                else:
239                    # otherwise add to userlist
240                    userlist[email] = {'username':'',
241                                       'name':mailuser['name'],
242                                       'email':email,
243                                       'subscribed':1}
244        return userlist
245
246    def _get_user_email(self, req):
247        address = req.session.get('email')
248        if not address:
249            domain = self.config.get('notification', 'smtp_default_domain')
250            if not domain:
251                domain = self.config.get('announcer', 'email_default_domain')
252            if domain:
253                address = "%s@%s" % (req.authname, domain)
254       
255        return address
Note: See TracBrowser for help on using the repository browser.