1 from django.contrib.auth.decorators import login_required
\r
2 from forum.models import User
\r
3 from django.db.models import Q, Count
\r
4 from django.core.paginator import Paginator, EmptyPage, InvalidPage
\r
5 from django.template.defaultfilters import slugify
\r
6 from django.contrib.contenttypes.models import ContentType
\r
7 from django.core.urlresolvers import reverse
\r
8 from django.shortcuts import render_to_response, get_object_or_404
\r
9 from django.template import RequestContext
\r
10 from django.http import HttpResponse, HttpResponseRedirect, Http404
\r
11 from forum.http_responses import HttpResponseUnauthorized
\r
12 from django.utils.translation import ugettext as _
\r
13 from django.utils.http import urlquote_plus
\r
14 from django.utils.html import strip_tags
\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
\r
20 from datetime import datetime, date
\r
22 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction
\r
23 from forum.modules import ui
\r
28 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
30 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
33 sortby = request.GET.get('sort', 'reputation')
\r
34 suser = request.REQUEST.get('q', "")
\r
36 page = int(request.GET.get('page', '1'))
\r
41 if sortby == "newest":
\r
42 objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE)
\r
43 elif sortby == "last":
\r
44 objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE)
\r
45 elif sortby == "user":
\r
46 objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE)
\r
49 objects_list = Paginator(User.objects.all().order_by('-is_active', '-reputation'), USERS_PAGE_SIZE)
\r
50 base_url = reverse('users') + '?sort=%s&' % sortby
\r
52 sortby = "reputation"
\r
53 objects_list = Paginator(User.objects.filter(username__icontains=suser).order_by('-reputation'), USERS_PAGE_SIZE
\r
55 base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby)
\r
58 users = objects_list.page(page)
\r
59 except (EmptyPage, InvalidPage):
\r
60 users = objects_list.page(objects_list.num_pages)
\r
68 'is_paginated' : is_paginated,
\r
69 'pages': objects_list.num_pages,
\r
71 'has_previous': users.has_previous(),
\r
72 'has_next': users.has_next(),
\r
73 'previous': users.previous_page_number(),
\r
74 'next': users.next_page_number(),
\r
75 'base_url' : base_url
\r
81 def edit_user(request, id):
\r
82 user = get_object_or_404(User, id=id)
\r
83 if not (request.user.is_superuser or request.user == user):
\r
84 return HttpResponseUnauthorized(request)
\r
85 if request.method == "POST":
\r
86 form = EditUserForm(user, request.POST)
\r
88 new_email = sanitize_html(form.cleaned_data['email'])
\r
90 if new_email != user.email:
\r
91 user.email = new_email
\r
92 user.email_isvalid = False
\r
94 if settings.EDITABLE_SCREEN_NAME:
\r
95 user.username = sanitize_html(form.cleaned_data['username'])
\r
96 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
97 user.website = sanitize_html(form.cleaned_data['website'])
\r
98 user.location = sanitize_html(form.cleaned_data['city'])
\r
99 user.date_of_birth = form.cleaned_data['birthday']
\r
100 if user.date_of_birth == "None":
\r
101 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
102 user.about = sanitize_html(form.cleaned_data['about'])
\r
105 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
107 request.user.message_set.create(message=_("Profile updated."))
\r
108 return HttpResponseRedirect(user.get_profile_url())
\r
110 form = EditUserForm(user)
\r
111 return render_to_response('users/edit.html', {
\r
114 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
115 }, context_instance=RequestContext(request))
\r
118 @decorate.withfn(decorators.command)
\r
119 def user_powers(request, id, action, status):
\r
120 if not request.user.is_superuser:
\r
121 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
123 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
124 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
126 user = get_object_or_404(User, id=id)
\r
127 new_state = action == 'grant'
\r
129 if status == 'super':
\r
130 user.is_superuser = new_state
\r
131 elif status == 'staff':
\r
132 user.is_staff = new_state
\r
137 return decorators.RefreshPageCommand()
\r
140 @decorate.withfn(decorators.command)
\r
141 def award_points(request, id):
\r
142 if not request.POST:
\r
143 return render_to_response('users/karma_bonus.html')
\r
145 if not request.user.is_superuser:
\r
146 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
149 points = int(request.POST['points'])
\r
151 raise decorators.CommandException(_("Invalid number of points to award."))
\r
153 user = get_object_or_404(User, id=id)
\r
155 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
157 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
159 return {'commands': {
\r
160 'update_profile_karma': [user.reputation]
\r
164 @decorate.withfn(decorators.command)
\r
165 def suspend(request, id):
\r
166 user = get_object_or_404(User, id=id)
\r
168 if not request.user.is_superuser:
\r
169 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
171 if not request.POST.get('bantype', None):
\r
172 if user.is_suspended():
\r
173 suspension = user.suspension
\r
174 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
175 return decorators.RefreshPageCommand()
\r
177 return render_to_response('users/suspend_user.html')
\r
180 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
181 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
182 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
186 if data['bantype'] == 'forxdays':
\r
188 data['forxdays'] = int(request.POST['forxdays'])
\r
190 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
192 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
194 return decorators.RefreshPageCommand()
\r
197 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
199 def decorated(fn, request, id, slug=None):
\r
200 user = get_object_or_404(User, id=id)
\r
201 if private and not (user == request.user or request.user.is_superuser):
\r
202 return HttpResponseUnauthorized(request)
\r
203 context = fn(request, user)
\r
205 rev_page_title = user.username + " - " + tab_description
\r
209 "active_tab" : tab_name,
\r
210 "tab_description" : tab_description,
\r
211 "page_title" : rev_page_title,
\r
212 "can_view_private": (user == request.user) or request.user.is_superuser
\r
214 return render_to_response(template, context, context_instance=RequestContext(request))
\r
217 def url_getter(vu):
\r
219 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
220 except NoReverseMatch:
\r
221 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
223 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
224 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
227 return decorate.withfn(decorated)(fn)
\r
231 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
232 def user_profile(request, user):
\r
233 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
234 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
236 up_votes = user.vote_up_count
\r
237 down_votes = user.vote_down_count
\r
238 votes_today = user.get_vote_count_today()
\r
239 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
241 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
242 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
244 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
245 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
248 "view_user" : user,
\r
249 "questions" : questions,
\r
250 "answers" : answers,
\r
251 "up_votes" : up_votes,
\r
252 "down_votes" : down_votes,
\r
253 "total_votes": up_votes + down_votes,
\r
254 "votes_today_left": votes_total-votes_today,
\r
255 "votes_total_per_day": votes_total,
\r
256 "user_tags" : user_tags[:50],
\r
258 "total_awards" : len(awards),
\r
261 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
262 def user_recent(request, user):
\r
263 activities = user.actions.exclude(
\r
264 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
265 '-action_date')[:USERS_PAGE_SIZE]
\r
267 return {"view_user" : user, "activities" : activities}
\r
270 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
271 def user_reputation(request, user):
\r
272 rep = list(user.reputes.order_by('date'))
\r
273 values = [r.value for r in rep]
\r
274 redux = lambda x, y: x+y
\r
276 graph_data = simplejson.dumps([
\r
277 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
278 for i in range(len(values))
\r
281 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
283 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
285 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
286 def user_votes(request, user):
\r
287 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
288 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
290 return {"view_user" : user, "votes" : votes}
\r
292 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
293 def user_favorites(request, user):
\r
294 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
296 return {"favorites" : favorites, "view_user" : user}
\r
298 @user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True, tabbed=False)
\r
299 def user_subscriptions(request, user):
\r
300 enabled = user.subscription_settings.enable_notifications
\r
302 if request.method == 'POST':
\r
303 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
305 if form.is_valid():
\r
307 message = _('New subscription settings are now saved')
\r
309 if 'notswitch' in request.POST:
\r
310 enabled = not enabled
\r
313 message = _('Notifications are now enabled')
\r
315 message = _('Notifications are now disabled')
\r
317 user.subscription_settings.enable_notifications = enabled
\r
318 user.subscription_settings.save()
\r
320 request.user.message_set.create(message=message)
\r
322 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
324 return {'view_user':user, 'notificatons_on': enabled, 'form':form}
\r
327 def account_settings(request):
\r
329 msg = request.GET.get('msg', '')
\r
332 return render_to_response('account_settings.html', {
\r
334 'is_openid': is_openid
\r
335 }, context_instance=RequestContext(request))
\r