1 from forum.models import User
\r
2 from django.db.models import Q, Count
\r
3 from django.core.paginator import Paginator, EmptyPage, InvalidPage
\r
4 from django.template.defaultfilters import slugify
\r
5 from django.contrib.contenttypes.models import ContentType
\r
6 from django.core.urlresolvers import reverse
\r
7 from django.shortcuts import render_to_response, get_object_or_404
\r
8 from django.template import RequestContext
\r
9 from django.http import HttpResponse, HttpResponseRedirect, Http404
\r
10 from forum.http_responses import HttpResponseUnauthorized
\r
11 from django.utils.translation import ugettext as _
\r
12 from django.utils.http import urlquote_plus
\r
13 from django.utils.html import strip_tags
\r
14 from django.utils.encoding import smart_unicode
\r
15 from django.utils import simplejson
\r
16 from django.core.urlresolvers import reverse, NoReverseMatch
\r
17 from forum.forms import *
\r
18 from forum.utils.html import sanitize_html
\r
19 from forum.modules import decorate, ReturnImediatelyException
\r
20 from datetime import datetime, date
\r
21 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction, ReportAction
\r
22 from forum.modules import ui
\r
23 from forum.utils import pagination
\r
24 from forum.views.readers import QuestionListPaginatorContext, AnswerPaginatorContext
\r
25 from forum.settings import ONLINE_USERS
\r
31 class UserReputationSort(pagination.SimpleSort):
\r
32 def apply(self, objects):
\r
33 return objects.order_by('-is_active', self.order_by)
\r
35 class UserListPaginatorContext(pagination.PaginatorContext):
\r
36 def __init__(self, pagesizes=(20, 35, 60), default_pagesize=35):
\r
37 super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
\r
38 (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
\r
39 (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
\r
40 (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
\r
41 (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
\r
42 ), pagesizes=pagesizes, default_pagesize=default_pagesize)
\r
44 class SubscriptionListPaginatorContext(pagination.PaginatorContext):
\r
46 super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)
\r
48 class UserAnswersPaginatorContext(pagination.PaginatorContext):
\r
50 super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
\r
51 (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
\r
52 (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
\r
53 (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
\r
54 ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
\r
56 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
58 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
60 suser = request.REQUEST.get('q', "")
\r
61 users = User.objects.all()
\r
64 users = users.filter(username__icontains=suser)
\r
66 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
72 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False)
\r
73 def online_users(request):
\r
74 suser = request.REQUEST.get('q', "")
\r
77 if request.GET.get("sort", None):
\r
79 sort = int(request.GET["sort"])
\r
81 logging.error('Found invalid sort "%s", loading %s, refered by %s' % (
\r
82 request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
87 if request.GET.get("page", None):
\r
89 page = int(request.GET["page"])
\r
91 logging.error('Found invalid page "%s", loading %s, refered by %s' % (
\r
92 request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
97 if request.GET.get("pagesize", None):
\r
99 pagesize = int(request.GET["pagesize"])
\r
101 logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % (
\r
102 request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
108 if sort == "reputation":
\r
109 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation)
\r
110 elif sort == "newest" :
\r
111 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
\r
112 elif sort == "last":
\r
113 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last)
\r
114 elif sort == "name":
\r
115 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name)
\r
116 elif sort == "oldest":
\r
117 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest)
\r
118 elif sort == "newest":
\r
119 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
\r
120 elif sort == "votes":
\r
121 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes)
\r
123 users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1])
\r
125 return render_to_response('users/online_users.html', {
\r
130 "pageSize" : pagesize,
\r
134 def edit_user(request, id, slug):
\r
135 user = get_object_or_404(User, id=id)
\r
136 if not (request.user.is_superuser or request.user == user):
\r
137 return HttpResponseUnauthorized(request)
\r
138 if request.method == "POST":
\r
139 form = EditUserForm(user, request.POST)
\r
140 if form.is_valid():
\r
141 new_email = sanitize_html(form.cleaned_data['email'])
\r
143 if new_email != user.email:
\r
144 user.email = new_email
\r
145 user.email_isvalid = False
\r
148 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
153 if settings.EDITABLE_SCREEN_NAME:
\r
154 user.username = sanitize_html(form.cleaned_data['username'])
\r
155 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
156 user.website = sanitize_html(form.cleaned_data['website'])
\r
157 user.location = sanitize_html(form.cleaned_data['city'])
\r
158 user.date_of_birth = form.cleaned_data['birthday']
\r
159 if user.date_of_birth == "None":
\r
160 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
161 user.about = sanitize_html(form.cleaned_data['about'])
\r
164 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
166 request.user.message_set.create(message=_("Profile updated."))
\r
167 return HttpResponseRedirect(user.get_profile_url())
\r
169 form = EditUserForm(user)
\r
170 return render_to_response('users/edit.html', {
\r
173 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
174 }, context_instance=RequestContext(request))
\r
177 @decorate.withfn(decorators.command)
\r
178 def user_powers(request, id, action, status):
\r
179 if not request.user.is_superuser:
\r
180 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
182 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
183 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
185 user = get_object_or_404(User, id=id)
\r
186 new_state = action == 'grant'
\r
188 if status == 'super':
\r
189 user.is_superuser = new_state
\r
190 elif status == 'staff':
\r
191 user.is_staff = new_state
\r
196 return decorators.RefreshPageCommand()
\r
199 @decorate.withfn(decorators.command)
\r
200 def award_points(request, id):
\r
201 if not request.POST:
\r
202 return render_to_response('users/karma_bonus.html')
\r
204 if not request.user.is_superuser:
\r
205 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
208 points = int(request.POST['points'])
\r
210 raise decorators.CommandException(_("Invalid number of points to award."))
\r
212 user = get_object_or_404(User, id=id)
\r
214 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
216 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
218 return {'commands': {
\r
219 'update_profile_karma': [user.reputation]
\r
223 @decorate.withfn(decorators.command)
\r
224 def suspend(request, id):
\r
225 user = get_object_or_404(User, id=id)
\r
227 if not request.user.is_superuser:
\r
228 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
230 if not request.POST.get('bantype', None):
\r
231 if user.is_suspended():
\r
232 suspension = user.suspension
\r
233 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
234 return decorators.RefreshPageCommand()
\r
236 return render_to_response('users/suspend_user.html')
\r
239 'bantype': request.POST.get('bantype', 'Indefinitely').strip(),
\r
240 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
241 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
245 if data['bantype'] == 'forxdays':
\r
247 data['forxdays'] = int(request.POST['forxdays'])
\r
249 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
251 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
253 return decorators.RefreshPageCommand()
\r
255 @decorate.withfn(decorators.command)
\r
256 def report_user(request, id):
\r
257 user = get_object_or_404(User, id=id)
\r
259 if not request.POST.get('publicmsg', None):
\r
260 return render_to_response('users/report_user.html')
\r
263 'publicmsg': request.POST.get('publicmsg', _('N/A')),
\r
267 ReportAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
270 return decorators.RefreshPageCommand()
\r
274 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
276 def params(request, id=None, slug=None):
\r
277 # Get the user object by id if the id parameter has been passed
\r
279 user = get_object_or_404(User, id=id)
\r
280 # ...or by slug if the slug has been given
\r
281 elif slug is not None:
\r
283 user = User.objects.get(username__iexact=slug)
\r
284 except User.DoesNotExist:
\r
287 if private and not (user == request.user or request.user.is_superuser):
\r
288 raise ReturnImediatelyException(HttpResponseUnauthorized(request))
\r
290 if render_to and (not render_to(user)):
\r
291 raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url()))
\r
293 return [request, user], { 'slug' : slug, }
\r
295 decorated = decorate.params.withfn(params)(fn)
\r
297 def result(context_or_response, request, user, **kwargs):
\r
298 rev_page_title = smart_unicode(user.username) + " - " + tab_description
\r
300 # Check whether the return type of the decorated function is a context or Http Response
\r
301 if isinstance(context_or_response, HttpResponse):
\r
302 response = context_or_response
\r
304 # If it is a response -- show it
\r
307 # ...if it is a context move forward, update it and render it to response
\r
308 context = context_or_response
\r
312 "active_tab" : tab_name,
\r
313 "tab_description" : tab_description,
\r
314 "page_title" : rev_page_title,
\r
315 "can_view_private": (user == request.user) or request.user.is_superuser
\r
317 return render_to_response(template, context, context_instance=RequestContext(request))
\r
319 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
322 def url_getter(vu):
\r
324 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))})
\r
325 except NoReverseMatch:
\r
327 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
328 except NoReverseMatch:
\r
329 return reverse(fn.__name__, kwargs={'slug': slugify(smart_unicode(vu.username))})
\r
331 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
332 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
339 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
340 def user_profile(request, user, **kwargs):
\r
341 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
342 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
344 # Check whether the passed slug matches the one for the user object
\r
345 slug = kwargs['slug']
\r
346 if slug != slugify(smart_unicode(user.username)):
\r
347 return HttpResponseRedirect(user.get_absolute_url())
\r
349 up_votes = user.vote_up_count
\r
350 down_votes = user.vote_down_count
\r
351 votes_today = user.get_vote_count_today()
\r
352 votes_total = user.can_vote_count_today()
\r
354 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
355 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
357 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
358 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
360 return pagination.paginated(request, (
\r
361 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), default_pagesize=15)),
\r
362 ('answers', UserAnswersPaginatorContext())), {
\r
363 "view_user" : user,
\r
364 "questions" : questions,
\r
365 "answers" : answers,
\r
366 "up_votes" : up_votes,
\r
367 "down_votes" : down_votes,
\r
368 "total_votes": up_votes + down_votes,
\r
369 "votes_today_left": votes_total-votes_today,
\r
370 "votes_total_per_day": votes_total,
\r
371 "user_tags" : user_tags[:50],
\r
373 "total_awards" : len(awards),
\r
376 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
377 def user_recent(request, user, **kwargs):
\r
378 activities = user.actions.exclude(
\r
379 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
380 '-action_date')[:USERS_PAGE_SIZE]
\r
382 return {"view_user" : user, "activities" : activities}
\r
385 @user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma'))
\r
386 def user_reputation(request, user, **kwargs):
\r
387 rep = list(user.reputes.order_by('date'))
\r
388 values = [r.value for r in rep]
\r
389 redux = lambda x, y: x+y
\r
391 graph_data = simplejson.dumps([
\r
392 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i+1], 0))
\r
393 for i in range(len(values))
\r
396 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
398 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
400 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
401 def user_votes(request, user, **kwargs):
\r
402 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
403 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
405 return {"view_user" : user, "votes" : votes}
\r
407 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
408 def user_favorites(request, user, **kwargs):
\r
409 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
411 return {"favorites" : favorites, "view_user" : user}
\r
413 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
\r
414 def user_subscriptions(request, user, **kwargs):
\r
415 return _user_subscriptions(request, user, **kwargs)
\r
417 def _user_subscriptions(request, user, **kwargs):
\r
420 tab = request.GET.get('tab', "settings")
\r
423 if tab == 'manage':
\r
426 auto = request.GET.get('auto', 'True')
\r
429 subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')
\r
432 subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')
\r
434 return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
\r
435 'subscriptions':subscriptions,
\r
438 'manage_open':manage_open,
\r
440 # Settings Tab and everything else
\r
442 manage_open = False
\r
443 if request.method == 'POST':
\r
444 manage_open = False
\r
445 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
447 if form.is_valid():
\r
449 message = _('New subscription settings are now saved')
\r
451 user.subscription_settings.enable_notifications = enabled
\r
452 user.subscription_settings.save()
\r
454 request.user.message_set.create(message=message)
\r
456 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
460 'notificatons_on': enabled,
\r
462 'manage_open':manage_open,
\r
465 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
466 def user_preferences(request, user, **kwargs):
\r
468 form = UserPreferencesForm(request.POST)
\r
470 if form.is_valid():
\r
471 user.prop.preferences = form.cleaned_data
\r
472 request.user.message_set.create(message=_('New preferences saved'))
\r
475 preferences = user.prop.preferences
\r
478 form = UserPreferencesForm(initial=preferences)
\r
480 form = UserPreferencesForm()
\r
482 return {'view_user': user, 'form': form}
\r