Author: brosner
Date: Wed Oct  1 09:35:10 2008
New Revision: 103

Added:
    branches/pluggable-backends/notification/admin.py
       - copied unchanged from r102, /trunk/notification/admin.py
    branches/pluggable-backends/notification/decorators.py
       - copied unchanged from r102, /trunk/notification/decorators.py
    branches/pluggable-backends/notification/engine.py
       - copied unchanged from r102, /trunk/notification/engine.py
    branches/pluggable-backends/notification/feeds.py
       - copied unchanged from r102, /trunk/notification/feeds.py
    branches/pluggable-backends/notification/lockfile.py
       - copied unchanged from r102, /trunk/notification/lockfile.py
    branches/pluggable-backends/notification/management/   (props changed)
       - copied from r102, /trunk/notification/management/
    branches/pluggable-backends/notification/management/__init__.py
       - copied unchanged from r102,  
/trunk/notification/management/__init__.py
    branches/pluggable-backends/notification/management/commands/   (props  
changed)
       - copied from r102, /trunk/notification/management/commands/
    branches/pluggable-backends/notification/management/commands/__init__.py
       - copied unchanged from r102,  
/trunk/notification/management/commands/__init__.py
     
branches/pluggable-backends/notification/management/commands/emit_notices.py
       - copied unchanged from r102,  
/trunk/notification/management/commands/emit_notices.py
     
branches/pluggable-backends/notification/management/commands/upgrade_notices.py
       - copied unchanged from r102,  
/trunk/notification/management/commands/upgrade_notices.py
    branches/pluggable-backends/notification/templates/notification/full.html
       - copied unchanged from r102,  
/trunk/notification/templates/notification/full.html
    branches/pluggable-backends/notification/templatetags/   (props changed)
       - copied from r102, /trunk/notification/templatetags/
    branches/pluggable-backends/notification/templatetags/__init__.py
       - copied unchanged from r102,  
/trunk/notification/templatetags/__init__.py
    branches/pluggable-backends/notification/templatetags/captureas_tag.py
       - copied unchanged from r102,  
/trunk/notification/templatetags/captureas_tag.py
Modified:
    branches/pluggable-backends/   (props changed)
    branches/pluggable-backends/notification/models.py
     
branches/pluggable-backends/notification/templates/notification/teaser.html
    branches/pluggable-backends/notification/urls.py
    branches/pluggable-backends/notification/views.py

Log:
pluggable-backends: Merged from trunk revisions 77-89 and 91-102. This is a  
very rough merge and has broken code.

Modified: branches/pluggable-backends/notification/models.py
==============================================================================
--- branches/pluggable-backends/notification/models.py  (original)
+++ branches/pluggable-backends/notification/models.py  Wed Oct  1 09:35:10  
2008
@@ -1,12 +1,22 @@
  import datetime

+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
  from django.db import models
+from django.db.models.query import QuerySet
  from django.conf import settings
  from django.core.urlresolvers import reverse
  from django.template import Context
  from django.template.loader import render_to_string

-from django.contrib.auth.models import User, SiteProfileNotAvailable
+from django.core.exceptions import ImproperlyConfigured
+
+from django.contrib.sites.models import Site
+from django.contrib.auth.models import User
+from django.contrib.auth.models import AnonymousUser

  from django.contrib.contenttypes.models import ContentType
  from django.contrib.contenttypes import generic
@@ -17,9 +27,11 @@
  from notification import backends
  from notification.message import encode_message

+class LanguageStoreNotAvailable(Exception):
+    pass

  class NoticeType(models.Model):
-
+
      label = models.CharField(_('label'), max_length=40)
      display = models.CharField(_('display'), max_length=50)
      description = models.CharField(_('description'), max_length=100)
@@ -29,10 +41,7 @@

      def __unicode__(self):
          return self.label
-
-    class Admin:
-        list_display = ('label', 'display', 'description', 'default')
-
+
      class Meta:
          verbose_name = _("notice type")
          verbose_name_plural = _("notice types")
@@ -53,18 +62,16 @@
      Indicates, for a given user, whether to send notifications
      of a given type to a given medium.
      """
-
+
      user = models.ForeignKey(User, verbose_name=_('user'))
      notice_type = models.ForeignKey(NoticeType, verbose_name=_('notice  
type'))
      medium = models.CharField(_('medium'), max_length=1,  
choices=NOTICE_MEDIA)
      send = models.BooleanField(_('send'))
-
-    class Admin:
-        list_display = ('id', 'user', 'notice_type', 'medium', 'send')
-
+
      class Meta:
          verbose_name = _("notice setting")
          verbose_name_plural = _("notice settings")
+        unique_together = ("user", "notice_type", "medium")


  def get_notification_setting(user, notice_type, medium):
@@ -82,17 +89,26 @@

  class NoticeManager(models.Manager):

-    def notices_for(self, user, archived=False):
+    def notices_for(self, user, archived=False, unseen=None, on_site=None):
          """
          returns Notice objects for the given user.

          If archived=False, it only include notices not archived.
          If archived=True, it returns all notices for that user.
+
+        If unseen=None, it includes all notices.
+        If unseen=True, return only unseen notices.
+        If unseen=False, return only seen notices.
          """
          if archived:
-            return self.filter(user=user)
+            qs = self.filter(user=user)
          else:
-            return self.filter(user=user, archived=archived)
+            qs = self.filter(user=user, archived=archived)
+        if unseen is not None:
+            qs = qs.filter(unseen=unseen)
+        if on_site is not None:
+            qs = qs.filter(on_site=on_site)
+        return qs

      def unseen_count_for(self, user):
          """
@@ -102,27 +118,28 @@
          return self.filter(user=user, unseen=True).count()

  class Notice(models.Model):
-
+
      user = models.ForeignKey(User, verbose_name=_('user'))
      message = models.TextField(_('message'))
      notice_type = models.ForeignKey(NoticeType, verbose_name=_('notice  
type'))
      added = models.DateTimeField(_('added'), default=datetime.datetime.now)
      unseen = models.BooleanField(_('unseen'), default=True)
      archived = models.BooleanField(_('archived'), default=False)
-
+    on_site = models.BooleanField(_('on site'))
+
      objects = NoticeManager()
-
+
      def __unicode__(self):
          return self.message
-
+
      def archive(self):
          self.archived = True
          self.save()
-
+
      def is_unseen(self):
          """
          returns value of self.unseen but also changes it to false.
-
+
          Use this in a template to mark an unseen notice differently the  
first
          time it is shown.
          """
@@ -131,19 +148,27 @@
              self.unseen = False
              self.save()
          return unseen
-
+
      class Meta:
          ordering = ["-added"]
          verbose_name = _("notice")
          verbose_name_plural = _("notices")
-
-    class Admin:
-        list_display =  
('message', 'user', 'notice_type', 'added', 'unseen', 'archived')
+
+    @models.permalink
+    def get_absolute_url(self):
+        return ("notification_notice", [str(self.pk)])
+
+class NoticeQueueBatch(models.Model):
+    """
+    A queued notice.
+    Denormalized data for a notice.
+    """
+    pickled_data = models.TextField()

  def create_notice_type(label, display, description, default=2):
      """
      Creates a new NoticeType.
-
+
      This is intended to be used by other apps as a post_syncdb manangement  
step.
      """
      try:
@@ -165,6 +190,23 @@
          NoticeType(label=label, display=display, description=description,  
default=default).save()
          print "Created %s NoticeType" % label

+def get_notification_language(user):
+    """
+    Returns site-specific notification language for this user. Raises
+    LanguageStoreNotAvailable if this site does not use translated
+    notifications.
+    """
+    if getattr(settings, 'NOTIFICATION_LANGUAGE_MODULE', False):
+        try:
+            app_label, model_name =  
settings.NOTIFICATION_LANGUAGE_MODULE.split('.')
+            model = models.get_model(app_label, model_name)
+            language_model =  
model._default_manager.get(user__id__exact=user.id)
+            if hasattr(language_model, 'language'):
+                return language_model.language
+        except (ImportError, ImproperlyConfigured, model.DoesNotExist):
+            raise LanguageStoreNotAvailable
+    raise LanguageStoreNotAvailable
+
  def get_formatted_messages(formats, label, context):
      """
      Returns a dictionary with the format identifier as the key. The values  
are
@@ -172,37 +214,42 @@
      """
      format_templates = {}
      for format in formats:
+        # conditionally turn off autoescaping for .txt extensions in format
+        if format.endswith(".txt"):
+            context.autoescape = False
          name = format.split(".")[0]
          format_templates[name] = render_to_string((
              'notification/%s/%s' % (label, format),
-            'notification/%s' % format), context)
+            'notification/%s' % format), context_instance=context)
      return format_templates

-def send(recipient, label, extra_context={}):
+def send(users, label, extra_context={}, on_site=True):
      """
      Creates a new notice.
-
+
      This is intended to be how other apps create new notices.
-
+
      notification.send(user, 'friends_invite_sent', {
          'spam': 'eggs',
          'foo': 'bar',
      )
+
+    You can pass in on_site=False to prevent the notice emitted from being
+    displayed on the site.
+
+    FIXME: this function needs some serious reworking.
      """
-    if not isinstance(recipient, (list, tuple)):
-        recipient = (recipient,)
+    notice_type = NoticeType.objects.get(label=label)

      notice_type = NoticeType.objects.get(label=label)
      backend_recipients = {}

-    context = Context({
-        "notice": ugettext(notice_type.display),
-        "notices_url": notices_url,
-        "current_site": current_site,
-    })
-    context.update(extra_context)
+    current_site = Site.objects.get_current()
+    notices_url = u"http://%s%s"; % (
+        unicode(current_site),
+        reverse("notification_notices"),
+    )

-    recipients = []
      current_language = get_language()

      formats = (
@@ -212,35 +259,44 @@
          'full.html',
      ) # TODO make formats configurable

-    for user in recipient:
-        # get user profiles if available
+    for user in users:
+        recipients = []
+        # get user language for user from language store defined in
+        # NOTIFICATION_LANGUAGE_MODULE setting
          try:
-            profile = user.get_profile()
-        except SiteProfileNotAvailable:
-            profile = None
-
-        # activate language of user to send message translated
-        if profile is not None:
-            # get language attribute of user profile
-            language = getattr(profile, "language", None)
-            if language is not None:
-                # activate the user's language
-                activate(language)
+            language = get_notification_language(user)
+        except LanguageStoreNotAvailable:
+            language = None
+
+        if language is not None:
+            # activate the user's language
+            activate(language)
+
+        # update context with user specific translations
+        context = Context({
+            "user": user,
+            "notice": ugettext(notice_type.display),
+            "notices_url": notices_url,
+            "current_site": current_site,
+        })
+        context.update(extra_context)

          # get prerendered format messages
+        # TODO: figure out how to handle this in the branch.
          messages = get_formatted_messages(formats, label, context)

          # Strip newlines from subject
+        # TODO: this should move to the email backend
          subject  
= ''.join(render_to_string('notification/email_subject.txt', {
              'message': messages['short'],
          }, context).splitlines())
-
+
          body = render_to_string('notification/email_body.txt', {
              'message': messages['plain'],
          }, context)
-
-        notice = Notice(user=user, message=message,  
notice_type=notice_type)
-        notice.save()
+
+        notice = Notice.objects.create(user=user,  
message=messages['teaser'],
+            notice_type=notice_type, on_site=on_site)
          for key, backend in NOTIFICATION_BACKENDS:
              recipients = backend_recipients.setdefault(key, [])
              if backend.can_send(user, notice_type):
@@ -251,6 +307,16 @@
      # reset environment to original language
      activate(current_language)

+def queue(users, label, extra_context={}, on_site=True):
+    if isinstance(users, QuerySet):
+        users = [row["pk"] for row in users.values("pk")]
+    else:
+        users = [user.pk for user in users]
+    notices = []
+    for user in users:
+        notices.append((user, label, extra_context, on_site))
+    NoticeQueueBatch(pickled_data=pickle.dumps(notices)).save()
+
  class ObservedItemManager(models.Manager):

      def all_for(self, observed, signal):
@@ -261,7 +327,7 @@
          content_type = ContentType.objects.get_for_model(observed)
          observed_items = self.filter(content_type=content_type,  
object_id=observed.id, signal=signal)
          return observed_items
-
+
      def get_for(self, observed, observer, signal):
          content_type = ContentType.objects.get_for_model(observed)
          observed_item = self.get(content_type=content_type,  
object_id=observed.id, user=observer, signal=signal)
@@ -271,42 +337,39 @@
  class ObservedItem(models.Model):

      user = models.ForeignKey(User, verbose_name=_('user'))
-
+
      content_type = models.ForeignKey(ContentType)
      object_id = models.PositiveIntegerField()
      observed_object =  
generic.GenericForeignKey('content_type', 'object_id')
-
+
      notice_type = models.ForeignKey(NoticeType, verbose_name=_('notice  
type'))
-    message_template = models.TextField(verbose_name=_('message template'))
-
+
      added = models.DateTimeField(_('added'), default=datetime.datetime.now)
-
+
      # the signal that will be listened to send the notice
      signal = models.TextField(verbose_name=_('signal'))
-
+
      objects = ObservedItemManager()
-
+
      class Meta:
          ordering = ['-added']
          verbose_name = _('observed item')
          verbose_name_plural = _('observed items')
-
-    class Admin:
-        pass
-
+
      def send_notice(self):
-        send([self.user], self.notice_type.label, self.message_template,
-             [self.observed_object])
+        send([self.user], self.notice_type.label,
+             {'observed': self.observed_object})


-def observe(observed, observer, notice_type_label, message_template,  
signal='post_save'):
+def observe(observed, observer, notice_type_label, signal='post_save'):
      """
      Create a new ObservedItem.
-
+
      To be used by applications to register a user as an observer for some  
object.
      """
      notice_type = NoticeType.objects.get(label=notice_type_label)
-    observed_item = ObservedItem(user=observer, observed_object=observed,  
notice_type=notice_type, message_template=message_template, signal=signal)
+    observed_item = ObservedItem(user=observer, observed_object=observed,
+                                 notice_type=notice_type, signal=signal)
      observed_item.save()
      return observed_item

@@ -327,6 +390,8 @@
      return observed_items

  def is_observing(observed, observer, signal='post_save'):
+    if isinstance(observer, AnonymousUser):
+        return False
      try:
          observed_items = ObservedItem.objects.get_for(observed, observer,  
signal)
          return True

Modified:  
branches/pluggable-backends/notification/templates/notification/teaser.html
==============================================================================
---  
branches/pluggable-backends/notification/templates/notification/teaser.html     
 
(original)
+++  
branches/pluggable-backends/notification/templates/notification/teaser.html     
 
Wed Oct  1 09:35:10 2008
@@ -1 +1 @@
-{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %}
+{% load i18n %}{% blocktrans %}{{ notice }}{% endblocktrans %}
\ No newline at end of file

Modified: branches/pluggable-backends/notification/urls.py
==============================================================================
--- branches/pluggable-backends/notification/urls.py    (original)
+++ branches/pluggable-backends/notification/urls.py    Wed Oct  1 09:35:10  
2008
@@ -2,8 +2,11 @@

  # @@@ from atom import Feed

-from notification.views import notices
+from notification.views import notices, mark_all_seen, feed_for_user,  
single

  urlpatterns = patterns('',
      url(r'^$', notices, name="notification_notices"),
+    url(r'^(\d+)/$', single, name="notification_notice"),
+    url(r'^feed/$', feed_for_user, name="notification_feed_for_user"),
+    url(r'^mark_all_seen/$', mark_all_seen,  
name="notification_mark_all_seen"),
  )

Modified: branches/pluggable-backends/notification/views.py
==============================================================================
--- branches/pluggable-backends/notification/views.py   (original)
+++ branches/pluggable-backends/notification/views.py   Wed Oct  1 09:35:10  
2008
@@ -1,14 +1,25 @@
-from django.shortcuts import render_to_response
-from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect, Http404
  from django.template import RequestContext
  from django.contrib.auth.decorators import login_required
+from django.contrib.syndication.views import feed

  from notification.models import *
+from notification.decorators import basic_auth_required,  
simple_basic_auth_callback
+from notification.feeds import NoticeUserFeed
+
[EMAIL PROTECTED](realm='Notices Feed',  
callback_func=simple_basic_auth_callback)
+def feed_for_user(request):
+    url = "feed/%s" % request.user.username
+    return feed(request, url, {
+        "feed": NoticeUserFeed,
+    })

  @login_required
  def notices(request):
      notice_types = NoticeType.objects.all()
-    notices = Notice.objects.notices_for(request.user)
+    notices = Notice.objects.notices_for(request.user, on_site=True)
      settings_table = []
      for notice_type in NoticeType.objects.all():
          settings_row = []
@@ -36,6 +47,15 @@
      }, context_instance=RequestContext(request))

  @login_required
+def single(request, id):
+    notice = get_object_or_404(Notice, id=id)
+    if request.user == notice.user:
+        return render_to_response("notification/single.html", {
+            "notice": notice,
+        }, context_instance=RequestContext(request))
+    raise Http404
+
[EMAIL PROTECTED]
  def archive(request, noticeid=None, next_page=None):
      if noticeid:
          try:
@@ -61,4 +81,12 @@
                  return HttpResponseRedirect(next_page)
          except Notice.DoesNotExist:
              return HttpResponseRedirect(next_page)
-    return HttpResponseRedirect(next_page)
\ No newline at end of file
+    return HttpResponseRedirect(next_page)
+
[EMAIL PROTECTED]
+def mark_all_seen(request):
+    for notice in Notice.objects.notices_for(request.user, unseen=True):
+        notice.unseen = False
+        notice.save()
+    return HttpResponseRedirect(reverse("notification_notices"))
+
\ No newline at end of file

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pinax-updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pinax-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to