Browse Source

Alerts system has been added

Ralfp 12 years ago
parent
commit
89cf689394

+ 0 - 0
misago/alerts/__init__.py


+ 0 - 0
misago/alerts/management/__init__.py


+ 0 - 0
misago/alerts/management/commands/__init__.py


+ 13 - 0
misago/alerts/management/commands/clearalerts.py

@@ -0,0 +1,13 @@
+from datetime import timedelta
+from django.core.management.base import BaseCommand
+from django.utils import timezone
+from misago.alerts.models import Alert
+
+class Command(BaseCommand):
+    """
+    This command is intended to work as CRON job fired every few days to delete old alerts
+    """
+    help = 'Clears old alerts'
+    def handle(self, *args, **options):
+        Alert.objects.filter(date__lte=timezone.now() - timedelta(days=14)).delete()
+        self.stdout.write('Old Alerts have been cleared.\n')        

+ 51 - 0
misago/alerts/models.py

@@ -0,0 +1,51 @@
+from django.db import models
+import base64
+import cgi
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+class Alert(models.Model):
+    user = models.ForeignKey('users.User')
+    date = models.DateTimeField()
+    message = models.TextField()
+    variables = models.TextField(null=True,blank=True)
+    
+    def vars(self):
+        try:
+            return pickle.loads(base64.decodestring(self.variables))
+        except Exception:
+            return {}
+    
+    def text(self, var, value):
+        value = cgi.escape(value, True)
+        try:
+            self.vars_raw[var] = value
+        except AttributeError:
+            self.vars_raw = {var: value}
+        return self
+    
+    def url(self, var, value, href, attrs=None):
+        url = '<a href="%s"' % cgi.escape(href, True)
+        if attrs:
+            for k, v in attrs.iterator():
+                url += ' %s="%s"' % (k, cgi.escape(v, True))
+        url += '>%s</a>' % value
+        try:
+            self.vars_raw[var] = url
+        except AttributeError:
+            self.vars_raw = {var: url}
+        return self
+    
+    def save_all(self, *args, **kwargs):
+        self.save(force_insert=True)
+        self.user.save(force_update=True)
+        
+    def save(self, *args, **kwargs):
+        try:
+            self.variables = base64.encodestring(pickle.dumps(self.vars_raw, pickle.HIGHEST_PROTOCOL))
+        except AttributeError:
+            self.variables = base64.encodestring(pickle.dumps({}, pickle.HIGHEST_PROTOCOL))
+        super(Alert, self).save(*args, **kwargs)
+        return self.user

+ 52 - 0
misago/alerts/views.py

@@ -0,0 +1,52 @@
+from django.template import RequestContext
+from django.utils import timezone
+from django.utils.translation import ugettext as _
+from misago.authn.decorators import block_guest
+from misago.views import error404
+
+@block_guest
+def show_alerts(request):
+    now = timezone.now()
+    alerts = {}
+    all_alerts = 0
+    if not request.user.alerts_date:
+        request.user.alerts_date = request.user.join_date
+    for alert in request.user.alert_set.order_by('-id'):
+        all_alerts += 1
+        alert.new = alert.date > request.user.alerts_date
+        diff = now - alert.date
+        if diff.days <= 0:
+            try:
+                alerts['today'].append(alert)
+            except KeyError:
+                alerts['today'] = [alert]
+        elif diff.days <= 1:
+            try:
+                alerts['yesterday'].append(alert)
+            except KeyError:
+                alerts['yesterday'] = [alert]
+        elif diff.days <= 7:
+            try:
+                alerts['week'].append(alert)
+            except KeyError:
+                alerts['week'] = [alert]
+        elif diff.days <= 30:
+            try:
+                alerts['month'].append(alert)
+            except KeyError:
+                alerts['mont'] = [alert]
+        else:
+            try:
+                alerts['older'].append(alert)
+            except KeyError:
+                alerts['older'] = [alert]
+    response = request.theme.render_to_response('alerts.html',
+                                                {
+                                                 'alerts': alerts
+                                                 },
+                                                context_instance=RequestContext(request));
+    request.user.alerts = all_alerts
+    request.user.alerts_new = 0
+    request.user.alerts_date = now
+    request.user.save(force_update=True)
+    return response

+ 1 - 0
misago/settings_base.py

@@ -163,6 +163,7 @@ INSTALLED_APPS = (
     'misago.template', # Templates extensions
     'misago.template', # Templates extensions
     'misago.themes', # Themes
     'misago.themes', # Themes
     'misago.users', # Users foundation
     'misago.users', # Users foundation
+    'misago.alerts', # Users Notifications
     'misago.team', # Forum Team List
     'misago.team', # Forum Team List
     'misago.prune', # Prune Users
     'misago.prune', # Prune Users
     'misago.ranks', # User Ranks
     'misago.ranks', # User Ranks

+ 1 - 0
misago/urls.py

@@ -16,6 +16,7 @@ urlpatterns = patterns('',
     url(r'^redirect/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'misago.views.redirection', name="redirect"),
     url(r'^redirect/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'misago.views.redirection', name="redirect"),
     url(r'^markdown/preview/$', 'misago.markdown.views.preview', name="md_preview"),
     url(r'^markdown/preview/$', 'misago.markdown.views.preview', name="md_preview"),
     url(r'^$', 'misago.views.home', name="index"),
     url(r'^$', 'misago.views.home', name="index"),
+    url(r'^alerts/$', 'misago.alerts.views.show_alerts', name="alerts"),
     url(r'^tos/$', 'misago.tos.views.forum_tos', name="tos"),
     url(r'^tos/$', 'misago.tos.views.forum_tos', name="tos"),
     url(r'^read/$', 'misago.views.read_all', name="read_all"),
     url(r'^read/$', 'misago.views.read_all', name="read_all"),
 )
 )

+ 7 - 0
misago/users/models.py

@@ -156,6 +156,7 @@ class User(models.Model):
     last_search = models.DateTimeField(null=True,blank=True)
     last_search = models.DateTimeField(null=True,blank=True)
     alerts = models.PositiveIntegerField(default=0)
     alerts = models.PositiveIntegerField(default=0)
     alerts_new = models.PositiveIntegerField(default=0)
     alerts_new = models.PositiveIntegerField(default=0)
+    alerts_date = models.DateTimeField(null=True,blank=True)
     activation = models.IntegerField(default=0)
     activation = models.IntegerField(default=0)
     token = models.CharField(max_length=12,null=True,blank=True)
     token = models.CharField(max_length=12,null=True,blank=True)
     avatar_ban = models.BooleanField(default=False)
     avatar_ban = models.BooleanField(default=False)
@@ -460,6 +461,12 @@ class User(models.Model):
         activations = ['none', 'user', 'admin', 'credentials']
         activations = ['none', 'user', 'admin', 'credentials']
         return activations[self.activation]
         return activations[self.activation]
     
     
+    def alert(self, message):
+        from misago.alerts.models import Alert
+        self.alerts += 1
+        self.alerts_new += 1
+        return Alert(user=self, message=message, date=tz_util.now())
+    
     def get_date(self):
     def get_date(self):
         return self.join_date
         return self.join_date
     
     

+ 2 - 0
misago/views.py

@@ -15,6 +15,8 @@ from misago.sessions.models import Session
 from misago.threads.models import Thread
 from misago.threads.models import Thread
 
 
 def home(request):
 def home(request):
+    # Alert ourselves
+    request.user.alert("%(username)s, you have checked board index page.").url('username', request.user.username, reverse('user', kwargs={'user': request.user.pk, 'username': request.user.username_slug})).save_all()
     # Threads ranking
     # Threads ranking
     popular_threads = cache.get('thread_ranking_%s' % request.user.make_acl_key(), 'nada')
     popular_threads = cache.get('thread_ranking_%s' % request.user.make_acl_key(), 'nada')
     if popular_threads == 'nada' and request.settings['thread_ranking_size'] > 0:
     if popular_threads == 'nada' and request.settings['thread_ranking_size'] > 0:

+ 1 - 0
static/sora/css/sora.css

@@ -1029,6 +1029,7 @@ td.lead-cell{color:#555555;font-weight:bold;}
 .profile-header .avatar-height .lead{color:#555555;}.profile-header .avatar-height .lead .muted{color:#959595;}
 .profile-header .avatar-height .lead{color:#555555;}.profile-header .avatar-height .lead .muted{color:#959595;}
 .profile-header .nav-tabs{margin-top:-22px;margin-bottom:0px;padding-left:142px;}
 .profile-header .nav-tabs{margin-top:-22px;margin-bottom:0px;padding-left:142px;}
 .avatar-menu h3{margin-top:0px;}
 .avatar-menu h3{margin-top:0px;}
+.alerts-list a{font-weight:bold;}
 .well-post.rank-team{border:1px solid #0099e6;-webkit-box-shadow:0px 0px 0px 3px #66ccff;-moz-box-shadow:0px 0px 0px 3px #66ccff;box-shadow:0px 0px 0px 3px #66ccff;}
 .well-post.rank-team{border:1px solid #0099e6;-webkit-box-shadow:0px 0px 0px 3px #66ccff;-moz-box-shadow:0px 0px 0px 3px #66ccff;box-shadow:0px 0px 0px 3px #66ccff;}
 .team-online.rank-team ul li{background-color:#0088cc;}.team-online.rank-team ul li div a{color:#ffffff;text-shadow:0px 1px 0px #000d13;}
 .team-online.rank-team ul li{background-color:#0088cc;}.team-online.rank-team ul li div a{color:#ffffff;text-shadow:0px 1px 0px #000d13;}
 .team-online.rank-team ul li div .muted{color:#02435e;}
 .team-online.rank-team ul li div .muted{color:#02435e;}

+ 7 - 1
static/sora/css/sora/utilities.less

@@ -43,4 +43,10 @@
   h3 {
   h3 {
     margin-top: 0px;
     margin-top: 0px;
   }
   }
-}
+}
+
+.alerts-list {
+  a {
+    font-weight: bold;
+  }
+}

+ 59 - 0
templates/sora/alerts.html

@@ -0,0 +1,59 @@
+{% extends "sora/layout.html" %}
+{% load i18n %}
+{% import "sora/macros.html" as macros with context %}
+
+{% block title %}{% if user.alerts_new -%}
+{{ macros.page_title(title=get_title(),parent=_('Your Notifications')) }}
+{%- else -%}
+{{ macros.page_title(title=get_title()) }}
+{%- endif %}{% endblock %}
+
+{% block content %}
+<div class="page-header">
+  <h1>{% if user.alerts_new %}{{ get_title() }} <small>{% trans %}Your Notifications{% endtrans %}</small>
+      {%- else -%}
+      {% trans %}Your Notifications{% endtrans %}{% endif %}</h1>
+</div>
+{% if alerts %}
+<div class="alerts-list">
+{% if alerts.today %}{{ alerts_list(_("Today Notifications"), alerts.today) }}{% endif %}
+{% if alerts.yesterday %}{{ alerts_list(_("Yesterday Notifications"), alerts.yesterday) }}{% endif %}
+{% if alerts.week %}{{ alerts_list(_("This Week Notifications"), alerts.week) }}{% endif %}
+{% if alerts.month %}{{ alerts_list(_("This Month Notifications"), alerts.month) }}{% endif %}
+{% if alerts.older %}{{ alerts_list(_("Older Notifications"), alerts.older) }}{% endif %}
+</div>
+{% else %}
+<p class="lead">{% trans %}Looks like you don't have any notifications... yet.{% endtrans %}</p>
+{% endif %}
+{% endblock %}
+
+{% macro get_title() -%}
+{% if user.alerts_new -%}
+{% trans count=user.alerts_new -%}
+You have one new alert
+{%- pluralize -%}
+You have {{ count }} new alerts
+{%- endtrans %}
+{%- else -%}
+{% trans %}Your Notifications{% endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+{% macro alerts_list(title, alerts) %}
+  <table class="table table-striped">
+    <thead>
+      <tr>
+        <th>{{ title }}</th>
+        <th class="span3">{% trans %}Date{% endtrans %}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {% for alert in alerts %}
+      <tr>
+        <td>{% if alert.new %}<span class="label label-warning">{% trans %}New{% endtrans %}</span> {% endif %}{{ (_(alert.message) % alert.vars())|safe }}</td>
+        <td class="muted">{{ alert.date|reltimesince }}</td>
+      </tr>
+      {% endfor %}
+    </tbody>
+  </table>
+{% endmacro %}

+ 1 - 1
templates/sora/userbar.html

@@ -3,7 +3,7 @@
     <div class="container">
     <div class="container">
       <ul class="nav">{% if user.is_authenticated() %}
       <ul class="nav">{% if user.is_authenticated() %}
         <li><a href="#" title="{% trans %}Active Reports{% endtrans %}" class="tooltip-bottom"><i class="icon-warning-sign"></i><span class="stat">5</span></a></li>
         <li><a href="#" title="{% trans %}Active Reports{% endtrans %}" class="tooltip-bottom"><i class="icon-warning-sign"></i><span class="stat">5</span></a></li>
-        <li><a href="#" title="{% trans %}Your Notifications{% endtrans %}" class="tooltip-bottom"><i class="icon-fire"></i><span class="stat att">13</span></a></li>
+        <li><a href="{% url 'alerts' %}" title="{% if user.alerts_new %}{% trans %}You have new notifications!{% endtrans %}{% else %}{% trans %}Your Notifications{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-fire"></i><span class="stat{% if user.alerts_new %} att{% endif %}">{% if user.alerts_new %}{{ user.alerts_new }}{% else %}{{ user.alerts }}{% endif %}</span></a></li>
         <li><a href="#" title="{% trans %}Private messages{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="stat">2</span></a></li>
         <li><a href="#" title="{% trans %}Private messages{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="stat">2</span></a></li>
         <li><a href="#" title="{% trans %}People you are following{% endtrans %}" class="tooltip-bottom"><i class="icon-heart"></i></a></li>
         <li><a href="#" title="{% trans %}People you are following{% endtrans %}" class="tooltip-bottom"><i class="icon-heart"></i></a></li>
         <li><a href="#" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>{% endif %}
         <li><a href="#" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>{% endif %}