summaryrefslogtreecommitdiffstats
path: root/forum
diff options
context:
space:
mode:
authorJoseph Turian <turian@gmail.com>2010-02-07 22:57:47 -0500
committerJoseph Turian <turian@gmail.com>2010-02-07 22:57:47 -0500
commitabc5fe0302f513848ec50dd8e8cbf4754bb7f841 (patch)
tree076c17d4477be103e2f0df5539eeabdb42e68960 /forum
parentdbc5b04fb67a9441d02fec59222c993b0d4297ab (diff)
downloadaskbot-abc5fe0302f513848ec50dd8e8cbf4754bb7f841.tar.gz
askbot-abc5fe0302f513848ec50dd8e8cbf4754bb7f841.tar.bz2
askbot-abc5fe0302f513848ec50dd8e8cbf4754bb7f841.zip
Moved applications into apps/ directory
Diffstat (limited to 'forum')
-rw-r--r--forum/__init__.py0
-rw-r--r--forum/admin.py74
-rw-r--r--forum/auth.py498
-rw-r--r--forum/const.py92
-rw-r--r--forum/diff.py66
-rw-r--r--forum/feed.py43
-rw-r--r--forum/forms.py355
-rw-r--r--forum/forms.py.orig352
-rw-r--r--forum/management/__init__.py0
-rw-r--r--forum/management/commands/__init__.py0
-rw-r--r--forum/management/commands/base_command.py35
-rw-r--r--forum/management/commands/clean_award_badges.py59
-rw-r--r--forum/management/commands/message_to_everyone.py12
-rw-r--r--forum/management/commands/multi_award_badges.py348
-rw-r--r--forum/management/commands/once_award_badges.py350
-rw-r--r--forum/management/commands/sample_command.py7
-rw-r--r--forum/management/commands/send_email_alerts.py192
-rw-r--r--forum/management/commands/subscribe_everyone.py32
-rw-r--r--forum/managers.py241
-rw-r--r--forum/models.py949
-rw-r--r--forum/sitemap.py14
-rw-r--r--forum/templatetags/__init__.py0
-rw-r--r--forum/templatetags/extra_filters.py98
-rw-r--r--forum/templatetags/extra_tags.py351
-rw-r--r--forum/templatetags/smart_if.py401
-rw-r--r--forum/urls.py92
-rw-r--r--forum/user.py74
-rw-r--r--forum/views.py2412
28 files changed, 0 insertions, 7147 deletions
diff --git a/forum/__init__.py b/forum/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/forum/__init__.py
+++ /dev/null
diff --git a/forum/admin.py b/forum/admin.py
deleted file mode 100644
index 482da048..00000000
--- a/forum/admin.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from django.contrib import admin
-from models import *
-
-
-class AnonymousQuestionAdmin(admin.ModelAdmin):
- """AnonymousQuestion admin class"""
-
-class QuestionAdmin(admin.ModelAdmin):
- """Question admin class"""
-
-class TagAdmin(admin.ModelAdmin):
- """Tag admin class"""
-
-class Answerdmin(admin.ModelAdmin):
- """Answer admin class"""
-
-class CommentAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class VoteAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class FlaggedItemAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class FavoriteQuestionAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class QuestionRevisionAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class AnswerRevisionAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class AwardAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class BadgeAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class ReputeAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class ActivityAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class BookAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class BookAuthorInfoAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class BookAuthorRssAdmin(admin.ModelAdmin):
- """ admin class"""
-
-
-admin.site.register(Question, QuestionAdmin)
-admin.site.register(Tag, TagAdmin)
-admin.site.register(Answer, Answerdmin)
-admin.site.register(Comment, CommentAdmin)
-admin.site.register(Vote, VoteAdmin)
-admin.site.register(FlaggedItem, FlaggedItemAdmin)
-admin.site.register(FavoriteQuestion, FavoriteQuestionAdmin)
-admin.site.register(QuestionRevision, QuestionRevisionAdmin)
-admin.site.register(AnswerRevision, AnswerRevisionAdmin)
-admin.site.register(Badge, BadgeAdmin)
-admin.site.register(Award, AwardAdmin)
-admin.site.register(Repute, ReputeAdmin)
-admin.site.register(Activity, ActivityAdmin)
-admin.site.register(Book, BookAdmin)
-admin.site.register(BookAuthorInfo, BookAuthorInfoAdmin)
-admin.site.register(BookAuthorRss, BookAuthorRssAdmin)
diff --git a/forum/auth.py b/forum/auth.py
deleted file mode 100644
index 1569482f..00000000
--- a/forum/auth.py
+++ /dev/null
@@ -1,498 +0,0 @@
-"""
-Authorisation related functions.
-
-The actions a User is authorised to perform are dependent on their reputation
-and superuser status.
-"""
-import datetime
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
-from django.db import transaction
-from models import Repute
-from models import Question
-from models import Answer
-from const import TYPE_REPUTATION
-import logging
-question_type = ContentType.objects.get_for_model(Question)
-answer_type = ContentType.objects.get_for_model(Answer)
-
-VOTE_UP = 15
-FLAG_OFFENSIVE = 15
-POST_IMAGES = 15
-LEAVE_COMMENTS = 50
-UPLOAD_FILES = 60
-VOTE_DOWN = 100
-CLOSE_OWN_QUESTIONS = 250
-RETAG_OTHER_QUESTIONS = 500
-REOPEN_OWN_QUESTIONS = 500
-EDIT_COMMUNITY_WIKI_POSTS = 750
-EDIT_OTHER_POSTS = 2000
-DELETE_COMMENTS = 2000
-VIEW_OFFENSIVE_FLAGS = 2000
-DISABLE_URL_NOFOLLOW = 2000
-CLOSE_OTHER_QUESTIONS = 3000
-LOCK_POSTS = 4000
-
-VOTE_RULES = {
- 'scope_votes_per_user_per_day' : 30, # how many votes of one user has everyday
- 'scope_flags_per_user_per_day' : 5, # how many times user can flag posts everyday
- 'scope_warn_votes_left' : 10, # start when to warn user how many votes left
- 'scope_deny_unvote_days' : 1, # if 1 days passed, user can't cancel votes.
- 'scope_flags_invisible_main_page' : 3, # post doesn't show on main page if has more than 3 offensive flags
- 'scope_flags_delete_post' : 5, # post will be deleted if it has more than 5 offensive flags
-}
-
-REPUTATION_RULES = {
- 'initial_score' : 1,
- 'scope_per_day_by_upvotes' : 200,
- 'gain_by_upvoted' : 10,
- 'gain_by_answer_accepted' : 15,
- 'gain_by_accepting_answer' : 2,
- 'gain_by_downvote_canceled' : 2,
- 'gain_by_canceling_downvote' : 1,
- 'lose_by_canceling_accepted_answer' : -2,
- 'lose_by_accepted_answer_cancled' : -15,
- 'lose_by_downvoted' : -2,
- 'lose_by_flagged' : -2,
- 'lose_by_downvoting' : -1,
- 'lose_by_flagged_lastrevision_3_times': -30,
- 'lose_by_flagged_lastrevision_5_times': -100,
- 'lose_by_upvote_canceled' : -10,
-}
-
-def can_moderate_users(user):
- return user.is_superuser
-
-def can_vote_up(user):
- """Determines if a User can vote Questions and Answers up."""
- return user.is_authenticated() and (
- user.reputation >= VOTE_UP or
- user.is_superuser)
-
-def can_flag_offensive(user):
- """Determines if a User can flag Questions and Answers as offensive."""
- return user.is_authenticated() and (
- user.reputation >= FLAG_OFFENSIVE or
- user.is_superuser)
-
-def can_add_comments(user,subject):
- """Determines if a User can add comments to Questions and Answers."""
- if user.is_authenticated():
- if user.id == subject.author.id:
- return True
- if user.reputation >= LEAVE_COMMENTS:
- return True
- if user.is_superuser:
- return True
- if isinstance(subject,Answer) and subject.question.author.id == user.id:
- return True
- return False
-
-def can_vote_down(user):
- """Determines if a User can vote Questions and Answers down."""
- return user.is_authenticated() and (
- user.reputation >= VOTE_DOWN or
- user.is_superuser)
-
-def can_retag_questions(user):
- """Determines if a User can retag Questions."""
- return user.is_authenticated() and (
- RETAG_OTHER_QUESTIONS <= user.reputation < EDIT_OTHER_POSTS or
- user.is_superuser)
-
-def can_edit_post(user, post):
- """Determines if a User can edit the given Question or Answer."""
- return user.is_authenticated() and (
- user.id == post.author_id or
- (post.wiki and user.reputation >= EDIT_COMMUNITY_WIKI_POSTS) or
- user.reputation >= EDIT_OTHER_POSTS or
- user.is_superuser)
-
-def can_delete_comment(user, comment):
- """Determines if a User can delete the given Comment."""
- return user.is_authenticated() and (
- user.id == comment.user_id or
- user.reputation >= DELETE_COMMENTS or
- user.is_superuser)
-
-def can_view_offensive_flags(user):
- """Determines if a User can view offensive flag counts."""
- return user.is_authenticated() and (
- user.reputation >= VIEW_OFFENSIVE_FLAGS or
- user.is_superuser)
-
-def can_close_question(user, question):
- """Determines if a User can close the given Question."""
- return user.is_authenticated() and (
- (user.id == question.author_id and
- user.reputation >= CLOSE_OWN_QUESTIONS) or
- user.reputation >= CLOSE_OTHER_QUESTIONS or
- user.is_superuser)
-
-def can_lock_posts(user):
- """Determines if a User can lock Questions or Answers."""
- return user.is_authenticated() and (
- user.reputation >= LOCK_POSTS or
- user.is_superuser)
-
-def can_follow_url(user):
- """Determines if the URL link can be followed by Google search engine."""
- return user.reputation >= DISABLE_URL_NOFOLLOW
-
-def can_accept_answer(user, question, answer):
- return (user.is_authenticated() and
- question.author != answer.author and
- question.author == user) or user.is_superuser
-
-# now only support to reopen own question except superuser
-def can_reopen_question(user, question):
- return (user.is_authenticated() and
- user.id == question.author_id and
- user.reputation >= REOPEN_OWN_QUESTIONS) or user.is_superuser
-
-def can_delete_post(user, post):
- if user.is_superuser:
- return True
- elif user.is_authenticated() and user == post.author:
- if isinstance(post,Answer):
- return True
- elif isinstance(post,Question):
- answers = post.answers.all()
- for answer in answers:
- if user != answer.author and answer.deleted == False:
- return False
- return True
- else:
- return False
- else:
- return False
-
-def can_view_deleted_post(user, post):
- return user.is_superuser
-
-# user preferences view permissions
-def is_user_self(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_view_user_votes(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_view_user_preferences(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_view_user_edit(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_upload_files(request_user):
- return (request_user.is_authenticated() and request_user.reputation >= UPLOAD_FILES) or \
- request_user.is_superuser
-
-###########################################
-## actions and reputation changes event
-###########################################
-def calculate_reputation(origin, offset):
- result = int(origin) + int(offset)
- if (result > 0):
- return result
- else:
- return 1
-
-@transaction.commit_on_success
-def onFlaggedItem(item, post, user):
-
- item.save()
- post.offensive_flag_count = post.offensive_flag_count + 1
- post.save()
-
- post.author.reputation = calculate_reputation(post.author.reputation,
- int(REPUTATION_RULES['lose_by_flagged']))
- post.author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=post.author,
- negative=int(REPUTATION_RULES['lose_by_flagged']),
- question=question, reputed_at=datetime.datetime.now(),
- reputation_type=-4,
- reputation=post.author.reputation)
- reputation.save()
-
- #todo: These should be updated to work on same revisions.
- if post.offensive_flag_count == VOTE_RULES['scope_flags_invisible_main_page'] :
- post.author.reputation = calculate_reputation(post.author.reputation,
- int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']))
- post.author.save()
-
- reputation = Repute(user=post.author,
- negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-6,
- reputation=post.author.reputation)
- reputation.save()
-
- elif post.offensive_flag_count == VOTE_RULES['scope_flags_delete_post']:
- post.author.reputation = calculate_reputation(post.author.reputation,
- int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']))
- post.author.save()
-
- reputation = Repute(user=post.author,
- negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-7,
- reputation=post.author.reputation)
- reputation.save()
-
- post.deleted = True
- #post.deleted_at = datetime.datetime.now()
- #post.deleted_by = Admin
- post.save()
-
-
-@transaction.commit_on_success
-def onAnswerAccept(answer, user):
- answer.accepted = True
- answer.accepted_at = datetime.datetime.now()
- answer.question.answer_accepted = True
- answer.save()
- answer.question.save()
-
- answer.author.reputation = calculate_reputation(answer.author.reputation,
- int(REPUTATION_RULES['gain_by_answer_accepted']))
- answer.author.save()
- reputation = Repute(user=answer.author,
- positive=int(REPUTATION_RULES['gain_by_answer_accepted']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=2,
- reputation=answer.author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['gain_by_accepting_answer']))
- user.save()
- reputation = Repute(user=user,
- positive=int(REPUTATION_RULES['gain_by_accepting_answer']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=3,
- reputation=user.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onAnswerAcceptCanceled(answer, user):
- answer.accepted = False
- answer.accepted_at = None
- answer.question.answer_accepted = False
- answer.save()
- answer.question.save()
-
- answer.author.reputation = calculate_reputation(answer.author.reputation,
- int(REPUTATION_RULES['lose_by_accepted_answer_cancled']))
- answer.author.save()
- reputation = Repute(user=answer.author,
- negative=int(REPUTATION_RULES['lose_by_accepted_answer_cancled']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-2,
- reputation=answer.author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['lose_by_canceling_accepted_answer']))
- user.save()
- reputation = Repute(user=user,
- negative=int(REPUTATION_RULES['lose_by_canceling_accepted_answer']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-1,
- reputation=user.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onUpVoted(vote, post, user):
- vote.save()
-
- post.vote_up_count = int(post.vote_up_count) + 1
- post.score = int(post.score) + 1
- post.save()
-
- if not post.wiki:
- author = post.author
- if Repute.objects.get_reputation_by_upvoted_today(author) < int(REPUTATION_RULES['scope_per_day_by_upvotes']):
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['gain_by_upvoted']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- positive=int(REPUTATION_RULES['gain_by_upvoted']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=1,
- reputation=author.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onUpVotedCanceled(vote, post, user):
- vote.delete()
-
- post.vote_up_count = int(post.vote_up_count) - 1
- if post.vote_up_count < 0:
- post.vote_up_count = 0
- post.score = int(post.score) - 1
- post.save()
-
- if not post.wiki:
- author = post.author
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['lose_by_upvote_canceled']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- negative=int(REPUTATION_RULES['lose_by_upvote_canceled']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-8,
- reputation=author.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onDownVoted(vote, post, user):
- vote.save()
-
- post.vote_down_count = int(post.vote_down_count) + 1
- post.score = int(post.score) - 1
- post.save()
-
- if not post.wiki:
- author = post.author
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['lose_by_downvoted']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- negative=int(REPUTATION_RULES['lose_by_downvoted']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-3,
- reputation=author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['lose_by_downvoting']))
- user.save()
-
- reputation = Repute(user=user,
- negative=int(REPUTATION_RULES['lose_by_downvoting']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-5,
- reputation=user.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onDownVotedCanceled(vote, post, user):
- vote.delete()
-
- post.vote_down_count = int(post.vote_down_count) - 1
- if post.vote_down_count < 0:
- post.vote_down_count = 0
- post.score = post.score + 1
- post.save()
-
- if not post.wiki:
- author = post.author
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['gain_by_downvote_canceled']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- positive=int(REPUTATION_RULES['gain_by_downvote_canceled']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=4,
- reputation=author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['gain_by_canceling_downvote']))
- user.save()
-
- reputation = Repute(user=user,
- positive=int(REPUTATION_RULES['gain_by_canceling_downvote']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=5,
- reputation=user.reputation)
- reputation.save()
-
-def onDeleteCanceled(post, user):
- post.deleted = False
- post.deleted_by = None
- post.deleted_at = None
- post.save()
- logging.debug('now restoring something')
- if isinstance(post,Answer):
- logging.debug('updated answer count on undelete, have %d' % post.question.answer_count)
- Question.objects.update_answer_count(post.question)
- elif isinstance(post,Question):
- for tag in list(post.tags.all()):
- if tag.used_count == 1 and tag.deleted:
- tag.deleted = False
- tag.deleted_by = None
- tag.deleted_at = None
- tag.save()
-
-def onDeleted(post, user):
- post.deleted = True
- post.deleted_by = user
- post.deleted_at = datetime.datetime.now()
- post.save()
-
- if isinstance(post, Question):
- for tag in list(post.tags.all()):
- if tag.used_count == 1:
- tag.deleted = True
- tag.deleted_by = user
- tag.deleted_at = datetime.datetime.now()
- else:
- tag.used_count = tag.used_count - 1
- tag.save()
-
- answers = post.answers.all()
- if user == post.author:
- if len(answers) > 0:
- msg = _('Your question and all of it\'s answers have been deleted')
- else:
- msg = _('Your question has been deleted')
- else:
- if len(answers) > 0:
- msg = _('The question and all of it\'s answers have been deleted')
- else:
- msg = _('The question has been deleted')
- user.message_set.create(message=msg)
- logging.debug('posted a message %s' % msg)
- for answer in answers:
- onDeleted(answer, user)
- elif isinstance(post, Answer):
- Question.objects.update_answer_count(post.question)
- logging.debug('updated answer count to %d' % post.question.answer_count)
diff --git a/forum/const.py b/forum/const.py
deleted file mode 100644
index 76fd4a24..00000000
--- a/forum/const.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# encoding:utf-8
-from django.utils.translation import ugettext as _
-"""
-All constants could be used in other modules
-For reasons that models, views can't have unicode text in this project, all unicode text go here.
-"""
-CLOSE_REASONS = (
- (1, _('duplicate question')),
- (2, _('question is off-topic or not relevant')),
- (3, _('too subjective and argumentative')),
- (4, _('is not an answer to the question')),
- (5, _('the question is answered, right answer was accepted')),
- (6, _('problem is not reproducible or outdated')),
- #(7, u'太局部、本地化的问题',)
- (7, _('question contains offensive inappropriate, or malicious remarks')),
- (8, _('spam or advertising')),
-)
-
-TYPE_REPUTATION = (
- (1, 'gain_by_upvoted'),
- (2, 'gain_by_answer_accepted'),
- (3, 'gain_by_accepting_answer'),
- (4, 'gain_by_downvote_canceled'),
- (5, 'gain_by_canceling_downvote'),
- (-1, 'lose_by_canceling_accepted_answer'),
- (-2, 'lose_by_accepted_answer_cancled'),
- (-3, 'lose_by_downvoted'),
- (-4, 'lose_by_flagged'),
- (-5, 'lose_by_downvoting'),
- (-6, 'lose_by_flagged_lastrevision_3_times'),
- (-7, 'lose_by_flagged_lastrevision_5_times'),
- (-8, 'lose_by_upvote_canceled'),
-)
-
-TYPE_ACTIVITY_ASK_QUESTION=1
-TYPE_ACTIVITY_ANSWER=2
-TYPE_ACTIVITY_COMMENT_QUESTION=3
-TYPE_ACTIVITY_COMMENT_ANSWER=4
-TYPE_ACTIVITY_UPDATE_QUESTION=5
-TYPE_ACTIVITY_UPDATE_ANSWER=6
-TYPE_ACTIVITY_PRIZE=7
-TYPE_ACTIVITY_MARK_ANSWER=8
-TYPE_ACTIVITY_VOTE_UP=9
-TYPE_ACTIVITY_VOTE_DOWN=10
-TYPE_ACTIVITY_CANCEL_VOTE=11
-TYPE_ACTIVITY_DELETE_QUESTION=12
-TYPE_ACTIVITY_DELETE_ANSWER=13
-TYPE_ACTIVITY_MARK_OFFENSIVE=14
-TYPE_ACTIVITY_UPDATE_TAGS=15
-TYPE_ACTIVITY_FAVORITE=16
-TYPE_ACTIVITY_USER_FULL_UPDATED = 17
-TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT = 18
-#TYPE_ACTIVITY_EDIT_QUESTION=17
-#TYPE_ACTIVITY_EDIT_ANSWER=18
-
-TYPE_ACTIVITY = (
- (TYPE_ACTIVITY_ASK_QUESTION, _('question')),
- (TYPE_ACTIVITY_ANSWER, _('answer')),
- (TYPE_ACTIVITY_COMMENT_QUESTION, _('commented question')),
- (TYPE_ACTIVITY_COMMENT_ANSWER, _('commented answer')),
- (TYPE_ACTIVITY_UPDATE_QUESTION, _('edited question')),
- (TYPE_ACTIVITY_UPDATE_ANSWER, _('edited answer')),
- (TYPE_ACTIVITY_PRIZE, _('received award')),
- (TYPE_ACTIVITY_MARK_ANSWER, _('marked best answer')),
- (TYPE_ACTIVITY_VOTE_UP, _('upvoted')),
- (TYPE_ACTIVITY_VOTE_DOWN, _('downvoted')),
- (TYPE_ACTIVITY_CANCEL_VOTE, _('canceled vote')),
- (TYPE_ACTIVITY_DELETE_QUESTION, _('deleted question')),
- (TYPE_ACTIVITY_DELETE_ANSWER, _('deleted answer')),
- (TYPE_ACTIVITY_MARK_OFFENSIVE, _('marked offensive')),
- (TYPE_ACTIVITY_UPDATE_TAGS, _('updated tags')),
- (TYPE_ACTIVITY_FAVORITE, _('selected favorite')),
- (TYPE_ACTIVITY_USER_FULL_UPDATED, _('completed user profile')),
- (TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT, _('email update sent to user')),
-)
-
-TYPE_RESPONSE = {
- 'QUESTION_ANSWERED' : 'question_answered',
- 'QUESTION_COMMENTED': 'question_commented',
- 'ANSWER_COMMENTED' : 'answer_commented',
- 'ANSWER_ACCEPTED' : 'answer_accepted',
-}
-
-CONST = {
- 'closed' : _('[closed]'),
- 'deleted' : _('[deleted]'),
- 'default_version' : _('initial version'),
- 'retagged' : _('retagged'),
-}
-
-#how to filter questions by tags in email digests?
-TAG_EMAIL_FILTER_CHOICES = (('ignored', _('exclude ignored tags')),('interesting',_('allow only selected tags')))
diff --git a/forum/diff.py b/forum/diff.py
deleted file mode 100644
index d741d788..00000000
--- a/forum/diff.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/python2.2
-"""HTML Diff: http://www.aaronsw.com/2002/diff
-Rough code, badly documented. Send me comments and patches."""
-
-__author__ = 'Aaron Swartz <me@aaronsw.com>'
-__copyright__ = '(C) 2003 Aaron Swartz. GNU GPL 2.'
-__version__ = '0.22'
-
-import difflib, string
-
-def isTag(x): return x[0] == "<" and x[-1] == ">"
-
-def textDiff(a, b):
- """Takes in strings a and b and returns a human-readable HTML diff."""
-
- out = []
- a, b = html2list(a), html2list(b)
- s = difflib.SequenceMatcher(None, a, b)
- for e in s.get_opcodes():
- if e[0] == "replace":
- # @@ need to do something more complicated here
- # call textDiff but not for html, but for some html... ugh
- # gonna cop-out for now
- out.append('<del>'+''.join(a[e[1]:e[2]]) + '</del><ins>'+''.join(b[e[3]:e[4]])+"</ins>")
- elif e[0] == "delete":
- out.append('<del >'+ ''.join(a[e[1]:e[2]]) + "</del>")
- elif e[0] == "insert":
- out.append('<ins >'+''.join(b[e[3]:e[4]]) + "</ins>")
- elif e[0] == "equal":
- out.append(''.join(b[e[3]:e[4]]))
- else:
- raise "Um, something's broken. I didn't expect a '" + `e[0]` + "'."
- return ''.join(out)
-
-def html2list(x, b=0):
- mode = 'char'
- cur = ''
- out = []
- for c in x:
- if mode == 'tag':
- if c == '>':
- if b: cur += ']'
- else: cur += c
- out.append(cur); cur = ''; mode = 'char'
- else: cur += c
- elif mode == 'char':
- if c == '<':
- out.append(cur)
- if b: cur = '['
- else: cur = c
- mode = 'tag'
- elif c in string.whitespace: out.append(cur+c); cur = ''
- else: cur += c
- out.append(cur)
- return filter(lambda x: x is not '', out)
-
-if __name__ == '__main__':
- import sys
- try:
- a, b = sys.argv[1:3]
- except ValueError:
- print "htmldiff: highlight the differences between two html files"
- print "usage: " + sys.argv[0] + " a b"
- sys.exit(1)
- print textDiff(open(a).read(), open(b).read())
-
diff --git a/forum/feed.py b/forum/feed.py
deleted file mode 100644
index e4b929e9..00000000
--- a/forum/feed.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Syndication feed class for subsribtion
-# Purpose:
-#
-# Author: Mike
-#
-# Created: 29/01/2009
-# Copyright: (c) CNPROG.COM 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
-from django.utils.translation import ugettext as _
-from models import Question
-from django.conf import settings
-class RssLastestQuestionsFeed(Feed):
- title = settings.APP_TITLE + _(' - ')+ _('latest questions')
- link = settings.APP_URL #+ '/' + _('question/')
- description = settings.APP_DESCRIPTION
- #ttl = 10
- copyright = settings.APP_COPYRIGHT
-
- def item_link(self, item):
- return self.link + item.get_absolute_url()
-
- def item_author_name(self, item):
- return item.author.username
-
- def item_author_link(self, item):
- return item.author.get_profile_url()
-
- def item_pubdate(self, item):
- return item.added_at
-
- def items(self, item):
- return Question.objects.filter(deleted=False).order_by('-last_activity_at')[:30]
-
-def main():
- pass
-
-if __name__ == '__main__':
- main()
diff --git a/forum/forms.py b/forum/forms.py
deleted file mode 100644
index 308f853b..00000000
--- a/forum/forms.py
+++ /dev/null
@@ -1,355 +0,0 @@
-import re
-from datetime import date
-from django import forms
-from models import *
-from const import *
-from django.utils.translation import ugettext as _
-from utils.forms import NextUrlField, UserNameField
-from recaptcha_django import ReCaptchaField
-from django.conf import settings
-import logging
-
-class TitleField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TitleField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('title')
- self.help_text = _('please enter a descriptive title for your question')
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('title must be > 10 characters'))
-
- return value
-
-class EditorField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(EditorField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.Textarea(attrs={'id':'editor'})
- self.label = _('content')
- self.help_text = u''
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('question content must be > 10 characters'))
-
- return value
-
-class TagNamesField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TagNamesField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('tags')
- #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
- self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
- self.initial = ''
-
- def clean(self, value):
- value = super(TagNamesField, self).clean(value)
- data = value.strip()
- if len(data) < 1:
- raise forms.ValidationError(_('tags are required'))
-
- split_re = re.compile(r'[ ,]+')
- list = split_re.split(data)
- list_temp = []
- if len(list) > 5:
- raise forms.ValidationError(_('please use 5 tags or less'))
- for tag in list:
- if len(tag) > 20:
- raise forms.ValidationError(_('tags must be shorter than 20 characters'))
- #take tag regex from settings
- tagname_re = re.compile(r'[a-z0-9]+')
- if not tagname_re.match(tag):
- raise forms.ValidationError(_('please use following characters in tags: letters \'a-z\', numbers, and characters \'.-_#\''))
- # only keep one same tag
- if tag not in list_temp and len(tag.strip()) > 0:
- list_temp.append(tag)
- return u' '.join(list_temp)
-
-class WikiField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(WikiField, self).__init__(*args, **kwargs)
- self.required = False
- self.label = _('community wiki')
- self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
- def clean(self,value):
- return value and settings.WIKI_ON
-
-class EmailNotifyField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(EmailNotifyField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget.attrs['class'] = 'nomargin'
-
-class SummaryField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(SummaryField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 300
- self.label = _('update summary:')
- self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
-
-class ModerateUserForm(forms.ModelForm):
- is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"),
- required=False)
-
- def clean_is_approved(self):
- if 'is_approved' not in self.cleaned_data:
- self.cleaned_data['is_approved'] = False
- return self.cleaned_data['is_approved']
-
- class Meta:
- model = User
- fields = ('is_approved',)
-
-class NotARobotForm(forms.Form):
- recaptcha = ReCaptchaField()
-
-class FeedbackForm(forms.Form):
- name = forms.CharField(label=_('Your name:'), required=False)
- email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
- message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
- next = NextUrlField()
-
-class AskForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- wiki = WikiField()
-
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
-
-class AnswerForm(forms.Form):
- text = EditorField()
- wiki = WikiField()
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email_notify = EmailNotifyField()
- def __init__(self, question, user, *args, **kwargs):
- super(AnswerForm, self).__init__(*args, **kwargs)
- self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
- if question.wiki and settings.WIKI_ON:
- self.fields['wiki'].initial = True
- if user.is_authenticated():
- if user in question.followed_by.all():
- self.fields['email_notify'].initial = True
- return
- self.fields['email_notify'].initial = False
-
-
-class CloseForm(forms.Form):
- reason = forms.ChoiceField(choices=CLOSE_REASONS)
-
-class RetagQuestionForm(forms.Form):
- tags = TagNamesField()
- # initialize the default values
- def __init__(self, question, *args, **kwargs):
- super(RetagQuestionForm, self).__init__(*args, **kwargs)
- self.fields['tags'].initial = question.tagnames
-
-class RevisionForm(forms.Form):
- """
- Lists revisions of a Question or Answer
- """
- revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
-
- def __init__(self, post, latest_revision, *args, **kwargs):
- super(RevisionForm, self).__init__(*args, **kwargs)
- revisions = post.revisions.all().values_list(
- 'revision', 'author__username', 'revised_at', 'summary')
- date_format = '%c'
- self.fields['revision'].choices = [
- (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
- for r in revisions]
- self.fields['revision'].initial = latest_revision.revision
-
-class EditQuestionForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- summary = SummaryField()
-
- def __init__(self, question, revision, *args, **kwargs):
- super(EditQuestionForm, self).__init__(*args, **kwargs)
- self.fields['title'].initial = revision.title
- self.fields['text'].initial = revision.text
- self.fields['tags'].initial = revision.tagnames
- # Once wiki mode is enabled, it can't be disabled
- if not question.wiki:
- self.fields['wiki'] = WikiField()
-
-class EditAnswerForm(forms.Form):
- text = EditorField()
- summary = SummaryField()
-
- def __init__(self, answer, revision, *args, **kwargs):
- super(EditAnswerForm, self).__init__(*args, **kwargs)
- self.fields['text'].initial = revision.text
-
-class EditUserForm(forms.Form):
- email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- if settings.EDITABLE_SCREEN_NAME:
- username = UserNameField(label=_('Screen name'))
- realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
- about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
-
- def __init__(self, user, *args, **kwargs):
- super(EditUserForm, self).__init__(*args, **kwargs)
- logging.debug('initializing the form')
- if settings.EDITABLE_SCREEN_NAME:
- self.fields['username'].initial = user.username
- self.fields['username'].user_instance = user
- self.fields['email'].initial = user.email
- self.fields['realname'].initial = user.real_name
- self.fields['website'].initial = user.website
- self.fields['city'].initial = user.location
-
- if user.date_of_birth is not None:
- self.fields['birthday'].initial = user.date_of_birth
- else:
- self.fields['birthday'].initial = '1990-01-01'
- self.fields['about'].initial = user.about
- self.user = user
-
- def clean_email(self):
- """For security reason one unique email in database"""
- if self.user.email != self.cleaned_data['email']:
- #todo dry it, there is a similar thing in openidauth
- if settings.EMAIL_UNIQUE == True:
- if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- return self.cleaned_data['email']
-
-class TagFilterSelectionForm(forms.ModelForm):
- tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
- initial='ignored',
- label=_('Choose email tag filter'),
- widget=forms.RadioSelect)
- class Meta:
- model = User
- fields = ('tag_filter_setting',)
-
- def save(self):
- before = self.instance.tag_filter_setting
- super(TagFilterSelectionForm, self).save()
- after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
- if before != after:
- return True
- return False
-
-class EditUserEmailFeedsForm(forms.Form):
- WN = (('w',_('weekly')),('n',_('no email')))
- DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
- FORM_TO_MODEL_MAP = {
- 'all_questions':'q_all',
- 'asked_by_me':'q_ask',
- 'answered_by_me':'q_ans',
- 'individually_selected':'q_sel',
- }
- NO_EMAIL_INITIAL = {
- 'all_questions':'n',
- 'asked_by_me':'n',
- 'answered_by_me':'n',
- 'individually_selected':'n',
- }
- asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Asked by me'))
- answered_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Answered by me'))
- individually_selected = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Individually selected'))
- all_questions = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Entire forum (tag filtered)'),)
-
- def set_initial_values(self,user=None):
- KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
- if user != None:
- settings = EmailFeedSetting.objects.filter(subscriber=user)
- initial_values = {}
- for setting in settings:
- feed_type = setting.feed_type
- form_field = KEY_MAP[feed_type]
- frequency = setting.frequency
- initial_values[form_field] = frequency
- self.initial = initial_values
- return self
-
- def reset(self):
- self.cleaned_data['all_questions'] = 'n'
- self.cleaned_data['asked_by_me'] = 'n'
- self.cleaned_data['answered_by_me'] = 'n'
- self.cleaned_data['individually_selected'] = 'n'
- self.initial = self.NO_EMAIL_INITIAL
- return self
-
- def save(self,user,save_unbound=False):
- """
- with save_unbound==True will bypass form validation and save initial values
- """
- changed = False
- for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
- s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
- feed_type=feed_type)
- if save_unbound:
- #just save initial values instead
- if form_field in self.initial:
- new_value = self.initial[form_field]
- else:
- new_value = self.fields[form_field].initial
- else:
- new_value = self.cleaned_data[form_field]
- if s.frequency != new_value:
- s.frequency = new_value
- s.save()
- changed = True
- else:
- if created:
- s.save()
- if form_field == 'individually_selected':
- feed_type = ContentType.objects.get_for_model(Question)
- user.followed_questions.clear()
- return changed
-
-
-class SimpleEmailSubscribeForm(forms.Form):
- SIMPLE_SUBSCRIBE_CHOICES = (
- ('y',_('okay, let\'s try!')),
- ('n',_('no OSQA community email please, thanks'))
- )
- subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
- error_messages={'required':_('please choose one of the options above')},
- choices=SIMPLE_SUBSCRIBE_CHOICES)
-
- def save(self,user=None):
- EFF = EditUserEmailFeedsForm
- if self.cleaned_data['subscribe'] == 'y':
- email_settings_form = EFF()
- logging.debug('%s wants to subscribe' % user.username)
- else:
- email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
- email_settings_form.save(user,save_unbound=True)
diff --git a/forum/forms.py.orig b/forum/forms.py.orig
deleted file mode 100644
index 42becc11..00000000
--- a/forum/forms.py.orig
+++ /dev/null
@@ -1,352 +0,0 @@
-import re
-from datetime import date
-from django import forms
-from models import *
-from const import *
-from django.utils.translation import ugettext as _
-from utils.forms import NextUrlField, UserNameField
-from recaptcha_django import ReCaptchaField
-from django.conf import settings
-import logging
-
-class TitleField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TitleField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('title')
- self.help_text = _('please enter a descriptive title for your question')
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('title must be > 10 characters'))
-
- return value
-
-class EditorField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(EditorField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.Textarea(attrs={'id':'editor'})
- self.label = _('content')
- self.help_text = u''
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('question content must be > 10 characters'))
-
- return value
-
-class TagNamesField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TagNamesField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('tags')
- #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
- self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
- self.initial = ''
-
- def clean(self, value):
- value = super(TagNamesField, self).clean(value)
- data = value.strip()
- if len(data) < 1:
- raise forms.ValidationError(_('tags are required'))
-
- split_re = re.compile(r'[ ,]+')
- list = split_re.split(data)
- list_temp = []
- if len(list) > 5:
- raise forms.ValidationError(_('please use 5 tags or less'))
- for tag in list:
- if len(tag) > 20:
- raise forms.ValidationError(_('tags must be shorter than 20 characters'))
- #take tag regex from settings
- tagname_re = re.compile(r'[a-z0-9]+')
- if not tagname_re.match(tag):
- raise forms.ValidationError(_('please use following characters in tags: letters \'a-z\', numbers, and characters \'.-_#\''))
- # only keep one same tag
- if tag not in list_temp and len(tag.strip()) > 0:
- list_temp.append(tag)
- return u' '.join(list_temp)
-
-class WikiField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(WikiField, self).__init__(*args, **kwargs)
- self.required = False
- self.label = _('community wiki')
- self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
- def clean(self,value):
- return value and settings.WIKI_ON
-
-class EmailNotifyField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(EmailNotifyField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget.attrs['class'] = 'nomargin'
-
-class SummaryField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(SummaryField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 300
- self.label = _('update summary:')
- self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
-
-class ModerateUserForm(forms.ModelForm):
- is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"),
- required=False)
-
- def clean_is_approved(self):
- if 'is_approved' not in self.cleaned_data:
- self.cleaned_data['is_approved'] = False
- return self.cleaned_data['is_approved']
-
- class Meta:
- model = User
- fields = ('is_approved',)
-
-class NotARobotForm(forms.Form):
- recaptcha = ReCaptchaField()
-
-class FeedbackForm(forms.Form):
- name = forms.CharField(label=_('Your name:'), required=False)
- email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
- message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
- next = NextUrlField()
-
-class AskForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- wiki = WikiField()
-
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
-
-class AnswerForm(forms.Form):
- text = EditorField()
- wiki = WikiField()
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email_notify = EmailNotifyField()
- def __init__(self, question, user, *args, **kwargs):
- super(AnswerForm, self).__init__(*args, **kwargs)
- self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
- if question.wiki and settings.WIKI_ON:
- self.fields['wiki'].initial = True
- if user.is_authenticated():
- if user in question.followed_by.all():
- self.fields['email_notify'].initial = True
- return
- self.fields['email_notify'].initial = False
-
-
-class CloseForm(forms.Form):
- reason = forms.ChoiceField(choices=CLOSE_REASONS)
-
-class RetagQuestionForm(forms.Form):
- tags = TagNamesField()
- # initialize the default values
- def __init__(self, question, *args, **kwargs):
- super(RetagQuestionForm, self).__init__(*args, **kwargs)
- self.fields['tags'].initial = question.tagnames
-
-class RevisionForm(forms.Form):
- """
- Lists revisions of a Question or Answer
- """
- revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
-
- def __init__(self, post, latest_revision, *args, **kwargs):
- super(RevisionForm, self).__init__(*args, **kwargs)
- revisions = post.revisions.all().values_list(
- 'revision', 'author__username', 'revised_at', 'summary')
- date_format = '%c'
- self.fields['revision'].choices = [
- (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
- for r in revisions]
- self.fields['revision'].initial = latest_revision.revision
-
-class EditQuestionForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- summary = SummaryField()
-
- def __init__(self, question, revision, *args, **kwargs):
- super(EditQuestionForm, self).__init__(*args, **kwargs)
- self.fields['title'].initial = revision.title
- self.fields['text'].initial = revision.text
- self.fields['tags'].initial = revision.tagnames
- # Once wiki mode is enabled, it can't be disabled
- if not question.wiki:
- self.fields['wiki'] = WikiField()
-
-class EditAnswerForm(forms.Form):
- text = EditorField()
- summary = SummaryField()
-
- def __init__(self, answer, revision, *args, **kwargs):
- super(EditAnswerForm, self).__init__(*args, **kwargs)
- self.fields['text'].initial = revision.text
-
-class EditUserForm(forms.Form):
- email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- #username = UserNameField(label=_('Screen name'))
- realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
- about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
-
- def __init__(self, user, *args, **kwargs):
- super(EditUserForm, self).__init__(*args, **kwargs)
- self.fields['username'].initial = user.username
- self.fields['username'].user_instance = user
- self.fields['email'].initial = user.email
- self.fields['realname'].initial = user.real_name
- self.fields['website'].initial = user.website
- self.fields['city'].initial = user.location
-
- if user.date_of_birth is not None:
- self.fields['birthday'].initial = user.date_of_birth
- else:
- self.fields['birthday'].initial = '1990-01-01'
- self.fields['about'].initial = user.about
- self.user = user
-
- def clean_email(self):
- """For security reason one unique email in database"""
- if self.user.email != self.cleaned_data['email']:
- #todo dry it, there is a similar thing in openidauth
- if settings.EMAIL_UNIQUE == True:
- if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- return self.cleaned_data['email']
-
-class TagFilterSelectionForm(forms.ModelForm):
- tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
- initial='ignored',
- label=_('Choose email tag filter'),
- widget=forms.RadioSelect)
- class Meta:
- model = User
- fields = ('tag_filter_setting',)
-
- def save(self):
- before = self.instance.tag_filter_setting
- super(TagFilterSelectionForm, self).save()
- after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
- if before != after:
- return True
- return False
-
-class EditUserEmailFeedsForm(forms.Form):
- WN = (('w',_('weekly')),('n',_('no email')))
- DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
- FORM_TO_MODEL_MAP = {
- 'all_questions':'q_all',
- 'asked_by_me':'q_ask',
- 'answered_by_me':'q_ans',
- 'individually_selected':'q_sel',
- }
- NO_EMAIL_INITIAL = {
- 'all_questions':'n',
- 'asked_by_me':'n',
- 'answered_by_me':'n',
- 'individually_selected':'n',
- }
- asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Asked by me'))
- answered_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Answered by me'))
- individually_selected = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Individually selected'))
- all_questions = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Entire forum (tag filtered)'),)
-
- def set_initial_values(self,user=None):
- KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
- if user != None:
- settings = EmailFeedSetting.objects.filter(subscriber=user)
- initial_values = {}
- for setting in settings:
- feed_type = setting.feed_type
- form_field = KEY_MAP[feed_type]
- frequency = setting.frequency
- initial_values[form_field] = frequency
- self.initial = initial_values
- return self
-
- def reset(self):
- self.cleaned_data['all_questions'] = 'n'
- self.cleaned_data['asked_by_me'] = 'n'
- self.cleaned_data['answered_by_me'] = 'n'
- self.cleaned_data['individually_selected'] = 'n'
- self.initial = self.NO_EMAIL_INITIAL
- return self
-
- def save(self,user,save_unbound=False):
- """
- with save_unbound==True will bypass form validation and save initial values
- """
- changed = False
- for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
- s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
- feed_type=feed_type)
- if save_unbound:
- #just save initial values instead
- if form_field in self.initial:
- new_value = self.initial[form_field]
- else:
- new_value = self.fields[form_field].initial
- else:
- new_value = self.cleaned_data[form_field]
- if s.frequency != new_value:
- s.frequency = new_value
- s.save()
- changed = True
- else:
- if created:
- s.save()
- if form_field == 'individually_selected':
- feed_type = ContentType.objects.get_for_model(Question)
- user.followed_questions.clear()
- return changed
-
-
-class SimpleEmailSubscribeForm(forms.Form):
- SIMPLE_SUBSCRIBE_CHOICES = (
- ('y',_('okay, let\'s try!')),
- ('n',_('no OSQA community email please, thanks'))
- )
- subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
- error_messages={'required':_('please choose one of the options above')},
- choices=SIMPLE_SUBSCRIBE_CHOICES)
-
- def save(self,user=None):
- EFF = EditUserEmailFeedsForm
- if self.cleaned_data['subscribe'] == 'y':
- email_settings_form = EFF()
- logging.debug('%s wants to subscribe' % user.username)
- else:
- email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
- email_settings_form.save(user,save_unbound=True)
diff --git a/forum/management/__init__.py b/forum/management/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/forum/management/__init__.py
+++ /dev/null
diff --git a/forum/management/commands/__init__.py b/forum/management/commands/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/forum/management/commands/__init__.py
+++ /dev/null
diff --git a/forum/management/commands/base_command.py b/forum/management/commands/base_command.py
deleted file mode 100644
index c073bf7a..00000000
--- a/forum/management/commands/base_command.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike, Sailing
-#
-# Created: 22/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-
-from datetime import datetime, date
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-from forum.const import *
-
-class BaseCommand(NoArgsCommand):
- def update_activities_auditted(self, cursor, activity_ids):
- # update processed rows to auditted
- if len(activity_ids):
- query = "UPDATE activity SET is_auditted = 1 WHERE id in (%s)"\
- % ','.join('%s' % item for item in activity_ids)
- cursor.execute(query)
-
-
-
-
-
diff --git a/forum/management/commands/clean_award_badges.py b/forum/management/commands/clean_award_badges.py
deleted file mode 100644
index 117e3a5f..00000000
--- a/forum/management/commands/clean_award_badges.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike
-#
-# Created: 18/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-#!/usr/bin/env python
-#encoding:utf-8
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
- try:
- try:
- self.clean_awards()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def clean_awards(self):
- Award.objects.all().delete()
-
- award_type =ContentType.objects.get_for_model(Award)
- Activity.objects.filter(content_type=award_type).delete()
-
- for user in User.objects.all():
- user.gold = 0
- user.silver = 0
- user.bronze = 0
- user.save()
-
- for badge in Badge.objects.all():
- badge.awarded_count = 0
- badge.save()
-
- query = "UPDATE activity SET is_auditted = 0"
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- finally:
- cursor.close()
- connection.close()
-
-def main():
- pass
-
-if __name__ == '__main__':
- main() \ No newline at end of file
diff --git a/forum/management/commands/message_to_everyone.py b/forum/management/commands/message_to_everyone.py
deleted file mode 100644
index c020c178..00000000
--- a/forum/management/commands/message_to_everyone.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from django.contrib.auth.models import User
-import sys
-
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
- msg = None
- if msg == None:
- print 'to run this command, please first edit the file %s' % __file__
- sys.exit(1)
- for u in User.objects.all():
- u.message_set.create(message = msg % u.username)
diff --git a/forum/management/commands/multi_award_badges.py b/forum/management/commands/multi_award_badges.py
deleted file mode 100644
index 6b330cf9..00000000
--- a/forum/management/commands/multi_award_badges.py
+++ /dev/null
@@ -1,348 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike, Sailing
-#
-# Created: 22/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-
-from datetime import datetime, date
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-from forum.const import *
-from base_command import BaseCommand
-"""
-(1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0),
-(2, '压力白领', 3, '压力白领', '删除自己有3个以上反对票的帖子', 1, 0),
-(3, '优秀回答', 3, '优秀回答', '回答好评10次以上', 1, 0),
-(4, '优秀问题', 3, '优秀问题', '问题好评10次以上', 1, 0),
-(5, '评论家', 3, '评论家', '评论10次以上', 0, 0),
-(6, '流行问题', 3, '流行问题', '问题的浏览量超过1000人次', 1, 0),
-(7, '巡逻兵', 3, '巡逻兵', '第一次标记垃圾帖子', 0, 0),
-(8, '清洁工', 3, '清洁工', '第一次撤销投票', 0, 0),
-(9, '批评家', 3, '批评家', '第一次反对票', 0, 0),
-(10, '小编', 3, '小编', '第一次编辑更新', 0, 0),
-(11, '村长', 3, '村长', '第一次重新标签', 0, 0),
-(12, '学者', 3, '学者', '第一次标记答案', 0, 0),
-(13, '学生', 3, '学生', '第一次提问并且有一次以上赞成票', 0, 0),
-(14, '支持者', 3, '支持者', '第一次赞成票', 0, 0),
-(15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0),
-(16, '自传作者', 3, '自传作者', '完整填写用户资料所有选项', 0, 0),
-(17, '自学成才', 3, '自学成才', '回答自己的问题并且有3个以上赞成票', 1, 0),
-(18, '最有价值回答', 1, '最有价值回答', '回答超过100次赞成票', 1, 0),
-(19, '最有价值问题', 1, '最有价值问题', '问题超过100次赞成票', 1, 0),
-(20, '万人迷', 1, '万人迷', '问题被100人以上收藏', 1, 0),
-(21, '著名问题', 1, '著名问题', '问题的浏览量超过10000人次', 1, 0),
-(22, 'alpha用户', 2, 'alpha用户', '内测期间的活跃用户', 0, 0),
-(23, '极好回答', 2, '极好回答', '回答超过25次赞成票', 1, 0),
-(24, '极好问题', 2, '极好问题', '问题超过25次赞成票', 1, 0),
-(25, '受欢迎问题', 2, '受欢迎问题', '问题被25人以上收藏', 1, 0),
-(26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0),
-(27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0),
-(28, '通才', 2, '通才', '在多个标签领域活跃', 0, 0),
-(29, '专家', 2, '专家', '在一个标签领域活跃出众', 0, 0),
-(30, '老鸟', 2, '老鸟', '活跃超过一年的用户', 0, 0),
-(31, '最受关注问题', 2, '最受关注问题', '问题的浏览量超过2500人次', 1, 0),
-(32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0),
-(33, 'beta用户', 2, 'beta用户', 'beta期间活跃参与', 0, 0),
-(34, '导师', 2, '导师', '被指定为最佳答案并且赞成票40以上', 1, 0),
-(35, '巫师', 2, '巫师', '在提问60天之后回答并且赞成票5次以上', 1, 0),
-(36, '分类专家', 2, '分类专家', '创建的标签被50个以上问题使用', 1, 0);
-
-
-TYPE_ACTIVITY_ASK_QUESTION=1
-TYPE_ACTIVITY_ANSWER=2
-TYPE_ACTIVITY_COMMENT_QUESTION=3
-TYPE_ACTIVITY_COMMENT_ANSWER=4
-TYPE_ACTIVITY_UPDATE_QUESTION=5
-TYPE_ACTIVITY_UPDATE_ANSWER=6
-TYPE_ACTIVITY_PRIZE=7
-TYPE_ACTIVITY_MARK_ANSWER=8
-TYPE_ACTIVITY_VOTE_UP=9
-TYPE_ACTIVITY_VOTE_DOWN=10
-TYPE_ACTIVITY_CANCEL_VOTE=11
-TYPE_ACTIVITY_DELETE_QUESTION=12
-TYPE_ACTIVITY_DELETE_ANSWER=13
-TYPE_ACTIVITY_MARK_OFFENSIVE=14
-TYPE_ACTIVITY_UPDATE_TAGS=15
-TYPE_ACTIVITY_FAVORITE=16
-TYPE_ACTIVITY_USER_FULL_UPDATED = 17
-"""
-
-class Command(BaseCommand):
- def handle_noargs(self, **options):
- try:
- try:
- self.delete_question_be_voted_up_3()
- self.delete_answer_be_voted_up_3()
- self.delete_question_be_vote_down_3()
- self.delete_answer_be_voted_down_3()
- self.answer_be_voted_up_10()
- self.question_be_voted_up_10()
- self.question_view_1000()
- self.answer_self_question_be_voted_up_3()
- self.answer_be_voted_up_100()
- self.question_be_voted_up_100()
- self.question_be_favorited_100()
- self.question_view_10000()
- self.answer_be_voted_up_25()
- self.question_be_voted_up_25()
- self.question_be_favorited_25()
- self.question_view_2500()
- self.answer_be_accepted_and_voted_up_40()
- self.question_be_answered_after_60_days_and_be_voted_up_5()
- self.created_tag_be_used_in_question_50()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def delete_question_be_voted_up_3(self):
- """
- (1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\
- act.activity_type = %s AND\
- q.vote_up_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_QUESTION)
- self.__process_activities_badge(query, 1, Question)
-
- def delete_answer_be_voted_up_3(self):
- """
- (1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\
- act.activity_type = %s AND\
- an.vote_up_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_ANSWER)
- self.__process_activities_badge(query, 1, Answer)
-
- def delete_question_be_vote_down_3(self):
- """
- (2, '压力白领', 3, '压力白领', '删除自己有3个以上反对票的帖子', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\
- act.activity_type = %s AND\
- q.vote_down_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_QUESTION)
- content_type = ContentType.objects.get_for_model(Question)
- self.__process_activities_badge(query, 2, Question)
-
- def delete_answer_be_voted_down_3(self):
- """
- (2, '压力白领', 3, '压力白领', '删除自己有3个以上反对票的帖子', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\
- act.activity_type = %s AND\
- an.vote_down_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_ANSWER)
- self.__process_activities_badge(query, 2, Answer)
-
- def answer_be_voted_up_10(self):
- """
- (3, '优秀回答', 3, '优秀回答', '回答好评10次以上', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, answer a WHERE act.object_id = a.id AND\
- act.activity_type = %s AND \
- a.vote_up_count >= 10 AND\
- act.is_auditted = 0" % (TYPE_ACTIVITY_ANSWER)
- self.__process_activities_badge(query, 3, Answer)
-
- def question_be_voted_up_10(self):
- """
- (4, '优秀问题', 3, '优秀问题', '问题好评10次以上', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, question q WHERE act.object_id = q.id AND\
- act.activity_type = %s AND \
- q.vote_up_count >= 10 AND\
- act.is_auditted = 0" % (TYPE_ACTIVITY_ASK_QUESTION)
- self.__process_activities_badge(query, 4, Question)
-
- def question_view_1000(self):
- """
- (6, '流行问题', 3, '流行问题', '问题的浏览量超过1000人次', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, question q WHERE act.activity_type = %s AND\
- act.object_id = q.id AND \
- q.view_count >= 1000 AND\
- act.object_id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 6)
- self.__process_activities_badge(query, 6, Question, False)
-
- def answer_self_question_be_voted_up_3(self):
- """
- (17, '自学成才', 3, '自学成才', '回答自己的问题并且有3个以上赞成票', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, answer an WHERE act.activity_type = %s AND\
- act.object_id = an.id AND\
- an.vote_up_count >= 3 AND\
- act.user_id = (SELECT user_id FROM question q WHERE q.id = an.question_id) AND\
- act.object_id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 17)
- self.__process_activities_badge(query, 17, Question, False)
-
- def answer_be_voted_up_100(self):
- """
- (18, '最有价值回答', 1, '最有价值回答', '回答超过100次赞成票', 1, 0),
- """
- query = "SELECT an.id, an.author_id FROM answer an WHERE an.vote_up_count >= 100 AND an.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (18)
-
- self.__process_badge(query, 18, Answer)
-
- def question_be_voted_up_100(self):
- """
- (19, '最有价值问题', 1, '最有价值问题', '问题超过100次赞成票', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.vote_up_count >= 100 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (19)
-
- self.__process_badge(query, 19, Question)
-
- def question_be_favorited_100(self):
- """
- (20, '万人迷', 1, '万人迷', '问题被100人以上收藏', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.favourite_count >= 100 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (20)
-
- self.__process_badge(query, 20, Question)
-
- def question_view_10000(self):
- """
- (21, '著名问题', 1, '著名问题', '问题的浏览量超过10000人次', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.view_count >= 10000 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (21)
-
- self.__process_badge(query, 21, Question)
-
- def answer_be_voted_up_25(self):
- """
- (23, '极好回答', 2, '极好回答', '回答超过25次赞成票', 1, 0),
- """
- query = "SELECT a.id, a.author_id FROM answer a WHERE a.vote_up_count >= 25 AND a.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (23)
-
- self.__process_badge(query, 23, Answer)
-
- def question_be_voted_up_25(self):
- """
- (24, '极好问题', 2, '极好问题', '问题超过25次赞成票', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.vote_up_count >= 25 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (24)
-
- self.__process_badge(query, 24, Question)
-
- def question_be_favorited_25(self):
- """
- (25, '受欢迎问题', 2, '受欢迎问题', '问题被25人以上收藏', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.favourite_count >= 25 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (25)
-
- self.__process_badge(query, 25, Question)
-
- def question_view_2500(self):
- """
- (31, '最受关注问题', 2, '最受关注问题', '问题的浏览量超过2500人次', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.view_count >= 2500 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (31)
-
- self.__process_badge(query, 31, Question)
-
- def answer_be_accepted_and_voted_up_40(self):
- """
- (34, '导师', 2, '导师', '被指定为最佳答案并且赞成票40以上', 1, 0),
- """
- query = "SELECT a.id, a.author_id FROM answer a WHERE a.vote_up_count >= 40 AND\
- a.accepted = 1 AND\
- a.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (34)
-
- self.__process_badge(query, 34, Answer)
-
- def question_be_answered_after_60_days_and_be_voted_up_5(self):
- """
- (35, '巫师', 2, '巫师', '在提问60天之后回答并且赞成票5次以上', 1, 0),
- """
- query = "SELECT a.id, a.author_id FROM question q, answer a WHERE q.id = a.question_id AND\
- DATEDIFF(a.added_at, q.added_at) >= 60 AND\
- a.vote_up_count >= 5 AND \
- a.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (35)
-
- self.__process_badge(query, 35, Answer)
-
- def created_tag_be_used_in_question_50(self):
- """
- (36, '分类专家', 2, '分类专家', '创建的标签被50个以上问题使用', 1, 0);
- """
- query = "SELECT t.id, t.created_by_id FROM tag t, auth_user u WHERE t.created_by_id = u.id AND \
- t. used_count >= 50 AND \
- t.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (36)
-
- self.__process_badge(query, 36, Tag)
-
- def __process_activities_badge(self, query, badge, content_object, update_auditted=True):
- content_type = ContentType.objects.get_for_model(content_object)
-
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- if update_auditted:
- activity_ids = []
- badge = get_object_or_404(Badge, id=badge)
- for row in rows:
- activity_id = row[0]
- user_id = row[1]
- object_id = row[2]
-
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
-
- if update_auditted:
- activity_ids.append(activity_id)
-
- if update_auditted:
- self.update_activities_auditted(cursor, activity_ids)
- finally:
- cursor.close()
-
- def __process_badge(self, query, badge, content_object):
- content_type = ContentType.objects.get_for_model(Answer)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- badge = get_object_or_404(Badge, id=badge)
- for row in rows:
- object_id = row[0]
- user_id = row[1]
-
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- finally:
- cursor.close()
diff --git a/forum/management/commands/once_award_badges.py b/forum/management/commands/once_award_badges.py
deleted file mode 100644
index 8c913348..00000000
--- a/forum/management/commands/once_award_badges.py
+++ /dev/null
@@ -1,350 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike, Sailing
-#
-# Created: 18/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-
-from datetime import datetime, date
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-from forum.const import *
-from base_command import BaseCommand
-"""
-(1, '炼狱法师', 3, '炼狱法师', '删除自己有3个以上赞成票的帖子', 1, 0),
-(2, '压力白领', 3, '压力白领', '删除自己有3个以上反对票的帖子', 1, 0),
-(3, '优秀回答', 3, '优秀回答', '回答好评10次以上', 1, 0),
-(4, '优秀问题', 3, '优秀问题', '问题好评10次以上', 1, 0),
-(5, '评论家', 3, '评论家', '评论10次以上', 0, 0),
-(6, '流行问题', 3, '流行问题', '问题的浏览量超过1000人次', 1, 0),
-(7, '巡逻兵', 3, '巡逻兵', '第一次标记垃圾帖子', 0, 0),
-(8, '清洁工', 3, '清洁工', '第一次撤销投票', 0, 0),
-(9, '批评家', 3, '批评家', '第一次反对票', 0, 0),
-(10, '小编', 3, '小编', '第一次编辑更新', 0, 0),
-(11, '村长', 3, '村长', '第一次重新标签', 0, 0),
-(12, '学者', 3, '学者', '第一次标记答案', 0, 0),
-(13, '学生', 3, '学生', '第一次提问并且有一次以上赞成票', 0, 0),
-(14, '支持者', 3, '支持者', '第一次赞成票', 0, 0),
-(15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0),
-(16, '自传作者', 3, '自传作者', '完整填写用户资料所有选项', 0, 0),
-(17, '自学成才', 3, '自学成才', '回答自己的问题并且有3个以上赞成票', 1, 0),
-(18, '最有价值回答', 1, '最有价值回答', '回答超过100次赞成票', 1, 0),
-(19, '最有价值问题', 1, '最有价值问题', '问题超过100次赞成票', 1, 0),
-(20, '万人迷', 1, '万人迷', '问题被100人以上收藏', 1, 0),
-(21, '著名问题', 1, '著名问题', '问题的浏览量超过10000人次', 1, 0),
-(22, 'alpha用户', 2, 'alpha用户', '内测期间的活跃用户', 0, 0),
-(23, '极好回答', 2, '极好回答', '回答超过25次赞成票', 1, 0),
-(24, '极好问题', 2, '极好问题', '问题超过25次赞成票', 1, 0),
-(25, '受欢迎问题', 2, '受欢迎问题', '问题被25人以上收藏', 1, 0),
-(26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0),
-(27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0),
-(28, '通才', 2, '通才', '在多个标签领域活跃', 0, 0),
-(29, '专家', 2, '专家', '在一个标签领域活跃出众', 0, 0),
-(30, '老鸟', 2, '老鸟', '活跃超过一年的用户', 0, 0),
-(31, '最受关注问题', 2, '最受关注问题', '问题的浏览量超过2500人次', 1, 0),
-(32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0),
-(33, 'beta用户', 2, 'beta用户', 'beta期间活跃参与', 0, 0),
-(34, '导师', 2, '导师', '被指定为最佳答案并且赞成票40以上', 1, 0),
-(35, '巫师', 2, '巫师', '在提问60天之后回答并且赞成票5次以上', 1, 0),
-(36, '分类专家', 2, '分类专家', '创建的标签被50个以上问题使用', 1, 0);
-
-
-TYPE_ACTIVITY_ASK_QUESTION=1
-TYPE_ACTIVITY_ANSWER=2
-TYPE_ACTIVITY_COMMENT_QUESTION=3
-TYPE_ACTIVITY_COMMENT_ANSWER=4
-TYPE_ACTIVITY_UPDATE_QUESTION=5
-TYPE_ACTIVITY_UPDATE_ANSWER=6
-TYPE_ACTIVITY_PRIZE=7
-TYPE_ACTIVITY_MARK_ANSWER=8
-TYPE_ACTIVITY_VOTE_UP=9
-TYPE_ACTIVITY_VOTE_DOWN=10
-TYPE_ACTIVITY_CANCEL_VOTE=11
-TYPE_ACTIVITY_DELETE_QUESTION=12
-TYPE_ACTIVITY_DELETE_ANSWER=13
-TYPE_ACTIVITY_MARK_OFFENSIVE=14
-TYPE_ACTIVITY_UPDATE_TAGS=15
-TYPE_ACTIVITY_FAVORITE=16
-TYPE_ACTIVITY_USER_FULL_UPDATED = 17
-"""
-
-BADGE_AWARD_TYPE_FIRST = {
- TYPE_ACTIVITY_MARK_OFFENSIVE : 7,
- TYPE_ACTIVITY_CANCEL_VOTE: 8,
- TYPE_ACTIVITY_VOTE_DOWN : 9,
- TYPE_ACTIVITY_UPDATE_QUESTION : 10,
- TYPE_ACTIVITY_UPDATE_ANSWER : 10,
- TYPE_ACTIVITY_UPDATE_TAGS : 11,
- TYPE_ACTIVITY_MARK_ANSWER : 12,
- TYPE_ACTIVITY_VOTE_UP : 14,
- TYPE_ACTIVITY_USER_FULL_UPDATED: 16
-
-}
-
-class Command(BaseCommand):
- def handle_noargs(self, **options):
- try:
- try:
- self.alpha_user()
- self.beta_user()
- self.first_type_award()
- self.first_ask_be_voted()
- self.first_answer_be_voted()
- self.first_answer_be_voted_10()
- self.vote_count_300()
- self.edit_count_100()
- self.comment_count_10()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def alpha_user(self):
- """
- Before Jan 25, 2009(Chinese New Year Eve and enter into Beta for CNProg), every registered user
- will be awarded the "Alpha" badge if he has any activities.
- """
- alpha_end_date = date(2009, 1, 25)
- if date.today() < alpha_end_date:
- badge = get_object_or_404(Badge, id=22)
- for user in User.objects.all():
- award = Award.objects.filter(user=user, badge=badge)
- if award and not badge.multiple:
- continue
- activities = Activity.objects.filter(user=user)
- if len(activities) > 0:
- new_award = Award(user=user, badge=badge)
- new_award.save()
-
- def beta_user(self):
- """
- Before Feb 25, 2009, every registered user
- will be awarded the "Beta" badge if he has any activities.
- """
- beta_end_date = date(2009, 2, 25)
- if date.today() < beta_end_date:
- badge = get_object_or_404(Badge, id=33)
- for user in User.objects.all():
- award = Award.objects.filter(user=user, badge=badge)
- if award and not badge.multiple:
- continue
- activities = Activity.objects.filter(user=user)
- if len(activities) > 0:
- new_award = Award(user=user, badge=badge)
- new_award.save()
-
- def first_type_award(self):
- """
- This will award below badges for users first behaviors:
-
- (7, '巡逻兵', 3, '巡逻兵', '第一次标记垃圾帖子', 0, 0),
- (8, '清洁工', 3, '清洁工', '第一次撤销投票', 0, 0),
- (9, '批评家', 3, '批评家', '第一次反对票', 0, 0),
- (10, '小编', 3, '小编', '第一次编辑更新', 0, 0),
- (11, '村长', 3, '村长', '第一次重新标签', 0, 0),
- (12, '学者', 3, '学者', '第一次标记答案', 0, 0),
- (14, '支持者', 3, '支持者', '第一次赞成票', 0, 0),
- (16, '自传作者', 3, '自传作者', '完整填写用户资料所有选项', 0, 0),
- """
- activity_types = ','.join('%s' % item for item in BADGE_AWARD_TYPE_FIRST.keys())
- # ORDER BY user_id, activity_type
- query = "SELECT id, user_id, activity_type, content_type_id, object_id FROM activity WHERE is_auditted = 0 AND activity_type IN (%s) ORDER BY user_id, activity_type" % activity_types
-
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
- # collect activity_id in current process
- activity_ids = []
- last_user_id = 0
- last_activity_type = 0
- for row in rows:
- activity_ids.append(row[0])
- user_id = row[1]
- activity_type = row[2]
- content_type_id = row[3]
- object_id = row[4]
-
- # if the user and activity are same as the last, continue
- if user_id == last_user_id and activity_type == last_activity_type:
- continue;
-
- user = get_object_or_404(User, id=user_id)
- badge = get_object_or_404(Badge, id=BADGE_AWARD_TYPE_FIRST[activity_type])
- content_type = get_object_or_404(ContentType, id=content_type_id)
-
- count = Award.objects.filter(user=user, badge=badge).count()
- if count and not badge.multiple:
- continue
- else:
- # new award
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
-
- # set the current user_id and activity_type to last
- last_user_id = user_id
- last_activity_type = activity_type
-
- # update processed rows to auditted
- self.update_activities_auditted(cursor, activity_ids)
- finally:
- cursor.close()
-
- def first_ask_be_voted(self):
- """
- For user asked question and got first upvote, we award him following badge:
-
- (13, '学生', 3, '学生', '第一次提问并且有一次以上赞成票', 0, 0),
- """
- query = "SELECT act.user_id, q.vote_up_count, act.object_id FROM " \
- "activity act, question q WHERE act.activity_type = %s AND " \
- "act.object_id = q.id AND " \
- "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 13)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- badge = get_object_or_404(Badge, id=13)
- content_type = ContentType.objects.get_for_model(Question)
- awarded_users = []
- for row in rows:
- user_id = row[0]
- vote_up_count = row[1]
- object_id = row[2]
- if vote_up_count > 0 and user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
- def first_answer_be_voted(self):
- """
- When user answerd questions and got first upvote, we award him following badge:
-
- (15, '教师', 3, '教师', '第一次回答问题并且得到一个以上赞成票', 0, 0),
- """
- query = "SELECT act.user_id, a.vote_up_count, act.object_id FROM " \
- "activity act, answer a WHERE act.activity_type = %s AND " \
- "act.object_id = a.id AND " \
- "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 15)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- awarded_users = []
- badge = get_object_or_404(Badge, id=15)
- content_type = ContentType.objects.get_for_model(Answer)
- for row in rows:
- user_id = row[0]
- vote_up_count = row[1]
- object_id = row[2]
- if vote_up_count > 0 and user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
- def first_answer_be_voted_10(self):
- """
- (32, '学问家', 2, '学问家', '第一次回答被投赞成票10次以上', 0, 0)
- """
- query = "SELECT act.user_id, act.object_id FROM " \
- "activity act, answer a WHERE act.object_id = a.id AND " \
- "act.activity_type = %s AND " \
- "a.vote_up_count >= 10 AND " \
- "act.user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 32)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- awarded_users = []
- badge = get_object_or_404(Badge, id=32)
- content_type = ContentType.objects.get_for_model(Answer)
- for row in rows:
- user_id = row[0]
- if user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- object_id = row[1]
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
- def vote_count_300(self):
- """
- (26, '优秀市民', 2, '优秀市民', '投票300次以上', 0, 0)
- """
- query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
- "activity_type = %s OR " \
- "activity_type = %s AND " \
- "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
- "GROUP BY user_id HAVING vote_count >= 300" % (TYPE_ACTIVITY_VOTE_UP, TYPE_ACTIVITY_VOTE_DOWN, 26)
-
- self.__award_for_count_num(query, 26)
-
- def edit_count_100(self):
- """
- (27, '编辑主任', 2, '编辑主任', '编辑了100个帖子', 0, 0)
- """
- query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
- "activity_type = %s OR " \
- "activity_type = %s AND " \
- "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
- "GROUP BY user_id HAVING vote_count >= 100" % (TYPE_ACTIVITY_UPDATE_QUESTION, TYPE_ACTIVITY_UPDATE_ANSWER, 27)
-
- self.__award_for_count_num(query, 27)
-
- def comment_count_10(self):
- """
- (5, '评论家', 3, '评论家', '评论10次以上', 0, 0),
- """
- query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
- "activity_type = %s OR " \
- "activity_type = %s AND " \
- "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
- "GROUP BY user_id HAVING vote_count >= 10" % (TYPE_ACTIVITY_COMMENT_QUESTION, TYPE_ACTIVITY_COMMENT_ANSWER, 5)
- self.__award_for_count_num(query, 5)
-
- def __award_for_count_num(self, query, badge):
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- awarded_users = []
- badge = get_object_or_404(Badge, id=badge)
- for row in rows:
- vote_count = row[0]
- user_id = row[1]
-
- if user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
-def main():
- pass
-
-if __name__ == '__main__':
- main()
diff --git a/forum/management/commands/sample_command.py b/forum/management/commands/sample_command.py
deleted file mode 100644
index 55e67235..00000000
--- a/forum/management/commands/sample_command.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from forum.models import Comment
-
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
- objs = Comment.objects.all()
- print objs \ No newline at end of file
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
deleted file mode 100644
index 62f13d69..00000000
--- a/forum/management/commands/send_email_alerts.py
+++ /dev/null
@@ -1,192 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.db.models import Q, F
-from forum.models import *
-from forum import const
-from django.core.mail import EmailMessage
-from django.utils.translation import ugettext as _
-from django.utils.translation import ungettext
-import datetime
-from django.conf import settings
-import logging
-from utils.odict import OrderedDict
-
-class Command(NoArgsCommand):
- def handle_noargs(self,**options):
- try:
- try:
- self.send_email_alerts()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def get_updated_questions_for_user(self,user):
- q_sel = None
- q_ask = None
- q_ans = None
- q_all = None
- now = datetime.datetime.now()
- Q_set1 = Question.objects.exclude(
- last_activity_by=user,
- ).exclude(
- last_activity_at__lt=user.date_joined
- ).filter(
- Q(viewed__who=user,viewed__when__lt=F('last_activity_at')) | \
- ~Q(viewed__who=user)
- ).exclude(
- deleted=True
- ).exclude(
- closed=True
- )
-
- user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
- for feed in user_feeds:
- cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
- if feed.reported_at == None or feed.reported_at <= cutoff_time:
- Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
- feed.reported_at = now
- feed.save()#may not actually report anything, depending on filters below
- if feed.feed_type == 'q_sel':
- q_sel = Q_set.filter(followed_by=user)
- q_sel.cutoff_time = cutoff_time #store cutoff time per query set
- elif feed.feed_type == 'q_ask':
- q_ask = Q_set.filter(author=user)
- q_ask.cutoff_time = cutoff_time
- elif feed.feed_type == 'q_ans':
- q_ans = Q_set.filter(answers__author=user)
- q_ans.cutoff_time = cutoff_time
- elif feed.feed_type == 'q_all':
- if user.tag_filter_setting == 'ignored':
- ignored_tags = Tag.objects.filter(user_selections__reason='bad',user_selections__user=user)
- q_all = Q_set.exclude( tags__in=ignored_tags )
- else:
- selected_tags = Tag.objects.filter(user_selections__reason='good',user_selections__user=user)
- q_all = Q_set.filter( tags__in=selected_tags )
- q_all.cutoff_time = cutoff_time
- #build list in this order
- q_list = OrderedDict()
- def extend_question_list(src, dst):
- """src is a query set with questions
- or an empty list
- dst - is an ordered dictionary
- """
- if src is None:
- return #will not do anything if subscription of this type is not used
- cutoff_time = src.cutoff_time
- for q in src:
- if q in dst:
- if cutoff_time < dst[q]['cutoff_time']:
- dst[q]['cutoff_time'] = cutoff_time
- else:
- #initialise a questions metadata dictionary to use for email reporting
- dst[q] = {'cutoff_time':cutoff_time}
-
- extend_question_list(q_sel, q_list)
- extend_question_list(q_ask, q_list)
- extend_question_list(q_ans, q_list)
- extend_question_list(q_all, q_list)
-
- ctype = ContentType.objects.get_for_model(Question)
- EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT
- for q, meta_data in q_list.items():
- #todo use Activity, but first start keeping more Activity records
- #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
- #because currently activity is not fully recorded to through
- #revision records to see what kind modifications were done on
- #the questions and answers
- try:
- update_info = Activity.objects.get(content_type=ctype,
- object_id=q.id,
- activity_type=EMAIL_UPDATE_ACTIVITY)
- emailed_at = update_info.active_at
- except Activity.DoesNotExist:
- update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY)
- emailed_at = datetime.datetime(1970,1,1)#long time ago
- except Activity.MultipleObjectsReturned:
- raise Exception('server error - multiple question email activities found per user-question pair')
-
- q_rev = QuestionRevision.objects.filter(question=q,\
- revised_at__lt=cutoff_time,\
- revised_at__gt=emailed_at)
- q_rev = q_rev.exclude(author=user)
- meta_data['q_rev'] = len(q_rev)
- if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
- meta_data['q_rev'] = 0
- meta_data['new_q'] = True
- else:
- meta_data['new_q'] = False
-
- new_ans = Answer.objects.filter(question=q,\
- added_at__lt=cutoff_time,\
- added_at__gt=emailed_at)
- new_ans = new_ans.exclude(author=user)
- meta_data['new_ans'] = len(new_ans)
- ans_rev = AnswerRevision.objects.filter(answer__question=q,\
- revised_at__lt=cutoff_time,\
- revised_at__gt=emailed_at)
- ans_rev = ans_rev.exclude(author=user)
- meta_data['ans_rev'] = len(ans_rev)
- if len(q_rev) == 0 and len(new_ans) == 0 and len(ans_rev) == 0:
- meta_data['nothing_new'] = True
- else:
- meta_data['nothing_new'] = False
- update_info.active_at = now
- update_info.save() #save question email update activity
- return q_list
-
- def __action_count(self,string,number,output):
- if number > 0:
- output.append(_(string) % {'num':number})
-
- def send_email_alerts(self):
-
- #todo: move this to template
- for user in User.objects.all():
- q_list = self.get_updated_questions_for_user(user)
- num_q = 0
- num_moot = 0
- for meta_data in q_list.values():
- if meta_data['nothing_new'] == False:
- num_q += 1
- else:
- num_moot += 1
- if num_q > 0:
- url_prefix = settings.APP_URL
- subject = _('email update message subject')
- print 'have %d updated questions for %s' % (num_q, user.username)
- text = ungettext('%(name)s, this is an update message header for a question',
- '%(name)s, this is an update message header for %(num)d questions',num_q) \
- % {'num':num_q, 'name':user.username}
-
- text += '<ul>'
- for q, meta_data in q_list.items():
- act_list = []
- if meta_data['nothing_new']:
- continue
- else:
- if meta_data['new_q']:
- act_list.append(_('new question'))
- self.__action_count('%(num)d rev', meta_data['q_rev'],act_list)
- self.__action_count('%(num)d ans', meta_data['new_ans'],act_list)
- self.__action_count('%(num)d ans rev',meta_data['ans_rev'],act_list)
- act_token = ', '.join(act_list)
- text += '<li><a href="%s?sort=latest">%s</a> <font color="#777777">(%s)</font></li>' \
- % (url_prefix + q.get_absolute_url(), q.title, act_token)
- text += '</ul>'
- if num_moot > 0:
- text += '<p></p>'
- text += ungettext('There is also one question which was recently '\
- +'updated but you might not have seen its latest version.',
- 'There are also %(num)d more questions which were recently updated '\
- +'but you might not have seen their latest version.',num_moot) \
- % {'num':num_moot,}
- text += _('Perhaps you could look up previously sent forum reminders in your mailbox.')
- text += '</p>'
-
- link = url_prefix + user.get_profile_url() + '?sort=email_subscriptions'
- text += _('go to %(link)s to change frequency of email updates or %(email)s administrator') \
- % {'link':link, 'email':settings.ADMINS[0][1]}
- msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [user.email])
- msg.content_subtype = 'html'
- msg.send()
diff --git a/forum/management/commands/subscribe_everyone.py b/forum/management/commands/subscribe_everyone.py
deleted file mode 100644
index c79528f3..00000000
--- a/forum/management/commands/subscribe_everyone.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.db.models import Q, F
-from forum.models import *
-from django.core.mail import EmailMessage
-from django.utils.translation import ugettext as _
-from django.utils.translation import ungettext
-import datetime
-from django.conf import settings
-
-class Command(NoArgsCommand):
- def handle_noargs(self,**options):
- try:
- try:
- self.subscribe_everyone()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def subscribe_everyone(self):
-
- feed_type_info = EmailFeedSetting.FEED_TYPES
- for user in User.objects.all():
- for feed_type in feed_type_info:
- try:
- feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type = feed_type[0])
- except EmailFeedSetting.DoesNotExist:
- feed_setting = EmailFeedSetting(subscriber=user,feed_type=feed_type[0])
- feed_setting.frequency = 'w'
- feed_setting.reported_at = None
- feed_setting.save()
diff --git a/forum/managers.py b/forum/managers.py
deleted file mode 100644
index 3f580bd3..00000000
--- a/forum/managers.py
+++ /dev/null
@@ -1,241 +0,0 @@
-import datetime
-import time
-import logging
-from django.contrib.auth.models import User, UserManager
-from django.db import connection, models, transaction
-from django.db.models import Q
-from forum.models import *
-from urllib import quote, unquote
-
-class QuestionManager(models.Manager):
-
- def update_tags(self, question, tagnames, user):
- """
- Updates Tag associations for a question to match the given
- tagname string.
-
- Returns ``True`` if tag usage counts were updated as a result,
- ``False`` otherwise.
- """
- from forum.models import Tag
- current_tags = list(question.tags.all())
- current_tagnames = set(t.name for t in current_tags)
- updated_tagnames = set(t for t in tagnames.split(' ') if t)
- modified_tags = []
-
- removed_tags = [t for t in current_tags
- if t.name not in updated_tagnames]
- if removed_tags:
- modified_tags.extend(removed_tags)
- question.tags.remove(*removed_tags)
-
- added_tagnames = updated_tagnames - current_tagnames
- if added_tagnames:
- added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
- user)
- modified_tags.extend(added_tags)
- question.tags.add(*added_tags)
-
- if modified_tags:
- Tag.objects.update_use_counts(modified_tags)
- return True
-
- return False
-
- def update_answer_count(self, question):
- """
- Executes an UPDATE query to update denormalised data with the
- number of answers the given question has.
- """
-
- # for some reasons, this Answer class failed to be imported,
- # although we have imported all classes from models on top.
- from forum.models import Answer
- self.filter(id=question.id).update(
- answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
-
- def update_view_count(self, question):
- """
- update counter+1 when user browse question page
- """
- self.filter(id=question.id).update(view_count = question.view_count + 1)
-
- def update_favorite_count(self, question):
- """
- update favourite_count for given question
- """
- from forum.models import FavoriteQuestion
- self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
-
- def get_similar_questions(self, question):
- """
- Get 10 similar questions for given one.
- This will search the same tag list for give question(by exactly same string) first.
- Questions with the individual tags will be added to list if above questions are not full.
- """
- #print datetime.datetime.now()
- questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
-
- tags_list = question.tags.all()
- for tag in tags_list:
- extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
- for item in extend_questions:
- if item not in questions and len(questions) < 10:
- questions.append(item)
-
- #print datetime.datetime.now()
- return questions
-
-class TagManager(models.Manager):
- UPDATE_USED_COUNTS_QUERY = (
- 'UPDATE tag '
- 'SET used_count = ('
- 'SELECT COUNT(*) FROM question_tags '
- 'INNER JOIN question ON question_id=question.id '
- 'WHERE tag_id = tag.id AND question.deleted=False'
- ') '
- 'WHERE id IN (%s)')
-
- def get_valid_tags(self, page_size):
- from forum.models import Tag
- tags = Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
- return tags
-
- def get_or_create_multiple(self, names, user):
- """
- Fetches a list of Tags with the given names, creating any Tags
- which don't exist when necesssary.
- """
- tags = list(self.filter(name__in=names))
- #Set all these tag visible
- for tag in tags:
- if tag.deleted:
- tag.deleted = False
- tag.deleted_by = None
- tag.deleted_at = None
- tag.save()
-
- if len(tags) < len(names):
- existing_names = set(tag.name for tag in tags)
- new_names = [name for name in names if name not in existing_names]
- tags.extend([self.create(name=name, created_by=user)
- for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
-
- return tags
-
- def update_use_counts(self, tags):
- """Updates the given Tags with their current use counts."""
- if not tags:
- return
- cursor = connection.cursor()
- query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
- cursor.execute(query, [tag.id for tag in tags])
- transaction.commit_unless_managed()
-
- def get_tags_by_questions(self, questions):
- question_ids = []
- for question in questions:
- question_ids.append(question.id)
-
- question_ids_str = ','.join([str(id) for id in question_ids])
- related_tags = self.extra(
- tables=['tag', 'question_tags'],
- where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
- ).distinct()
-
- return related_tags
-
-class AnswerManager(models.Manager):
- GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
- def get_answers_from_question(self, question, user=None):
- """
- Retrieves visibile answers for the given question. Delete answers
- are only visibile to the person who deleted them.
- """
-
- if user is None or not user.is_authenticated():
- return self.filter(question=question, deleted=False)
- else:
- return self.filter(Q(question=question),
- Q(deleted=False) | Q(deleted_by=user))
-
- def get_answers_from_questions(self, user_id):
- """
- Retrieves visibile answers for the given question. Which are not included own answers
- """
- cursor = connection.cursor()
- cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
- return cursor.fetchall()
-
-class VoteManager(models.Manager):
- COUNT_UP_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = 1"
- COUNT_DOWN_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = -1"
- COUNT_VOTES_PER_DAY_BY_USER = "SELECT COUNT(*) FROM vote WHERE user_id = %s AND DATE(voted_at) = %s"
- def get_up_vote_count_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_UP_VOTE_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
- else:
- return 0
-
- def get_down_vote_count_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_DOWN_VOTE_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
- else:
- return 0
-
- def get_votes_count_today_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_VOTES_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-
-class FlaggedItemManager(models.Manager):
- COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = %s"
- def get_flagged_items_count_today(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-
-class ReputeManager(models.Manager):
- COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = %s"
- def get_reputation_by_upvoted_today(self, user):
- """
- For one user in one day, he can only earn rep till certain score (ep. +200)
- by upvoted(also substracted from upvoted canceled). This is because we need
- to prohibit gaming system by upvoting/cancel again and again.
- """
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-class AwardManager(models.Manager):
- def get_recent_awards(self):
- awards = super(AwardManager, self).extra(
- select={'badge_id': 'badge.id', 'badge_name':'badge.name',
- 'badge_description': 'badge.description', 'badge_type': 'badge.type',
- 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
- },
- tables=['award', 'badge', 'auth_user'],
- order_by=['-awarded_at'],
- where=['auth_user.id=award.user_id AND badge_id=badge.id'],
- ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
- return awards
diff --git a/forum/models.py b/forum/models.py
deleted file mode 100644
index acde1033..00000000
--- a/forum/models.py
+++ /dev/null
@@ -1,949 +0,0 @@
-# encoding:utf-8
-import datetime
-import hashlib
-from urllib import quote_plus, urlencode
-from django.db import models, IntegrityError
-from django.utils.http import urlquote as django_urlquote
-from django.utils.html import strip_tags
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
-from django.contrib.contenttypes import generic
-from django.contrib.contenttypes.models import ContentType
-from django.template.defaultfilters import slugify
-from django.db.models.signals import post_delete, post_save, pre_save
-from django.utils.translation import ugettext as _
-from django.utils.safestring import mark_safe
-from django.contrib.sitemaps import ping_google
-import django.dispatch
-from django.conf import settings
-import logging
-
-if settings.USE_SPHINX_SEARCH == True:
- from djangosphinx.models import SphinxSearch
-
-from forum.managers import *
-from const import *
-
-def get_object_comments(self):
- comments = self.comments.all().order_by('id')
- return comments
-
-def post_get_last_update_info(self):
- when = self.added_at
- who = self.author
- if self.last_edited_at and self.last_edited_at > when:
- when = self.last_edited_at
- who = self.last_edited_by
- comments = self.comments.all()
- if len(comments) > 0:
- for c in comments:
- if c.added_at > when:
- when = c.added_at
- who = c.user
- return when, who
-
-class EmailFeedSetting(models.Model):
- DELTA_TABLE = {
- 'w':datetime.timedelta(7),
- 'd':datetime.timedelta(1),
- 'n':datetime.timedelta(-1),
- }
- FEED_TYPES = (
- ('q_all',_('Entire forum')),
- ('q_ask',_('Questions that I asked')),
- ('q_ans',_('Questions that I answered')),
- ('q_sel',_('Individually selected questions')),
- )
- UPDATE_FREQUENCY = (
- ('w',_('Weekly')),
- ('d',_('Daily')),
- ('n',_('No email')),
- )
- subscriber = models.ForeignKey(User)
- feed_type = models.CharField(max_length=16,choices=FEED_TYPES)
- frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n')
- added_at = models.DateTimeField(auto_now_add=True)
- reported_at = models.DateTimeField(null=True)
-
- def save(self,*args,**kwargs):
- type = self.feed_type
- subscriber = self.subscriber
- similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id)
- if len(similar) > 0:
- raise IntegrityError('email feed setting already exists')
- super(EmailFeedSetting,self).save(*args,**kwargs)
-
-class Tag(models.Model):
- name = models.CharField(max_length=255, unique=True)
- created_by = models.ForeignKey(User, related_name='created_tags')
- deleted = models.BooleanField(default=False)
- deleted_at = models.DateTimeField(null=True, blank=True)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_tags')
- # Denormalised data
- used_count = models.PositiveIntegerField(default=0)
-
- objects = TagManager()
-
- class Meta:
- db_table = u'tag'
- ordering = ('-used_count', 'name')
-
- def __unicode__(self):
- return self.name
-
-class Comment(models.Model):
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='comments')
- comment = models.CharField(max_length=300)
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- class Meta:
- ordering = ('-added_at',)
- db_table = u'comment'
-
- def save(self,**kwargs):
- super(Comment,self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def __unicode__(self):
- return self.comment
-
-class Vote(models.Model):
- VOTE_UP = +1
- VOTE_DOWN = -1
- VOTE_CHOICES = (
- (VOTE_UP, u'Up'),
- (VOTE_DOWN, u'Down'),
- )
-
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='votes')
- vote = models.SmallIntegerField(choices=VOTE_CHOICES)
- voted_at = models.DateTimeField(default=datetime.datetime.now)
-
- objects = VoteManager()
-
- class Meta:
- unique_together = ('content_type', 'object_id', 'user')
- db_table = u'vote'
- def __unicode__(self):
- return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote)
-
- def is_upvote(self):
- return self.vote == self.VOTE_UP
-
- def is_downvote(self):
- return self.vote == self.VOTE_DOWN
-
-class FlaggedItem(models.Model):
- """A flag on a Question or Answer indicating offensive content."""
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='flagged_items')
- flagged_at = models.DateTimeField(default=datetime.datetime.now)
-
- objects = FlaggedItemManager()
-
- class Meta:
- unique_together = ('content_type', 'object_id', 'user')
- db_table = u'flagged_item'
- def __unicode__(self):
- return '[%s] flagged at %s' %(self.user, self.flagged_at)
-
-class Question(models.Model):
- title = models.CharField(max_length=300)
- author = models.ForeignKey(User, related_name='questions')
- added_at = models.DateTimeField(default=datetime.datetime.now)
- tags = models.ManyToManyField(Tag, related_name='questions')
- # Status
- wiki = models.BooleanField(default=False)
- wikified_at = models.DateTimeField(null=True, blank=True)
- answer_accepted = models.BooleanField(default=False)
- closed = models.BooleanField(default=False)
- closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
- closed_at = models.DateTimeField(null=True, blank=True)
- close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True)
- deleted = models.BooleanField(default=False)
- deleted_at = models.DateTimeField(null=True, blank=True)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_questions')
- locked = models.BooleanField(default=False)
- locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_questions')
- locked_at = models.DateTimeField(null=True, blank=True)
- followed_by = models.ManyToManyField(User, related_name='followed_questions')
- # Denormalised data
- score = models.IntegerField(default=0)
- vote_up_count = models.IntegerField(default=0)
- vote_down_count = models.IntegerField(default=0)
- answer_count = models.PositiveIntegerField(default=0)
- comment_count = models.PositiveIntegerField(default=0)
- view_count = models.PositiveIntegerField(default=0)
- offensive_flag_count = models.SmallIntegerField(default=0)
- favourite_count = models.PositiveIntegerField(default=0)
- last_edited_at = models.DateTimeField(null=True, blank=True)
- last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_questions')
- last_activity_at = models.DateTimeField(default=datetime.datetime.now)
- last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
- tagnames = models.CharField(max_length=125)
- summary = models.CharField(max_length=180)
- html = models.TextField()
- comments = generic.GenericRelation(Comment)
- votes = generic.GenericRelation(Vote)
- flagged_items = generic.GenericRelation(FlaggedItem)
-
- if settings.USE_SPHINX_SEARCH == True:
- search = SphinxSearch(
- index=' '.join(settings.SPHINX_SEARCH_INDICES),
- mode='SPH_MATCH_ALL',
- )
- logging.debug('have sphinx search')
-
- objects = QuestionManager()
-
- def delete(self):
- super(Question, self).delete()
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def save(self, **kwargs):
- """
- Overridden to manually manage addition of tags when the object
- is first saved.
-
- This is required as we're using ``tagnames`` as the sole means of
- adding and editing tags.
- """
- initial_addition = (self.id is None)
- super(Question, self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
- if initial_addition:
- tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
- self.author)
- self.tags.add(*tags)
- Tag.objects.update_use_counts(tags)
-
- def tagname_list(self):
- """Creates a list of Tag names from the ``tagnames`` attribute."""
- return [name for name in self.tagnames.split(u' ')]
-
- def tagname_meta_generator(self):
- return u','.join([unicode(tag) for tag in self.tagname_list()])
-
- def get_absolute_url(self):
- return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
-
- def has_favorite_by_user(self, user):
- if not user.is_authenticated():
- return False
- return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
-
- def get_answer_count_by_user(self, user_id):
- query_set = Answer.objects.filter(author__id=user_id)
- return query_set.filter(question=self).count()
-
- def get_question_title(self):
- if self.closed:
- attr = CONST['closed']
- elif self.deleted:
- attr = CONST['deleted']
- else:
- attr = None
- if attr is not None:
- return u'%s %s' % (self.title, attr)
- else:
- return self.title
-
- def get_revision_url(self):
- return reverse('question_revisions', args=[self.id])
-
- def get_latest_revision(self):
- return self.revisions.all()[0]
-
- get_comments = get_object_comments
-
- def get_last_update_info(self):
-
- when, who = post_get_last_update_info(self)
-
- answers = self.answers.all()
- if len(answers) > 0:
- for a in answers:
- a_when, a_who = a.get_last_update_info()
- if a_when > when:
- when = a_when
- who = a_who
-
- return when, who
-
- def get_update_summary(self,last_reported_at=None,recipient_email=''):
- edited = False
- if self.last_edited_at and self.last_edited_at > last_reported_at:
- if self.last_edited_by.email != recipient_email:
- edited = True
- comments = []
- for comment in self.comments.all():
- if comment.added_at > last_reported_at and comment.user.email != recipient_email:
- comments.append(comment)
- new_answers = []
- answer_comments = []
- modified_answers = []
- commented_answers = []
- import sets
- commented_answers = sets.Set([])
- for answer in self.answers.all():
- if (answer.added_at > last_reported_at and answer.author.email != recipient_email):
- new_answers.append(answer)
- if (answer.last_edited_at
- and answer.last_edited_at > last_reported_at
- and answer.last_edited_by.email != recipient_email):
- modified_answers.append(answer)
- for comment in answer.comments.all():
- if comment.added_at > last_reported_at and comment.user.email != recipient_email:
- commented_answers.add(answer)
- answer_comments.append(comment)
-
- #create the report
- if edited or new_answers or modified_answers or answer_comments:
- out = []
- if edited:
- out.append(_('%(author)s modified the question') % {'author':self.last_edited_by.username})
- if new_answers:
- names = sets.Set(map(lambda x: x.author.username,new_answers))
- people = ', '.join(names)
- out.append(_('%(people)s posted %(new_answer_count)s new answers') \
- % {'new_answer_count':len(new_answers),'people':people})
- if comments:
- names = sets.Set(map(lambda x: x.user.username,comments))
- people = ', '.join(names)
- out.append(_('%(people)s commented the question') % {'people':people})
- if answer_comments:
- names = sets.Set(map(lambda x: x.user.username,answer_comments))
- people = ', '.join(names)
- if len(commented_answers) > 1:
- out.append(_('%(people)s commented answers') % {'people':people})
- else:
- out.append(_('%(people)s commented an answer') % {'people':people})
- url = settings.APP_URL + self.get_absolute_url()
- retval = '<a href="%s">%s</a>:<br>\n' % (url,self.title)
- out = map(lambda x: '<li>' + x + '</li>',out)
- retval += '<ul>' + '\n'.join(out) + '</ul><br>\n'
- return retval
- else:
- return None
-
- def __unicode__(self):
- return self.title
-
- class Meta:
- db_table = u'question'
-
-class QuestionView(models.Model):
- question = models.ForeignKey(Question, related_name='viewed')
- who = models.ForeignKey(User, related_name='question_views')
- when = models.DateTimeField()
-
-class FavoriteQuestion(models.Model):
- """A favorite Question of a User."""
- question = models.ForeignKey(Question)
- user = models.ForeignKey(User, related_name='user_favorite_questions')
- added_at = models.DateTimeField(default=datetime.datetime.now)
- class Meta:
- db_table = u'favorite_question'
- def __unicode__(self):
- return '[%s] favorited at %s' %(self.user, self.added_at)
-
-class MarkedTag(models.Model):
- TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
- tag = models.ForeignKey(Tag, related_name='user_selections')
- user = models.ForeignKey(User, related_name='tag_selections')
- reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
-
-class QuestionRevision(models.Model):
- """A revision of a Question."""
- question = models.ForeignKey(Question, related_name='revisions')
- revision = models.PositiveIntegerField(blank=True)
- title = models.CharField(max_length=300)
- author = models.ForeignKey(User, related_name='question_revisions')
- revised_at = models.DateTimeField()
- tagnames = models.CharField(max_length=125)
- summary = models.CharField(max_length=300, blank=True)
- text = models.TextField()
-
- class Meta:
- db_table = u'question_revision'
- ordering = ('-revision',)
-
- def get_question_title(self):
- return self.question.title
-
- def get_absolute_url(self):
- #print 'in QuestionRevision.get_absolute_url()'
- return reverse('question_revisions', args=[self.question.id])
-
- def save(self, **kwargs):
- """Looks up the next available revision number."""
- if not self.revision:
- self.revision = QuestionRevision.objects.filter(
- question=self.question).values_list('revision',
- flat=True)[0] + 1
- super(QuestionRevision, self).save(**kwargs)
-
- def __unicode__(self):
- return u'revision %s of %s' % (self.revision, self.title)
-
-class AnonymousAnswer(models.Model):
- question = models.ForeignKey(Question, related_name='anonymous_answers')
- session_key = models.CharField(max_length=40) #session id for anonymous questions
- wiki = models.BooleanField(default=False)
- added_at = models.DateTimeField(default=datetime.datetime.now)
- ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
- author = models.ForeignKey(User,null=True)
- text = models.TextField()
- summary = models.CharField(max_length=180)
-
- def publish(self,user):
- from forum.views import create_new_answer
- added_at = datetime.datetime.now()
- #print user.id
- create_new_answer(question=self.question,wiki=self.wiki,
- added_at=added_at,text=self.text,
- author=user)
- self.delete()
-
-class AnonymousQuestion(models.Model):
- title = models.CharField(max_length=300)
- session_key = models.CharField(max_length=40) #session id for anonymous questions
- text = models.TextField()
- summary = models.CharField(max_length=180)
- tagnames = models.CharField(max_length=125)
- wiki = models.BooleanField(default=False)
- added_at = models.DateTimeField(default=datetime.datetime.now)
- ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
- author = models.ForeignKey(User,null=True)
-
- def publish(self,user):
- from forum.views import create_new_question
- added_at = datetime.datetime.now()
- create_new_question(title=self.title, author=user, added_at=added_at,
- wiki=self.wiki, tagnames=self.tagnames,
- summary=self.summary, text=self.text)
- self.delete()
-
-class Answer(models.Model):
- question = models.ForeignKey(Question, related_name='answers')
- author = models.ForeignKey(User, related_name='answers')
- added_at = models.DateTimeField(default=datetime.datetime.now)
- # Status
- wiki = models.BooleanField(default=False)
- wikified_at = models.DateTimeField(null=True, blank=True)
- accepted = models.BooleanField(default=False)
- accepted_at = models.DateTimeField(null=True, blank=True)
- deleted = models.BooleanField(default=False)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_answers')
- locked = models.BooleanField(default=False)
- locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_answers')
- locked_at = models.DateTimeField(null=True, blank=True)
- # Denormalised data
- score = models.IntegerField(default=0)
- vote_up_count = models.IntegerField(default=0)
- vote_down_count = models.IntegerField(default=0)
- comment_count = models.PositiveIntegerField(default=0)
- offensive_flag_count = models.SmallIntegerField(default=0)
- last_edited_at = models.DateTimeField(null=True, blank=True)
- last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_answers')
- html = models.TextField()
- comments = generic.GenericRelation(Comment)
- votes = generic.GenericRelation(Vote)
- flagged_items = generic.GenericRelation(FlaggedItem)
-
- objects = AnswerManager()
-
- get_comments = get_object_comments
- get_last_update_info = post_get_last_update_info
-
- def save(self,**kwargs):
- super(Answer,self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def get_user_vote(self, user):
- if user.__class__.__name__ == "AnonymousUser":
- return None
-
- votes = self.votes.filter(user=user)
- if votes and votes.count() > 0:
- return votes[0]
- else:
- return None
-
- def get_latest_revision(self):
- return self.revisions.all()[0]
-
- def get_question_title(self):
- return self.question.title
-
- def get_absolute_url(self):
- return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(slugify(self.question.title)), self.id)
-
- class Meta:
- db_table = u'answer'
-
- def __unicode__(self):
- return self.html
-
-class AnswerRevision(models.Model):
- """A revision of an Answer."""
- answer = models.ForeignKey(Answer, related_name='revisions')
- revision = models.PositiveIntegerField()
- author = models.ForeignKey(User, related_name='answer_revisions')
- revised_at = models.DateTimeField()
- summary = models.CharField(max_length=300, blank=True)
- text = models.TextField()
-
- def get_absolute_url(self):
- return reverse('answer_revisions', kwargs={'id':self.answer.id})
-
- def get_question_title(self):
- return self.answer.question.title
-
- class Meta:
- db_table = u'answer_revision'
- ordering = ('-revision',)
-
- def save(self, **kwargs):
- """Looks up the next available revision number if not set."""
- if not self.revision:
- self.revision = AnswerRevision.objects.filter(
- answer=self.answer).values_list('revision',
- flat=True)[0] + 1
- super(AnswerRevision, self).save(**kwargs)
-
-class Badge(models.Model):
- """Awarded for notable actions performed on the site by Users."""
- GOLD = 1
- SILVER = 2
- BRONZE = 3
- TYPE_CHOICES = (
- (GOLD, _('gold')),
- (SILVER, _('silver')),
- (BRONZE, _('bronze')),
- )
-
- name = models.CharField(max_length=50)
- type = models.SmallIntegerField(choices=TYPE_CHOICES)
- slug = models.SlugField(max_length=50, blank=True)
- description = models.CharField(max_length=300)
- multiple = models.BooleanField(default=False)
- # Denormalised data
- awarded_count = models.PositiveIntegerField(default=0)
-
- class Meta:
- db_table = u'badge'
- ordering = ('name',)
- unique_together = ('name', 'type')
-
- def __unicode__(self):
- return u'%s: %s' % (self.get_type_display(), self.name)
-
- def save(self, **kwargs):
- if not self.slug:
- self.slug = self.name#slugify(self.name)
- super(Badge, self).save(**kwargs)
-
- def get_absolute_url(self):
- return '%s%s/' % (reverse('badge', args=[self.id]), self.slug)
-
-class Award(models.Model):
- """The awarding of a Badge to a User."""
- user = models.ForeignKey(User, related_name='award_user')
- badge = models.ForeignKey(Badge, related_name='award_badge')
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- awarded_at = models.DateTimeField(default=datetime.datetime.now)
- notified = models.BooleanField(default=False)
- objects = AwardManager()
-
- def __unicode__(self):
- return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at)
-
- class Meta:
- db_table = u'award'
-
-class Repute(models.Model):
- """The reputation histories for user"""
- user = models.ForeignKey(User)
- positive = models.SmallIntegerField(default=0)
- negative = models.SmallIntegerField(default=0)
- question = models.ForeignKey(Question)
- reputed_at = models.DateTimeField(default=datetime.datetime.now)
- reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION)
- reputation = models.IntegerField(default=1)
- objects = ReputeManager()
-
- def __unicode__(self):
- return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at)
-
- class Meta:
- db_table = u'repute'
-
-class Activity(models.Model):
- """
- We keep some history data for user activities
- """
- user = models.ForeignKey(User)
- activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY)
- active_at = models.DateTimeField(default=datetime.datetime.now)
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- is_auditted = models.BooleanField(default=False)
-
- def __unicode__(self):
- return u'[%s] was active at %s' % (self.user.username, self.active_at)
-
- class Meta:
- db_table = u'activity'
-
-class Book(models.Model):
- """
- Model for book info
- """
- user = models.ForeignKey(User)
- title = models.CharField(max_length=255)
- short_name = models.CharField(max_length=255)
- author = models.CharField(max_length=255)
- price = models.DecimalField(max_digits=6, decimal_places=2)
- pages = models.SmallIntegerField()
- published_at = models.DateTimeField()
- publication = models.CharField(max_length=255)
- cover_img = models.CharField(max_length=255)
- tagnames = models.CharField(max_length=125)
- added_at = models.DateTimeField()
- last_edited_at = models.DateTimeField()
- questions = models.ManyToManyField(Question, related_name='book', db_table='book_question')
-
- def get_absolute_url(self):
- return reverse('book', args=[django_urlquote(slugify(self.short_name))])
-
- def __unicode__(self):
- return self.title
- class Meta:
- db_table = u'book'
-
-class BookAuthorInfo(models.Model):
- """
- Model for book author info
- """
- user = models.ForeignKey(User)
- book = models.ForeignKey(Book)
- blog_url = models.CharField(max_length=255)
- added_at = models.DateTimeField()
- last_edited_at = models.DateTimeField()
-
- class Meta:
- db_table = u'book_author_info'
-
-class BookAuthorRss(models.Model):
- """
- Model for book author blog rss
- """
- user = models.ForeignKey(User)
- book = models.ForeignKey(Book)
- title = models.CharField(max_length=255)
- url = models.CharField(max_length=255)
- rss_created_at = models.DateTimeField()
- added_at = models.DateTimeField()
-
- class Meta:
- db_table = u'book_author_rss'
-
-class AnonymousEmail(models.Model):
- #validation key, if used
- key = models.CharField(max_length=32)
- email = models.EmailField(null=False,unique=True)
- isvalid = models.BooleanField(default=False)
-
-# User extend properties
-QUESTIONS_PER_PAGE_CHOICES = (
- (10, u'10'),
- (30, u'30'),
- (50, u'50'),
-)
-
-def user_is_username_taken(cls,username):
- try:
- cls.objects.get(username=username)
- return True
- except cls.MultipleObjectsReturned:
- return True
- except cls.DoesNotExist:
- return False
-
-def user_get_q_sel_email_feed_frequency(self):
- #print 'looking for frequency for user %s' % self
- try:
- feed_setting = EmailFeedSetting.objects.get(subscriber=self,feed_type='q_sel')
- except Exception, e:
- #print 'have error %s' % e.message
- raise e
- #print 'have freq=%s' % feed_setting.frequency
- return feed_setting.frequency
-
-User.add_to_class('is_approved', models.BooleanField(default=False))
-User.add_to_class('email_isvalid', models.BooleanField(default=False))
-User.add_to_class('email_key', models.CharField(max_length=32, null=True))
-User.add_to_class('reputation', models.PositiveIntegerField(default=1))
-User.add_to_class('gravatar', models.CharField(max_length=32))
-User.add_to_class('favorite_questions',
- models.ManyToManyField(Question, through=FavoriteQuestion,
- related_name='favorited_by'))
-User.add_to_class('badges', models.ManyToManyField(Badge, through=Award,
- related_name='awarded_to'))
-User.add_to_class('gold', models.SmallIntegerField(default=0))
-User.add_to_class('silver', models.SmallIntegerField(default=0))
-User.add_to_class('bronze', models.SmallIntegerField(default=0))
-User.add_to_class('questions_per_page',
- models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10))
-User.add_to_class('last_seen',
- models.DateTimeField(default=datetime.datetime.now))
-User.add_to_class('real_name', models.CharField(max_length=100, blank=True))
-User.add_to_class('website', models.URLField(max_length=200, blank=True))
-User.add_to_class('location', models.CharField(max_length=100, blank=True))
-User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
-User.add_to_class('about', models.TextField(blank=True))
-User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
-User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
-User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
-User.add_to_class('tag_filter_setting',
- models.CharField(
- max_length=16,
- choices=TAG_EMAIL_FILTER_CHOICES,
- default='ignored'
- )
- )
-
-# custom signal
-tags_updated = django.dispatch.Signal(providing_args=["question"])
-edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"])
-delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
-mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"])
-user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"])
-user_logged_in = django.dispatch.Signal(providing_args=["session"])
-
-
-def get_messages(self):
- messages = []
- for m in self.message_set.all():
- messages.append(m.message)
- return messages
-
-def delete_messages(self):
- self.message_set.all().delete()
-
-def get_profile_url(self):
- """Returns the URL for this User's profile."""
- return '%s%s/' % (reverse('user', args=[self.id]), slugify(self.username))
-
-def get_profile_link(self):
- profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(),self.username)
- logging.debug('in get profile link %s' % profile_link)
- return mark_safe(profile_link)
-
-User.add_to_class('get_profile_url', get_profile_url)
-User.add_to_class('get_profile_link', get_profile_link)
-User.add_to_class('get_messages', get_messages)
-User.add_to_class('delete_messages', delete_messages)
-
-def calculate_gravatar_hash(instance, **kwargs):
- """Calculates a User's gravatar hash from their email address."""
- if kwargs.get('raw', False):
- return
- instance.gravatar = hashlib.md5(instance.email).hexdigest()
-
-def record_ask_event(instance, created, **kwargs):
- if created:
- activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION)
- activity.save()
-
-def record_answer_event(instance, created, **kwargs):
- if created:
- activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER)
- activity.save()
-
-def record_comment_event(instance, created, **kwargs):
- if created:
- from django.contrib.contenttypes.models import ContentType
- question_type = ContentType.objects.get_for_model(Question)
- question_type_id = question_type.id
- if (instance.content_type_id == question_type_id):
- type = TYPE_ACTIVITY_COMMENT_QUESTION
- else:
- type = TYPE_ACTIVITY_COMMENT_ANSWER
- activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type)
- activity.save()
-
-def record_revision_question_event(instance, created, **kwargs):
- if created and instance.revision <> 1:
- activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_QUESTION)
- activity.save()
-
-def record_revision_answer_event(instance, created, **kwargs):
- if created and instance.revision <> 1:
- activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_ANSWER)
- activity.save()
-
-def record_award_event(instance, created, **kwargs):
- """
- After we awarded a badge to user, we need to record this activity and notify user.
- We also recaculate awarded_count of this badge and user information.
- """
- if created:
- activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance,
- activity_type=TYPE_ACTIVITY_PRIZE)
- activity.save()
-
- instance.badge.awarded_count += 1
- instance.badge.save()
-
- if instance.badge.type == Badge.GOLD:
- instance.user.gold += 1
- if instance.badge.type == Badge.SILVER:
- instance.user.silver += 1
- if instance.badge.type == Badge.BRONZE:
- instance.user.bronze += 1
- instance.user.save()
-
-def notify_award_message(instance, created, **kwargs):
- """
- Notify users when they have been awarded badges by using Django message.
- """
- if created:
- user = instance.user
- user.message_set.create(message=u"Congratulations, you have received a badge '%s'" % instance.badge.name)
-
-def record_answer_accepted(instance, created, **kwargs):
- """
- when answer is accepted, we record this for question author - who accepted it.
- """
- if not created and instance.accepted:
- activity = Activity(user=instance.question.author, active_at=datetime.datetime.now(), \
- content_object=instance, activity_type=TYPE_ACTIVITY_MARK_ANSWER)
- activity.save()
-
-def update_last_seen(instance, created, **kwargs):
- """
- when user has activities, we update 'last_seen' time stamp for him
- """
- user = instance.user
- user.last_seen = datetime.datetime.now()
- user.save()
-
-def record_vote(instance, created, **kwargs):
- """
- when user have voted
- """
- if created:
- if instance.vote == 1:
- vote_type = TYPE_ACTIVITY_VOTE_UP
- else:
- vote_type = TYPE_ACTIVITY_VOTE_DOWN
-
- activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=vote_type)
- activity.save()
-
-def record_cancel_vote(instance, **kwargs):
- """
- when user canceled vote, the vote will be deleted.
- """
- activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_CANCEL_VOTE)
- activity.save()
-
-def record_delete_question(instance, delete_by, **kwargs):
- """
- when user deleted the question
- """
- if instance.__class__ == "Question":
- activity_type = TYPE_ACTIVITY_DELETE_QUESTION
- else:
- activity_type = TYPE_ACTIVITY_DELETE_ANSWER
-
- activity = Activity(user=delete_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=activity_type)
- activity.save()
-
-def record_mark_offensive(instance, mark_by, **kwargs):
- activity = Activity(user=mark_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE)
- activity.save()
-
-def record_update_tags(question, **kwargs):
- """
- when user updated tags of the question
- """
- activity = Activity(user=question.author, active_at=datetime.datetime.now(), content_object=question, activity_type=TYPE_ACTIVITY_UPDATE_TAGS)
- activity.save()
-
-def record_favorite_question(instance, created, **kwargs):
- """
- when user add the question in him favorite questions list.
- """
- if created:
- activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE)
- activity.save()
-
-def record_user_full_updated(instance, **kwargs):
- activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED)
- activity.save()
-
-def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs):
- aq_list = AnonymousQuestion.objects.filter(session_key = session_key)
- aa_list = AnonymousAnswer.objects.filter(session_key = session_key)
- import settings
- if settings.EMAIL_VALIDATION == 'on':#add user to the record
- for aq in aq_list:
- aq.author = user
- aq.save()
- for aa in aa_list:
- aa.author = user
- aa.save()
- #maybe add pending posts message?
- else: #just publish the questions
- for aq in aq_list:
- aq.publish(user)
- for aa in aa_list:
- aa.publish(user)
-
-#signal for User modle save changes
-pre_save.connect(calculate_gravatar_hash, sender=User)
-post_save.connect(record_ask_event, sender=Question)
-post_save.connect(record_answer_event, sender=Answer)
-post_save.connect(record_comment_event, sender=Comment)
-post_save.connect(record_revision_question_event, sender=QuestionRevision)
-post_save.connect(record_revision_answer_event, sender=AnswerRevision)
-post_save.connect(record_award_event, sender=Award)
-post_save.connect(notify_award_message, sender=Award)
-post_save.connect(record_answer_accepted, sender=Answer)
-post_save.connect(update_last_seen, sender=Activity)
-post_save.connect(record_vote, sender=Vote)
-post_delete.connect(record_cancel_vote, sender=Vote)
-delete_post_or_answer.connect(record_delete_question, sender=Question)
-delete_post_or_answer.connect(record_delete_question, sender=Answer)
-mark_offensive.connect(record_mark_offensive, sender=Question)
-mark_offensive.connect(record_mark_offensive, sender=Answer)
-tags_updated.connect(record_update_tags, sender=Question)
-post_save.connect(record_favorite_question, sender=FavoriteQuestion)
-user_updated.connect(record_user_full_updated, sender=User)
-user_logged_in.connect(post_stored_anonymous_content)
diff --git a/forum/sitemap.py b/forum/sitemap.py
deleted file mode 100644
index c0c60b5e..00000000
--- a/forum/sitemap.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.contrib.sitemaps import Sitemap
-from forum.models import Question
-
-class QuestionsSitemap(Sitemap):
- changefreq = 'daily'
- priority = 0.5
- def items(self):
- return Question.objects.exclude(deleted=True)
-
- def lastmod(self, obj):
- return obj.last_activity_at
-
- def location(self, obj):
- return obj.get_absolute_url()
diff --git a/forum/templatetags/__init__.py b/forum/templatetags/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/forum/templatetags/__init__.py
+++ /dev/null
diff --git a/forum/templatetags/extra_filters.py b/forum/templatetags/extra_filters.py
deleted file mode 100644
index 22ec0109..00000000
--- a/forum/templatetags/extra_filters.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from django import template
-from django.core import serializers
-from forum import auth
-import logging
-
-register = template.Library()
-
-@template.defaultfilters.stringfilter
-@register.filter
-def collapse(input):
- return ' '.join(input.split())
-
-@register.filter
-def can_moderate_users(user):
- return auth.can_moderate_users(user)
-
-@register.filter
-def can_vote_up(user):
- return auth.can_vote_up(user)
-
-@register.filter
-def can_flag_offensive(user):
- return auth.can_flag_offensive(user)
-
-@register.filter
-def can_add_comments(user,subject):
- return auth.can_add_comments(user,subject)
-
-@register.filter
-def can_vote_down(user):
- return auth.can_vote_down(user)
-
-@register.filter
-def can_retag_questions(user):
- return auth.can_retag_questions(user)
-
-@register.filter
-def can_edit_post(user, post):
- return auth.can_edit_post(user, post)
-
-@register.filter
-def can_delete_comment(user, comment):
- return auth.can_delete_comment(user, comment)
-
-@register.filter
-def can_view_offensive_flags(user):
- return auth.can_view_offensive_flags(user)
-
-@register.filter
-def can_close_question(user, question):
- return auth.can_close_question(user, question)
-
-@register.filter
-def can_lock_posts(user):
- return auth.can_lock_posts(user)
-
-@register.filter
-def can_accept_answer(user, question, answer):
- return auth.can_accept_answer(user, question, answer)
-
-@register.filter
-def can_reopen_question(user, question):
- return auth.can_reopen_question(user, question)
-
-@register.filter
-def can_delete_post(user, post):
- return auth.can_delete_post(user, post)
-
-@register.filter
-def can_view_user_edit(request_user, target_user):
- return auth.can_view_user_edit(request_user, target_user)
-
-@register.filter
-def can_view_user_votes(request_user, target_user):
- return auth.can_view_user_votes(request_user, target_user)
-
-@register.filter
-def can_view_user_preferences(request_user, target_user):
- return auth.can_view_user_preferences(request_user, target_user)
-
-@register.filter
-def is_user_self(request_user, target_user):
- return auth.is_user_self(request_user, target_user)
-
-@register.filter
-def cnprog_intword(number):
- try:
- if 1000 <= number < 10000:
- string = str(number)[0:1]
- return "<span class=""thousand"">%sk</span>" % string
- else:
- return number
- except:
- return number
-
-@register.filter
-def json_serialize(object):
- return serializers.serialize('json',object)
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
deleted file mode 100644
index 4f79e497..00000000
--- a/forum/templatetags/extra_tags.py
+++ /dev/null
@@ -1,351 +0,0 @@
-import time
-import os
-import posixpath
-import datetime
-import math
-import re
-import logging
-from django import template
-from django.utils.encoding import smart_unicode
-from django.utils.safestring import mark_safe
-from forum.const import *
-from forum.models import Question, Answer, QuestionRevision, AnswerRevision
-from django.utils.translation import ugettext as _
-from django.utils.translation import ungettext
-from django.conf import settings
-
-register = template.Library()
-
-GRAVATAR_TEMPLATE = ('<img class="gravatar" width="%(size)s" height="%(size)s" '
- 'src="http://www.gravatar.com/avatar/%(gravatar_hash)s'
- '?s=%(size)s&amp;d=identicon&amp;r=PG" '
- 'alt="%(username)s\'s gravatar image" />')
-
-@register.simple_tag
-def gravatar(user, size):
- """
- Creates an ``<img>`` for a user's Gravatar with a given size.
-
- This tag can accept a User object, or a dict containing the
- appropriate values.
- """
- try:
- gravatar = user['gravatar']
- username = user['username']
- except (TypeError, AttributeError, KeyError):
- gravatar = user.gravatar
- username = user.username
- return mark_safe(GRAVATAR_TEMPLATE % {
- 'size': size,
- 'gravatar_hash': gravatar,
- 'username': template.defaultfilters.urlencode(username),
- })
-
-MAX_FONTSIZE = 18
-MIN_FONTSIZE = 12
-@register.simple_tag
-def tag_font_size(max_size, min_size, current_size):
- """
- do a logarithmic mapping calcuation for a proper size for tagging cloud
- Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
- """
- #avoid invalid calculation
- if current_size == 0:
- current_size = 1
- try:
- weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
- except:
- weight = 0
- return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
-
-
-LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
-LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
-NUM_PAGES_OUTSIDE_RANGE = 1
-ADJACENT_PAGES = 2
-@register.inclusion_tag("paginator.html")
-def cnprog_paginator(context):
- """
- custom paginator tag
- Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
- """
- if (context["is_paginated"]):
- " Initialize variables "
- in_leading_range = in_trailing_range = False
- pages_outside_leading_range = pages_outside_trailing_range = range(0)
-
- if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
- in_leading_range = in_trailing_range = True
- page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
- elif (context["page"] <= LEADING_PAGE_RANGE):
- in_leading_range = True
- page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
- pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
- elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
- in_trailing_range = True
- page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
- pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
- else:
- page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
- pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
- pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
-
- extend_url = context.get('extend_url', '')
- return {
- "base_url": context["base_url"],
- "is_paginated": context["is_paginated"],
- "previous": context["previous"],
- "has_previous": context["has_previous"],
- "next": context["next"],
- "has_next": context["has_next"],
- "page": context["page"],
- "pages": context["pages"],
- "page_numbers": page_numbers,
- "in_leading_range" : in_leading_range,
- "in_trailing_range" : in_trailing_range,
- "pages_outside_leading_range": pages_outside_leading_range,
- "pages_outside_trailing_range": pages_outside_trailing_range,
- "extend_url" : extend_url
- }
-
-@register.inclusion_tag("pagesize.html")
-def cnprog_pagesize(context):
- """
- display the pagesize selection boxes for paginator
- """
- if (context["is_paginated"]):
- return {
- "base_url": context["base_url"],
- "pagesize" : context["pagesize"],
- "is_paginated": context["is_paginated"]
- }
-
-@register.inclusion_tag("post_contributor_info.html")
-def post_contributor_info(post,contributor_type='original_author'):
- """contributor_type: original_author|last_updater
- """
- if isinstance(post,Question):
- post_type = 'question'
- elif isinstance(post,Answer):
- post_type = 'answer'
- elif isinstance(post,AnswerRevision) or isinstance(post,QuestionRevision):
- post_type = 'revision'
- return {
- 'post':post,
- 'post_type':post_type,
- 'wiki_on':settings.WIKI_ON,
- 'contributor_type':contributor_type
- }
-
-@register.simple_tag
-def get_score_badge(user):
- BADGE_TEMPLATE = '<span class="score" title="%(reputation)s %(reputationword)s">%(reputation)s</span>'
- if user.gold > 0 :
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgesword)s">'
- '<span class="badge1">&#9679;</span>'
- '<span class="badgecount">%(gold)s</span>'
- '</span>')
- if user.silver > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgesword)s">'
- '<span class="silver">&#9679;</span>'
- '<span class="badgecount">%(silver)s</span>'
- '</span>')
- if user.bronze > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgesword)s">'
- '<span class="bronze">&#9679;</span>'
- '<span class="badgecount">%(bronze)s</span>'
- '</span>')
- BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
- return mark_safe(BADGE_TEMPLATE % {
- 'reputation' : user.reputation,
- 'gold' : user.gold,
- 'silver' : user.silver,
- 'bronze' : user.bronze,
- 'badgesword' : _('badges'),
- 'reputationword' : _('reputation points'),
- })
-
-@register.simple_tag
-def get_score_badge_by_details(rep, gold, silver, bronze):
- BADGE_TEMPLATE = '<span class="reputation-score" title="%(reputation)s %(repword)s">%(reputation)s</span>'
- if gold > 0 :
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(gold)s %(badgeword)s">'
- '<span class="badge1">&#9679;</span>'
- '<span class="badgecount">%(gold)s</span>'
- '</span>')
- if silver > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(silver)s %(badgeword)s">'
- '<span class="badge2">&#9679;</span>'
- '<span class="badgecount">%(silver)s</span>'
- '</span>')
- if bronze > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, '<span title="%(bronze)s %(badgeword)s">'
- '<span class="badge3">&#9679;</span>'
- '<span class="badgecount">%(bronze)s</span>'
- '</span>')
- BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
- return mark_safe(BADGE_TEMPLATE % {
- 'reputation' : rep,
- 'gold' : gold,
- 'silver' : silver,
- 'bronze' : bronze,
- 'repword' : _('reputation points'),
- 'badgeword' : _('badges'),
- })
-
-@register.simple_tag
-def get_user_vote_image(dic, key, arrow):
- if dic.has_key(key):
- if int(dic[key]) == int(arrow):
- return '-on'
- return ''
-
-@register.simple_tag
-def get_age(birthday):
- current_time = datetime.datetime(*time.localtime()[0:6])
- year = birthday.year
- month = birthday.month
- day = birthday.day
- diff = current_time - datetime.datetime(year,month,day,0,0,0)
- return diff.days / 365
-
-@register.simple_tag
-def get_total_count(up_count, down_count):
- return up_count + down_count
-
-@register.simple_tag
-def format_number(value):
- strValue = str(value)
- if len(strValue) <= 3:
- return strValue
- result = ''
- first = ''
- pattern = re.compile('(-?\d+)(\d{3})')
- m = re.match(pattern, strValue)
- while m != None:
- first = m.group(1)
- second = m.group(2)
- result = ',' + second + result
- strValue = first + ',' + second
- m = re.match(pattern, strValue)
- return first + result
-
-@register.simple_tag
-def convert2tagname_list(question):
- question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
- return ''
-
-@register.simple_tag
-def diff_date(date, limen=2):
- now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
- diff = now - date
- days = diff.days
- hours = int(diff.seconds/3600)
- minutes = int(diff.seconds/60)
-
- if days > 2:
- if date.year == now.year:
- return date.strftime("%b %d at %H:%M")
- else:
- return date.strftime("%b %d '%y at %H:%M")
- elif days == 2:
- return _('2 days ago')
- elif days == 1:
- return _('yesterday')
- elif minutes >= 60:
- return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
- else:
- return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
-
-@register.simple_tag
-def get_latest_changed_timestamp():
- try:
- from time import localtime, strftime
- from os import path
- root = settings.SITE_SRC_ROOT
- dir = (
- root,
- '%s/forum' % root,
- '%s/templates' % root,
- )
- stamp = (path.getmtime(d) for d in dir)
- latest = max(stamp)
- timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
- except:
- timestr = ''
- return timestr
-
-@register.simple_tag
-def href(url):
- url = '///' + settings.FORUM_SCRIPT_ALIAS + '/' + url
- return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
-
-class ItemSeparatorNode(template.Node):
- def __init__(self,separator):
- sep = separator.strip()
- if sep[0] == sep[-1] and sep[0] in ('\'','"'):
- sep = sep[1:-1]
- else:
- raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
- self.content = sep
- def render(self,context):
- return self.content
-
-class JoinItemListNode(template.Node):
- def __init__(self,separator=ItemSeparatorNode("''"), items=()):
- self.separator = separator
- self.items = items
- def render(self,context):
- out = []
- empty_re = re.compile(r'^\s*$')
- for item in self.items:
- bit = item.render(context)
- if not empty_re.search(bit):
- out.append(bit)
- return self.separator.render(context).join(out)
-
-@register.tag(name="joinitems")
-def joinitems(parser,token):
- try:
- tagname,junk,sep_token = token.split_contents()
- except ValueError:
- raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
- if junk == 'using':
- sep_node = ItemSeparatorNode(sep_token)
- else:
- raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
- nodelist = []
- while True:
- nodelist.append(parser.parse(('separator','endjoinitems')))
- next = parser.next_token()
- if next.contents == 'endjoinitems':
- break
-
- return JoinItemListNode(separator=sep_node,items=nodelist)
-
-class BlockResourceNode(template.Node):
- def __init__(self,nodelist):
- self.items = nodelist
- def render(self,context):
- out = '///' + settings.FORUM_SCRIPT_ALIAS
- if self.items:
- out += '/'
- for item in self.items:
- bit = item.render(context)
- out += bit
- out = os.path.normpath(out) + '?v=%d' % settings.RESOURCE_REVISION
- return out.replace(' ','')
-
-@register.tag(name='blockresource')
-def blockresource(parser,token):
- try:
- tagname = token.split_contents()
- except ValueError:
- raise template.TemplateSyntaxError("blockresource tag does not use arguments")
- nodelist = []
- while True:
- nodelist.append(parser.parse(('endblockresource')))
- next = parser.next_token()
- if next.contents == 'endblockresource':
- break
- return BlockResourceNode(nodelist)
diff --git a/forum/templatetags/smart_if.py b/forum/templatetags/smart_if.py
deleted file mode 100644
index ca3b43fe..00000000
--- a/forum/templatetags/smart_if.py
+++ /dev/null
@@ -1,401 +0,0 @@
-"""
-A smarter {% if %} tag for django templates.
-
-While retaining current Django functionality, it also handles equality,
-greater than and less than operators. Some common case examples::
-
- {% if articles|length >= 5 %}...{% endif %}
- {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
-"""
-import unittest
-from django import template
-
-
-register = template.Library()
-
-
-#==============================================================================
-# Calculation objects
-#==============================================================================
-
-class BaseCalc(object):
- def __init__(self, var1, var2=None, negate=False):
- self.var1 = var1
- self.var2 = var2
- self.negate = negate
-
- def resolve(self, context):
- try:
- var1, var2 = self.resolve_vars(context)
- outcome = self.calculate(var1, var2)
- except:
- outcome = False
- if self.negate:
- return not outcome
- return outcome
-
- def resolve_vars(self, context):
- var2 = self.var2 and self.var2.resolve(context)
- return self.var1.resolve(context), var2
-
- def calculate(self, var1, var2):
- raise NotImplementedError()
-
-
-class Or(BaseCalc):
- def calculate(self, var1, var2):
- return var1 or var2
-
-
-class And(BaseCalc):
- def calculate(self, var1, var2):
- return var1 and var2
-
-
-class Equals(BaseCalc):
- def calculate(self, var1, var2):
- return var1 == var2
-
-
-class Greater(BaseCalc):
- def calculate(self, var1, var2):
- return var1 > var2
-
-
-class GreaterOrEqual(BaseCalc):
- def calculate(self, var1, var2):
- return var1 >= var2
-
-
-class In(BaseCalc):
- def calculate(self, var1, var2):
- return var1 in var2
-
-
-#==============================================================================
-# Tests
-#==============================================================================
-
-class TestVar(object):
- """
- A basic self-resolvable object similar to a Django template variable. Used
- to assist with tests.
- """
- def __init__(self, value):
- self.value = value
-
- def resolve(self, context):
- return self.value
-
-
-class SmartIfTests(unittest.TestCase):
- def setUp(self):
- self.true = TestVar(True)
- self.false = TestVar(False)
- self.high = TestVar(9000)
- self.low = TestVar(1)
-
- def assertCalc(self, calc, context=None):
- """
- Test a calculation is True, also checking the inverse "negate" case.
- """
- context = context or {}
- self.assert_(calc.resolve(context))
- calc.negate = not calc.negate
- self.assertFalse(calc.resolve(context))
-
- def assertCalcFalse(self, calc, context=None):
- """
- Test a calculation is False, also checking the inverse "negate" case.
- """
- context = context or {}
- self.assertFalse(calc.resolve(context))
- calc.negate = not calc.negate
- self.assert_(calc.resolve(context))
-
- def test_or(self):
- self.assertCalc(Or(self.true))
- self.assertCalcFalse(Or(self.false))
- self.assertCalc(Or(self.true, self.true))
- self.assertCalc(Or(self.true, self.false))
- self.assertCalc(Or(self.false, self.true))
- self.assertCalcFalse(Or(self.false, self.false))
-
- def test_and(self):
- self.assertCalc(And(self.true, self.true))
- self.assertCalcFalse(And(self.true, self.false))
- self.assertCalcFalse(And(self.false, self.true))
- self.assertCalcFalse(And(self.false, self.false))
-
- def test_equals(self):
- self.assertCalc(Equals(self.low, self.low))
- self.assertCalcFalse(Equals(self.low, self.high))
-
- def test_greater(self):
- self.assertCalc(Greater(self.high, self.low))
- self.assertCalcFalse(Greater(self.low, self.low))
- self.assertCalcFalse(Greater(self.low, self.high))
-
- def test_greater_or_equal(self):
- self.assertCalc(GreaterOrEqual(self.high, self.low))
- self.assertCalc(GreaterOrEqual(self.low, self.low))
- self.assertCalcFalse(GreaterOrEqual(self.low, self.high))
-
- def test_in(self):
- list_ = TestVar([1,2,3])
- invalid_list = TestVar(None)
- self.assertCalc(In(self.low, list_))
- self.assertCalcFalse(In(self.low, invalid_list))
-
- def test_parse_bits(self):
- var = IfParser([True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([False]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([False, 'or', True]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([False, 'and', True]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser(['not', False, 'and', 'not', False]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser(['not', 'not', True]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, '=', 1]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, 'not', '=', 1]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([1, 'not', 'not', '=', 1]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, '!=', 1]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([3, '>', 2]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([1, '<', 2]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([2, 'not', 'in', [2, 3]]).parse()
- self.assertFalse(var.resolve({}))
-
- var = IfParser([1, 'or', 1, '=', 2]).parse()
- self.assert_(var.resolve({}))
-
- def test_boolean(self):
- var = IfParser([True, 'and', True, 'and', True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([False, 'or', False, 'or', True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([True, 'and', False, 'or', True]).parse()
- self.assert_(var.resolve({}))
- var = IfParser([False, 'or', True, 'and', True]).parse()
- self.assert_(var.resolve({}))
-
- var = IfParser([True, 'and', True, 'and', False]).parse()
- self.assertFalse(var.resolve({}))
- var = IfParser([False, 'or', False, 'or', False]).parse()
- self.assertFalse(var.resolve({}))
- var = IfParser([False, 'or', True, 'and', False]).parse()
- self.assertFalse(var.resolve({}))
- var = IfParser([False, 'and', True, 'or', False]).parse()
- self.assertFalse(var.resolve({}))
-
- def test_invalid(self):
- self.assertRaises(ValueError, IfParser(['not']).parse)
- self.assertRaises(ValueError, IfParser(['==']).parse)
- self.assertRaises(ValueError, IfParser([1, 'in']).parse)
- self.assertRaises(ValueError, IfParser([1, '>', 'in']).parse)
- self.assertRaises(ValueError, IfParser([1, '==', 'not', 'not']).parse)
- self.assertRaises(ValueError, IfParser([1, 2]).parse)
-
-
-OPERATORS = {
- '=': (Equals, True),
- '==': (Equals, True),
- '!=': (Equals, False),
- '>': (Greater, True),
- '>=': (GreaterOrEqual, True),
- '<=': (Greater, False),
- '<': (GreaterOrEqual, False),
- 'or': (Or, True),
- 'and': (And, True),
- 'in': (In, True),
-}
-BOOL_OPERATORS = ('or', 'and')
-
-
-class IfParser(object):
- error_class = ValueError
-
- def __init__(self, tokens):
- self.tokens = tokens
-
- def _get_tokens(self):
- return self._tokens
-
- def _set_tokens(self, tokens):
- self._tokens = tokens
- self.len = len(tokens)
- self.pos = 0
-
- tokens = property(_get_tokens, _set_tokens)
-
- def parse(self):
- if self.at_end():
- raise self.error_class('No variables provided.')
- var1 = self.get_bool_var()
- while not self.at_end():
- op, negate = self.get_operator()
- var2 = self.get_bool_var()
- var1 = op(var1, var2, negate=negate)
- return var1
-
- def get_token(self, eof_message=None, lookahead=False):
- negate = True
- token = None
- pos = self.pos
- while token is None or token == 'not':
- if pos >= self.len:
- if eof_message is None:
- raise self.error_class()
- raise self.error_class(eof_message)
- token = self.tokens[pos]
- negate = not negate
- pos += 1
- if not lookahead:
- self.pos = pos
- return token, negate
-
- def at_end(self):
- return self.pos >= self.len
-
- def create_var(self, value):
- return TestVar(value)
-
- def get_bool_var(self):
- """
- Returns either a variable by itself or a non-boolean operation (such as
- ``x == 0`` or ``x < 0``).
-
- This is needed to keep correct precedence for boolean operations (i.e.
- ``x or x == 0`` should be ``x or (x == 0)``, not ``(x or x) == 0``).
- """
- var = self.get_var()
- if not self.at_end():
- op_token = self.get_token(lookahead=True)[0]
- if isinstance(op_token, basestring) and (op_token not in
- BOOL_OPERATORS):
- op, negate = self.get_operator()
- return op(var, self.get_var(), negate=negate)
- return var
-
- def get_var(self):
- token, negate = self.get_token('Reached end of statement, still '
- 'expecting a variable.')
- if isinstance(token, basestring) and token in OPERATORS:
- raise self.error_class('Expected variable, got operator (%s).' %
- token)
- var = self.create_var(token)
- if negate:
- return Or(var, negate=True)
- return var
-
- def get_operator(self):
- token, negate = self.get_token('Reached end of statement, still '
- 'expecting an operator.')
- if not isinstance(token, basestring) or token not in OPERATORS:
- raise self.error_class('%s is not a valid operator.' % token)
- if self.at_end():
- raise self.error_class('No variable provided after "%s".' % token)
- op, true = OPERATORS[token]
- if not true:
- negate = not negate
- return op, negate
-
-
-#==============================================================================
-# Actual templatetag code.
-#==============================================================================
-
-class TemplateIfParser(IfParser):
- error_class = template.TemplateSyntaxError
-
- def __init__(self, parser, *args, **kwargs):
- self.template_parser = parser
- return super(TemplateIfParser, self).__init__(*args, **kwargs)
-
- def create_var(self, value):
- return self.template_parser.compile_filter(value)
-
-
-class SmartIfNode(template.Node):
- def __init__(self, var, nodelist_true, nodelist_false=None):
- self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
- self.var = var
-
- def render(self, context):
- if self.var.resolve(context):
- return self.nodelist_true.render(context)
- if self.nodelist_false:
- return self.nodelist_false.render(context)
- return ''
-
- def __repr__(self):
- return "<Smart If node>"
-
- def __iter__(self):
- for node in self.nodelist_true:
- yield node
- if self.nodelist_false:
- for node in self.nodelist_false:
- yield node
-
- def get_nodes_by_type(self, nodetype):
- nodes = []
- if isinstance(self, nodetype):
- nodes.append(self)
- nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
- if self.nodelist_false:
- nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
- return nodes
-
-
-@register.tag('if')
-def smart_if(parser, token):
- """
- A smarter {% if %} tag for django templates.
-
- While retaining current Django functionality, it also handles equality,
- greater than and less than operators. Some common case examples::
-
- {% if articles|length >= 5 %}...{% endif %}
- {% if "ifnotequal tag" != "beautiful" %}...{% endif %}
-
- Arguments and operators _must_ have a space between them, so
- ``{% if 1>2 %}`` is not a valid smart if tag.
-
- All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``),
- ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
- """
- bits = token.split_contents()[1:]
- var = TemplateIfParser(parser, bits).parse()
- nodelist_true = parser.parse(('else', 'endif'))
- token = parser.next_token()
- if token.contents == 'else':
- nodelist_false = parser.parse(('endif',))
- parser.delete_first_token()
- else:
- nodelist_false = None
- return SmartIfNode(var, nodelist_true, nodelist_false)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/forum/urls.py b/forum/urls.py
deleted file mode 100644
index 42746d44..00000000
--- a/forum/urls.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import os.path
-from django.conf.urls.defaults import *
-from django.contrib import admin
-from forum import views as app
-from forum.feed import RssLastestQuestionsFeed
-from forum.sitemap import QuestionsSitemap
-from django.utils.translation import ugettext as _
-
-admin.autodiscover()
-feeds = {
- 'rss': RssLastestQuestionsFeed
-}
-sitemaps = {
- 'questions': QuestionsSitemap
-}
-
-APP_PATH = os.path.dirname(os.path.dirname(__file__))
-urlpatterns = patterns('',
- url(r'^$', app.index, name='index'),
- url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
- (r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/content/images/favicon.ico'}),
- (r'^favicon\.gif$', 'django.views.generic.simple.redirect_to', {'url': '/content/images/favicon.gif'}),
- (r'^content/(?P<path>.*)$', 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH, 'templates/content').replace('\\','/')}
- ),
- (r'^%s(?P<path>.*)$' % _('upfiles/'), 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH, 'templates/upfiles').replace('\\','/')}
- ),
- (r'^%s/$' % _('signin/'), 'django_authopenid.views.signin'),
- url(r'^%s$' % _('about/'), app.about, name='about'),
- url(r'^%s$' % _('faq/'), app.faq, name='faq'),
- url(r'^%s$' % _('privacy/'), app.privacy, name='privacy'),
- url(r'^%s$' % _('logout/'), app.logout, name='logout'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('comments/')), app.answer_comments, name='answer_comments'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('edit/')), app.edit_answer, name='edit_answer'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')), app.answer_revisions, name='answer_revisions'),
- url(r'^%s$' % _('questions/'), app.questions, name='questions'),
- url(r'^%s%s$' % (_('questions/'), _('ask/')), app.ask, name='ask'),
- url(r'^%s%s$' % (_('questions/'), _('unanswered/')), app.unanswered, name='unanswered'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('edit/')), app.edit_question, name='edit_question'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')), app.close, name='close'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')), app.reopen, name='reopen'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')), app.answer, name='answer'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.vote, name='vote'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.question_revisions, name='question_revisions'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.question_comments, name='question_comments'),
- url(r'^%s$' % _('command/'), app.ajax_command, name='call_ajax'),
-
- url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('questions/'), _('comments/'),_('delete/')), \
- app.delete_comment, kwargs={'commented_object_type':'question'},\
- name='delete_question_comment'),
-
- url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('answers/'), _('comments/'),_('delete/')), \
- app.delete_comment, kwargs={'commented_object_type':'answer'}, \
- name='delete_answer_comment'), \
- #place general question item in the end of other operations
- url(r'^%s(?P<id>\d+)/' % _('question/'), app.question, name='question'),
- url(r'^%s$' % _('tags/'), app.tags, name='tags'),
- url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.tag, name='tag_questions'),
-
- url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.mark_tag, \
- kwargs={'reason':'good','action':'add'}, \
- name='mark_interesting_tag'),
-
- url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.mark_tag, \
- kwargs={'reason':'bad','action':'add'}, \
- name='mark_ignored_tag'),
-
- url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.mark_tag, \
- kwargs={'action':'remove'}, \
- name='mark_ignored_tag'),
-
- url(r'^%s$' % _('users/'),app.users, name='users'),
- url(r'^%s(?P<id>\d+)/$' % _('moderate-user/'), app.moderate_user, name='moderate_user'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), app.edit_user, name='edit_user'),
- url(r'^%s(?P<id>\d+)//*' % _('users/'), app.user, name='user'),
- url(r'^%s$' % _('badges/'),app.badges, name='badges'),
- url(r'^%s(?P<id>\d+)//*' % _('badges/'), app.badge, name='badge'),
- url(r'^%s%s$' % (_('messages/'), _('markread/')),app.read_message, name='read_message'),
- # (r'^admin/doc/' % _('admin/doc'), include('django.contrib.admindocs.urls')),
- (r'^%s(.*)' % _('nimda/'), admin.site.root),
- url(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
- (r'^%s$' % _('upload/'), app.upload),
- url(r'^%s$' % _('books/'), app.books, name='books'),
- url(r'^%s%s(?P<short_name>[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'),
- url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.book, name='book'),
- url(r'^%s$' % _('search/'), app.search, name='search'),
- url(r'^%s$' % _('feedback/'), app.feedback, name='feedback'),
- (r'^%sfb/' % _('account/'), include('fbconnect.urls')),
- (r'^%s' % _('account/'), include('django_authopenid.urls')),
- (r'^i18n/', include('django.conf.urls.i18n')),
-)
diff --git a/forum/user.py b/forum/user.py
deleted file mode 100644
index 40bf6a89..00000000
--- a/forum/user.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from django.utils.translation import ugettext as _
-class UserView:
- def __init__(self, id, tab_title, tab_description, page_title, view_name, template_file, data_size=0):
- self.id = id
- self.tab_title = tab_title
- self.tab_description = tab_description
- self.page_title = page_title
- self.view_name = view_name
- self.template_file = template_file
- self.data_size = data_size
-
-
-USER_TEMPLATE_VIEWS = (
- UserView(
- id = 'stats',
- tab_title = _('overview'),
- tab_description = _('user profile'),
- page_title = _('user profile overview'),
- view_name = 'user_stats',
- template_file = 'user_stats.html'
- ),
- UserView(
- id = 'recent',
- tab_title = _('recent activity'),
- tab_description = _('recent user activity'),
- page_title = _('profile - recent activity'),
- view_name = 'user_recent',
- template_file = 'user_recent.html',
- data_size = 50
- ),
- UserView(
- id = 'responses',
- tab_title = _('responses'),
- tab_description = _('comments and answers to others questions'),
- page_title = _('profile - responses'),
- view_name = 'user_responses',
- template_file = 'user_responses.html',
- data_size = 50
- ),
- UserView(
- id = 'reputation',
- tab_title = _('reputation'),
- tab_description = _('user reputation in the community'),
- page_title = _('profile - user reputation'),
- view_name = 'user_reputation',
- template_file = 'user_reputation.html'
- ),
- UserView(
- id = 'favorites',
- tab_title = _('favorite questions'),
- tab_description = _('users favorite questions'),
- page_title = _('profile - favorite questions'),
- view_name = 'user_favorites',
- template_file = 'user_favorites.html',
- data_size = 50
- ),
- UserView(
- id = 'votes',
- tab_title = _('casted votes'),
- tab_description = _('user vote record'),
- page_title = _('profile - votes'),
- view_name = 'user_votes',
- template_file = 'user_votes.html',
- data_size = 50
- ),
- UserView(
- id = 'email_subscriptions',
- tab_title = _('email subscriptions'),
- tab_description = _('email subscription settings'),
- page_title = _('profile - email subscriptions'),
- view_name = 'user_email_subscriptions',
- template_file = 'user_email_subscriptions.html'
- )
-)
diff --git a/forum/views.py b/forum/views.py
deleted file mode 100644
index 4d214bad..00000000
--- a/forum/views.py
+++ /dev/null
@@ -1,2412 +0,0 @@
-# encoding:utf-8
-import os.path
-import time, datetime, calendar, random
-import logging
-from urllib import quote, unquote
-from django.conf import settings
-from django.core.files.storage import default_storage
-from django.shortcuts import render_to_response, get_object_or_404
-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
-#from lxml.html.diff import htmldiff
-from forum.diff import textDiff as htmldiff
-from forum.forms import *
-from forum.models import *
-from forum.auth import *
-from forum.const import *
-from forum.user import *
-from forum import auth
-from utils.forms 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"
- # 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
- 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)
- try:
- pattern = r'/%s%s%d/([\w-]+)' % (settings.FORUM_SCRIPT_ALIAS,_('question/'), question.id)
- path_re = re.compile(pattern)
- logging.debug(pattern)
- logging.debug(request.path)
- m = path_re.match(request.path)
- if m:
- slug = m.group(1)
- logging.debug('have slug %s' % slug)
- assert(slug == slugify(question.title))
- else:
- logging.debug('no match!')
- except:
- return HttpResponseRedirect(question.get_absolute_url())
-
- 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)
- 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 answer in answers:
- vote = answer.get_user_vote(request.user)
- if vote is not None and not user_answer_votes.has_key(answer.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 = ('<h1>%(title)s</h1>\n'
- '<div class="text">%(html)s</div>\n'
- '<div class="tags">%(tags)s</div>')
-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(['<a class="post-tag">%s</a>' % 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(['<a class="post-tag">%s</a>' % 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 = ('<div class="text">%(html)s</div>')
-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))
-
-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 == "name":
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
- else:
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), 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'] += '<br/>'
- 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=False 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=False AND question.deleted=False 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=False 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=False AND activity.user_id=%s AND '+
- 'activity.activity_type=%s AND question.deleted=False'],
- 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=False'],
- 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=False AND question.deleted=False'],
- 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=False 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=False AND answer.deleted=False 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=False AND question.deleted=False 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=False AND question.deleted=False 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=False 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=False 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=False AND question.deleted=False AND '+
- 'answer.author_id = %s AND answer.accepted=True 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=True 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]
- ).distinct('id')
-
- 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
-
- #<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>
- xml_template = "<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>"
-
- 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_PG_FTS:
- objects = Question.objects.filter(deleted=False).extra(
- select={
- 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)",
- },
- where=["tsv @@ plainto_tsquery(%s)"],
- params=[keywords],
- select_params=[keywords]
- ).order_by('-ranking')
-
- elif 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)
-
- #if is_search is true in the context, prepend this string to soting tabs urls
- search_uri = "?q=%s&page=%d&t=question" % ("+".join(keywords.split()), page)
-
- 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,
- "is_search": True,
- "search_uri": search_uri,
- "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