第九届CUIT信息安全大赛正常进行,围观地址:http://hack.myclover.org/
在论坛加了个类似微博的@功能,在回复帖子的时候可以@系统中的用户,被@的用户可以收到自己被@的通知可以做出相应的处理。
关于model
#-*- coding:utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
import datetime
from geek.geekchallenge.models import *
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
import re
class Reply(models.Model):
thread = models.ForeignKey(Thread, verbose_name=u"所属帖子")
author = models.ForeignKey(Team, verbose_name=u"回复作者")
content = models.TextField(verbose_name=u"回复内容")
submit_time = models.DateTimeField(verbose_name=u"发表时间", auto_now_add=True)
update_time = models.DateTimeField(verbose_name=u"更新时间", blank=True, null=True, editable=False)
events = generic.GenericRelation('Event')
def __unicode__(self):
return self.submit_time.strftime("%Y-%m-%d %H:%m:%S")
class Meta:
verbose_name_plural = u"帖子回复"
@receiver(post_save, sender=Reply, dispatch_uid="ashin_unique_identifier")
def post_save_handler(sender, instance, **kwargs):
reply = instance
team_name_pattern = re.compile('(?<=@)(\w+)', re.UNICODE)
at_team_names = set(re.findall(team_name_pattern, reply.content))
if at_team_names:
for at_team_name in at_team_names:
if at_team_name != reply.author.user.username:
try:
at_team = User.objects.get(username=at_team_name)
event = Event(author=reply.author.user, event=reply, at_team=at_team)
event.save()
except:
pass
elif reply.author != reply.thread.author:
event = Event(author=reply.author.user, event=reply, at_team=reply.thread.author.user)
event.save()
class Event(models.Model):
content_type = models.ForeignKey(ContentType, verbose_name=u"被触发的模型")
object_id = models.PositiveIntegerField(verbose_name=u"被触发模型ID")
author = models.ForeignKey(User, verbose_name=u"事件发起者", related_name="author")
event = generic.GenericForeignKey('content_type', 'object_id')
at_team = models.ForeignKey(User, verbose_name=u"提到的人", related_name="at_team")
#两个外键都指向User必须使用related_name参数
submit_time = models.DateTimeField(verbose_name=u"@时间", auto_now_add=True)
is_readed = models.BooleanField(verbose_name=u"已读", default=False)
is_deleted = models.BooleanField(verbose_name=u"已被删除", default=False)
def __unicode__(self):
return u"%s在回复%s的帖子《%s》中提到了%s"%(self.author, self.event.thread.author, self.event.thread, self.at_team)
class Meta:
verbose_name_plural = u"回复新闻"
ordering = ["-submit_time"]
在用户按钮处显示有多少条未读@消息
<script>
function get_new_at_num(){
$.get('/forum/new-at-num/',function(data){
if (data != 0){
$('#at_tip').text('('+data+'条@我未读)');
$('#id_global_at').text(' ('+data+')');
}
else{
$('#at_tip').text('');
$('#id_global_at').text('');
}
window.setTimeout(get_new_at_num, 5000)
});
};
$(function(){
get_new_at_num();
});
</script>
<div class="btn-group pull-right">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="">
<i class="icon-user"></i>{% if request.user.is_authenticated %} `request`.`user`.`username` <span id="at_tip"></span>{% else %} 游客{% endif %}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
{% if request.user.is_authenticated %}
<li><a href="/team-info/"><i class="icon-cog"></i> 团队信息</a></li>
<li><a href="/forum/my-threads/"><i class="icon-th-list"></i> 我的帖子</a></li>
<li><a href="/forum/at-me/"><i class="icon-envelope"></i> @我的回复<font color="red" id="id_global_at"></font></a></li>
<li class="divider"></li>
<li><a href="/accounts/logout/"><i class="icon-off"></i> 退出</a></li>
{% else %}
<li><a href="/accounts/register/"><i class="icon-pencil"></i> 注册</a></li>
<li class="divider"></li>
<li><a href="/accounts/login/"><i class="icon-leaf"></i> 登录</a></li>
{% endif %}
</ul>
</div><!-- userbtn -->
ajax每隔5秒请求一次服务器返回未读消息的条数,有就显示在页面上
@login_required
def new_at_num(request):
unread_count = Event.objects.filter(at_team = request.user, is_deleted=False, is_readed = False).count()
return HttpResponse(unread_count)
@的消息页面
页面代码
<div>
<p>当前位置: <a href="/forum/">论坛首页</a> » @我的回复</p>
<p>共 <b id="id_all_count">{{results|length}}</b> 条, 未读 <b id="id_unread_count">`unread_count`</b> 条</p>
</div>
<div class="span10">
{% if paged_events.object_list %}
<table class="table table-hover table-striped">
<tr id="id_news_table_head">
<th>点击查看@我的回复</th>
<th>@我的时间</th>
<th>阅读状态</th>
<th>操作</th>
</tr>
{% for news in paged_events.object_list %}
<tr id="id_news_`news`.`id`">
<td><a href="/forum/`news`.`event`.`thread`.`forum`.`id`/`news`.`event`.`thread`.`id`/?reading=`news`.`id`#reply-`news`.`event`.`id`">` news`.`author `在回复` news`.`event`.`thread`.`author`的帖子《`news`.`event`.`thread`.`title`》中提到了您</a></td>
<td>{{ news.submit_time|date:"Y-m-d H:i:s"}}</td>
<td>{% if news.is_readed %}已读{% else %}<font color="red" id="id_unread_`news`.`id`">未读</font>{% endif %}</td>
<td><button id="id_readed_news_`news`.`id`" οnclick="readed_news('`news`.`id`');" class="btn btn-mini btn-success" {% if news.is_readed %}disabled{% endif %}>设为已读</button> <button id="id_delete_news" οnclick="delete_news('`news`.`id`');" class="btn btn-mini btn-warning">删除记录</button></td>
</tr>
{% endfor %}
</table>
{% endif %}
</div><!--span10-->
<script>
function readed_news(id){
var url = '/forum/readed-at-me/'+id+'/';
$.post(url, {'csrfmiddlewaretoken':'` csrf_token `'}, function(data){
if (data=='success'){
$('#id_unread_'+id).attr('color', '').text('已读');
$('#id_readed_news_'+id).attr('disabled', 'true');
$('#id_unread_count').text($('#id_unread_count').text()-1)
}else{
alert('操作失败!');
}
});
}
function delete_news(id){
var url = '/forum/delete-at-me/'+id+'/';
$.post(url, {'csrfmiddlewaretoken':'` csrf_token `'}, function(data){
if(data=='success'){
if ($('#id_unread_'+id).text() == '未读'){
$('#id_unread_count').text($('#id_unread_count').text()-1)
}
$('#id_news_'+id).remove();
$('#id_all_count').text($('#id_all_count').text()-1)
}else{
alert('操作失败!');
}
});
}
</script>
处理函数
@login_required
def at_me(request):
#最新帖子
latest_threads = Thread.objects.all()[:5]
#回复事件
results = Event.objects.filter(at_team = request.user, is_deleted=False)
unread_count = Event.objects.filter(at_team = request.user, is_deleted=False, is_readed = False).count()
#分页
events_paginator = Paginator(results, 15)
try:
page = int(request.GET.get('page', 1))
except ValueError:
page = 1
try:
paged_events = events_paginator.page(page)
except (EmptyPage, InvalidPage):
paged_events = events_paginator.page(events_paginator.num_pages)
return render_to_response('at-me.html', {"results":results,'unread_count':unread_count, 'latest_threads':latest_threads, "paged_events":paged_events, "events_paginator":events_paginator}, context_instance=RequestContext(request))
@login_required
def readed_news(request, news_id):
news = get_object_or_404(Event, id=news_id, at_team=request.user)
if request.method == 'POST':
news.is_readed=True
news.save()
return HttpResponse("success")
return HttpResponse("error")
@login_required
def delete_news(request, news_id):
news = get_object_or_404(Event, id=news_id, at_team=request.user)
if request.method == 'POST':
news.is_deleted = True
news.save()
return HttpResponse("success")
return HttpResponse("error")
在回复中@用户
实现代码:
<script src="/static/js/userAutoTips.js"></script> <style type="text/css" > ol, ul { list-style: none outside none; } .recipients-tips{ font-family:Tahoma, Arial;position:absolute; background:#282828; z-index:2147483647; padding:2px; border:2px solid #33b5e5; display:none;} .recipients-tips li a{display:block; padding:2px 5px; cursor:pointer;} .autoSelected{background:#131517;} </style> <script type="text/javascript"> userAutoTips({id:'id_content'}); </script>
用了网上找的一个别人写好的只要检测到textarea中有@就会显示列表的代码,自己该了一些设置和获取最近活跃的10位用户
@login_required
def nearest_users(request):
threads = Thread.objects.all()
replies = Reply.objects.all()
users = [thread.author.user.username for thread in threads]
users.extend([reply.author.user.username for reply in replies])
users = set(users)
if request.user.username in users:
users.remove(request.user.username)
user_name_list = []
for user in users:
user_name_list.append({}.fromkeys(('user', 'name'), user))
show_count = 15
if len(user_name_list) > show_count:
user_name_list = user_name_list[:show_count]
data = simplejson.dumps(user_name_list)
return HttpResponse(data, mimetype="application/json")
UserAutoTips.js放到微盘了:
http://vdisk.weibo.com/s/naFdb
文章为阿小信的个人笔记,转载请注明出处。
转载于:https://blog.51cto.com/ashin/1200995