Commit ebb25ab5 by benjaoming

Adding notify frontend.

parent 1a0396df
from django.contrib import admin
from django_notify import models
admin.site.register(models.NotificationType)
admin.site.register(models.Notification)
admin.site.register(models.Settings)
admin.site.register(models.Subscription)
\ No newline at end of file
# -*- coding: utf-8 -*-
from django.utils import simplejson as json
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
def login_required_ajax(func):
"""Similar to login_required. But if the request is an ajax request, then
it returns an error in json with a 403 status code."""
def wrap(request, *args, **kwargs):
if request.is_ajax():
if not request.user or not request.user.is_authenticated():
return json_view(lambda *a, **kw: {'error': 'not logged in'})(request, status=403)
return func(request, *args, **kwargs)
else:
return login_required(func)(request, *args, **kwargs)
return wrap
def json_view(func):
def wrap(request, *args, **kwargs):
obj = func(request, *args, **kwargs)
data = json.dumps(obj, ensure_ascii=False)
status = kwargs.get('status', 200)
response = HttpResponse(mimetype='application/json', status=status)
response.write(data)
return response
return wrap
...@@ -4,9 +4,10 @@ from django.db.models import Q ...@@ -4,9 +4,10 @@ from django.db.models import Q
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django_notify import settings
class NotificationType(models.Model): class NotificationType(models.Model):
""" """
Notification types are added on-the-fly by the Notification types are added on-the-fly by the
...@@ -19,6 +20,11 @@ class NotificationType(models.Model): ...@@ -19,6 +20,11 @@ class NotificationType(models.Model):
def __unicode__(self): def __unicode__(self):
return self.key return self.key
class Meta:
db_tablespace = settings.DB_TABLESPACE
verbose_name = _(u'type')
verbose_name_plural = _(u'types')
class Settings(models.Model): class Settings(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
...@@ -28,6 +34,11 @@ class Settings(models.Model): ...@@ -28,6 +34,11 @@ class Settings(models.Model):
def __unicode__(self): def __unicode__(self):
return self.user return self.user
class Meta:
db_tablespace = settings.DB_TABLESPACE
verbose_name = _(u'settings')
verbose_name_plural = _(u'settings')
class Subscription(models.Model): class Subscription(models.Model):
settings = models.ForeignKey(Settings) settings = models.ForeignKey(Settings)
...@@ -39,6 +50,11 @@ class Subscription(models.Model): ...@@ -39,6 +50,11 @@ class Subscription(models.Model):
def __unicode__(self): def __unicode__(self):
return _("Subscription for: %s") % self.settings.user return _("Subscription for: %s") % self.settings.user
class Meta:
db_tablespace = settings.DB_TABLESPACE
verbose_name = _(u'notification type')
verbose_name_plural = _(u'notification types')
class Notification(models.Model): class Notification(models.Model):
subscription = models.ForeignKey(Subscription) subscription = models.ForeignKey(Subscription)
...@@ -70,3 +86,8 @@ class Notification(models.Model): ...@@ -70,3 +86,8 @@ class Notification(models.Model):
def __unicode__(self): def __unicode__(self):
return "%s: %s" % (self.subscription.settings.user, self.message) return "%s: %s" % (self.subscription.settings.user, self.message)
class Meta:
db_tablespace = settings.DB_TABLESPACE
verbose_name = _(u'notification')
verbose_name_plural = _(u'notifications')
...@@ -2,6 +2,9 @@ from django.conf import settings as django_settings ...@@ -2,6 +2,9 @@ from django.conf import settings as django_settings
_ = lambda x: x _ = lambda x: x
# don't change this :)
DB_TABLESPACE = 'notify'
# You need to switch this setting on, otherwise nothing will happen :) # You need to switch this setting on, otherwise nothing will happen :)
ENABLED = getattr(django_settings, "NOTIFY_ENABLED", True) ENABLED = getattr(django_settings, "NOTIFY_ENABLED", True)
......
# -*- coding: utf-8 -*-
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('',
url('^json/get/$', 'django_notify.views.get_notifications', name='json_get', kwargs={}),
url('^json/mark-read/$', 'django_notify.views.mark_read', name='json_mark_read_base', kwargs={}),
url('^json/mark-read/(\d+)/$', 'django_notify.views.mark_read', name='json_mark_read', kwargs={}),
url('^goto/(?P<notification_id>\d+)/$', 'django_notify.views.goto', name='goto', kwargs={}),
url('^goto/$', 'django_notify.views.goto', name='goto_base', kwargs={}),
)
def get_pattern(app_name="notify", namespace="notify"):
"""Every url resolution takes place as "notify:view_name".
https://docs.djangoproject.com/en/dev/topics/http/urls/#topics-http-reversing-url-namespaces
"""
return urlpatterns, app_name, namespace
\ No newline at end of file
# Create your views here. # -*- coding: utf-8 -*-
from django_notify.decorators import json_view, login_required_ajax
from django_notify import models
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, get_object_or_404
@login_required_ajax
@json_view
def get_notifications(request, latest_id=None, is_viewed=False, max_results=10):
notifications = models.Notification.objects.filter(subscription__settings__user=request.user,)
if not is_viewed is None:
notifications = notifications.filter(is_viewed=is_viewed)
if not latest_id is None:
notifications = notifications.filter(latest_id__gt=latest_id)
notifications = notifications[:max_results]
from django.contrib.humanize.templatetags.humanize import naturaltime
return {'success': True,
'objects': [{'pk': n.pk,
'message': n.message,
'url': n.url,
'type': n.subscription.notification_type.key,
'since': naturaltime(n.created)} for n in notifications]}
@login_required
def goto(request, notification_id=None):
referer = request.META.get('HTTP_REFERER', '')
if not notification_id:
return redirect(referer)
notification = get_object_or_404(models.Notification,
subscription__settings__user=request.user,
id=notification_id)
notification.is_viewed=True
notification.save()
if not notification.url is None:
return redirect(notification.url)
return redirect(referer)
@login_required_ajax
@json_view
def mark_read(request, up_to_id, notification_type_id=None):
notifications = models.Notification.objects.filter(subscription__settings__user=request.user,
id__lte=up_to_id)
if notification_type_id:
notifications = notifications.filter(notification_type__id=notification_type_id)
notifications.update(is_viewed=True)
return {'success': True}
\ No newline at end of file
...@@ -4,9 +4,11 @@ from django.contrib import admin ...@@ -4,9 +4,11 @@ from django.contrib import admin
admin.autodiscover() admin.autodiscover()
from wiki.urls import get_pattern as wiki_pattern from wiki.urls import get_pattern as wiki_pattern
from django_notify.urls import get_pattern as notify_pattern
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')), url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^notify/', include(notify_pattern())),
url(r'', include(wiki_pattern())), url(r'', include(wiki_pattern())),
) )
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.utils import simplejson as json
from django.http import HttpResponse, HttpResponseForbidden,\ from django.http import HttpResponse, HttpResponseForbidden,\
HttpResponseNotFound HttpResponseNotFound
from wiki.core.exceptions import NoRootURL from wiki.core.exceptions import NoRootURL
from django.core import serializers
JSONSerializer = serializers.get_serializer("json")
def json_view(func): def json_view(func):
def wrap(request, *a, **kw): def wrap(request, *a, **kw):
obj = func(request, *a, **kw) obj = func(request, *a, **kw)
data = json.dumps(obj, ensure_ascii=False) json_serializer = JSONSerializer()
json_serializer.serialize(obj)
data = json_serializer.getvalue()
response = HttpResponse(mimetype='application/json') response = HttpResponse(mimetype='application/json')
response.write(data) response.write(data)
return response return response
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django import forms from django import forms
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from itertools import chain
from editors import editor
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from wiki import models
from django.forms.util import flatatt from django.forms.util import flatatt
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.utils.html import escape, conditional_escape from django.utils.html import escape, conditional_escape
from itertools import chain
from wiki import models
from wiki.editors import editor
from wiki.core.diff import simple_merge from wiki.core.diff import simple_merge
class CreateRoot(forms.Form): class CreateRoot(forms.Form):
......
notify_latest_id = 0;
function notify_update() {
jsonWrapper(URL_NOTIFY_GET_NEW, function (data) {
if (data.success) {
$('.notification-cnt').html(data.objects.length);
if (data.objects.length> 0) {
$('.notification-cnt').addClass('badge-important');
$('.notifications-empty').hide();
} else {
$('.notification-cnt').removeClass('badge-important');
}
for (var i=0; i < data.objects.length; i++) {
n = data.objects[i];
notify_latest_id = n.pk>notify_latest_id ? n.pk:notify_latest_id;
$('.notification-li-container').prepend($('<li><a href="'+URL_NOTIFY_GOTO+n.pk+'/"><div>'+n.message+'</div><div class="since">'+n.since+'</div></a></li>'))
}
}
});
}
function notify_mark_read() {
url = URL_NOTIFY_MARK_READ+notify_latest_id+'/';
jsonWrapper(url, function (data) {
if (data.success) {
notify_update();
}
});
}
$(document).ready(function () {
notify_update();
// Update every second minute.
setInterval("notify_update()", 120000);
})
{% load i18n sekizai_tags %}
<script type="text/javascript">
URL_NOTIFY_GET_NEW = "{% url notify:json_get %}";
URL_NOTIFY_MARK_READ = "{% url notify:json_mark_read_base %}";
URL_NOTIFY_GOTO = "{% url notify:goto_base %}";
</script>
<script type="text/javascript" src="{{ STATIC_URL }}wiki/plugins/notifications/js/ui.js"></script>
<ul class="nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="badge notification-cnt">0</span>
{% trans "notifications" %}
<b class="caret"></b>
</a>
<ul class="dropdown-menu notification-list">
<div class="notification-li-container"></div>
<li class="notifications-empty"><a href="#"><em>{% trans "No notifications" %}</em></a></li>
<li class="divider"></li>
<li><a href="#" onclick="notify_mark_read()">{% trans "Clear notifications list" %}</a></li>
</ul>
</li>
</ul>
function ajaxError(){}
$.ajaxSetup({
timeout: 7000,
cache: false,
error: function(e, xhr, settings, exception) {
ajaxError();
}
});
function jsonWrapper(url, callback) {
$.getJSON(url, function(data) {
if (data == null) {
ajaxError();
} else {
callback(data);
}
});
}
function ajaxError(){}
$.ajaxSetup({
timeout: 7000,
cache: false,
error: function(e, xhr, settings, exception) {
ajaxError();
}
});
function jsonWrapper(url, callback) {
$.getJSON(url, function(data) {
if (data == null) {
ajaxError();
} else {
callback(data);
}
});
}
function get_diff_json(url, put_in_element) { function get_diff_json(url, put_in_element) {
jsonWrapper(url, function (data) { jsonWrapper(url, function (data) {
......
{% load sekizai_tags %}{% load url from future %}<!DOCTYPE html> {% load sekizai_tags i18n %}{% load url from future %}<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
...@@ -22,10 +22,16 @@ ...@@ -22,10 +22,16 @@
.asteriskField { font-size: 20px; margin-left: 5px; } .asteriskField { font-size: 20px; margin-left: 5px; }
.notification-list .since {
font-size: 80%;
color: #CCC;
}
</style> </style>
<link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap-responsive.css" rel="stylesheet"> <link href="{{ STATIC_URL }}wiki/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
<script src="{{ STATIC_URL }}wiki/js/jquery.min.js"></script> <script src="{{ STATIC_URL }}wiki/js/jquery.min.js"></script>
<script src="{{ STATIC_URL }}wiki/js/core.js"></script>
{% render_block "css" %} {% render_block "css" %}
{% render_block "js" %} {% render_block "js" %}
...@@ -50,11 +56,21 @@ ...@@ -50,11 +56,21 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
</a> </a>
<a class="brand" href="{% url 'wiki:root' %}">django-wiki</a> <a class="brand" href="{% url 'wiki:root' %}">django-wiki</a>
<div class="pull-right">
{% if user %}
{% include "wiki/plugins/notifications/menubaritem.html" %}
{% else %}
<ul class="nav">
<li>
<a href="{% url 'admin:index' %}">{% trans "Log in" %}</a>
</li>
</ul>
{% endif %}
</div>
<div class="nav-collapse"> <div class="nav-collapse">
<ul class="nav"> <ul class="nav">
<li class="active"><a href="#">Home</a></li> <li class="active"><a href="/">Home</a></li>
<li><a href="#about">About</a></li> <li><a href="https://github.com/benjaoming/django-wiki" target="_blank">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul> </ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment