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
204 if render_to and (not render_to(user)):
\r
205 return HttpResponseRedirect(user.get_profile_url())
\r
207 context = fn(request, user)
\r
209 rev_page_title = user.username + " - " + tab_description
\r
213 "active_tab" : tab_name,
\r
214 "tab_description" : tab_description,
\r
215 "page_title" : rev_page_title,
\r
216 "can_view_private": (user == request.user) or request.user.is_superuser
\r
218 return render_to_response(template, context, context_instance=RequestContext(request))
\r
221 def url_getter(vu):
\r
223 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
224 except NoReverseMatch:
\r
225 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
227 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
228 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
231 return decorate.withfn(decorated)(fn)
\r
235 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
236 def user_profile(request, user):
\r
237 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
238 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
240 up_votes = user.vote_up_count
\r
241 down_votes = user.vote_down_count
\r
242 votes_today = user.get_vote_count_today()
\r
243 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
245 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
246 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
248 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
249 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
252 "view_user" : user,
\r
253 "questions" : questions,
\r
254 "answers" : answers,
\r
255 "up_votes" : up_votes,
\r
256 "down_votes" : down_votes,
\r
257 "total_votes": up_votes + down_votes,
\r
258 "votes_today_left": votes_total-votes_today,
\r
259 "votes_total_per_day": votes_total,
\r
260 "user_tags" : user_tags[:50],
\r
262 "total_awards" : len(awards),
\r
265 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
266 def user_recent(request, user):
\r
267 activities = user.actions.exclude(
\r
268 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
269 '-action_date')[:USERS_PAGE_SIZE]
\r
271 return {"view_user" : user, "activities" : activities}
\r
274 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
275 def user_reputation(request, user):
\r
276 rep = list(user.reputes.order_by('date'))
\r
277 values = [r.value for r in rep]
\r
278 redux = lambda x, y: x+y
\r
280 graph_data = simplejson.dumps([
\r
281 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
282 for i in range(len(values))
\r
285 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
287 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
289 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
290 def user_votes(request, user):
\r
291 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
292 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
294 return {"view_user" : user, "votes" : votes}
\r
296 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
297 def user_favorites(request, user):
\r
298 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
300 return {"favorites" : favorites, "view_user" : user}
\r
302 @user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True, tabbed=False)
\r
303 def user_subscriptions(request, user):
\r
304 enabled = user.subscription_settings.enable_notifications
\r
306 if request.method == 'POST':
\r
307 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
309 if form.is_valid():
\r
311 message = _('New subscription settings are now saved')
\r
313 if 'notswitch' in request.POST:
\r
314 enabled = not enabled
\r
317 message = _('Notifications are now enabled')
\r
319 message = _('Notifications are now disabled')
\r
321 user.subscription_settings.enable_notifications = enabled
\r
322 user.subscription_settings.save()
\r
324 request.user.message_set.create(message=message)
\r
326 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
328 return {'view_user':user, 'notificatons_on': enabled, 'form':form}
\r
331 def account_settings(request):
\r
333 msg = request.GET.get('msg', '')
\r
336 return render_to_response('account_settings.html', {
\r
338 'is_openid': is_openid
\r
339 }, context_instance=RequestContext(request))
\r