# encoding:utf-8 import calendar from django.conf import settings from django.contrib.auth.decorators import login_required from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404 from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.template import RequestContext, loader from django.utils.html import * from django.utils import simplejson from django.core import serializers from django.core.mail import mail_admins from django.db import transaction from django.db.models import Count, Q from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext as _ from django.utils.datastructures import SortedDict from django.template.defaultfilters import slugify from django.core.exceptions import PermissionDenied from utils.html import sanitize_html from utils.decorators import ajax_method, ajax_login_required from markdown2 import Markdown import os.path import random import time import datetime from forum import auth from forum.auth import * from forum.const import * from forum.diff import textDiff as htmldiff from forum.forms import * from forum.models import * from forum.user import * from forum import auth from django_authopenid.util import get_next_url # used in index page INDEX_PAGE_SIZE = 20 INDEX_AWARD_SIZE = 15 INDEX_TAGS_SIZE = 100 # used in tags list DEFAULT_PAGE_SIZE = 60 # used in questions QUESTIONS_PAGE_SIZE = 10 # used in users USERS_PAGE_SIZE = 35 # used in answers ANSWERS_PAGE_SIZE = 10 markdowner = Markdown(html4tags=True) question_type = ContentType.objects.get_for_model(Question) answer_type = ContentType.objects.get_for_model(Answer) comment_type = ContentType.objects.get_for_model(Comment) question_revision_type = ContentType.objects.get_for_model(QuestionRevision) answer_revision_type = ContentType.objects.get_for_model(AnswerRevision) repute_type = ContentType.objects.get_for_model(Repute) question_type_id = question_type.id answer_type_id = answer_type.id comment_type_id = comment_type.id question_revision_type_id = question_revision_type.id answer_revision_type_id = answer_revision_type.id repute_type_id = repute_type.id def _get_tags_cache_json(): tags = Tag.objects.filter(deleted=False).all() tags_list = [] for tag in tags: dic = {'n': tag.name, 'c': tag.used_count} tags_list.append(dic) tags = simplejson.dumps(tags_list) return tags def _get_and_remember_questions_sort_method(request, view_dic, default): if default not in view_dic: raise Exception('default value must be in view_dic') q_sort_method = request.REQUEST.get('sort', None) if q_sort_method == None: q_sort_method = request.session.get('questions_sort_method', default) if q_sort_method not in view_dic: q_sort_method = default request.session['questions_sort_method'] = q_sort_method return q_sort_method, view_dic[q_sort_method] def index(request): view_dic = { "latest":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score", } view_id, orderby = _get_and_remember_questions_sort_method(request, view_dic, 'latest') page_size = request.session.get('pagesize', QUESTIONS_PAGE_SIZE) questions = Question.objects.exclude(deleted=True).order_by(orderby)[:page_size] # RISK - inner join queries questions = questions.select_related() tags = Tag.objects.get_valid_tags(INDEX_TAGS_SIZE) awards = Award.objects.get_recent_awards() (interesting_tag_names, ignored_tag_names) = (None, None) if request.user.is_authenticated(): pt = MarkedTag.objects.filter(user=request.user) interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True) ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True) tags_autocomplete = _get_tags_cache_json() return render_to_response('index.html', { 'interesting_tag_names': interesting_tag_names, 'tags_autocomplete': tags_autocomplete, 'ignored_tag_names': ignored_tag_names, "questions" : questions, "tab_id" : view_id, "tags" : tags, "awards" : awards[:INDEX_AWARD_SIZE], }, context_instance=RequestContext(request)) def about(request): return render_to_response('about.html', context_instance=RequestContext(request)) def faq(request): data = { 'gravatar_faq_url': reverse('faq') + '#gravatar', 'send_email_key_url': reverse('send_email_key'), 'ask_question_url': reverse('ask'), } return render_to_response('faq.html', data, context_instance=RequestContext(request)) def feedback(request): data = {} form = None if request.method == "POST": form = FeedbackForm(request.POST) if form.is_valid(): if not request.user.is_authenticated: data['email'] = form.cleaned_data.get('email',None) data['message'] = form.cleaned_data['message'] data['name'] = form.cleaned_data.get('name',None) message = render_to_response('feedback_email.txt',data,context_instance=RequestContext(request)) mail_admins(_('Q&A forum feedback'), message) msg = _('Thanks for the feedback!') request.user.message_set.create(message=msg) return HttpResponseRedirect(get_next_url(request)) else: form = FeedbackForm(initial={'next':get_next_url(request)}) data['form'] = form return render_to_response('feedback.html', data, context_instance=RequestContext(request)) feedback.CANCEL_MESSAGE=_('We look forward to hearing your feedback! Please, give it next time :)') def privacy(request): return render_to_response('privacy.html', context_instance=RequestContext(request)) def unanswered(request): return questions(request, unanswered=True) def questions(request, tagname=None, unanswered=False): """ List of Questions, Tagged questions, and Unanswered questions. """ # template file # "questions.html" or maybe index.html in the future template_file = "questions.html" # get pagesize from session, if failed then get default value pagesize = request.session.get("pagesize", 10) try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" } view_id, orderby = _get_and_remember_questions_sort_method(request,view_dic,'latest') # check if request is from tagged questions qs = Question.objects.exclude(deleted=True) if tagname is not None: qs = qs.filter(tags__name = unquote(tagname)) if unanswered: qs = qs.exclude(answer_accepted=True) author_name = None #user contributed questions & answers if 'user' in request.GET: try: author_name = request.GET['user'] u = User.objects.get(username=author_name) qs = qs.filter(Q(author=u) | Q(answers__author=u)) except User.DoesNotExist: author_name = None if request.user.is_authenticated(): uid_str = str(request.user.id) qs = qs.extra( select = SortedDict([ ( 'interesting_score', 'SELECT COUNT(1) FROM forum_markedtag, question_tags ' + 'WHERE forum_markedtag.user_id = %s ' + 'AND forum_markedtag.tag_id = question_tags.tag_id ' + 'AND forum_markedtag.reason = "good" ' + 'AND question_tags.question_id = question.id' ), ]), select_params = (uid_str,), ) if request.user.hide_ignored_questions: ignored_tags = Tag.objects.filter(user_selections__reason='bad', user_selections__user = request.user) qs = qs.exclude(tags__in=ignored_tags) else: qs = qs.extra( select = SortedDict([ ( 'ignored_score', 'SELECT COUNT(1) FROM forum_markedtag, question_tags ' + 'WHERE forum_markedtag.user_id = %s ' + 'AND forum_markedtag.tag_id = question_tags.tag_id ' + 'AND forum_markedtag.reason = "bad" ' + 'AND question_tags.question_id = question.id' ) ]), select_params = (uid_str, ) ) qs = qs.select_related(depth=1).order_by(orderby) objects_list = Paginator(qs, pagesize) questions = objects_list.page(page) # Get related tags from this page objects if questions.object_list.count() > 0: related_tags = Tag.objects.get_tags_by_questions(questions.object_list) else: related_tags = None tags_autocomplete = _get_tags_cache_json() # get the list of interesting and ignored tags (interesting_tag_names, ignored_tag_names) = (None, None) if request.user.is_authenticated(): pt = MarkedTag.objects.filter(user=request.user) interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True) ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True) return render_to_response(template_file, { "questions" : questions, "author_name" : author_name, "tab_id" : view_id, "questions_count" : objects_list.count, "tags" : related_tags, "tags_autocomplete" : tags_autocomplete, "searchtag" : tagname, "is_unanswered" : unanswered, "interesting_tag_names": interesting_tag_names, 'ignored_tag_names': ignored_tag_names, "context" : { 'is_paginated' : True, 'pages': objects_list.num_pages, 'page': page, 'has_previous': questions.has_previous(), 'has_next': questions.has_next(), 'previous': questions.previous_page_number(), 'next': questions.next_page_number(), 'base_url' : request.path + '?sort=%s&' % view_id, 'pagesize' : pagesize }}, context_instance=RequestContext(request)) def create_new_answer( question=None, author=None,\ added_at=None, wiki=False,\ text='', email_notify=False): html = sanitize_html(markdowner.convert(text)) #create answer answer = Answer( question=question, author=author, added_at=added_at, wiki=wiki, html=html ) if answer.wiki: answer.last_edited_by = answer.author answer.last_edited_at = added_at answer.wikified_at = added_at answer.save() #update question data question.last_activity_at = added_at question.last_activity_by = author question.save() Question.objects.update_answer_count(question) #update revision AnswerRevision.objects.create( answer=answer, revision=1, author=author, revised_at=added_at, summary=CONST['default_version'], text=text ) #set notification/delete if email_notify: if author not in question.followed_by.all(): question.followed_by.add(author) else: #not sure if this is necessary. ajax should take care of this... try: question.followed_by.remove(author) except: pass def create_new_question(title=None, author=None, added_at=None, wiki=False, tagnames=None, summary=None, text=None): """this is not a view and maybe should become one of the methods on Question object? """ html = sanitize_html(markdowner.convert(text)) question = Question( title=title, author=author, added_at=added_at, last_activity_at=added_at, last_activity_by=author, wiki=wiki, tagnames=tagnames, html=html, summary=summary ) if question.wiki: question.last_edited_by = question.author question.last_edited_at = added_at question.wikified_at = added_at question.save() # create the first revision QuestionRevision.objects.create( question=question, revision=1, title=question.title, author=author, revised_at=added_at, tagnames=question.tagnames, summary=CONST['default_version'], text=text ) return question #TODO: allow anynomus user to ask question by providing email and username. @login_required def ask(request): if request.method == "POST": form = AskForm(request.POST) if form.is_valid(): added_at = datetime.datetime.now() title = strip_tags(form.cleaned_data['title'].strip()) wiki = form.cleaned_data['wiki'] tagnames = form.cleaned_data['tags'].strip() text = form.cleaned_data['text'] html = sanitize_html(markdowner.convert(text)) summary = strip_tags(html)[:120] if request.user.is_authenticated(): author = request.user question = create_new_question( title=title, author=author, added_at=added_at, wiki=wiki, tagnames=tagnames, summary=summary, text=text ) return HttpResponseRedirect(question.get_absolute_url()) else: request.session.flush() session_key = request.session.session_key question = AnonymousQuestion( session_key=session_key, title=title, tagnames=tagnames, wiki=wiki, text=text, summary=summary, added_at=added_at, ip_addr=request.META['REMOTE_ADDR'], ) question.save() return HttpResponseRedirect(reverse('user_signin_new_question')) else: form = AskForm() tags = _get_tags_cache_json() return render_to_response('ask.html', { 'form' : form, 'tags' : tags, 'email_validation_faq_url':reverse('faq') + '#validate', }, context_instance=RequestContext(request)) def question(request, id): try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 view_id = request.GET.get('sort', None) view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score" } try: orderby = view_dic[view_id] except KeyError: qsm = request.session.get('questions_sort_method',None) if qsm in ('mostvoted','latest'): logging.debug('loaded from session ' + qsm) if qsm == 'mostvoted': view_id = 'votes' orderby = '-score' else: view_id = 'latest' orderby = '-added_at' else: view_id = "votes" orderby = "-score" logging.debug('view_id=' + str(view_id)) question = get_object_or_404(Question, id=id) if question.deleted and not can_view_deleted_post(request.user, question): raise Http404 answer_form = AnswerForm(question, request.user) answers = Answer.objects.get_answers_from_question(question, request.user, orderby) answers = answers.select_related(depth=1) favorited = question.has_favorite_by_user(request.user) if request.user.is_authenticated(): question_vote = question.votes.select_related().filter(user=request.user) else: question_vote = None #is this correct? if question_vote is not None and question_vote.count() > 0: question_vote = question_vote[0] user_answer_votes = {} for vote in question.get_user_votes_in_answers(request.user): if not user_answer_votes.has_key(vote.object_id): vote_value = -1 if vote.is_upvote(): vote_value = 1 user_answer_votes[answer.id] = vote_value if answers is not None: answers = answers.order_by("-accepted", orderby) filtered_answers = [] for answer in answers: if answer.deleted == True: if answer.author_id == request.user.id: filtered_answers.append(answer) else: filtered_answers.append(answer) objects_list = Paginator(filtered_answers, ANSWERS_PAGE_SIZE) page_objects = objects_list.page(page) #todo: merge view counts per user and per session #1) view count per session update_view_count = False if 'question_view_times' not in request.session: request.session['question_view_times'] = {} last_seen = request.session['question_view_times'].get(question.id,None) updated_when, updated_who = question.get_last_update_info() if updated_who != request.user: if last_seen: if last_seen < updated_when: update_view_count = True else: update_view_count = True request.session['question_view_times'][question.id] = datetime.datetime.now() if update_view_count: question.view_count += 1 question.save() #2) question view count per user if request.user.is_authenticated(): try: question_view = QuestionView.objects.get(who=request.user, question=question) except QuestionView.DoesNotExist: question_view = QuestionView(who=request.user, question=question) question_view.when = datetime.datetime.now() question_view.save() return render_to_response('question.html', { "question": question, "question_vote": question_vote, "question_comment_count":question.comments.count(), "answer": answer_form, "answers": page_objects.object_list, "user_answer_votes": user_answer_votes, "tags": question.tags.all(), "tab_id": view_id, "favorited": favorited, "similar_questions": Question.objects.get_similar_questions(question), "context": { 'is_paginated': True, 'pages': objects_list.num_pages, 'page': page, 'has_previous': page_objects.has_previous(), 'has_next': page_objects.has_next(), 'previous': page_objects.previous_page_number(), 'next': page_objects.next_page_number(), 'base_url': request.path + '?sort=%s&' % view_id, 'extend_url': "#sort-top" } }, context_instance=RequestContext(request)) @login_required def close(request, id): question = get_object_or_404(Question, id=id) if not can_close_question(request.user, question): return HttpResponse('Permission denied.') if request.method == 'POST': form = CloseForm(request.POST) if form.is_valid(): reason = form.cleaned_data['reason'] question.closed = True question.closed_by = request.user question.closed_at = datetime.datetime.now() question.close_reason = reason question.save() return HttpResponseRedirect(question.get_absolute_url()) else: form = CloseForm() return render_to_response('close.html', { 'form': form, 'question': question, }, context_instance=RequestContext(request)) @login_required def reopen(request, id): question = get_object_or_404(Question, id=id) # open question if not can_reopen_question(request.user, question): return HttpResponse('Permission denied.') if request.method == 'POST': Question.objects.filter(id=question.id).update(closed=False, closed_by=None, closed_at=None, close_reason=None) return HttpResponseRedirect(question.get_absolute_url()) else: return render_to_response('reopen.html', { 'question': question, }, context_instance=RequestContext(request)) @login_required def edit_question(request, id): question = get_object_or_404(Question, id=id) if question.deleted and not can_view_deleted_post(request.user, question): raise Http404 if can_edit_post(request.user, question): return _edit_question(request, question) elif can_retag_questions(request.user): return _retag_question(request, question) else: raise Http404 def _retag_question(request, question): if request.method == 'POST': form = RetagQuestionForm(question, request.POST) if form.is_valid(): if form.has_changed(): latest_revision = question.get_latest_revision() retagged_at = datetime.datetime.now() # Update the Question itself Question.objects.filter(id=question.id).update( tagnames=form.cleaned_data['tags'], last_edited_at=retagged_at, last_edited_by=request.user, last_activity_at=retagged_at, last_activity_by=request.user ) # Update the Question's tag associations tags_updated = Question.objects.update_tags(question, form.cleaned_data['tags'], request.user) # Create a new revision QuestionRevision.objects.create( question=question, title=latest_revision.title, author=request.user, revised_at=retagged_at, tagnames=form.cleaned_data['tags'], summary=CONST['retagged'], text=latest_revision.text ) # send tags updated singal tags_updated.send(sender=question.__class__, question=question) return HttpResponseRedirect(question.get_absolute_url()) else: form = RetagQuestionForm(question) return render_to_response('question_retag.html', { 'question': question, 'form': form, 'tags': _get_tags_cache_json(), }, context_instance=RequestContext(request)) def _edit_question(request, question): latest_revision = question.get_latest_revision() revision_form = None if request.method == 'POST': if 'select_revision' in request.POST: # user has changed revistion number revision_form = RevisionForm(question, latest_revision, request.POST) if revision_form.is_valid(): # Replace with those from the selected revision form = EditQuestionForm(question, QuestionRevision.objects.get(question=question, revision=revision_form.cleaned_data['revision'])) else: form = EditQuestionForm(question, latest_revision, request.POST) else: # Always check modifications against the latest revision form = EditQuestionForm(question, latest_revision, request.POST) if form.is_valid(): html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) if form.has_changed(): edited_at = datetime.datetime.now() tags_changed = (latest_revision.tagnames != form.cleaned_data['tags']) tags_updated = False # Update the Question itself updated_fields = { 'title': form.cleaned_data['title'], 'last_edited_at': edited_at, 'last_edited_by': request.user, 'last_activity_at': edited_at, 'last_activity_by': request.user, 'tagnames': form.cleaned_data['tags'], 'summary': strip_tags(html)[:120], 'html': html, } # only save when it's checked # because wiki doesn't allow to be edited if last version has been enabled already # and we make sure this in forms. if ('wiki' in form.cleaned_data and form.cleaned_data['wiki']): updated_fields['wiki'] = True updated_fields['wikified_at'] = edited_at Question.objects.filter( id=question.id).update(** updated_fields) # Update the Question's tag associations if tags_changed: tags_updated = Question.objects.update_tags( question, form.cleaned_data['tags'], request.user) # Create a new revision revision = QuestionRevision( question=question, title=form.cleaned_data['title'], author=request.user, revised_at=edited_at, tagnames=form.cleaned_data['tags'], text=form.cleaned_data['text'], ) if form.cleaned_data['summary']: revision.summary = form.cleaned_data['summary'] else: revision.summary = 'No.%s Revision' % latest_revision.revision revision.save() return HttpResponseRedirect(question.get_absolute_url()) else: revision_form = RevisionForm(question, latest_revision) form = EditQuestionForm(question, latest_revision) return render_to_response('question_edit.html', { 'question': question, 'revision_form': revision_form, 'form': form, 'tags': _get_tags_cache_json() }, context_instance=RequestContext(request)) @login_required def edit_answer(request, id): answer = get_object_or_404(Answer, id=id) if answer.deleted and not can_view_deleted_post(request.user, answer): raise Http404 elif not can_edit_post(request.user, answer): raise Http404 else: latest_revision = answer.get_latest_revision() if request.method == "POST": if 'select_revision' in request.POST: # user has changed revistion number revision_form = RevisionForm(answer, latest_revision, request.POST) if revision_form.is_valid(): # Replace with those from the selected revision form = EditAnswerForm(answer, AnswerRevision.objects.get(answer=answer, revision=revision_form.cleaned_data['revision'])) else: form = EditAnswerForm(answer, latest_revision, request.POST) else: form = EditAnswerForm(answer, latest_revision, request.POST) if form.is_valid(): html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) if form.has_changed(): edited_at = datetime.datetime.now() updated_fields = { 'last_edited_at': edited_at, 'last_edited_by': request.user, 'html': html, } Answer.objects.filter(id=answer.id).update(** updated_fields) revision = AnswerRevision( answer=answer, author=request.user, revised_at=edited_at, text=form.cleaned_data['text'] ) if form.cleaned_data['summary']: revision.summary = form.cleaned_data['summary'] else: revision.summary = 'No.%s Revision' % latest_revision.revision revision.save() answer.question.last_activity_at = edited_at answer.question.last_activity_by = request.user answer.question.save() return HttpResponseRedirect(answer.get_absolute_url()) else: revision_form = RevisionForm(answer, latest_revision) form = EditAnswerForm(answer, latest_revision) return render_to_response('answer_edit.html', { 'answer': answer, 'revision_form': revision_form, 'form': form, }, context_instance=RequestContext(request)) QUESTION_REVISION_TEMPLATE = ('

%(title)s

\n' '
%(html)s
\n' '
%(tags)s
') def question_revisions(request, id): post = get_object_or_404(Question, id=id) revisions = list(post.revisions.all()) revisions.reverse() for i, revision in enumerate(revisions): revision.html = QUESTION_REVISION_TEMPLATE % { 'title': revision.title, 'html': sanitize_html(markdowner.convert(revision.text)), 'tags': ' '.join(['%s' % tag for tag in revision.tagnames.split(' ')]), } if i > 0: revisions[i].diff = htmldiff(revisions[i-1].html, revision.html) else: revisions[i].diff = QUESTION_REVISION_TEMPLATE % { 'title': revisions[0].title, 'html': sanitize_html(markdowner.convert(revisions[0].text)), 'tags': ' '.join(['%s' % tag for tag in revisions[0].tagnames.split(' ')]), } revisions[i].summary = _('initial version') return render_to_response('revisions_question.html', { 'post': post, 'revisions': revisions, }, context_instance=RequestContext(request)) ANSWER_REVISION_TEMPLATE = ('
%(html)s
') def answer_revisions(request, id): post = get_object_or_404(Answer, id=id) revisions = list(post.revisions.all()) revisions.reverse() for i, revision in enumerate(revisions): revision.html = ANSWER_REVISION_TEMPLATE % { 'html': sanitize_html(markdowner.convert(revision.text)) } if i > 0: revisions[i].diff = htmldiff(revisions[i-1].html, revision.html) else: revisions[i].diff = revisions[i].text revisions[i].summary = _('initial version') return render_to_response('revisions_answer.html', { 'post': post, 'revisions': revisions, }, context_instance=RequestContext(request)) @login_required def answer(request, id): question = get_object_or_404(Question, id=id) if request.method == "POST": form = AnswerForm(question, request.user, request.POST) if form.is_valid(): wiki = form.cleaned_data['wiki'] text = form.cleaned_data['text'] update_time = datetime.datetime.now() if request.user.is_authenticated(): create_new_answer( question=question, author=request.user, added_at=update_time, wiki=wiki, text=text, email_notify=form.cleaned_data['email_notify'] ) else: request.session.flush() html = sanitize_html(markdowner.convert(text)) summary = strip_tags(html)[:120] anon = AnonymousAnswer( question=question, wiki=wiki, text=text, summary=summary, session_key=request.session.session_key, ip_addr=request.META['REMOTE_ADDR'], ) anon.save() return HttpResponseRedirect(reverse('user_signin_new_answer')) return HttpResponseRedirect(question.get_absolute_url()) def tags(request): stag = "" is_paginated = True sortby = request.GET.get('sort', 'used') try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 if request.method == "GET": stag = request.GET.get("q", "").strip() if stag != '': objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE) else: if sortby == "used": sortby = "-used_count" else: sortby = "name" objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by(sortby), DEFAULT_PAGE_SIZE) try: tags = objects_list.page(page) except (EmptyPage, InvalidPage): tags = objects_list.page(objects_list.num_pages) return render_to_response('tags.html', { "tags" : tags, "stag" : stag, "tab_id" : sortby, "keywords" : stag, "context" : { 'is_paginated' : is_paginated, 'pages': objects_list.num_pages, 'page': page, 'has_previous': tags.has_previous(), 'has_next': tags.has_next(), 'previous': tags.previous_page_number(), 'next': tags.next_page_number(), 'base_url' : reverse('tags') + '?sort=%s&' % sortby } }, context_instance=RequestContext(request)) def tag(request, tag): return questions(request, tagname=tag) def vote(request, id): """ vote_type: acceptAnswer : 0, questionUpVote : 1, questionDownVote : 2, favorite : 4, answerUpVote: 5, answerDownVote:6, offensiveQuestion : 7, offensiveAnswer:8, removeQuestion: 9, removeAnswer:10 questionSubscribeUpdates:11 accept answer code: response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default response_data['success'] = 0, failed 1, Success - by default response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel) vote code: allowed = -3, Don't have enough votes left -2, Don't have enough reputation score -1, Vote his own post 0, no allowed - Anonymous 1, Allowed - by default status = 0, By default 1, Cancel 2, Vote is too old to be canceled offensive code: allowed = -3, Don't have enough flags left -2, Don't have enough reputation score to do this 0, not allowed 1, allowed status = 0, by default 1, can't do it again """ response_data = { "allowed": 1, "success": 1, "status": 0, "count": 0, "message": '' } def can_vote(vote_score, user): if vote_score == 1: return can_vote_up(request.user) else: return can_vote_down(request.user) try: if not request.user.is_authenticated(): response_data['allowed'] = 0 response_data['success'] = 0 elif request.is_ajax(): question = get_object_or_404(Question, id=id) vote_type = request.POST.get('type') #accept answer if vote_type == '0': answer_id = request.POST.get('postId') answer = get_object_or_404(Answer, id=answer_id) # make sure question author is current user if question.author == request.user: # answer user who is also question author is not allow to accept answer if answer.author == question.author: response_data['success'] = 0 response_data['allowed'] = -1 # check if answer has been accepted already elif answer.accepted: onAnswerAcceptCanceled(answer, request.user) response_data['status'] = 1 else: # set other answers in this question not accepted first for answer_of_question in Answer.objects.get_answers_from_question(question, request.user): if answer_of_question != answer and answer_of_question.accepted: onAnswerAcceptCanceled(answer_of_question, request.user) #make sure retrieve data again after above author changes, they may have related data answer = get_object_or_404(Answer, id=answer_id) onAnswerAccept(answer, request.user) else: response_data['allowed'] = 0 response_data['success'] = 0 # favorite elif vote_type == '4': has_favorited = False fav_questions = FavoriteQuestion.objects.filter(question=question) # if the same question has been favorited before, then delete it if fav_questions is not None: for item in fav_questions: if item.user == request.user: item.delete() response_data['status'] = 1 response_data['count'] = len(fav_questions) - 1 if response_data['count'] < 0: response_data['count'] = 0 has_favorited = True # if above deletion has not been executed, just insert a new favorite question if not has_favorited: new_item = FavoriteQuestion(question=question, user=request.user) new_item.save() response_data['count'] = FavoriteQuestion.objects.filter(question=question).count() Question.objects.update_favorite_count(question) elif vote_type in ['1', '2', '5', '6']: post_id = id post = question vote_score = 1 if vote_type in ['5', '6']: answer_id = request.POST.get('postId') answer = get_object_or_404(Answer, id=answer_id) post_id = answer_id post = answer if vote_type in ['2', '6']: vote_score = -1 if post.author == request.user: response_data['allowed'] = -1 elif not can_vote(vote_score, request.user): response_data['allowed'] = -2 elif post.votes.filter(user=request.user).count() > 0: vote = post.votes.filter(user=request.user)[0] # unvote should be less than certain time if (datetime.datetime.now().day - vote.voted_at.day) >= VOTE_RULES['scope_deny_unvote_days']: response_data['status'] = 2 else: voted = vote.vote if voted > 0: # cancel upvote onUpVotedCanceled(vote, post, request.user) else: # cancel downvote onDownVotedCanceled(vote, post, request.user) response_data['status'] = 1 response_data['count'] = post.score elif Vote.objects.get_votes_count_today_from_user(request.user) >= VOTE_RULES['scope_votes_per_user_per_day']: response_data['allowed'] = -3 else: vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now()) if vote_score > 0: # upvote onUpVoted(vote, post, request.user) else: # downvote onDownVoted(vote, post, request.user) votes_left = VOTE_RULES['scope_votes_per_user_per_day'] - Vote.objects.get_votes_count_today_from_user(request.user) if votes_left <= VOTE_RULES['scope_warn_votes_left']: response_data['message'] = u'%s votes left' % votes_left response_data['count'] = post.score elif vote_type in ['7', '8']: post = question post_id = id if vote_type == '8': post_id = request.POST.get('postId') post = get_object_or_404(Answer, id=post_id) if FlaggedItem.objects.get_flagged_items_count_today(request.user) >= VOTE_RULES['scope_flags_per_user_per_day']: response_data['allowed'] = -3 elif not can_flag_offensive(request.user): response_data['allowed'] = -2 elif post.flagged_items.filter(user=request.user).count() > 0: response_data['status'] = 1 else: item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now()) onFlaggedItem(item, post, request.user) response_data['count'] = post.offensive_flag_count # send signal when question or answer be marked offensive mark_offensive.send(sender=post.__class__, instance=post, mark_by=request.user) elif vote_type in ['9', '10']: post = question post_id = id if vote_type == '10': post_id = request.POST.get('postId') post = get_object_or_404(Answer, id=post_id) if not can_delete_post(request.user, post): response_data['allowed'] = -2 elif post.deleted == True: logging.debug('debug restoring post in view') onDeleteCanceled(post, request.user) response_data['status'] = 1 else: onDeleted(post, request.user) delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user) elif vote_type == '11':#subscribe q updates user = request.user if user.is_authenticated(): if user not in question.followed_by.all(): question.followed_by.add(user) if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False: response_data['message'] = \ _('subscription saved, %(email)s needs validation, see %(details_url)s') \ % {'email':user.email,'details_url':reverse('faq') + '#validate'} feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type='q_sel') if feed_setting.frequency == 'n': feed_setting.frequency = 'd' feed_setting.save() if 'message' in response_data: response_data['message'] += '
' response_data['message'] = _('email update frequency has been set to daily') #response_data['status'] = 1 #responst_data['allowed'] = 1 else: pass #response_data['status'] = 0 #response_data['allowed'] = 0 elif vote_type == '12':#unsubscribe q updates user = request.user if user.is_authenticated(): if user in question.followed_by.all(): question.followed_by.remove(user) else: response_data['success'] = 0 response_data['message'] = u'Request mode is not supported. Please try again.' data = simplejson.dumps(response_data) except Exception, e: response_data['message'] = str(e) data = simplejson.dumps(response_data) return HttpResponse(data, mimetype="application/json") @ajax_login_required def mark_tag(request, tag=None, **kwargs): action = kwargs['action'] ts = MarkedTag.objects.filter(user=request.user, tag__name=tag) if action == 'remove': logging.debug('deleting tag %s' % tag) ts.delete() else: reason = kwargs['reason'] if len(ts) == 0: try: t = Tag.objects.get(name=tag) mt = MarkedTag(user=request.user, reason=reason, tag=t) mt.save() except: pass else: ts.update(reason=reason) return HttpResponse(simplejson.dumps(''), mimetype="application/json") @ajax_login_required def ajax_toggle_ignored_questions(request): if request.user.hide_ignored_questions: new_hide_setting = False else: new_hide_setting = True request.user.hide_ignored_questions = new_hide_setting request.user.save() @ajax_method def ajax_command(request): if 'command' not in request.POST: return HttpResponseForbidden(mimetype="application/json") if request.POST['command'] == 'toggle-ignored-questions': return ajax_toggle_ignored_questions(request) def users(request): is_paginated = True sortby = request.GET.get('sort', 'reputation') suser = request.REQUEST.get('q', "") try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 if suser == "": if sortby == "newest": objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE) elif sortby == "last": objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE) elif sortby == "user": objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE) # default else: objects_list = Paginator(User.objects.all().order_by('-reputation'), USERS_PAGE_SIZE) base_url = reverse('users') + '?sort=%s&' % sortby else: sortby = "reputation" objects_list = Paginator(User.objects.extra(where=['username like %s'], params=['%' + suser + '%']).order_by('-reputation'), USERS_PAGE_SIZE) base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby) try: users = objects_list.page(page) except (EmptyPage, InvalidPage): users = objects_list.page(objects_list.num_pages) return render_to_response('users.html', { "users" : users, "suser" : suser, "keywords" : suser, "tab_id" : sortby, "context" : { 'is_paginated' : is_paginated, 'pages': objects_list.num_pages, 'page': page, 'has_previous': users.has_previous(), 'has_next': users.has_next(), 'previous': users.previous_page_number(), 'next': users.next_page_number(), 'base_url' : base_url } }, context_instance=RequestContext(request)) def user(request, id): sort = request.GET.get('sort', 'stats') user_view = dict((v.id, v) for v in USER_TEMPLATE_VIEWS).get(sort, USER_TEMPLATE_VIEWS[0]) from forum import views func = getattr(views, user_view.view_name) return func(request, id, user_view) @login_required def moderate_user(request, id): """ajax handler of user moderation """ if not auth.can_moderate_users(request.user) or request.method != 'POST': raise Http404 if not request.is_ajax(): return HttpResponseForbidden(mimetype="application/json") user = get_object_or_404(User, id=id) form = ModerateUserForm(request.POST, instance=user) if form.is_valid(): form.save() logging.debug('data saved') response = HttpResponse(simplejson.dumps(''), mimetype="application/json") else: response = HttpResponseForbidden(mimetype="application/json") return response @login_required def edit_user(request, id): user = get_object_or_404(User, id=id) if request.user != user: raise Http404 if request.method == "POST": form = EditUserForm(user, request.POST) if form.is_valid(): new_email = sanitize_html(form.cleaned_data['email']) from django_authopenid.views import set_new_email set_new_email(user, new_email) user.username = sanitize_html(form.cleaned_data['username']) user.real_name = sanitize_html(form.cleaned_data['realname']) user.website = sanitize_html(form.cleaned_data['website']) user.location = sanitize_html(form.cleaned_data['city']) user.date_of_birth = sanitize_html(form.cleaned_data['birthday']) if len(user.date_of_birth) == 0: user.date_of_birth = '1900-01-01' user.about = sanitize_html(form.cleaned_data['about']) user.save() # send user updated singal if full fields have been updated if user.email and user.real_name and user.website and user.location and \ user.date_of_birth and user.about: user_updated.send(sender=user.__class__, instance=user, updated_by=user) return HttpResponseRedirect(user.get_profile_url()) else: form = EditUserForm(user) return render_to_response('user_edit.html', { 'form' : form, 'gravatar_faq_url' : reverse('faq') + '#gravatar', }, context_instance=RequestContext(request)) def user_stats(request, user_id, user_view): user = get_object_or_404(User, id=user_id) questions = Question.objects.extra( select={ 'vote_count': 'question.score', 'favorited_myself': 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id', 'la_user_id': 'auth_user.id', 'la_username': 'auth_user.username', 'la_user_gold': 'auth_user.gold', 'la_user_silver': 'auth_user.silver', 'la_user_bronze': 'auth_user.bronze', 'la_user_reputation': 'auth_user.reputation' }, select_params=[user_id], tables=['question', 'auth_user'], where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'], params=[user_id], order_by=['-vote_count', '-last_activity_at'] ).values('vote_count', 'favorited_myself', 'id', 'title', 'author_id', 'added_at', 'answer_accepted', 'answer_count', 'comment_count', 'view_count', 'favourite_count', 'summary', 'tagnames', 'vote_up_count', 'vote_down_count', 'last_activity_at', 'la_user_id', 'la_username', 'la_user_gold', 'la_user_silver', 'la_user_bronze', 'la_user_reputation')[:100] answered_questions = Question.objects.extra( select={ 'vote_up_count' : 'answer.vote_up_count', 'vote_down_count' : 'answer.vote_down_count', 'answer_id' : 'answer.id', 'accepted' : 'answer.accepted', 'vote_count' : 'answer.score', 'comment_count' : 'answer.comment_count' }, tables=['question', 'answer'], where=['answer.deleted=0 AND question.deleted=0 AND answer.author_id=%s AND answer.question_id=question.id'], params=[user_id], order_by=['-vote_count', '-answer_id'], select_params=[user_id] ).distinct().values('comment_count', 'id', 'answer_id', 'title', 'author_id', 'accepted', 'vote_count', 'answer_count', 'vote_up_count', 'vote_down_count')[:100] up_votes = Vote.objects.get_up_vote_count_from_user(user) down_votes = Vote.objects.get_down_vote_count_from_user(user) votes_today = Vote.objects.get_votes_count_today_from_user(user) votes_total = VOTE_RULES['scope_votes_per_user_per_day'] question_id_set = set(map(lambda v: v['id'], list(questions))) \ | set(map(lambda v: v['id'], list(answered_questions))) user_tags = Tag.objects.filter(questions__id__in = question_id_set) try: from django.db.models import Count awards = Award.objects.extra( select={'id': 'badge.id', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, tables=['award', 'badge'], order_by=['-awarded_at'], where=['user_id=%s AND badge_id=badge.id'], params=[user.id] ).values('id', 'name', 'description', 'type') total_awards = awards.count() awards = awards.annotate(count = Count('badge__id')) user_tags = user_tags.annotate(user_tag_usage_count=Count('name')) except ImportError: awards = Award.objects.extra( select={'id': 'badge.id', 'count': 'count(badge_id)', 'name':'badge.name', 'description': 'badge.description', 'type': 'badge.type'}, tables=['award', 'badge'], order_by=['-awarded_at'], where=['user_id=%s AND badge_id=badge.id'], params=[user.id] ).values('id', 'count', 'name', 'description', 'type') total_awards = awards.count() awards.query.group_by = ['badge_id'] user_tags = user_tags.extra( select={'user_tag_usage_count': 'COUNT(1)',}, order_by=['-user_tag_usage_count'], ) user_tags.query.group_by = ['name'] if auth.can_moderate_users(request.user): moderate_user_form = ModerateUserForm(instance=user) else: moderate_user_form = None return render_to_response(user_view.template_file,{ 'moderate_user_form': moderate_user_form, "tab_name" : user_view.id, "tab_description" : user_view.tab_description, "page_title" : user_view.page_title, "view_user" : user, "questions" : questions, "answered_questions" : answered_questions, "up_votes" : up_votes, "down_votes" : down_votes, "total_votes": up_votes + down_votes, "votes_today_left": votes_total-votes_today, "votes_total_per_day": votes_total, "user_tags" : user_tags[:50], "tags" : tags, "awards": awards, "total_awards" : total_awards, }, context_instance=RequestContext(request)) def user_recent(request, user_id, user_view): user = get_object_or_404(User, id=user_id) def get_type_name(type_id): for item in TYPE_ACTIVITY: if type_id in item: return item[1] class Event: def __init__(self, time, type, title, summary, answer_id, question_id): self.time = time self.type = get_type_name(type) self.type_id = type self.title = title self.summary = summary slug_title = slugify(title) self.title_link = reverse('question', kwargs={'id':question_id}) + u'%s' % slug_title if int(answer_id) > 0: self.title_link += '#%s' % answer_id class AwardEvent: def __init__(self, time, type, id): self.time = time self.type = get_type_name(type) self.type_id = type self.badge = get_object_or_404(Badge, id=id) activities = [] # ask questions questions = Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', 'active_at' : 'activity.active_at', 'activity_type' : 'activity.activity_type' }, tables=['activity', 'question'], where=['activity.content_type_id = %s AND activity.object_id = ' + 'question.id AND question.deleted=0 AND activity.user_id = %s AND activity.activity_type = %s'], params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION], order_by=['-activity.active_at'] ).values( 'title', 'question_id', 'active_at', 'activity_type' ) if len(questions) > 0: questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \ q['question_id'])) for q in questions] activities.extend(questions) # answers answers = Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', 'answer_id' : 'answer.id', 'active_at' : 'activity.active_at', 'activity_type' : 'activity.activity_type' }, tables=['activity', 'answer', 'question'], where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' + 'answer.question_id=question.id AND answer.deleted=0 AND activity.user_id=%s AND '+ 'activity.activity_type=%s AND question.deleted=0'], params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER], order_by=['-activity.active_at'] ).values( 'title', 'question_id', 'answer_id', 'active_at', 'activity_type' ) if len(answers) > 0: answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \ q['question_id'])) for q in answers] activities.extend(answers) # question comments comments = Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'comment.object_id', 'added_at' : 'comment.added_at', 'activity_type' : 'activity.activity_type' }, tables=['activity', 'question', 'comment'], where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+ 'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+ 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' + 'question.deleted=0'], params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION], order_by=['-comment.added_at'] ).values( 'title', 'question_id', 'added_at', 'activity_type' ) if len(comments) > 0: comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \ q['question_id'])) for q in comments] activities.extend(comments) # answer comments comments = Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', 'answer_id' : 'answer.id', 'added_at' : 'comment.added_at', 'activity_type' : 'activity.activity_type' }, tables=['activity', 'question', 'answer', 'comment'], where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+ 'activity.user_id = comment.user_id AND comment.object_id=answer.id AND '+ 'comment.content_type_id=%s AND question.id = answer.question_id AND '+ 'activity.user_id = %s AND activity.activity_type=%s AND '+ 'answer.deleted=0 AND question.deleted=0'], params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER], order_by=['-comment.added_at'] ).values( 'title', 'question_id', 'answer_id', 'added_at', 'activity_type' ) if len(comments) > 0: comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', q['answer_id'], \ q['question_id'])) for q in comments] activities.extend(comments) # question revisions revisions = Activity.objects.extra( select={ 'title' : 'question_revision.title', 'question_id' : 'question_revision.question_id', 'added_at' : 'activity.active_at', 'activity_type' : 'activity.activity_type', 'summary' : 'question_revision.summary' }, tables=['activity', 'question_revision', 'question'], where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+ 'question_revision.id=question.id AND question.deleted=0 AND '+ 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+ 'activity.activity_type=%s'], params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION], order_by=['-activity.active_at'] ).values( 'title', 'question_id', 'added_at', 'activity_type', 'summary' ) if len(revisions) > 0: revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], '0', \ q['question_id'])) for q in revisions] activities.extend(revisions) # answer revisions revisions = Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', 'answer_id' : 'answer.id', 'added_at' : 'activity.active_at', 'activity_type' : 'activity.activity_type', 'summary' : 'answer_revision.summary' }, tables=['activity', 'answer_revision', 'question', 'answer'], where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+ 'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+ 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+ 'question.deleted=0 AND answer.deleted=0 AND '+ 'activity.activity_type=%s'], params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER], order_by=['-activity.active_at'] ).values( 'title', 'question_id', 'added_at', 'answer_id', 'activity_type', 'summary' ) if len(revisions) > 0: revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], \ q['answer_id'], q['question_id'])) for q in revisions] activities.extend(revisions) # accepted answers accept_answers = Activity.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', 'added_at' : 'activity.active_at', 'activity_type' : 'activity.activity_type', }, tables=['activity', 'answer', 'question'], where=['activity.content_type_id = %s AND activity.object_id = answer.id AND '+ 'activity.user_id = question.author_id AND activity.user_id = %s AND '+ 'answer.deleted=0 AND question.deleted=0 AND '+ 'answer.question_id=question.id AND activity.activity_type=%s'], params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER], order_by=['-activity.active_at'] ).values( 'title', 'question_id', 'added_at', 'activity_type', ) if len(accept_answers) > 0: accept_answers = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \ q['question_id'])) for q in accept_answers] activities.extend(accept_answers) #award history awards = Activity.objects.extra( select={ 'badge_id': 'badge.id', 'awarded_at': 'award.awarded_at', 'activity_type': 'activity.activity_type' }, tables=['activity', 'award', 'badge'], where=['activity.user_id = award.user_id AND activity.user_id = %s AND ' + 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'], params=[user_id, TYPE_ACTIVITY_PRIZE], order_by=['-activity.active_at'] ).values( 'badge_id', 'awarded_at', 'activity_type' ) if len(awards) > 0: awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards] activities.extend(awards) activities.sort(lambda x, y: cmp(y.time, x.time)) return render_to_response(user_view.template_file,{ "tab_name" : user_view.id, "tab_description" : user_view.tab_description, "page_title" : user_view.page_title, "view_user" : user, "activities" : activities[:user_view.data_size] }, context_instance=RequestContext(request)) def user_responses(request, user_id, user_view): """ We list answers for question, comments, and answer accepted by others for this user. """ class Response: def __init__(self, type, title, question_id, answer_id, time, username, user_id, content): self.type = type self.title = title self.titlelink = reverse('question', args=[question_id]) + u'%s#%s' % (slugify(title), answer_id) self.time = time self.userlink = reverse('users') + u'%s/%s/' % (user_id, username) self.username = username self.content = u'%s ...' % strip_tags(content)[:300] def __unicode__(self): return u'%s %s' % (self.type, self.titlelink) user = get_object_or_404(User, id=user_id) responses = [] answers = Answer.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'question.id', 'answer_id' : 'answer.id', 'added_at' : 'answer.added_at', 'html' : 'answer.html', 'username' : 'auth_user.username', 'user_id' : 'auth_user.id' }, select_params=[user_id], tables=['answer', 'question', 'auth_user'], where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+ 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'], params=[user_id, user_id], order_by=['-answer.id'] ).values( 'title', 'question_id', 'answer_id', 'added_at', 'html', 'username', 'user_id' ) if len(answers) > 0: answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'], a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] responses.extend(answers) # question comments comments = Comment.objects.extra( select={ 'title' : 'question.title', 'question_id' : 'comment.object_id', 'added_at' : 'comment.added_at', 'comment' : 'comment.comment', 'username' : 'auth_user.username', 'user_id' : 'auth_user.id' }, tables=['question', 'auth_user', 'comment'], where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+ 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'], params=[user_id, question_type_id, user_id], order_by=['-comment.added_at'] ).values( 'title', 'question_id', 'added_at', 'comment', 'username', 'user_id' ) if len(comments) > 0: comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'], '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] responses.extend(comments) # answer comments comments = Comment.objects.extra( select={ 'title': 'question.title', 'question_id': 'question.id', 'answer_id': 'answer.id', 'added_at': 'comment.added_at', 'comment': 'comment.comment', 'username': 'auth_user.username', 'user_id': 'auth_user.id' }, tables=['answer', 'auth_user', 'comment', 'question'], where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND ' + 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id ' + 'AND question.id = answer.question_id'], params=[user_id, answer_type_id, user_id], order_by=['-comment.added_at'] ).values( 'title', 'question_id', 'answer_id', 'added_at', 'comment', 'username', 'user_id' ) if len(comments) > 0: comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'], c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments] responses.extend(comments) # answer has been accepted answers = Answer.objects.extra( select={ 'title': 'question.title', 'question_id': 'question.id', 'answer_id': 'answer.id', 'added_at': 'answer.accepted_at', 'html': 'answer.html', 'username': 'auth_user.username', 'user_id': 'auth_user.id' }, select_params=[user_id], tables=['answer', 'question', 'auth_user'], where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND ' + 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'], params=[user_id], order_by=['-answer.id'] ).values( 'title', 'question_id', 'answer_id', 'added_at', 'html', 'username', 'user_id' ) if len(answers) > 0: answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'], a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers] responses.extend(answers) # sort posts by time responses.sort(lambda x, y: cmp(y.time, x.time)) return render_to_response(user_view.template_file, { "tab_name": user_view.id, "tab_description": user_view.tab_description, "page_title": user_view.page_title, "view_user": user, "responses": responses[:user_view.data_size], }, context_instance=RequestContext(request)) def user_votes(request, user_id, user_view): user = get_object_or_404(User, id=user_id) if not can_view_user_votes(request.user, user): raise Http404 votes = [] question_votes = Vote.objects.extra( select={ 'title': 'question.title', 'question_id': 'question.id', 'answer_id': 0, 'voted_at': 'vote.voted_at', 'vote': 'vote', }, select_params=[user_id], tables=['vote', 'question', 'auth_user'], where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id ' + 'AND vote.user_id=auth_user.id'], params=[question_type_id, user_id], order_by=['-vote.id'] ).values( 'title', 'question_id', 'answer_id', 'voted_at', 'vote', ) if(len(question_votes) > 0): votes.extend(question_votes) answer_votes = Vote.objects.extra( select={ 'title': 'question.title', 'question_id': 'question.id', 'answer_id': 'answer.id', 'voted_at': 'vote.voted_at', 'vote': 'vote', }, select_params=[user_id], tables=['vote', 'answer', 'question', 'auth_user'], where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id ' + 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'], params=[answer_type_id, user_id], order_by=['-vote.id'] ).values( 'title', 'question_id', 'answer_id', 'voted_at', 'vote', ) if(len(answer_votes) > 0): votes.extend(answer_votes) votes.sort(lambda x, y: cmp(y['voted_at'], x['voted_at'])) return render_to_response(user_view.template_file, { "tab_name": user_view.id, "tab_description": user_view.tab_description, "page_title": user_view.page_title, "view_user": user, "votes": votes[:user_view.data_size] }, context_instance=RequestContext(request)) def user_reputation(request, user_id, user_view): user = get_object_or_404(User, id=user_id) try: from django.db.models import Sum reputation = Repute.objects.extra( select={'question_id':'question_id', 'title': 'question.title'}, tables=['repute', 'question'], order_by=['-reputed_at'], where=['user_id=%s AND question_id=question.id'], params=[user.id] ).values('question_id', 'title', 'reputed_at', 'reputation') reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative")) except ImportError: reputation = Repute.objects.extra( select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id', 'title': 'question.title'}, tables=['repute', 'question'], order_by=['-reputed_at'], where=['user_id=%s AND question_id=question.id'], params=[user.id] ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation') reputation.query.group_by = ['question_id'] rep_list = [] for rep in Repute.objects.filter(user=user).order_by('reputed_at'): dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation) rep_list.append(dic) reps = ','.join(rep_list) reps = '[%s]' % reps return render_to_response(user_view.template_file, { "tab_name": user_view.id, "tab_description": user_view.tab_description, "page_title": user_view.page_title, "view_user": user, "reputation": reputation, "reps": reps }, context_instance=RequestContext(request)) def user_favorites(request, user_id, user_view): user = get_object_or_404(User, id=user_id) questions = Question.objects.extra( select={ 'vote_count': 'question.vote_up_count + question.vote_down_count', 'favorited_myself': 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s ' + 'AND f.question_id = question.id', 'la_user_id': 'auth_user.id', 'la_username': 'auth_user.username', 'la_user_gold': 'auth_user.gold', 'la_user_silver': 'auth_user.silver', 'la_user_bronze': 'auth_user.bronze', 'la_user_reputation': 'auth_user.reputation' }, select_params=[user_id], tables=['question', 'auth_user', 'favorite_question'], where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id ' + 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'], params=[user_id], order_by=['-vote_count', '-question.id'] ).values('vote_count', 'favorited_myself', 'id', 'title', 'author_id', 'added_at', 'answer_accepted', 'answer_count', 'comment_count', 'view_count', 'favourite_count', 'summary', 'tagnames', 'vote_up_count', 'vote_down_count', 'last_activity_at', 'la_user_id', 'la_username', 'la_user_gold', 'la_user_silver', 'la_user_bronze', 'la_user_reputation') return render_to_response(user_view.template_file, { "tab_name": user_view.id, "tab_description": user_view.tab_description, "page_title": user_view.page_title, "questions": questions[:user_view.data_size], "view_user": user }, context_instance=RequestContext(request)) def user_email_subscriptions(request, user_id, user_view): user = get_object_or_404(User, id=user_id) if request.method == 'POST': email_feeds_form = EditUserEmailFeedsForm(request.POST) tag_filter_form = TagFilterSelectionForm(request.POST, instance=user) if email_feeds_form.is_valid() and tag_filter_form.is_valid(): action_status = None tag_filter_saved = tag_filter_form.save() if tag_filter_saved: action_status = _('changes saved') if 'save' in request.POST: feeds_saved = email_feeds_form.save(user) if feeds_saved: action_status = _('changes saved') elif 'stop_email' in request.POST: email_stopped = email_feeds_form.reset().save(user) initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL email_feeds_form = EditUserEmailFeedsForm(initial=initial_values) if email_stopped: action_status = _('email updates canceled') else: email_feeds_form = EditUserEmailFeedsForm() email_feeds_form.set_initial_values(user) tag_filter_form = TagFilterSelectionForm(instance=user) action_status = None return render_to_response(user_view.template_file,{ 'tab_name':user_view.id, 'tab_description':user_view.tab_description, 'page_title':user_view.page_title, 'view_user':user, 'email_feeds_form':email_feeds_form, 'tag_filter_selection_form':tag_filter_form, 'action_status':action_status, }, context_instance=RequestContext(request)) def question_comments(request, id): question = get_object_or_404(Question, id=id) user = request.user return __comments(request, question, 'question') def answer_comments(request, id): answer = get_object_or_404(Answer, id=id) user = request.user return __comments(request, answer, 'answer') def __comments(request, obj, type): # only support get comments by ajax now user = request.user if request.is_ajax(): if request.method == "GET": response = __generate_comments_json(obj, type, user) elif request.method == "POST": if auth.can_add_comments(user,obj): comment_data = request.POST.get('comment') comment = Comment(content_object=obj, comment=comment_data, user=request.user) comment.save() obj.comment_count = obj.comment_count + 1 obj.save() response = __generate_comments_json(obj, type, user) else: response = HttpResponseForbidden(mimetype="application/json") return response def __generate_comments_json(obj, type, user): comments = obj.comments.all().order_by('id') # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null} json_comments = [] from forum.templatetags.extra_tags import diff_date for comment in comments: comment_user = comment.user delete_url = "" if user != None and auth.can_delete_comment(user, comment): #/posts/392845/comments/219852/delete #todo translate this url delete_url = reverse(index) + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id) json_comments.append({"id" : comment.id, "object_id" : obj.id, "comment_age" : diff_date(comment.added_at), "text" : comment.comment, "user_display_name" : comment_user.username, "user_url" : comment_user.get_profile_url(), "delete_url" : delete_url }) data = simplejson.dumps(json_comments) return HttpResponse(data, mimetype="application/json") def delete_comment(request, object_id='', comment_id='', commented_object_type=None): response = None commented_object = None if commented_object_type == 'question': commented_object = Question elif commented_object_type == 'answer': commented_object = Answer if request.is_ajax(): comment = get_object_or_404(Comment, id=comment_id) if auth.can_delete_comment(request.user, comment): obj = get_object_or_404(commented_object, id=object_id) obj.comments.remove(comment) obj.comment_count = obj.comment_count - 1 obj.save() user = request.user return __generate_comments_json(obj, commented_object_type, user) raise PermissionDenied() def logout(request): return render_to_response('logout.html', { 'next' : get_next_url(request), }, context_instance=RequestContext(request)) def badges(request): badges = Badge.objects.all().order_by('type') my_badges = [] if request.user.is_authenticated(): my_badges = Award.objects.filter(user=request.user) my_badges.query.group_by = ['badge_id'] return render_to_response('badges.html', { 'badges' : badges, 'mybadges' : my_badges, 'feedback_faq_url' : reverse('feedback'), }, context_instance=RequestContext(request)) def badge(request, id): badge = get_object_or_404(Badge, id=id) awards = Award.objects.extra( select={'id': 'auth_user.id', 'name': 'auth_user.username', 'rep':'auth_user.reputation', 'gold': 'auth_user.gold', 'silver': 'auth_user.silver', 'bronze': 'auth_user.bronze'}, tables=['award', 'auth_user'], where=['badge_id=%s AND user_id=auth_user.id'], params=[id] ).values('id').distinct() return render_to_response('badge.html', { 'awards': awards, 'badge': badge, }, context_instance=RequestContext(request)) def read_message(request): if request.method == "POST": if request.POST['formdata'] == 'required': request.session['message_silent'] = 1 if request.user.is_authenticated(): request.user.delete_messages() return HttpResponse('') def upload(request): class FileTypeNotAllow(Exception): pass class FileSizeNotAllow(Exception): pass class UploadPermissionNotAuthorized(Exception): pass #%s xml_template = "%s" try: f = request.FILES['file-upload'] # check upload permission if not can_upload_files(request.user): raise UploadPermissionNotAuthorized # check file type file_name_suffix = os.path.splitext(f.name)[1].lower() if not file_name_suffix in settings.ALLOW_FILE_TYPES: raise FileTypeNotAllow # generate new file name new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix # use default storage to store file default_storage.save(new_file_name, f) # check file size # byte size = default_storage.size(new_file_name) if size > settings.ALLOW_MAX_FILE_SIZE: default_storage.delete(new_file_name) raise FileSizeNotAllow result = xml_template % ('Good', '', default_storage.url(new_file_name)) except UploadPermissionNotAuthorized: result = xml_template % ('', _('uploading images is limited to users with >60 reputation points'), '') except FileTypeNotAllow: result = xml_template % ('', _("allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"), '') except FileSizeNotAllow: result = xml_template % ('', _("maximum upload file size is %sK") % settings.ALLOW_MAX_FILE_SIZE / 1024, '') except Exception: result = xml_template % ('', _('Error uploading file. Please contact the site administrator. Thank you. %s' % Exception), '') return HttpResponse(result, mimetype="application/xml") def books(request): return HttpResponseRedirect(reverse('books') + '/mysql-zhaoyang') def book(request, short_name, unanswered=False): """ 1. questions list 2. book info 3. author info and blog rss items """ """ List of Questions, Tagged questions, and Unanswered questions. """ books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) match_count = len(books) if match_count == 0: raise Http404 else: # the book info book = books[0] # get author info author_info = BookAuthorInfo.objects.get(book=book) # get author rss info author_rss = BookAuthorRss.objects.filter(book=book) # get pagesize from session, if failed then get default value user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE) # set pagesize equal to logon user specified value in database if request.user.is_authenticated() and request.user.questions_per_page > 0: user_page_size = request.user.questions_per_page try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 view_id = request.GET.get('sort', None) view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"} try: orderby = view_dic[view_id] except KeyError: view_id = "latest" orderby = "-added_at" # check if request is from tagged questions if unanswered: # check if request is from unanswered questions # Article.objects.filter(publications__id__exact=1) objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby) else: objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby) # RISK - inner join queries objects = objects.select_related(); objects_list = Paginator(objects, user_page_size) questions = objects_list.page(page) return render_to_response('book.html', { "book": book, "author_info": author_info, "author_rss": author_rss, "questions": questions, "context": { 'is_paginated': True, 'pages': objects_list.num_pages, 'page': page, 'has_previous': questions.has_previous(), 'has_next': questions.has_next(), 'previous': questions.previous_page_number(), 'next': questions.next_page_number(), 'base_url': request.path + '?sort=%s&' % view_id, 'pagesize': user_page_size } }, context_instance=RequestContext(request)) @login_required def ask_book(request, short_name): if request.method == "POST": form = AskForm(request.POST) if form.is_valid(): added_at = datetime.datetime.now() html = sanitize_html(markdowner.convert(form.cleaned_data['text'])) question = Question( title=strip_tags(form.cleaned_data['title']), author=request.user, added_at=added_at, last_activity_at=added_at, last_activity_by=request.user, wiki=form.cleaned_data['wiki'], tagnames=form.cleaned_data['tags'].strip(), html=html, summary=strip_tags(html)[:120] ) if question.wiki: question.last_edited_by = question.author question.last_edited_at = added_at question.wikified_at = added_at question.save() # create the first revision QuestionRevision.objects.create( question=question, revision=1, title=question.title, author=request.user, revised_at=added_at, tagnames=question.tagnames, summary=CONST['default_version'], text=form.cleaned_data['text'] ) books = Book.objects.extra(where=['short_name = %s'], params=[short_name]) match_count = len(books) if match_count == 1: # the book info book = books[0] book.questions.add(question) return HttpResponseRedirect(question.get_absolute_url()) else: form = AskForm() tags = _get_tags_cache_json() return render_to_response('ask.html', { 'form' : form, 'tags' : tags, 'email_validation_faq_url': reverse('faq') + '#validate', }, context_instance=RequestContext(request)) def search(request): """ Search by question, user and tag keywords. For questions now we only search keywords in question title. """ if request.method == "GET": keywords = request.GET.get("q") search_type = request.GET.get("t") try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 if keywords is None: return HttpResponseRedirect(reverse(index)) if search_type == 'tag': return HttpResponseRedirect(reverse('tags') + '?q=%s&page=%s' % (keywords.strip(), page)) elif search_type == "user": return HttpResponseRedirect(reverse('users') + '?q=%s&page=%s' % (keywords.strip(), page)) elif search_type == "question": template_file = "questions.html" # Set flag to False by default. If it is equal to True, then need to be saved. pagesize_changed = False # get pagesize from session, if failed then get default value user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE) # set pagesize equal to logon user specified value in database if request.user.is_authenticated() and request.user.questions_per_page > 0: user_page_size = request.user.questions_per_page try: page = int(request.GET.get('page', '1')) # get new pagesize from UI selection pagesize = int(request.GET.get('pagesize', user_page_size)) if pagesize <> user_page_size: pagesize_changed = True except ValueError: page = 1 pagesize = user_page_size # save this pagesize to user database if pagesize_changed: request.session["pagesize"] = pagesize if request.user.is_authenticated(): user = request.user user.questions_per_page = pagesize user.save() view_id = request.GET.get('sort', None) view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"} try: orderby = view_dic[view_id] except KeyError: view_id = "latest" orderby = "-added_at" if settings.USE_SPHINX_SEARCH == True: #search index is now free of delete questions and answers #so there is not "antideleted" filtering here objects = Question.search.query(keywords) #no related selection either because we're relying on full text search here else: objects = Question.objects.filter(deleted=False).extra(where=['title like %s'], params=['%' + keywords + '%']).order_by(orderby) # RISK - inner join queries objects = objects.select_related(); objects_list = Paginator(objects, pagesize) questions = objects_list.page(page) # Get related tags from this page objects related_tags = [] for question in questions.object_list: tags = list(question.tags.all()) for tag in tags: if tag not in related_tags: related_tags.append(tag) return render_to_response(template_file, { "questions": questions, "tab_id": view_id, "questions_count": objects_list.count, "tags": related_tags, "searchtag": None, "searchtitle": keywords, "keywords": keywords, "is_unanswered": False, "context": { 'is_paginated': True, 'pages': objects_list.num_pages, 'page': page, 'has_previous': questions.has_previous(), 'has_next': questions.has_next(), 'previous': questions.previous_page_number(), 'next': questions.next_page_number(), 'base_url': request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id), 'pagesize': pagesize }}, context_instance=RequestContext(request)) else: raise Http404