Django Postman changelog
========================
+Version 3.0.2, October 2013
+---------------------------
+* Rename test_urls.py to urls_for_tests.py, for adjustment with the new test discovery feature of Django 1.6.
+* Fix the need for some translations to become lazy, introduced by the conversion to class-based views.
+* Fix issue #36, BooleanField definition needs an explicit default value for Django 1.6.
+* Fix issue #35, the app can work without the sites framework.
+
Version 3.0.1, August 2013
--------------------------
* Fix issue #32, an IndexError when a Paginator is used and the folder is empty.
# The short X.Y version.\r
version = '3.0'\r
# The full version, including alpha/beta/rc tags.\r
-release = '3.0.1.post1'\r
+release = '3.0.2'\r
\r
# The language for content autogenerated by Sphinx. Refer to documentation\r
# for a list of supported languages.\r
from __future__ import unicode_literals
# following PEP 386: N.N[.N]+[{a|b|c|rc}N[.N]+][.postN][.devN]
-VERSION = (3, 0, 1)
+VERSION = (3, 0, 2)
PREREL = ()
-POST = 1
+POST = 0
DEV = 0
# options
# This file is distributed under the same license as the django-postman package.
#
# Translators:
-# Patrick Samson <maxcom@laposte.net>, 2011.
+# Gwildor <gwildorsok@gmail.com>, 2013
+# Patrick Samson <maxcom@laposte.net>, 2011
msgid ""
msgstr ""
"Project-Id-Version: django-postman\n"
"Report-Msgid-Bugs-To: http://bitbucket.org/psam/django-postman/issues\n"
-"POT-Creation-Date: 2012-12-10 23:13+0100\n"
-"PO-Revision-Date: 2010-12-27 15:10+0000\n"
-"Last-Translator: Patrick Samson <maxcom@laposte.net>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"POT-Creation-Date: 2012-12-10 23:00+0100\n"
+"PO-Revision-Date: 2013-10-11 14:53+0000\n"
+"Last-Translator: Gwildor <gwildorsok@gmail.com>\n"
+"Language-Team: Dutch (http://www.transifex.com/projects/p/django-postman/language/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: .\models.py:28
msgid "Accepted"
-msgstr ""
+msgstr "Geaccepteerd"
#: .\models.py:29 .\templates\postman\view.html.py:14
msgid "Rejected"
"\n"
"{sender} wrote:\n"
"{body}\n"
-msgstr ""
-"\n"
-"\n"
-"{sender} schreef:\n"
-"{body}\n"
+msgstr "\n\n{sender} schreef:\n{body}\n"
#: .\utils.py:63
msgid "Re: {subject}"
#: .\views.py:146 .\views.py:208
msgid "Message rejected for at least one recipient."
-msgstr ""
+msgstr "Bericht geweigerd voor ten minste een ontvanger."
#: .\views.py:299
msgid "Select at least one object."
#: .\templates\admin\postman\pendingmessage\submit_line.html.py:6
msgid "Accept"
-msgstr ""
+msgstr "Accepteer"
#: .\templates\admin\postman\pendingmessage\submit_line.html.py:7
msgid "Reject"
msgid ""
"Messages in this folder will never be removed. You can use this folder for "
"long term storage."
-msgstr ""
+msgstr "Berichten in deze map worden nooit verwijderd. Je kunt deze map gebruiken voor langdurige opslag."
#: .\templates\postman\base.html.py:4
msgid "Messaging"
#: .\templates\postman\email_user.txt.py:5
#: .\templates\postman\email_visitor.txt.py:5
msgid ", for the following reason:"
-msgstr ""
+msgstr ", voor de volgende reden:"
#: .\templates\postman\email_user.txt.py:9
#: .\templates\postman\email_visitor.txt.py:10
msgid ""
"Note: This message is issued by an automated system.\n"
"Do not reply, this would not be taken into account."
-msgstr ""
+msgstr "NB: Dit bericht is verstuurd door een automatisch systeem.\nReageren helpt niet, dat wordt niet verwerkt."
#: .\templates\postman\email_user_subject.txt.py:1
#: .\templates\postman\email_visitor_subject.txt.py:1
msgid ""
"Messages in this folder can be removed from time to time. For long term "
"storage, use instead the archive folder."
-msgstr ""
+msgstr "Berichten in deze map kunnen van tijd tot tijd verwijderd worden. Gebruik de archiefmap voor langdurige opslag."
#: .\templates\postman\view.html.py:6
msgid "Conversation"
+++ /dev/null
-"""
-URLconf for tests.py usage.
-
-"""
-from __future__ import unicode_literals
-
-from django.conf import settings
-try:
- from django.conf.urls import patterns, include, url # django 1.4
-except ImportError:
- from django.conf.urls.defaults import * # "patterns, include, url" is enough for django 1.3, "*" for django 1.2
-from django.forms import ValidationError
-from django.views.generic.base import RedirectView
-
-from . import OPTIONS
-from .views import (InboxView, SentView, ArchivesView, TrashView,
- WriteView, ReplyView, MessageView, ConversationView,
- ArchiveView, DeleteView, UndeleteView)
-
-
-# user_filter function set
-def user_filter_reason(user):
- if user.get_username() == 'bar':
- return 'some reason'
- return None
-def user_filter_no_reason(user):
- return ''
-def user_filter_false(user):
- return False
-def user_filter_exception(user):
- if user.get_username() == 'bar':
- raise ValidationError(['first good reason', "anyway, I don't like {0}".format(user.get_username())])
- return None
-
-# exchange_filter function set
-def exch_filter_reason(sender, recipient, recipients_list):
- if recipient.get_username() == 'bar':
- return 'some reason'
- return None
-def exch_filter_no_reason(sender, recipient, recipients_list):
- return ''
-def exch_filter_false(sender, recipient, recipients_list):
- return False
-def exch_filter_exception(sender, recipient, recipients_list):
- if recipient.get_username() == 'bar':
- raise ValidationError(['first good reason', "anyway, I don't like {0}".format(recipient.get_username())])
- return None
-
-# auto-moderation function set
-def moderate_as_51(message):
- return 51
-def moderate_as_48(message):
- return (48, "some reason")
-moderate_as_48.default_reason = 'some default reason'
-
-# quote formatters
-def format_subject(subject):
- return "Re_ " + subject
-def format_body(sender, body):
- return "{0} _ {1}".format(sender, body)
-
-postman_patterns = patterns('postman.views',
- # Basic set
- url(r'^inbox/(?:(?P<option>'+OPTIONS+')/)?$', InboxView.as_view(), name='postman_inbox'),
- url(r'^sent/(?:(?P<option>'+OPTIONS+')/)?$', SentView.as_view(), name='postman_sent'),
- url(r'^archives/(?:(?P<option>'+OPTIONS+')/)?$', ArchivesView.as_view(), name='postman_archives'),
- url(r'^trash/(?:(?P<option>'+OPTIONS+')/)?$', TrashView.as_view(), name='postman_trash'),
- url(r'^write/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(), name='postman_write'),
- url(r'^reply/(?P<message_id>[\d]+)/$', ReplyView.as_view(), name='postman_reply'),
- url(r'^view/(?P<message_id>[\d]+)/$', MessageView.as_view(), name='postman_view'),
- url(r'^view/t/(?P<thread_id>[\d]+)/$', ConversationView.as_view(), name='postman_view_conversation'),
- url(r'^archive/$', ArchiveView.as_view(), name='postman_archive'),
- url(r'^delete/$', DeleteView.as_view(), name='postman_delete'),
- url(r'^undelete/$', UndeleteView.as_view(), name='postman_undelete'),
- (r'^$', RedirectView.as_view(url='inbox/')),
-
- # Customized set
- # 'success_url'
- url(r'^write_sent/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(success_url='postman_sent'), name='postman_write_with_success_url_to_sent'),
- url(r'^reply_sent/(?P<message_id>[\d]+)/$', ReplyView.as_view(success_url='postman_sent'), name='postman_reply_with_success_url_to_sent'),
- url(r'^archive_arch/$', ArchiveView.as_view(success_url='postman_archives'), name='postman_archive_with_success_url_to_archives'),
- url(r'^delete_arch/$', DeleteView.as_view(success_url='postman_archives'), name='postman_delete_with_success_url_to_archives'),
- url(r'^undelete_arch/$', UndeleteView.as_view(success_url='postman_archives'), name='postman_undelete_with_success_url_to_archives'),
- # 'max'
- url(r'^write_max/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(max=1), name='postman_write_with_max'),
- url(r'^reply_max/(?P<message_id>[\d]+)/$', ReplyView.as_view(max=1), name='postman_reply_with_max'),
- # 'user_filter' on write
- url(r'^write_user_filter_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_reason), name='postman_write_with_user_filter_reason'),
- url(r'^write_user_filter_no_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_no_reason), name='postman_write_with_user_filter_no_reason'),
- url(r'^write_user_filter_false/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_false), name='postman_write_with_user_filter_false'),
- url(r'^write_user_filter_exception/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_exception), name='postman_write_with_user_filter_exception'),
- # 'user_filter' on reply
- url(r'^reply_user_filter_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_reason), name='postman_reply_with_user_filter_reason'),
- url(r'^reply_user_filter_no_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_no_reason), name='postman_reply_with_user_filter_no_reason'),
- url(r'^reply_user_filter_false/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_false), name='postman_reply_with_user_filter_false'),
- url(r'^reply_user_filter_exception/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_exception), name='postman_reply_with_user_filter_exception'),
- # 'exchange_filter' on write
- url(r'^write_exch_filter_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_reason), name='postman_write_with_exch_filter_reason'),
- url(r'^write_exch_filter_no_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_no_reason), name='postman_write_with_exch_filter_no_reason'),
- url(r'^write_exch_filter_false/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_false), name='postman_write_with_exch_filter_false'),
- url(r'^write_exch_filter_exception/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_exception), name='postman_write_with_exch_filter_exception'),
- # 'exchange_filter' on reply
- url(r'^reply_exch_filter_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_reason), name='postman_reply_with_exch_filter_reason'),
- url(r'^reply_exch_filter_no_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_no_reason), name='postman_reply_with_exch_filter_no_reason'),
- url(r'^reply_exch_filter_false/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_false), name='postman_reply_with_exch_filter_false'),
- url(r'^reply_exch_filter_exception/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_exception), name='postman_reply_with_exch_filter_exception'),
- # 'auto_moderators'
- url(r'^write_moderate/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(auto_moderators=(moderate_as_51,moderate_as_48)), name='postman_write_moderate'),
- url(r'^reply_moderate/(?P<message_id>[\d]+)/$', ReplyView.as_view(auto_moderators=(moderate_as_51,moderate_as_48)), name='postman_reply_moderate'),
- # 'formatters'
- url(r'^reply_formatters/(?P<message_id>[\d]+)/$', ReplyView.as_view(formatters=(format_subject, format_body)), name='postman_reply_formatters'),
- url(r'^view_formatters/(?P<message_id>[\d]+)/$', MessageView.as_view(formatters=(format_subject, format_body)), name='postman_view_formatters'),
- # auto-complete
- url(r'^write_ac/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(autocomplete_channels=('postman_multiple_as1-1', None)), name='postman_write_auto_complete'),
- url(r'^reply_ac/(?P<message_id>[\d]+)/$', ReplyView.as_view(autocomplete_channel='postman_multiple_as1-1'), name='postman_reply_auto_complete'),
- # 'template_name'
- url(r'^inbox_template/(?:(?P<option>'+OPTIONS+')/)?$', InboxView.as_view(template_name='postman/fake.html'), name='postman_inbox_template'),
- url(r'^sent_template/(?:(?P<option>'+OPTIONS+')/)?$', SentView.as_view(template_name='postman/fake.html'), name='postman_sent_template'),
- url(r'^archives_template/(?:(?P<option>'+OPTIONS+')/)?$', ArchivesView.as_view(template_name='postman/fake.html'), name='postman_archives_template'),
- url(r'^trash_template/(?:(?P<option>'+OPTIONS+')/)?$', TrashView.as_view(template_name='postman/fake.html'), name='postman_trash_template'),
- url(r'^write_template/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(template_name='postman/fake.html'), name='postman_write_template'),
- url(r'^reply_template/(?P<message_id>[\d]+)/$', ReplyView.as_view(template_name='postman/fake.html'), name='postman_reply_template'),
- url(r'^view_template/(?P<message_id>[\d]+)/$', MessageView.as_view(template_name='postman/fake.html'), name='postman_view_template'),
- url(r'^view_template/t/(?P<thread_id>[\d]+)/$', ConversationView.as_view(template_name='postman/fake.html'), name='postman_view_conversation_template'),
-)
-
-urlpatterns = patterns('',
- (r'^accounts/login/$', 'django.contrib.auth.views.login'), # because of the login_required decorator
- (r'^messages/', include(postman_patterns)),
-)
-
-# because of fields.py/AutoCompleteWidget/render()/reverse()
-if 'ajax_select' in settings.INSTALLED_APPS:
- urlpatterns += patterns('',
- (r'^ajax_select/', include('ajax_select.urls')), # django-ajax-selects
- )
-
-# optional
-if 'notification' in settings.INSTALLED_APPS:
- urlpatterns += patterns('',
- (r'^notification/', include('notification.urls')), # django-notification
- )
Usual generic tests.
"""
def test_version(self):
- self.assertEqual(sys.modules['postman'].__version__, "3.0.1.post1")
+ self.assertEqual(sys.modules['postman'].__version__, "3.0.2")
class BaseTest(TestCase):
"""
Common configuration and helper functions for all tests.
"""
- urls = 'postman.test_urls'
+ urls = 'postman.urls_for_tests'
def setUp(self):
deactivate() # necessary for 1.4 to consider a new settings.LANGUAGE_CODE; 1.3 is fine with or without
self.check_status(m, sender_deleted_at=True)
self.assertEqual(len(mail.outbox), 0)
+ def check_contrib_messages(self, response, text):
+ if 'messages' in response.context: # contrib\messages\context_processors.py may be not there
+ messages = response.context['messages']
+ if messages != []: # contrib\messages\middleware.py may be not there
+ self.assertEqual(len(messages), 1)
+ for message in messages: # can only be iterated
+ self.assertEqual(str(message), text)
+
def check_write_post(self, extra={}, is_anonymous=False):
"Check message generation, redirection, and mandatory fields."
url = reverse('postman_write')
data = {'recipients': self.user2.get_username(), 'subject': 's', 'body': 'b'}
data.update(extra)
# default redirect is to the requestor page
- response = self.client.post(url, data, HTTP_REFERER=url)
+ response = self.client.post(url, data, HTTP_REFERER=url, follow=True)
self.assertRedirects(response, url)
+ self.check_contrib_messages(response, 'Message successfully sent.') # no such check for the following posts, one is enough
m = Message.objects.get()
pk = m.pk
self.check_message(m, is_anonymous)
url = reverse('postman_write')
data = {'subject': 's', 'body': 'b', 'recipients': self.user2.get_username()}
self.assertTrue(self.client.login(username='foo', password='pass'))
- response = self.client.post(reverse('postman_write_moderate'), data, HTTP_REFERER=url)
+ response = self.client.post(reverse('postman_write_moderate'), data, HTTP_REFERER=url, follow=True)
self.assertRedirects(response, url)
+ self.check_contrib_messages(response, 'Message rejected for at least one recipient.')
self.check_status(Message.objects.get(), status=STATUS_REJECTED, recipient_deleted_at=True,
moderation_date=True, moderation_reason="some reason")
# default redirect is to the requestor page
response = self.client.post(url, data, HTTP_REFERER=url)
self.assertRedirects(response, url)
+ # the check_contrib_messages() in test_write_post() is enough
self.check_message(Message.objects.get(pk=pk+1))
# fallback redirect is to inbox
response = self.client.post(url, data)
response = self.client.post(reverse('postman_reply_moderate', args=[pk]), data, HTTP_REFERER=url)
self.assertRedirects(response, url)
+ # the check_contrib_messages() in test_write_post_moderate() is enough
self.check_status(Message.objects.get(pk=pk+1), status=STATUS_REJECTED, recipient_deleted_at=True,
parent=m, thread=m,
moderation_date=True, moderation_reason="some reason")
response = self.client.get(url)
self.assertEqual(len(response.context['pm_messages']), 2)
- def check_update(self, view_name, field_bit, pk, field_value=None):
+ def check_update(self, view_name, success_msg, field_bit, pk, field_value=None):
"Check permission, redirection, field updates, invalid cases."
url = reverse(view_name)
url_with_success_url = reverse(view_name + '_with_success_url_to_archives')
self.assertTrue(self.client.login(username='foo', password='pass'))
# default redirect is to the requestor page
redirect_url = reverse('postman_sent')
- response = self.client.post(url, data, HTTP_REFERER=redirect_url)
+ response = self.client.post(url, data, HTTP_REFERER=redirect_url, follow=True) # 'follow' to access messages
self.assertRedirects(response, redirect_url)
+ self.check_contrib_messages(response, success_msg)
sender_kw = 'sender_{0}'.format(field_bit)
recipient_kw = 'recipient_{0}'.format(field_bit)
self.check_status(Message.objects.get(pk=pk), status=STATUS_ACCEPTED, **{sender_kw: field_value})
response = self.client.post(url_with_success_url + '?next=' + redirect_url, data, HTTP_REFERER='does not matter')
self.assertRedirects(response, redirect_url)
# missing payload
- response = self.client.post(url)
+ response = self.client.post(url, follow=True)
self.assertRedirects(response, reverse('postman_inbox'))
+ self.check_contrib_messages(response, 'Select at least one object.')
# not a POST
response = self.client.get(url, data)
self.assertTrue(self.client.login(username='foo', password='pass'))
response = self.client.post(url, data)
self.assertRedirects(response, reverse('postman_inbox'))
+ # contrib.messages are already tested with check_update()
sender_kw = 'sender_{0}'.format(field_bit)
recipient_kw = 'recipient_{0}'.format(field_bit)
self.check_status(Message.objects.get(pk=pk), status=STATUS_ACCEPTED, is_new=False, is_replied=True, thread=root_msg, **{sender_kw: field_value})
self.c21()
self.c12()
self.c13()
- self.check_update('postman_archive', 'archived', pk, True)
+ self.check_update('postman_archive', 'Messages or conversations successfully archived.', 'archived', pk, True)
def test_archive_conversation(self):
"Test archive action on conversations."
self.c21()
self.c12()
self.c13()
- self.check_update('postman_delete', 'deleted_at', pk, True)
+ self.check_update('postman_delete', 'Messages or conversations successfully deleted.', 'deleted_at', pk, True)
def test_delete_conversation(self):
"Test delete action on conversations."
self.c21(recipient_deleted_at=now())
self.c12(sender_deleted_at=now())
self.c13()
- self.check_update('postman_undelete', 'deleted_at', pk)
+ self.check_update('postman_undelete', 'Messages or conversations successfully recovered.', 'deleted_at', pk)
def test_undelete_conversation(self):
"Test undelete action on conversations."
ArchiveView, DeleteView, UndeleteView)
-urlpatterns = patterns('postman.views',
+urlpatterns = patterns('',
url(r'^inbox/(?:(?P<option>'+OPTIONS+')/)?$', InboxView.as_view(), name='postman_inbox'),
url(r'^sent/(?:(?P<option>'+OPTIONS+')/)?$', SentView.as_view(), name='postman_sent'),
url(r'^archives/(?:(?P<option>'+OPTIONS+')/)?$', ArchivesView.as_view(), name='postman_archives'),
--- /dev/null
+"""
+URLconf for tests.py usage.
+
+"""
+from __future__ import unicode_literals
+
+from django.conf import settings
+try:
+ from django.conf.urls import patterns, include, url # django 1.4
+except ImportError:
+ from django.conf.urls.defaults import * # "patterns, include, url" is enough for django 1.3, "*" for django 1.2
+from django.forms import ValidationError
+from django.views.generic.base import RedirectView
+
+from . import OPTIONS
+from .views import (InboxView, SentView, ArchivesView, TrashView,
+ WriteView, ReplyView, MessageView, ConversationView,
+ ArchiveView, DeleteView, UndeleteView)
+
+
+# user_filter function set
+def user_filter_reason(user):
+ if user.get_username() == 'bar':
+ return 'some reason'
+ return None
+def user_filter_no_reason(user):
+ return ''
+def user_filter_false(user):
+ return False
+def user_filter_exception(user):
+ if user.get_username() == 'bar':
+ raise ValidationError(['first good reason', "anyway, I don't like {0}".format(user.get_username())])
+ return None
+
+# exchange_filter function set
+def exch_filter_reason(sender, recipient, recipients_list):
+ if recipient.get_username() == 'bar':
+ return 'some reason'
+ return None
+def exch_filter_no_reason(sender, recipient, recipients_list):
+ return ''
+def exch_filter_false(sender, recipient, recipients_list):
+ return False
+def exch_filter_exception(sender, recipient, recipients_list):
+ if recipient.get_username() == 'bar':
+ raise ValidationError(['first good reason', "anyway, I don't like {0}".format(recipient.get_username())])
+ return None
+
+# auto-moderation function set
+def moderate_as_51(message):
+ return 51
+def moderate_as_48(message):
+ return (48, "some reason")
+moderate_as_48.default_reason = 'some default reason'
+
+# quote formatters
+def format_subject(subject):
+ return "Re_ " + subject
+def format_body(sender, body):
+ return "{0} _ {1}".format(sender, body)
+
+postman_patterns = patterns('',
+ # Basic set
+ url(r'^inbox/(?:(?P<option>'+OPTIONS+')/)?$', InboxView.as_view(), name='postman_inbox'),
+ url(r'^sent/(?:(?P<option>'+OPTIONS+')/)?$', SentView.as_view(), name='postman_sent'),
+ url(r'^archives/(?:(?P<option>'+OPTIONS+')/)?$', ArchivesView.as_view(), name='postman_archives'),
+ url(r'^trash/(?:(?P<option>'+OPTIONS+')/)?$', TrashView.as_view(), name='postman_trash'),
+ url(r'^write/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(), name='postman_write'),
+ url(r'^reply/(?P<message_id>[\d]+)/$', ReplyView.as_view(), name='postman_reply'),
+ url(r'^view/(?P<message_id>[\d]+)/$', MessageView.as_view(), name='postman_view'),
+ url(r'^view/t/(?P<thread_id>[\d]+)/$', ConversationView.as_view(), name='postman_view_conversation'),
+ url(r'^archive/$', ArchiveView.as_view(), name='postman_archive'),
+ url(r'^delete/$', DeleteView.as_view(), name='postman_delete'),
+ url(r'^undelete/$', UndeleteView.as_view(), name='postman_undelete'),
+ (r'^$', RedirectView.as_view(url='inbox/')),
+
+ # Customized set
+ # 'success_url'
+ url(r'^write_sent/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(success_url='postman_sent'), name='postman_write_with_success_url_to_sent'),
+ url(r'^reply_sent/(?P<message_id>[\d]+)/$', ReplyView.as_view(success_url='postman_sent'), name='postman_reply_with_success_url_to_sent'),
+ url(r'^archive_arch/$', ArchiveView.as_view(success_url='postman_archives'), name='postman_archive_with_success_url_to_archives'),
+ url(r'^delete_arch/$', DeleteView.as_view(success_url='postman_archives'), name='postman_delete_with_success_url_to_archives'),
+ url(r'^undelete_arch/$', UndeleteView.as_view(success_url='postman_archives'), name='postman_undelete_with_success_url_to_archives'),
+ # 'max'
+ url(r'^write_max/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(max=1), name='postman_write_with_max'),
+ url(r'^reply_max/(?P<message_id>[\d]+)/$', ReplyView.as_view(max=1), name='postman_reply_with_max'),
+ # 'user_filter' on write
+ url(r'^write_user_filter_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_reason), name='postman_write_with_user_filter_reason'),
+ url(r'^write_user_filter_no_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_no_reason), name='postman_write_with_user_filter_no_reason'),
+ url(r'^write_user_filter_false/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_false), name='postman_write_with_user_filter_false'),
+ url(r'^write_user_filter_exception/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(user_filter=user_filter_exception), name='postman_write_with_user_filter_exception'),
+ # 'user_filter' on reply
+ url(r'^reply_user_filter_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_reason), name='postman_reply_with_user_filter_reason'),
+ url(r'^reply_user_filter_no_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_no_reason), name='postman_reply_with_user_filter_no_reason'),
+ url(r'^reply_user_filter_false/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_false), name='postman_reply_with_user_filter_false'),
+ url(r'^reply_user_filter_exception/(?P<message_id>[\d]+)/$', ReplyView.as_view(user_filter=user_filter_exception), name='postman_reply_with_user_filter_exception'),
+ # 'exchange_filter' on write
+ url(r'^write_exch_filter_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_reason), name='postman_write_with_exch_filter_reason'),
+ url(r'^write_exch_filter_no_reason/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_no_reason), name='postman_write_with_exch_filter_no_reason'),
+ url(r'^write_exch_filter_false/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_false), name='postman_write_with_exch_filter_false'),
+ url(r'^write_exch_filter_exception/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(exchange_filter=exch_filter_exception), name='postman_write_with_exch_filter_exception'),
+ # 'exchange_filter' on reply
+ url(r'^reply_exch_filter_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_reason), name='postman_reply_with_exch_filter_reason'),
+ url(r'^reply_exch_filter_no_reason/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_no_reason), name='postman_reply_with_exch_filter_no_reason'),
+ url(r'^reply_exch_filter_false/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_false), name='postman_reply_with_exch_filter_false'),
+ url(r'^reply_exch_filter_exception/(?P<message_id>[\d]+)/$', ReplyView.as_view(exchange_filter=exch_filter_exception), name='postman_reply_with_exch_filter_exception'),
+ # 'auto_moderators'
+ url(r'^write_moderate/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(auto_moderators=(moderate_as_51,moderate_as_48)), name='postman_write_moderate'),
+ url(r'^reply_moderate/(?P<message_id>[\d]+)/$', ReplyView.as_view(auto_moderators=(moderate_as_51,moderate_as_48)), name='postman_reply_moderate'),
+ # 'formatters'
+ url(r'^reply_formatters/(?P<message_id>[\d]+)/$', ReplyView.as_view(formatters=(format_subject, format_body)), name='postman_reply_formatters'),
+ url(r'^view_formatters/(?P<message_id>[\d]+)/$', MessageView.as_view(formatters=(format_subject, format_body)), name='postman_view_formatters'),
+ # auto-complete
+ url(r'^write_ac/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(autocomplete_channels=('postman_multiple_as1-1', None)), name='postman_write_auto_complete'),
+ url(r'^reply_ac/(?P<message_id>[\d]+)/$', ReplyView.as_view(autocomplete_channel='postman_multiple_as1-1'), name='postman_reply_auto_complete'),
+ # 'template_name'
+ url(r'^inbox_template/(?:(?P<option>'+OPTIONS+')/)?$', InboxView.as_view(template_name='postman/fake.html'), name='postman_inbox_template'),
+ url(r'^sent_template/(?:(?P<option>'+OPTIONS+')/)?$', SentView.as_view(template_name='postman/fake.html'), name='postman_sent_template'),
+ url(r'^archives_template/(?:(?P<option>'+OPTIONS+')/)?$', ArchivesView.as_view(template_name='postman/fake.html'), name='postman_archives_template'),
+ url(r'^trash_template/(?:(?P<option>'+OPTIONS+')/)?$', TrashView.as_view(template_name='postman/fake.html'), name='postman_trash_template'),
+ url(r'^write_template/(?:(?P<recipients>[\w.@+-:]+)/)?$', WriteView.as_view(template_name='postman/fake.html'), name='postman_write_template'),
+ url(r'^reply_template/(?P<message_id>[\d]+)/$', ReplyView.as_view(template_name='postman/fake.html'), name='postman_reply_template'),
+ url(r'^view_template/(?P<message_id>[\d]+)/$', MessageView.as_view(template_name='postman/fake.html'), name='postman_view_template'),
+ url(r'^view_template/t/(?P<thread_id>[\d]+)/$', ConversationView.as_view(template_name='postman/fake.html'), name='postman_view_conversation_template'),
+)
+
+urlpatterns = patterns('',
+ (r'^accounts/login/$', 'django.contrib.auth.views.login'), # because of the login_required decorator
+ (r'^messages/', include(postman_patterns)),
+)
+
+# because of fields.py/AutoCompleteWidget/render()/reverse()
+if 'ajax_select' in settings.INSTALLED_APPS:
+ urlpatterns += patterns('',
+ (r'^ajax_select/', include('ajax_select.urls')), # django-ajax-selects
+ )
+
+# optional
+if 'notification' in settings.INSTALLED_APPS:
+ urlpatterns += patterns('',
+ (r'^notification/', include('notification.urls')), # django-notification
+ )
except ImportError:
from datetime import datetime
now = datetime.now
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext as _, ugettext_lazy as lz_
from django.views.decorators.csrf import csrf_protect
from django.views.generic import FormView, TemplateView, View
class ArchiveView(UpdateMessageMixin, View):
"""Mark messages/conversations as archived."""
field_bit = 'archived'
- success_msg = _("Messages or conversations successfully archived.")
+ success_msg = lz_("Messages or conversations successfully archived.")
field_value = True
class DeleteView(UpdateMessageMixin, View):
"""Mark messages/conversations as deleted."""
field_bit = 'deleted_at'
- success_msg = _("Messages or conversations successfully deleted.")
+ success_msg = lz_("Messages or conversations successfully deleted.")
field_value = now()
class UndeleteView(UpdateMessageMixin, View):
"""Revert messages/conversations from marked as deleted."""
field_bit = 'deleted_at'
- success_msg = _("Messages or conversations successfully recovered.")
+ success_msg = lz_("Messages or conversations successfully recovered.")