def user_reward(user, reward_type, topic_id=None, node_id=None):
    amount = settings.REP_GET_SETTING[reward_type]
    total = user.profile.reputation + amount
    stat = ReputationStat(user=user, stat_type=reward_type, amount=amount, total=total,
                          topic_id=topic_id, node_id=node_id)
    stat.save()
    user.profile.reputation = total class Notification(models.Model):
    user = models.ForeignKey(User, related_name='notifications')
    date = models.DateTimeField(auto_now=True, blank=False, null=False, db_index=True)
    detail = models.TextField(blank=False, null=False)
    read = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)
    
    class Meta():
        ordering = ['-date'] class Section(models.Model):
    name = models.CharField(max_length=32, unique=True, blank=False, null=False)
    date_created = models.DateTimeField(auto_now_add=True)
    order = models.IntegerField(default=1)
    last_modified = models.DateTimeField(auto_now=True, blank=True, null=True)
    
    class Meta():
        ordering = ['-order']
    
    def __str__(self):
        return self.name class Node(models.Model):
    section = models.ForeignKey(Section, on_delete=models.PROTECT, related_name='nodes')
    name = models.CharField(max_length=32, unique=True, blank=False, null=False)
    description = models.TextField(blank=False, null=False)
    icon_raw = models.ImageField("Node's icon", upload_to='upload/node/%Y%m%d', blank=True, null=True, default="")
    icon_l = models.ImageField("large icon", upload_to='node/%Y%m%d', blank=True, null=True, default="")
    icon_m = models.ImageField("medium icon", upload_to='node/%Y%m%d', blank=True, null=True, default="")
    icon_s = models.ImageField("small icon", upload_to='node/%Y%m%d', blank=True, null=True, default="")
    order = models.IntegerField(default=0)
    is_trash = models.BooleanField(default=False)
    date_created = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now=True, blank=True, null=True)
    watcher = models.ManyToManyField(User, blank=True, related_name='watch_nodes')
    
    class Meta():
        ordering = ['section', '-order']
    
    def __str__(self):
        return "%s/%s" % (self.section.name, self.name)
    
    def admin_image(self):
        if self.icon_s:
            return '<img src="/media/%s" width="24" height="24" />' % (self.icon_s)
        else:
            return None
    admin_image.allow_tags = True
    admin_image.short_description = 'Icon' class Topic(models.Model):
    node = models.ForeignKey(Node, on_delete=models.PROTECT, related_name='topics')
    author = models.ForeignKey(User, on_delete=models.PROTECT, related_name='topics')
    title = models.CharField(max_length=128, blank=False, null=False)
    markdown = models.TextField(blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    abstract = models.TextField(blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now=True, blank=True, null=True)
    liker = models.ManyToManyField(User, blank=True, related_name='like_topics')
    viewed = models.IntegerField(default=0)
    reply_count = models.IntegerField(default=0)
    last_replied = models.DateTimeField(blank=True, null=True)
    rank = models.IntegerField(default=10)
    reply_reward = models.BooleanField(default=False)
    like_reward = models.BooleanField(default=False)
    admin_star = models.BooleanField(default=False)
    deleted = models.BooleanField(default=False)
    editable = models.BooleanField(default=True)
    
    class Meta():
        ordering = ['-rank', '-date_created'] class Reply(models.Model):
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='replies')
    author = models.ForeignKey(User, on_delete=models.PROTECT, related_name='replies')
    markdown = models.TextField(blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    date_created = models.DateTimeField(auto_now_add=True)
    
    class Meta():
        ordering = ['date_created'] class TopicForm(forms.Form):
    title = forms.CharField(label=_("Topic title"), max_length=120,
        widget=forms.TextInput(attrs={'placeholder': _('Topic title')}),
        error_messages={'required': _("title can't be blank"),
                        'max_length': _('The title should be no longer than 120 characters')})
    content = forms.CharField(widget=forms.Textarea(attrs={'rows':4, 'placeholder': _('Say something')}),
        error_messages={'required': _("content can't be blank")})
    node = forms.ModelChoiceField(queryset=Node.objects.all(), empty_label=_('---- select a node ----'),
        error_messages={'required': _("node can't be blank"), 'invalid_choice': _("invalid choice")}) class ReplyForm(forms.Form):
    content = forms.CharField(widget=forms.Textarea(attrs={'rows':4, 'placeholder': _('Leave a comment')}),
        error_messages={'required': _("content can't be blank")}) def get_pagination(current_page, num_pages, count):
    page_list = []
    
    show_pages = 2*count+1
    if show_pages >= num_pages:
        page_list.extend(range(1, num_pages+1))
    elif current_page - count < 1:
        page_list.extend(range(1, show_pages+1))
    elif current_page + count > num_pages:
        page_list.extend(range(num_pages+1-show_pages, num_pages+1))
    else:
        page_list.extend(range(current_page-count, current_page+count+1))
    
    return page_list def topic_pagination(page, topics):
    paginator = Paginator(topics, NUM_PER_PAGE)
    try:
        topic_list = paginator.page(page)
    except PageNotAnInteger:
        topic_list = paginator.page(1)
    except EmptyPage:
        topic_list = paginator.page(paginator.num_pages)
    
    page_list = get_pagination(topic_list.number, paginator.num_pages, 2)
    
    return topic_list, page_list def get_metioned_user(sender, markdown):
    mentioned = set(re.findall(MENTION_REGEX, markdown)) - set([sender.username])
#     mentioned = set(re.findall(MENTION_REGEX, markdown))
    if mentioned:
        return User.objects.filter(username__in=mentioned)
    
    return None def render_markdown(md):
    rendered = mdp(md)
#     print(type(rendered), rendered)
    
    return rendered def author_required(view_func):
    def _wrapped_view_func(request, *args, **kwargs):
        topic_id = kwargs.get('topic_id')
        topic = get_object_or_404(Topic, id=topic_id)
        
        if topic.author == request.user:
            return view_func(request, *args, **kwargs)
        else:
            return HttpResponseForbidden()
    
    return _wrapped_view_func
import json
from django.views.generic import View
from django.views.generic.base import TemplateView
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import get_object_or_404
from django.conf import settings
from django.utils import timezone
from django.db import transaction
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.utils.text import Truncator
from niuauth.utils import user_reward
from niuauth.models import Notification
from forum.models import Section, Node, Topic, Reply
from forum.forms import TopicForm, ReplyForm
from forum.utils import get_pagination, topic_pagination, get_metioned_user
from forum.mismd import render_markdown
from forum.utils import author_required
        return data
    
    def post(self, request, *args, **kwargs):
        data = self.get_context_data()
        form = data["form"]
        
        if not self.request.user.profile.can_create_topic():
            return super(CreateTopicView, self).render_to_response(data)
        
        if form.is_valid():
            title = form.cleaned_data['title']
            md = form.cleaned_data['content']
            node = form.cleaned_data['node']
            rendered = render_markdown(md)
            abstract = Truncator(strip_tags(rendered)).chars(60)
            
            if node.is_trash:
                rank = 0
            else:
                rank = 10
            topic = Topic(node=node, author=request.user, title=title,
                          markdown=md, content=rendered, abstract=abstract, rank=rank)
            mentioned = get_metioned_user(request.user, md)
            
            self._commit_changes(topic, mentioned)
            
            return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id':topic.id}))
        
        return super(CreateTopicView, self).render_to_response(data)
    
    @transaction.atomic
    def _commit_changes(self, topic, mentioned_user):
        topic.save()
        
        if mentioned_user:
            for u in mentioned_user:
                data = {}
                data['topic'] = topic
                
                detail = render_to_string("forum/notification/create_topic_notification.html", data)
                n = Notification(user=u, detail=detail.strip())
                u.profile.has_notification = True
                u.profile.save()
                u.save()
                n.save()

create_topic_view = CreateTopicView.as_view()

@method_decorator(login_required, name='dispatch')
@method_decorator(author_required, name='dispatch')
class UpdateTopicView(TemplateView):
    template_name = "forum/update_topic.html"
    
    def get_context_data(self, **kwargs):
        data = super(UpdateTopicView, self).get_context_data(**kwargs)
        
        topic_id = self.kwargs.get('topic_id')
        topic = get_object_or_404(Topic, id=topic_id)
        default_data = {'title': topic.title,
                        'content': topic.markdown,
                        'node': topic.node}
        
        form = TopicForm(self.request.POST or None, initial=default_data)
        data["form"] = form
        data["topic_id"] = topic_id
        
        return data
    
    def post(self, request, *args, **kwargs):
        data = self.get_context_data()
        form = data["form"]
        if form.is_valid():
            title = form.cleaned_data['title']
            md = form.cleaned_data['content']
            node = form.cleaned_data['node']
            rendered = render_markdown(md)
            abstract = Truncator(strip_tags(rendered)).chars(60)
            
            topic_id = self.kwargs.get('topic_id')
            topic = get_object_or_404(Topic, id=topic_id)
            topic.node = node
            topic.title = title
            topic.markdown = md
            topic.content = rendered
            topic.abstract = abstract
            if node.is_trash:
                topic.rank = 0
            else:
                topic.rank = 10
            topic.save()
            
            return HttpResponseRedirect(reverse('topic_view', kwargs={'topic_id':topic_id}))
        
        return super(UpdateTopicView, self).render_to_response(data)

update_topic_view = UpdateTopicView.as_view()

class TopicView(TemplateView):
    template_name = "forum/topic.html"
    
    def get_context_data(self, **kwargs):
        data = super(TopicView, self).get_context_data(**kwargs)
        
        topic_id = self.kwargs['topic_id']
        topic = get_object_or_404(Topic, id=topic_id)
        data['topic'] = topic
        topic.viewed += 1
        topic.save()
        
        replies = topic.replies.all()
        
        paginator = Paginator(replies, 10)
        page = self.request.GET.get('page')
        try:
            reply_list = paginator.page(page)
        except PageNotAnInteger:
            reply_list = paginator.page(paginator.num_pages)
        except EmptyPage:
            reply_list = paginator.page(paginator.num_pages)
        

        page_list = get_pagination(reply_list.number, paginator.num_pages, 2)