diff --git a/forms.py b/forms.py index 0a1a5bd..0548a6d 100644 --- a/forms.py +++ b/forms.py @@ -88,7 +88,7 @@ ["Available Languages", "languages"],] class ListSubscribe(forms.Form): - """Form fields to join an existing list + """Form fields to join an existing list. """ listname = forms.EmailField( label = _('List Name'), @@ -118,7 +118,7 @@ # should add password! class ListUnsubscribe(forms.Form): - """Form fields to leave an existing list + """Form fields to leave an existing list. """ listname = forms.EmailField( label = _('List Name'), @@ -142,3 +142,24 @@ ) # should at one point add the password to be required as well! +class ListSettings(FieldsetForm): + """Form fields dealing with the list settings. + """ + # currently only one field for testing that the form works at all... + listname = forms.EmailField( + label = _('List Name'), + initial = '@mailman.state-of-mind.de', + error_messages = { + 'required': _('Please enter a name for your list.'), + 'invalid': _('Please enter a valid list name.') + } + ) + + class Meta: + """Class to handle the automatic insertion of fieldsets and divs. + + To use it: add a list for each wished fieldset. The first item in + the list should be the wished name of the fieldset, the following + the fields that should be included in the fieldset. + """ + layout = [["List Details", "listname"],] diff --git a/mailman_rest_client.py b/mailman_rest_client.py index 7e967f9..f90c78f 100644 --- a/mailman_rest_client.py +++ b/mailman_rest_client.py @@ -183,6 +183,8 @@ """ super(_Domain, self).__init__(host) self.info = self._http_request('/3.0/domains/' + email_host) + if self.info == 404: + raise ValueError def create_list(self, list_name): """Create a mailing list and return a list object. @@ -262,4 +264,7 @@ else: return sorted(response['entries'], key=itemgetter('self_link')) - + + def __str__(self): + """A string representation of a list.""" + return "A list object for the list '%s'." % self.info['fqdn_listname'] diff --git a/mockdata.py b/mockdata.py index 5405322..586c415 100644 --- a/mockdata.py +++ b/mockdata.py @@ -15,101 +15,102 @@ return http_req -def add_mock_data(fn): + +def add_mock_data(cls): """Decorator function to add mock data from the database to a list. """ - def __init__(self, *args): - self.mocked = cls(*args) - self.mocked.info['id'] = 9 - self.mocked.info['list_name'] = 'List name lorem ipsum dolor sit' - self.mocked.info['host_name'] = 'Host name lorem ipsum dolor sit' - self.mocked.info['list_id'] = 'Some list ID lorem ipsum dolor sit' - self.mocked.info['include_list_post_header'] = True - self.mocked.info['include_rfc2369_headers'] = True - self.mocked.info['autorespond_owner'] = 9 - self.mocked.info['autoresponse_owner_text'] = 'Auto response owner text lorem ipsum dolor sit' - self.mocked.info['autorespond_postings'] = 9 - self.mocked.info['autoresponse_postings_text'] = 'Auto response postings text lorem ipsum dolor sit' - self.mocked.info['autorespond_requests'] = 9 - self.mocked.info['autoresponse_request_text'] = 'Auto response request text lorem ipsum dolor sit' - self.mocked.info['autoresponse_grace_period'] = 'Auto response grace period lorem ipsum dolor sit' - self.mocked.info['ban_list'] = 'Ban list (BLOB format) lorem ipsum dolor sit' - self.mocked.info['bounce_info_stale_after'] = 'Bounce info stale after lorem ipsum dolor sit' - self.mocked.info['bounce_matching_headers'] = 'Bounce matching headers lorem ipsum dolor sit' - self.mocked.info['bounce_notify_owner_on_disable'] = True - self.mocked.info['bounce_notify_owner_on_removal'] = True - self.mocked.info['bounce_processing'] = True - self.mocked.info['bounce_score_threshold'] = 9 - self.mocked.info['bounce_unrecognized_goes_to_list_owner'] = True - self.mocked.info['bounce_you_are_disabled_warnings'] = 9 - self.mocked.info['bounce_you_are_disabled_warnings_interval'] = 'Bounce you are disabled warnings lorem ipsum dolor sit' - self.mocked.info['filter_content'] = True - self.mocked.info['collapse_alternatives'] = True - self.mocked.info['convert_html_to_plaintext'] = True - self.mocked.info['default_member_moderation'] = True - self.mocked.info['description'] = 'Description lorem ipsum dolor sit' - self.mocked.info['digest_footer'] = 'Digest footer lorem ipsum dolor sit' - self.mocked.info['digest_header'] = 'Digest header lorem ipsum dolor sit' - self.mocked.info['digest_is_default'] = True - self.mocked.info['digest_send_periodic'] = True - self.mocked.info['digest_size_threshold'] = 9 - self.mocked.info['digest_volume_frequency'] = 'Digest volume frequency lorem ipsum dolor sit' - self.mocked.info['digestable'] = True - self.mocked.info['discard_these_nonmembers'] = 'Discard these non members (BLOB format) lorem ipsum dolor sit' - self.mocked.info['emergency'] = True - self.mocked.info['encode_ascii_prefixes'] = True - self.mocked.info['first_strip_reply_to'] = True - self.mocked.info['forward_auto_discards'] = True - self.mocked.info['gateway_to_mail'] = True - self.mocked.info['gateway_to_news'] = True - self.mocked.info['generic_nonmember_action'] = 9 - self.mocked.info['goodbye_msg'] = 'Goodbye message lorem ipsum dolor sit' - self.mocked.info['header_matches'] = 'Header matches (BLOB format) lorem ipsum dolor sit' - self.mocked.info['hold_these_nonmembers'] = 'Hold these non members (BLOB format) lorem ipsum dolor sit' - self.mocked.info['info'] = 'Info lorem ipsum dolor sit' - self.mocked.info['linked_newsgroup'] = 'Linked newsgroup lorem ipsum dolor sit' - self.mocked.info['max_days_to_hold'] = 9 - self.mocked.info['max_message_size'] = 9 - self.mocked.info['max_num_recipients'] = 9 - self.mocked.info['member_moderation_action'] = True - self.mocked.info['member_moderation_notice'] = 'Member moderation notice lorem ipsum dolor sit' - self.mocked.info['mime_is_default_digest'] = True - self.mocked.info['moderator_password'] = 'Moderator password lorem ipsum dolor sit' - self.mocked.info['msg_footer'] = 'Message footer lorem ipsum dolor sit' - self.mocked.info['msg_header'] = 'Message header lorem ipsum dolor sit' - self.mocked.info['new_member_options'] = 9 - self.mocked.info['news_moderation'] = 'News moderation lorem ipsum dolor sit' - self.mocked.info['news_prefix_subject_too'] = True - self.mocked.info['nntp_host'] = 'Nntp host lorem ipsum dolor sit' - self.mocked.info['nondigestable'] = True - self.mocked.info['nonmember_rejection_notice'] = 'Non member rejection notice lorem ipsum dolor sit' - self.mocked.info['obscure_addresses'] = True - self.mocked.info['personalize'] = 'Personalize lorem ipsum dolor sit' - self.mocked.info['pipeline'] = 'Pipeline lorem ipsum dolor sit' - self.mocked.info['post_id'] = 9 - self.mocked.info['preferred_language'] = 'Preferred language lorem ipsum dolor sit' - self.mocked.info['private_roster'] = True - self.mocked.info['real_name'] = 'Real name lorem ipsum dolor sit' - self.mocked.info['reject_these_nonmembers'] = 'Reject these non members (BLOB format) lorem ipsum dolor sit' - self.mocked.info['reply_goes_to_list'] = 'Reply goes to list lorem ipsum dolor sit' - self.mocked.info['reply_to_address'] = 'Reply to address lorem ipsum dolor sit' - self.mocked.info['require_explicit_destination'] = True - self.mocked.info['respond_to_post_requests'] = True - self.mocked.info['scrub_nondigest'] = True - self.mocked.info['send_goodbye_msg'] = True - self.mocked.info['send_reminders'] = True - self.mocked.info['send_welcome_msg'] = True - self.mocked.info['start_chain'] = 'Start chain lorem ipsum dolor sit' - self.mocked.info['subject_prefix'] = 'Subject prefix lorem ipsum dolor sit' - self.mocked.info['subscribe_auto_approval'] = 'Subscribe auto approval (BLOB format) lorem ipsum dolor sit' - self.mocked.info['subscribe_policy'] = 9 - self.mocked.info['topics'] = 'Topics (BLOB format) lorem ipsum dolor sit' - self.mocked.info['topics_bodylines_limit'] = 9 - self.mocked.info['topics_enabled'] = True - self.mocked.info['unsubscribe_policy'] = 9 - self.mocked.info['welcome_msg'] = 'Welcome message lorem ipsum dolor sit' - return __init__(*args) + cls.__orig__init__ = cls.__init__ + def __init__(self, *args, **kwargs): + cls.__orig__init__(self, *args, **kwargs) + self.info['id'] = 9 + self.info['list_name'] = 'List name lorem ipsum dolor sit' + self.info['host_name'] = 'Host name lorem ipsum dolor sit' + self.info['list_id'] = 'Some list ID lorem ipsum dolor sit' + self.info['include_list_post_header'] = True + self.info['include_rfc2369_headers'] = True + self.info['autorespond_owner'] = 9 + self.info['autoresponse_owner_text'] = 'Auto response owner text lorem ipsum dolor sit' + self.info['autorespond_postings'] = 9 + self.info['autoresponse_postings_text'] = 'Auto response postings text lorem ipsum dolor sit' + self.info['autorespond_requests'] = 9 + self.info['autoresponse_request_text'] = 'Auto response request text lorem ipsum dolor sit' + self.info['autoresponse_grace_period'] = 'Auto response grace period lorem ipsum dolor sit' + self.info['ban_list'] = 'Ban list (BLOB format) lorem ipsum dolor sit' + self.info['bounce_info_stale_after'] = 'Bounce info stale after lorem ipsum dolor sit' + self.info['bounce_matching_headers'] = 'Bounce matching headers lorem ipsum dolor sit' + self.info['bounce_notify_owner_on_disable'] = True + self.info['bounce_notify_owner_on_removal'] = True + self.info['bounce_processing'] = True + self.info['bounce_score_threshold'] = 9 + self.info['bounce_unrecognized_goes_to_list_owner'] = True + self.info['bounce_you_are_disabled_warnings'] = 9 + self.info['bounce_you_are_disabled_warnings_interval'] = 'Bounce you are disabled warnings lorem ipsum dolor sit' + self.info['filter_content'] = True + self.info['collapse_alternatives'] = True + self.info['convert_html_to_plaintext'] = True + self.info['default_member_moderation'] = True + self.info['description'] = 'Description lorem ipsum dolor sit' + self.info['digest_footer'] = 'Digest footer lorem ipsum dolor sit' + self.info['digest_header'] = 'Digest header lorem ipsum dolor sit' + self.info['digest_is_default'] = True + self.info['digest_send_periodic'] = True + self.info['digest_size_threshold'] = 9 + self.info['digest_volume_frequency'] = 'Digest volume frequency lorem ipsum dolor sit' + self.info['digestable'] = True + self.info['discard_these_nonmembers'] = 'Discard these non members (BLOB format) lorem ipsum dolor sit' + self.info['emergency'] = True + self.info['encode_ascii_prefixes'] = True + self.info['first_strip_reply_to'] = True + self.info['forward_auto_discards'] = True + self.info['gateway_to_mail'] = True + self.info['gateway_to_news'] = True + self.info['generic_nonmember_action'] = 9 + self.info['goodbye_msg'] = 'Goodbye message lorem ipsum dolor sit' + self.info['header_matches'] = 'Header matches (BLOB format) lorem ipsum dolor sit' + self.info['hold_these_nonmembers'] = 'Hold these non members (BLOB format) lorem ipsum dolor sit' + self.info['info'] = 'Info lorem ipsum dolor sit' + self.info['linked_newsgroup'] = 'Linked newsgroup lorem ipsum dolor sit' + self.info['max_days_to_hold'] = 9 + self.info['max_message_size'] = 9 + self.info['max_num_recipients'] = 9 + self.info['member_moderation_action'] = True + self.info['member_moderation_notice'] = 'Member moderation notice lorem ipsum dolor sit' + self.info['mime_is_default_digest'] = True + self.info['moderator_password'] = 'Moderator password lorem ipsum dolor sit' + self.info['msg_footer'] = 'Message footer lorem ipsum dolor sit' + self.info['msg_header'] = 'Message header lorem ipsum dolor sit' + self.info['new_member_options'] = 9 + self.info['news_moderation'] = 'News moderation lorem ipsum dolor sit' + self.info['news_prefix_subject_too'] = True + self.info['nntp_host'] = 'Nntp host lorem ipsum dolor sit' + self.info['nondigestable'] = True + self.info['nonmember_rejection_notice'] = 'Non member rejection notice lorem ipsum dolor sit' + self.info['obscure_addresses'] = True + self.info['personalize'] = 'Personalize lorem ipsum dolor sit' + self.info['pipeline'] = 'Pipeline lorem ipsum dolor sit' + self.info['post_id'] = 9 + self.info['preferred_language'] = 'Preferred language lorem ipsum dolor sit' + self.info['private_roster'] = True + self.info['real_name'] = 'Real name lorem ipsum dolor sit' + self.info['reject_these_nonmembers'] = 'Reject these non members (BLOB format) lorem ipsum dolor sit' + self.info['reply_goes_to_list'] = 'Reply goes to list lorem ipsum dolor sit' + self.info['reply_to_address'] = 'Reply to address lorem ipsum dolor sit' + self.info['require_explicit_destination'] = True + self.info['respond_to_post_requests'] = True + self.info['scrub_nondigest'] = True + self.info['send_goodbye_msg'] = True + self.info['send_reminders'] = True + self.info['send_welcome_msg'] = True + self.info['start_chain'] = 'Start chain lorem ipsum dolor sit' + self.info['subject_prefix'] = 'Subject prefix lorem ipsum dolor sit' + self.info['subscribe_auto_approval'] = 'Subscribe auto approval (BLOB format) lorem ipsum dolor sit' + self.info['subscribe_policy'] = 9 + self.info['topics'] = 'Topics (BLOB format) lorem ipsum dolor sit' + self.info['topics_bodylines_limit'] = 9 + self.info['topics_enabled'] = True + self.info['unsubscribe_policy'] = 9 + self.info['welcome_msg'] = 'Welcome message lorem ipsum dolor sit' + cls.__init__ = __init__ - def __getattr__(self, name): - return getattr(self.mocked, name) - return fn + return cls + diff --git a/templates/mailman-django/lists/settings.html b/templates/mailman-django/lists/settings.html new file mode 100644 index 0000000..d5cca85 --- /dev/null +++ b/templates/mailman-django/lists/settings.html @@ -0,0 +1,17 @@ +{% extends "mailman-django/base.html" %} +{% load i18n %} + +{% block content %} + +

{% trans "List Settings" %}

+ +

Available List Settings

+ + + + +{% endblock %} diff --git a/urls.py b/urls.py index 3a1fd53..e793160 100644 --- a/urls.py +++ b/urls.py @@ -8,6 +8,7 @@ url(r'lists/new/$', 'list_new', name = 'list_new'), url(r'lists/(?P.+)/$', 'list_info', name = 'list_info'), url(r'delete_list/(?P.+)/$', 'list_delete', name = 'list_delete'), + url(r'settings/(?P.+)/$', 'list_settings', name = 'list_settings'), # to override the default templates specifiy your own: # url(r'lists/(?P.+)/$', 'list_info', dict(template = 'path/to/template.html'), name = 'list_info'), ) diff --git a/views.py b/views.py index b7dab50..942b36c 100644 --- a/views.py +++ b/views.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext as _ import re from mailman_rest_client import MailmanRESTClient, MailmanRESTClientError -from forms import ListNew, ListSubscribe, ListUnsubscribe +from forms import ListNew, ListSubscribe, ListUnsubscribe, ListSettings def list_new(request, template = 'mailman-django/lists/new.html'): @@ -26,7 +26,7 @@ domain = c.get_domain(parts[1]) except ValueError, e: try: - c.create_domain(parts[1]) + domain = c.create_domain(parts[1]) except MailmanRESTClientError, e: # I don't think this error can ever appear but I couldn't # trigger the one that might appear -- Anna @@ -36,10 +36,10 @@ return HttpResponseRedirect(reverse('list_index')) except MailmanRESTClientError, e: return HttpResponse(e) - + else: form = ListNew() - + return render_to_response(template, {'form': form}) @@ -117,7 +117,7 @@ 'listinfo': listinfo}) def list_delete(request, fqdn_listname = None, - template = 'mailman-django/lists/index.html'): + template = 'mailman-django/lists/index.html'): """Delete a list. """ # create a connection to Mailman and get the list @@ -137,3 +137,21 @@ except MailmanRESTClientError, e: return render_to_response('mailman-django/errors/generic.html', {'message': e}) + +def list_settings(request, fqdn_listname = None, + template = 'mailman-django/lists/settings.html'): + """The settings of a list.""" + if request.method == 'POST': + form = ListSettings(request.POST) + if form.is_valid(): + try: + c = MailmanRESTClient('localhost:8001') + except Exception, e: + return HttpResponse(e) + # code to update the form etc., will use PATCH/PUT etc. + else: + # should use GET to get all the info about the list + form = ListSettings() + c = MailmanRESTClient('localhost:8001') + the_list = c.get_list(fqdn_listname) + return render_to_response(template, {'list_settings': the_list.info})