2 Authorisation related functions.
4 The actions a User is authorised to perform are dependent on their reputation
8 from django.contrib.contenttypes.models import ContentType
9 from django.utils.translation import ugettext as _
10 from django.db import transaction
11 from models import Repute
12 from models import Question
13 from models import Answer
14 from const import TYPE_REPUTATION
16 question_type = ContentType.objects.get_for_model(Question)
17 answer_type = ContentType.objects.get_for_model(Answer)
25 CLOSE_OWN_QUESTIONS = 250
26 RETAG_OTHER_QUESTIONS = 500
27 REOPEN_OWN_QUESTIONS = 500
28 EDIT_COMMUNITY_WIKI_POSTS = 750
29 EDIT_OTHER_POSTS = 2000
30 DELETE_COMMENTS = 2000
31 VIEW_OFFENSIVE_FLAGS = 2000
32 DISABLE_URL_NOFOLLOW = 2000
33 CLOSE_OTHER_QUESTIONS = 3000
37 'scope_votes_per_user_per_day' : 30, # how many votes of one user has everyday
38 'scope_flags_per_user_per_day' : 5, # how many times user can flag posts everyday
39 'scope_warn_votes_left' : 10, # start when to warn user how many votes left
40 'scope_deny_unvote_days' : 1, # if 1 days passed, user can't cancel votes.
41 'scope_flags_invisible_main_page' : 3, # post doesn't show on main page if has more than 3 offensive flags
42 'scope_flags_delete_post' : 5, # post will be deleted if it has more than 5 offensive flags
47 'scope_per_day_by_upvotes' : 200,
48 'gain_by_upvoted' : 10,
49 'gain_by_answer_accepted' : 15,
50 'gain_by_accepting_answer' : 2,
51 'gain_by_downvote_canceled' : 2,
52 'gain_by_canceling_downvote' : 1,
53 'lose_by_canceling_accepted_answer' : -2,
54 'lose_by_accepted_answer_cancled' : -15,
55 'lose_by_downvoted' : -2,
56 'lose_by_flagged' : -2,
57 'lose_by_downvoting' : -1,
58 'lose_by_flagged_lastrevision_3_times': -30,
59 'lose_by_flagged_lastrevision_5_times': -100,
60 'lose_by_upvote_canceled' : -10,
63 def can_moderate_users(user):
64 return user.is_superuser
66 def can_vote_up(user):
67 """Determines if a User can vote Questions and Answers up."""
68 return user.is_authenticated() and (
69 user.reputation >= VOTE_UP or
72 def can_flag_offensive(user):
73 """Determines if a User can flag Questions and Answers as offensive."""
74 return user.is_authenticated() and (
75 user.reputation >= FLAG_OFFENSIVE or
78 def can_add_comments(user,subject):
79 """Determines if a User can add comments to Questions and Answers."""
80 if user.is_authenticated():
81 if user.id == subject.author.id:
83 if user.reputation >= LEAVE_COMMENTS:
87 if isinstance(subject,Answer) and subject.question.author.id == user.id:
91 def can_vote_down(user):
92 """Determines if a User can vote Questions and Answers down."""
93 return user.is_authenticated() and (
94 user.reputation >= VOTE_DOWN or
97 def can_retag_questions(user):
98 """Determines if a User can retag Questions."""
99 return user.is_authenticated() and (
100 RETAG_OTHER_QUESTIONS <= user.reputation < EDIT_OTHER_POSTS or
103 def can_edit_post(user, post):
104 """Determines if a User can edit the given Question or Answer."""
105 return user.is_authenticated() and (
106 user.id == post.author_id or
107 (post.wiki and user.reputation >= EDIT_COMMUNITY_WIKI_POSTS) or
108 user.reputation >= EDIT_OTHER_POSTS or
111 def can_delete_comment(user, comment):
112 """Determines if a User can delete the given Comment."""
113 return user.is_authenticated() and (
114 user.id == comment.user_id or
115 user.reputation >= DELETE_COMMENTS or
118 def can_view_offensive_flags(user):
119 """Determines if a User can view offensive flag counts."""
120 return user.is_authenticated() and (
121 user.reputation >= VIEW_OFFENSIVE_FLAGS or
124 def can_close_question(user, question):
125 """Determines if a User can close the given Question."""
126 return user.is_authenticated() and (
127 (user.id == question.author_id and
128 user.reputation >= CLOSE_OWN_QUESTIONS) or
129 user.reputation >= CLOSE_OTHER_QUESTIONS or
132 def can_lock_posts(user):
133 """Determines if a User can lock Questions or Answers."""
134 return user.is_authenticated() and (
135 user.reputation >= LOCK_POSTS or
138 def can_follow_url(user):
139 """Determines if the URL link can be followed by Google search engine."""
140 return user.reputation >= DISABLE_URL_NOFOLLOW
142 def can_accept_answer(user, question, answer):
143 return (user.is_authenticated() and
144 question.author != answer.author and
145 question.author == user) or user.is_superuser
147 # now only support to reopen own question except superuser
148 def can_reopen_question(user, question):
149 return (user.is_authenticated() and
150 user.id == question.author_id and
151 user.reputation >= REOPEN_OWN_QUESTIONS) or user.is_superuser
153 def can_delete_post(user, post):
154 if user.is_superuser:
156 elif user.is_authenticated() and user == post.author:
157 if isinstance(post,Answer):
159 elif isinstance(post,Question):
160 answers = post.answers.all()
161 for answer in answers:
162 if user != answer.author and answer.deleted == False:
170 def can_view_deleted_post(user, post):
171 return user.is_superuser
173 # user preferences view permissions
174 def is_user_self(request_user, target_user):
175 return (request_user.is_authenticated() and request_user == target_user)
177 def can_view_user_votes(request_user, target_user):
178 return (request_user.is_authenticated() and request_user == target_user)
180 def can_view_user_preferences(request_user, target_user):
181 return (request_user.is_authenticated() and request_user == target_user)
183 def can_view_user_edit(request_user, target_user):
184 return (request_user.is_authenticated() and request_user == target_user)
186 def can_upload_files(request_user):
187 return (request_user.is_authenticated() and request_user.reputation >= UPLOAD_FILES) or \
188 request_user.is_superuser
190 ###########################################
191 ## actions and reputation changes event
192 ###########################################
193 def calculate_reputation(origin, offset):
194 result = int(origin) + int(offset)
200 @transaction.commit_on_success
201 def onFlaggedItem(item, post, user):
204 post.offensive_flag_count = post.offensive_flag_count + 1
207 post.author.reputation = calculate_reputation(post.author.reputation,
208 int(REPUTATION_RULES['lose_by_flagged']))
212 if ContentType.objects.get_for_model(post) == answer_type:
213 question = post.question
215 reputation = Repute(user=post.author,
216 negative=int(REPUTATION_RULES['lose_by_flagged']),
217 question=question, reputed_at=datetime.datetime.now(),
219 reputation=post.author.reputation)
222 #todo: These should be updated to work on same revisions.
223 if post.offensive_flag_count == VOTE_RULES['scope_flags_invisible_main_page'] :
224 post.author.reputation = calculate_reputation(post.author.reputation,
225 int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']))
228 reputation = Repute(user=post.author,
229 negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']),
231 reputed_at=datetime.datetime.now(),
233 reputation=post.author.reputation)
236 elif post.offensive_flag_count == VOTE_RULES['scope_flags_delete_post']:
237 post.author.reputation = calculate_reputation(post.author.reputation,
238 int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']))
241 reputation = Repute(user=post.author,
242 negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']),
244 reputed_at=datetime.datetime.now(),
246 reputation=post.author.reputation)
250 #post.deleted_at = datetime.datetime.now()
251 #post.deleted_by = Admin
255 @transaction.commit_on_success
256 def onAnswerAccept(answer, user):
257 answer.accepted = True
258 answer.accepted_at = datetime.datetime.now()
259 answer.question.answer_accepted = True
261 answer.question.save()
263 answer.author.reputation = calculate_reputation(answer.author.reputation,
264 int(REPUTATION_RULES['gain_by_answer_accepted']))
266 reputation = Repute(user=answer.author,
267 positive=int(REPUTATION_RULES['gain_by_answer_accepted']),
268 question=answer.question,
269 reputed_at=datetime.datetime.now(),
271 reputation=answer.author.reputation)
274 user.reputation = calculate_reputation(user.reputation,
275 int(REPUTATION_RULES['gain_by_accepting_answer']))
277 reputation = Repute(user=user,
278 positive=int(REPUTATION_RULES['gain_by_accepting_answer']),
279 question=answer.question,
280 reputed_at=datetime.datetime.now(),
282 reputation=user.reputation)
285 @transaction.commit_on_success
286 def onAnswerAcceptCanceled(answer, user):
287 answer.accepted = False
288 answer.accepted_at = None
289 answer.question.answer_accepted = False
291 answer.question.save()
293 answer.author.reputation = calculate_reputation(answer.author.reputation,
294 int(REPUTATION_RULES['lose_by_accepted_answer_cancled']))
296 reputation = Repute(user=answer.author,
297 negative=int(REPUTATION_RULES['lose_by_accepted_answer_cancled']),
298 question=answer.question,
299 reputed_at=datetime.datetime.now(),
301 reputation=answer.author.reputation)
304 user.reputation = calculate_reputation(user.reputation,
305 int(REPUTATION_RULES['lose_by_canceling_accepted_answer']))
307 reputation = Repute(user=user,
308 negative=int(REPUTATION_RULES['lose_by_canceling_accepted_answer']),
309 question=answer.question,
310 reputed_at=datetime.datetime.now(),
312 reputation=user.reputation)
315 @transaction.commit_on_success
316 def onUpVoted(vote, post, user):
319 post.vote_up_count = int(post.vote_up_count) + 1
320 post.score = int(post.score) + 1
325 if Repute.objects.get_reputation_by_upvoted_today(author) < int(REPUTATION_RULES['scope_per_day_by_upvotes']):
326 author.reputation = calculate_reputation(author.reputation,
327 int(REPUTATION_RULES['gain_by_upvoted']))
331 if ContentType.objects.get_for_model(post) == answer_type:
332 question = post.question
334 reputation = Repute(user=author,
335 positive=int(REPUTATION_RULES['gain_by_upvoted']),
337 reputed_at=datetime.datetime.now(),
339 reputation=author.reputation)
342 @transaction.commit_on_success
343 def onUpVotedCanceled(vote, post, user):
346 post.vote_up_count = int(post.vote_up_count) - 1
347 if post.vote_up_count < 0:
348 post.vote_up_count = 0
349 post.score = int(post.score) - 1
354 author.reputation = calculate_reputation(author.reputation,
355 int(REPUTATION_RULES['lose_by_upvote_canceled']))
359 if ContentType.objects.get_for_model(post) == answer_type:
360 question = post.question
362 reputation = Repute(user=author,
363 negative=int(REPUTATION_RULES['lose_by_upvote_canceled']),
365 reputed_at=datetime.datetime.now(),
367 reputation=author.reputation)
370 @transaction.commit_on_success
371 def onDownVoted(vote, post, user):
374 post.vote_down_count = int(post.vote_down_count) + 1
375 post.score = int(post.score) - 1
380 author.reputation = calculate_reputation(author.reputation,
381 int(REPUTATION_RULES['lose_by_downvoted']))
385 if ContentType.objects.get_for_model(post) == answer_type:
386 question = post.question
388 reputation = Repute(user=author,
389 negative=int(REPUTATION_RULES['lose_by_downvoted']),
391 reputed_at=datetime.datetime.now(),
393 reputation=author.reputation)
396 user.reputation = calculate_reputation(user.reputation,
397 int(REPUTATION_RULES['lose_by_downvoting']))
400 reputation = Repute(user=user,
401 negative=int(REPUTATION_RULES['lose_by_downvoting']),
403 reputed_at=datetime.datetime.now(),
405 reputation=user.reputation)
408 @transaction.commit_on_success
409 def onDownVotedCanceled(vote, post, user):
412 post.vote_down_count = int(post.vote_down_count) - 1
413 if post.vote_down_count < 0:
414 post.vote_down_count = 0
415 post.score = post.score + 1
420 author.reputation = calculate_reputation(author.reputation,
421 int(REPUTATION_RULES['gain_by_downvote_canceled']))
425 if ContentType.objects.get_for_model(post) == answer_type:
426 question = post.question
428 reputation = Repute(user=author,
429 positive=int(REPUTATION_RULES['gain_by_downvote_canceled']),
431 reputed_at=datetime.datetime.now(),
433 reputation=author.reputation)
436 user.reputation = calculate_reputation(user.reputation,
437 int(REPUTATION_RULES['gain_by_canceling_downvote']))
440 reputation = Repute(user=user,
441 positive=int(REPUTATION_RULES['gain_by_canceling_downvote']),
443 reputed_at=datetime.datetime.now(),
445 reputation=user.reputation)
448 def onDeleteCanceled(post, user):
450 post.deleted_by = None
451 post.deleted_at = None
453 logging.debug('now restoring something')
454 if isinstance(post,Answer):
455 logging.debug('updated answer count on undelete, have %d' % post.question.answer_count)
456 Question.objects.update_answer_count(post.question)
457 elif isinstance(post,Question):
458 for tag in list(post.tags.all()):
459 if tag.used_count == 1 and tag.deleted:
461 tag.deleted_by = None
462 tag.deleted_at = None
465 def onDeleted(post, user):
467 post.deleted_by = user
468 post.deleted_at = datetime.datetime.now()
471 if isinstance(post, Question):
472 for tag in list(post.tags.all()):
473 if tag.used_count == 1:
475 tag.deleted_by = user
476 tag.deleted_at = datetime.datetime.now()
478 tag.used_count = tag.used_count - 1
481 answers = post.answers.all()
482 if user == post.author:
484 msg = _('Your question and all of it\'s answers have been deleted')
486 msg = _('Your question has been deleted')
489 msg = _('The question and all of it\'s answers have been deleted')
491 msg = _('The question has been deleted')
492 user.message_set.create(message=msg)
493 logging.debug('posted a message %s' % msg)
494 for answer in answers:
495 onDeleted(answer, user)
496 elif isinstance(post, Answer):
497 Question.objects.update_answer_count(post.question)
498 logging.debug('updated answer count to %d' % post.question.answer_count)