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
\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
33 class UserReputationSort(pagination.SimpleSort):
\r
34 def apply(self, objects):
\r
35 return objects.order_by('-is_active', self.order_by)
\r
37 class UserListPaginatorContext(pagination.PaginatorContext):
\r
38 def __init__(self, pagesizes=(20, 35, 60)):
\r
39 super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
\r
40 (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
\r
41 (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
\r
42 (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
\r
43 (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
\r
44 ), pagesizes=pagesizes)
\r
46 class SubscriptionListPaginatorContext(pagination.PaginatorContext):
\r
48 super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)
\r
50 class UserAnswersPaginatorContext(pagination.PaginatorContext):
\r
52 super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
\r
53 (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
\r
54 (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
\r
55 (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
\r
56 ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
\r
58 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
60 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
62 suser = request.REQUEST.get('q', "")
\r
63 users = User.objects.all()
\r
66 users = users.filter(username__icontains=suser)
\r
68 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
74 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False)
\r
75 def online_users(request):
\r
76 suser = request.REQUEST.get('q', "")
\r
79 if request.GET.get("sort", None):
\r
81 sort = int(request.GET["sort"])
\r
83 logging.error('Found invalid sort "%s", loading %s, refered by %s' % (
\r
84 request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
89 if request.GET.get("page", None):
\r
91 page = int(request.GET["page"])
\r
93 logging.error('Found invalid page "%s", loading %s, refered by %s' % (
\r
94 request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
99 if request.GET.get("pagesize", None):
\r
101 pagesize = int(request.GET["pagesize"])
\r
103 logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % (
\r
104 request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
110 if sort == "reputation":
\r
111 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation)
\r
112 elif sort == "newest" :
\r
113 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
\r
114 elif sort == "last":
\r
115 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last)
\r
116 elif sort == "name":
\r
117 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name)
\r
118 elif sort == "oldest":
\r
119 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest)
\r
120 elif sort == "newest":
\r
121 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
\r
122 elif sort == "votes":
\r
123 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes)
\r
125 users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1])
\r
127 return render_to_response('users/online_users.html', {
\r
132 "pageSize" : pagesize,
\r
136 def edit_user(request, id):
\r
137 user = get_object_or_404(User, id=id)
\r
138 if not (request.user.is_superuser or request.user == user):
\r
139 return HttpResponseUnauthorized(request)
\r
140 if request.method == "POST":
\r
141 form = EditUserForm(user, request.POST)
\r
142 if form.is_valid():
\r
143 new_email = sanitize_html(form.cleaned_data['email'])
\r
145 if new_email != user.email:
\r
146 user.email = new_email
\r
147 user.email_isvalid = False
\r
150 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
155 if settings.EDITABLE_SCREEN_NAME:
\r
156 user.username = sanitize_html(form.cleaned_data['username'])
\r
157 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
158 user.website = sanitize_html(form.cleaned_data['website'])
\r
159 user.location = sanitize_html(form.cleaned_data['city'])
\r
160 user.date_of_birth = form.cleaned_data['birthday']
\r
161 if user.date_of_birth == "None":
\r
162 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
163 user.about = sanitize_html(form.cleaned_data['about'])
\r
166 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
168 request.user.message_set.create(message=_("Profile updated."))
\r
169 return HttpResponseRedirect(user.get_profile_url())
\r
171 form = EditUserForm(user)
\r
172 return render_to_response('users/edit.html', {
\r
175 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
176 }, context_instance=RequestContext(request))
\r
179 @decorate.withfn(decorators.command)
\r
180 def user_powers(request, id, action, status):
\r
181 if not request.user.is_superuser:
\r
182 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
184 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
185 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
187 user = get_object_or_404(User, id=id)
\r
188 new_state = action == 'grant'
\r
190 if status == 'super':
\r
191 user.is_superuser = new_state
\r
192 elif status == 'staff':
\r
193 user.is_staff = new_state
\r
198 return decorators.RefreshPageCommand()
\r
201 @decorate.withfn(decorators.command)
\r
202 def award_points(request, id):
\r
203 if not request.POST:
\r
204 return render_to_response('users/karma_bonus.html')
\r
206 if not request.user.is_superuser:
\r
207 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
210 points = int(request.POST['points'])
\r
212 raise decorators.CommandException(_("Invalid number of points to award."))
\r
214 user = get_object_or_404(User, id=id)
\r
216 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
218 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
220 return {'commands': {
\r
221 'update_profile_karma': [user.reputation]
\r
225 @decorate.withfn(decorators.command)
\r
226 def suspend(request, id):
\r
227 user = get_object_or_404(User, id=id)
\r
229 if not request.user.is_superuser:
\r
230 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
232 if not request.POST.get('bantype', None):
\r
233 if user.is_suspended():
\r
234 suspension = user.suspension
\r
235 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
236 return decorators.RefreshPageCommand()
\r
238 return render_to_response('users/suspend_user.html')
\r
241 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
242 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
243 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
247 if data['bantype'] == 'forxdays':
\r
249 data['forxdays'] = int(request.POST['forxdays'])
\r
251 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
253 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
255 return decorators.RefreshPageCommand()
\r
258 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
260 def params(request, id, slug=None):
\r
261 user = get_object_or_404(User, id=id)
\r
262 if private and not (user == request.user or request.user.is_superuser):
\r
263 raise ReturnImediatelyException(HttpResponseUnauthorized(request))
\r
265 if render_to and (not render_to(user)):
\r
266 raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url()))
\r
268 return [request, user], {}
\r
270 decorated = decorate.params.withfn(params)(fn)
\r
272 def result(context, request, user):
\r
273 rev_page_title = smart_unicode(user.username) + " - " + tab_description
\r
277 "active_tab" : tab_name,
\r
278 "tab_description" : tab_description,
\r
279 "page_title" : rev_page_title,
\r
280 "can_view_private": (user == request.user) or request.user.is_superuser
\r
282 return render_to_response(template, context, context_instance=RequestContext(request))
\r
284 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
287 def url_getter(vu):
\r
289 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))})
\r
290 except NoReverseMatch:
\r
291 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
293 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
294 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
301 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
302 def user_profile(request, user):
\r
303 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
304 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
306 up_votes = user.vote_up_count
\r
307 down_votes = user.vote_down_count
\r
308 votes_today = user.get_vote_count_today()
\r
309 votes_total = user.can_vote_count_today()
\r
311 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
312 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
314 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
315 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
317 return pagination.paginated(request, (
\r
318 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),
\r
319 ('answers', UserAnswersPaginatorContext())), {
\r
320 "view_user" : user,
\r
321 "questions" : questions,
\r
322 "answers" : answers,
\r
323 "up_votes" : up_votes,
\r
324 "down_votes" : down_votes,
\r
325 "total_votes": up_votes + down_votes,
\r
326 "votes_today_left": votes_total-votes_today,
\r
327 "votes_total_per_day": votes_total,
\r
328 "user_tags" : user_tags[:50],
\r
330 "total_awards" : len(awards),
\r
333 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
334 def user_recent(request, user):
\r
335 activities = user.actions.exclude(
\r
336 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
337 '-action_date')[:USERS_PAGE_SIZE]
\r
339 return {"view_user" : user, "activities" : activities}
\r
342 @user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma'))
\r
343 def user_reputation(request, user):
\r
344 rep = list(user.reputes.order_by('date'))
\r
345 values = [r.value for r in rep]
\r
346 redux = lambda x, y: x+y
\r
348 graph_data = simplejson.dumps([
\r
349 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
350 for i in range(len(values))
\r
353 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
355 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
357 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
358 def user_votes(request, user):
\r
359 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
360 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
362 return {"view_user" : user, "votes" : votes}
\r
364 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
365 def user_favorites(request, user):
\r
366 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
368 return {"favorites" : favorites, "view_user" : user}
\r
370 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
\r
371 def user_subscriptions(request, user):
\r
374 tab = request.GET.get('tab', "settings")
\r
376 if tab == 'settings':
\r
377 manage_open = False
\r
378 if request.method == 'POST':
\r
379 manage_open = False
\r
380 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
382 if form.is_valid():
\r
384 message = _('New subscription settings are now saved')
\r
386 user.subscription_settings.enable_notifications = enabled
\r
387 user.subscription_settings.save()
\r
389 request.user.message_set.create(message=message)
\r
391 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
395 'notificatons_on': enabled,
\r
397 'manage_open':manage_open,
\r
400 elif tab == 'manage':
\r
403 auto = request.GET.get('auto', 'True')
\r
406 subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')
\r
409 subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')
\r
411 return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
\r
412 'subscriptions':subscriptions,
\r
415 'manage_open':manage_open,
\r
419 # todo: probably want to throw an error
\r
420 # error = "error to throw"
\r
428 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
429 def user_preferences(request, user):
\r
431 form = UserPreferencesForm(request.POST)
\r
433 if form.is_valid():
\r
434 user.prop.preferences = form.cleaned_data
\r
435 request.user.message_set.create(message=_('New preferences saved'))
\r
438 preferences = user.prop.preferences
\r
441 form = UserPreferencesForm(initial=preferences)
\r
443 form = UserPreferencesForm()
\r
445 return {'view_user': user, 'form': form}
\r