第九届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}}" onclick="readed_news('{{news.id}}');" class="btn btn-mini btn-success" {% if news.is_readed %}disabled{% endif %}>设为已读</button> <button id="id_delete_news" onclick="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
文章为阿小信的个人笔记,转载请注明出处。