Merge lp:~sophron/postorius/create_users into lp:postorius

Proposed by George Chatzisofroniou
Status: Merged
Merged at revision: 93
Proposed branch: lp:~sophron/postorius/create_users
Merge into: lp:postorius
Diff against target: 273 lines (+187/-2)
9 files modified
src/postorius/forms.py (+35/-0)
src/postorius/templates/postorius/base.html (+1/-0)
src/postorius/templates/postorius/menu/mm_user_nav.html (+8/-0)
src/postorius/templates/postorius/users/index.html (+32/-0)
src/postorius/templates/postorius/users/new.html (+12/-0)
src/postorius/templates/postorius/users/summary.html (+17/-0)
src/postorius/urls.py (+6/-1)
src/postorius/views/generic.py (+22/-0)
src/postorius/views/views.py (+54/-1)
To merge this branch: bzr merge lp:~sophron/postorius/create_users
Reviewer Review Type Date Requested Status
Florian Fuchs Approve
Review via email: mp+129567@code.launchpad.net

Description of the change

I worked on this bug: https://bugs.launchpad.net/postorius/+bug/1058445

I added the feature to create a mailman user account, but in order to edit the basic user data via the web interface, some additions to mailman client are required. See, https://bugs.launchpad.net/mailman.client/+bug/1066352 and https://bugs.launchpad.net/mailman.client/+bug/1066343.

We also need an icon for the new entry on the menu: https://bugs.launchpad.net/postorius/+bug/1066340

I think it's better to divide the whole 'edit user' feature into smaller ones (delete user, register addresses to a user, basic data edit, etc).

To post a comment you must log in.
lp:~sophron/postorius/create_users updated
92. By George Chatzisofroniou

Added the required templates for users

Revision history for this message
Florian Fuchs (flo-fuchs) wrote :

Hi George,

thank you for your changes (and the bug reports)! I have just merged them to the trunk. I only made some very small changes and additions (adjusted the docstring in MailmanUserView, added the user icon, some PEP8 fixes...). I also made the user list and details only available to superusers (I don't think every user should be able to see who else is there...).

So now that we have a method to add new users to the core, we need a way to connect them to the users in the postorius database (the ones added through django.contrib.auth). I added some comment about that in the bug description:

https://bugs.launchpad.net/postorius/+bug/1058445

Cheers and thanks again
Florian

review: Approve
Revision history for this message
Richard Wackerbarth (wacky) wrote :

Isn't the visibility of the list members a per_list policy setting?

In any case, we will need to have list administrators (who are not the superuser) be able to see the membership on their list.

I think that Postorius will require a better model for roles.

Richard

On Oct 25, 2012, at 3:19 AM, Florian Fuchs <email address hidden> wrote:

> Review: Approve
>
> Hi George,
>
> thank you for your changes (and the bug reports)! I have just merged them to the trunk. I only made some very small changes and additions (adjusted the docstring in MailmanUserView, added the user icon, some PEP8 fixes...). I also made the user list and details only available to superusers (I don't think every user should be able to see who else is there...).

Revision history for this message
Florian Fuchs (flo-fuchs) wrote :

Hi Richard,

2012/10/25 Richard Wackerbarth <email address hidden>:
> Isn't the visibility of the list members a per_list policy setting?

Please not that users/index is not a list of members, but a list of
the users stored in Mailman's core database. The members of a list are
displayed for each list separately and can be accessed only by
superusers and list owners.

> In any case, we will need to have list administrators (who are not the superuser) be able to see the membership on their list.
>
> I think that Postorius will require a better model for roles.

Yes, this is already possible. There are two custom permission
decorators for view functions (list_owner_required and
list_moderator_required) that check if the currently logged-in user is
either the owner or the moderator of a list. Currently those
restrictions are in user on the list members page, the moderation
page, the list settings etc.

Next step is providing forms to make existing users owners and/or moderators.

Cheers
Florian

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/postorius/forms.py'
2--- src/postorius/forms.py 2012-09-23 02:35:28 +0000
3+++ src/postorius/forms.py 2012-10-13 18:25:24 +0000
4@@ -534,6 +534,41 @@
5 "delivery_mode", "delivery_status"]]
6
7
8+class UserNew(FieldsetForm):
9+ """
10+ Form field to add a new user
11+ """
12+ display_name = forms.CharField(
13+ label=_('User Name'),
14+ required=True,
15+ error_messages={'required': _('Please enter a display name.'),
16+ 'invalid': _('Please enter a valid display name.')})
17+ email = forms.EmailField(
18+ label=_("User's email address"),
19+ error_messages={
20+ 'required': _("Please enter the user's email address.")},
21+ required=True)
22+ password = forms.CharField(
23+ label=_('Password'),
24+ required=True,
25+ error_messages={'required': _('Please enter a password.')},
26+ widget=forms.PasswordInput(render_value=False))
27+ password_repeat = forms.CharField(
28+ label=_('Repeat password'),
29+ required=True,
30+ error_messages={'required': _('Please repeat the password.')},
31+ widget=forms.PasswordInput(render_value=False))
32+
33+ def clean(self):
34+ cleaned_data = self.cleaned_data
35+ password = cleaned_data.get("password")
36+ password_repeat = cleaned_data.get("password_repeat")
37+ if password != password_repeat:
38+ raise forms.ValidationError("Passwords must be identical.")
39+
40+ return cleaned_data
41+
42+
43 class UserSettings(FieldsetForm):
44 """Form handling the user settings.
45 """
46
47=== modified file 'src/postorius/templates/postorius/base.html'
48--- src/postorius/templates/postorius/base.html 2012-08-29 15:03:37 +0000
49+++ src/postorius/templates/postorius/base.html 2012-10-13 18:25:24 +0000
50@@ -24,6 +24,7 @@
51 <ul class="mm_metaNav">
52 <li><a class="mm_lists" href="{% url list_index %}">Lists</a></li>
53 {% if user.is_authenticated %}
54+ <li><a class="mm_users" href="{% url user_index %}">Users</a></li>
55 <li><a class="mm_todos" href="{% url user_todos %}">Todos</a></li>
56 {% endif %}
57 {% if user.is_superuser %}
58
59=== added file 'src/postorius/templates/postorius/menu/mm_user_nav.html'
60--- src/postorius/templates/postorius/menu/mm_user_nav.html 1970-01-01 00:00:00 +0000
61+++ src/postorius/templates/postorius/menu/mm_user_nav.html 2012-10-13 18:25:24 +0000
62@@ -0,0 +1,8 @@
63+{% load i18n %}
64+<div class="mm_subHeader">
65+ <span class="mm_context">{{ mm_user.address }}</span>
66+ <ul class="mm_nav">
67+ <li class="mm_user_summary"><a href="{% url user_summary mm_user.user_id %}">{% trans "Info" %}</a></li>
68+ <li class="mm_new_user"><a class="btn btn-mini btn-success" href="{% url user_new %}">{% trans "New User" %}</a></li>
69+ </ul>
70+</div>
71
72=== added directory 'src/postorius/templates/postorius/users'
73=== added file 'src/postorius/templates/postorius/users/index.html'
74--- src/postorius/templates/postorius/users/index.html 1970-01-01 00:00:00 +0000
75+++ src/postorius/templates/postorius/users/index.html 2012-10-13 18:25:24 +0000
76@@ -0,0 +1,32 @@
77+{% extends "postorius/base.html" %}
78+{% load i18n %}
79+
80+{% block main %}
81+ {% if user.is_superuser %}
82+ <ul class="mm_nav">
83+ <li class="mm_new_user"><a class="btn btn-mini btn-success" href="{% url user_new %}">{% trans "New User" %}</a></li>
84+ </ul>
85+ {% endif %}
86+ <h1>{% trans 'Users' %}</h1>
87+
88+ <table class="table table-bordered table-striped">
89+ <thead>
90+ <tr>
91+ <th>{% trans 'Email' %}</th>
92+ <th>{% trans 'Display name' %}</th>
93+ </tr>
94+ </thead>
95+ <tbody>
96+ {% for mm_user in mm_users %}
97+ <tr>
98+ <td>
99+ <a href="{% url user_summary user_id=mm_user.user_id %}">{% for address in mm_user.addresses|slice:":1" %}{{ address }}{% endfor %}</a>
100+ </td>
101+ <td>
102+ {{ mm_user.display_name }}
103+ </td>
104+ </tr>
105+ {% endfor %}
106+ </tbody>
107+ </table>
108+{% endblock main %}
109
110=== added file 'src/postorius/templates/postorius/users/new.html'
111--- src/postorius/templates/postorius/users/new.html 1970-01-01 00:00:00 +0000
112+++ src/postorius/templates/postorius/users/new.html 2012-10-13 18:25:24 +0000
113@@ -0,0 +1,12 @@
114+{% extends extend_template %}
115+{% load i18n %}
116+
117+{% block main %}
118+ <h1>{% trans "Add a new User" %}</h1>
119+ <form action="{% url user_new %}" method="post" class="well"> {% csrf_token %}
120+ {{ form.as_p }}
121+ <div class="field">
122+ <button class="btn btn-success" type="submit">{% trans "Create User" %}</button>
123+ </div>
124+ </form>
125+{% endblock main %}
126
127=== added file 'src/postorius/templates/postorius/users/summary.html'
128--- src/postorius/templates/postorius/users/summary.html 1970-01-01 00:00:00 +0000
129+++ src/postorius/templates/postorius/users/summary.html 2012-10-13 18:25:24 +0000
130@@ -0,0 +1,17 @@
131+{% extends extend_template %}
132+{% load i18n %}
133+
134+{% block main %}
135+ {% include 'postorius/menu/mm_user_nav.html' %}
136+ <h1>{% trans 'Mailman User' %}</h1>
137+
138+ <p><strong>Display name:</strong> {{ mm_user.display_name}}</p>
139+
140+ <h2>Valid email addresses for this account:</h2>
141+ <ul>
142+ {% for address in mm_user.addresses %}
143+ <li>{{ address }}</li>
144+ {% endfor %}
145+ </ul>
146+
147+{% endblock main %}
148
149=== modified file 'src/postorius/urls.py'
150--- src/postorius/urls.py 2012-09-23 12:10:25 +0000
151+++ src/postorius/urls.py 2012-10-13 18:25:24 +0000
152@@ -26,7 +26,7 @@
153
154 urlpatterns = patterns(
155 'postorius.views',
156- (r'^$', 'list_index'),
157+ (r'^$', 'list_index'),
158 # /account/
159 url(r'^accounts/login/$', 'user_login', name='user_login'),
160 url(r'^accounts/logout/$', 'user_logout', name='user_logout'),
161@@ -76,4 +76,9 @@
162 url(r'^lists/(?P<fqdn_listname>[^/]+)/settings/(?P<visible_section>[^/]+)?'
163 '(?:/(?P<visible_option>.*))?$', 'list_settings',
164 name='list_settings'),
165+ # /users/
166+ url(r'^users/$', 'user_index', name='user_index'),
167+ url(r'^users/new/$', 'user_new', name='user_new'),
168+ url(r'^users/(?P<user_id>[^/]+)/$',
169+ UserSummaryView.as_view(), name='user_summary'),
170 ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
171
172=== modified file 'src/postorius/views/generic.py'
173--- src/postorius/views/generic.py 2012-09-26 21:13:41 +0000
174+++ src/postorius/views/generic.py 2012-10-13 18:25:24 +0000
175@@ -47,3 +47,25 @@
176 if 'template' in kwargs:
177 self.template = kwargs['template']
178 return super(MailingListView, self).dispatch(request, *args, **kwargs)
179+
180+class MailmanUserView(TemplateView):
181+ """A generic view for everything based on a mailman.client
182+ list object.
183+
184+ Sets self.mailing_list to list object if fqdn_listname in **kwargs.
185+ """
186+
187+ def _get_user(self, user_id):
188+ return MailmanUser.objects.get_or_404(address=user_id)
189+
190+ def dispatch(self, request, *args, **kwargs):
191+ # get the list object.
192+ if 'user_id' in kwargs:
193+ try:
194+ self.mm_user = self._get_user(kwargs['user_id'])
195+ except MailmanApiError:
196+ return utils.render_api_error(request)
197+ # set the template
198+ if 'template' in kwargs:
199+ self.template = kwargs['template']
200+ return super(MailmanUserView, self).dispatch(request, *args, **kwargs)
201
202=== modified file 'src/postorius/views/views.py'
203--- src/postorius/views/views.py 2012-09-26 21:07:54 +0000
204+++ src/postorius/views/views.py 2012-10-13 18:25:24 +0000
205@@ -44,7 +44,7 @@
206 MailmanApiError, Mailman404Error)
207 from postorius.forms import *
208 from postorius.auth.decorators import list_owner_required
209-from postorius.views.generic import MailingListView
210+from postorius.views.generic import MailingListView, MailmanUserView
211
212
213 logger = logging.getLogger(__name__)
214@@ -697,6 +697,59 @@
215 context_instance=RequestContext(request))
216
217
218+class UserSummaryView(MailmanUserView):
219+ """Shows a summary of a user.
220+ """
221+
222+ def get(self, request, user_id):
223+ settingsform = MembershipSettings()
224+ return render_to_response('postorius/users/summary.html',
225+ {'mm_user': self.mm_user,
226+ 'settingsform': settingsform},
227+ context_instance=RequestContext(request))
228+
229+
230+@login_required
231+def user_index(request, template='postorius/users/index.html'):
232+ """Show a table of all users.
233+ """
234+ error = None
235+ try:
236+ mm_users = MailmanUser.objects.all()
237+ except MailmanApiError:
238+ return utils.render_api_error(request)
239+ return render_to_response(template,
240+ {'error': error,
241+ 'mm_users': mm_users},
242+ context_instance=RequestContext(request))
243+
244+
245+@login_required
246+@user_passes_test(lambda u: u.is_superuser)
247+def user_new(request):
248+ message = None
249+ if request.method == 'POST':
250+ form = UserNew(request.POST)
251+ if form.is_valid():
252+ user = MailmanUser(display_name=form.cleaned_data['display_name'],
253+ email=form.cleaned_data['email'],
254+ password=form.cleaned_data['password'])
255+ try:
256+ user.save()
257+ except MailmanApiError:
258+ return utils.render_api_error(request)
259+ except HTTPError, e:
260+ messages.error(request, e)
261+ else:
262+ messages.success(request, _("New User registered"))
263+ return redirect("user_index")
264+ else:
265+ form = UserNew()
266+ return render_to_response('postorius/users/new.html',
267+ {'form': form, 'message': message},
268+ context_instance=RequestContext(request))
269+
270+
271 def user_logout(request):
272 logout(request)
273 return redirect('user_login')

Subscribers

People subscribed via source and target branches