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 import simplejson
\r
15 from django.core.urlresolvers import reverse, NoReverseMatch
\r
16 from forum.forms import *
\r
17 from forum.utils.html import sanitize_html
\r
18 from forum.modules import decorate
\r
19 from datetime import datetime, date
\r
20 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction
\r
21 from forum.modules import ui
\r
22 from forum.utils import pagination
\r
23 from forum.views.readers import QuestionListPaginatorContext, AnswerPaginatorContext
\r
24 from forum.settings import ONLINE_USERS
\r
32 class UserReputationSort(pagination.SimpleSort):
\r
33 def apply(self, objects):
\r
34 return objects.order_by('-is_active', self.order_by)
\r
36 class UserListPaginatorContext(pagination.PaginatorContext):
\r
38 super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
\r
39 (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
\r
40 (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
\r
41 (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
\r
42 (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
\r
43 ), pagesizes=(20, 35, 60))
\r
45 class SubscriptionListPaginatorContext(pagination.PaginatorContext):
\r
47 super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)
\r
49 class UserAnswersPaginatorContext(pagination.PaginatorContext):
\r
51 super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
\r
52 (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
\r
53 (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
\r
54 (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
\r
55 ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
\r
57 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
59 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
61 suser = request.REQUEST.get('q', "")
\r
62 users = User.objects.all()
\r
65 users = users.filter(username__icontains=suser)
\r
67 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
73 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False)
\r
74 def online_users(request):
\r
75 suser = request.REQUEST.get('q', "")
\r
78 if request.GET.get("sort", None):
\r
80 sort = int(request.GET["sort"])
\r
82 logging.error('Found invalid sort "%s", loading %s, refered by %s' % (
\r
83 request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
88 if request.GET.get("page", None):
\r
90 page = int(request.GET["page"])
\r
92 logging.error('Found invalid page "%s", loading %s, refered by %s' % (
\r
93 request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
98 if request.GET.get("pagesize", None):
\r
100 pagesize = int(request.GET["pagesize"])
\r
102 logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % (
\r
103 request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
\r
109 if sort == "reputation":
\r
110 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation)
\r
111 elif sort == "newest" :
\r
112 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
\r
113 elif sort == "last":
\r
114 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last)
\r
115 elif sort == "name":
\r
116 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name)
\r
117 elif sort == "oldest":
\r
118 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest)
\r
119 elif sort == "newest":
\r
120 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
\r
121 elif sort == "votes":
\r
122 users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes)
\r
124 users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1])
\r
126 return render_to_response('users/online_users.html', {
\r
131 "pageSize" : pagesize,
\r
135 def edit_user(request, id):
\r
136 user = get_object_or_404(User, id=id)
\r
137 if not (request.user.is_superuser or request.user == user):
\r
138 return HttpResponseUnauthorized(request)
\r
139 if request.method == "POST":
\r
140 form = EditUserForm(user, request.POST)
\r
141 if form.is_valid():
\r
142 new_email = sanitize_html(form.cleaned_data['email'])
\r
144 if new_email != user.email:
\r
145 user.email = new_email
\r
146 user.email_isvalid = False
\r
149 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
154 if settings.EDITABLE_SCREEN_NAME:
\r
155 user.username = sanitize_html(form.cleaned_data['username'])
\r
156 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
157 user.website = sanitize_html(form.cleaned_data['website'])
\r
158 user.location = sanitize_html(form.cleaned_data['city'])
\r
159 user.date_of_birth = form.cleaned_data['birthday']
\r
160 if user.date_of_birth == "None":
\r
161 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
162 user.about = sanitize_html(form.cleaned_data['about'])
\r
165 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
167 request.user.message_set.create(message=_("Profile updated."))
\r
168 return HttpResponseRedirect(user.get_profile_url())
\r
170 form = EditUserForm(user)
\r
171 return render_to_response('users/edit.html', {
\r
174 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
175 }, context_instance=RequestContext(request))
\r
178 @decorate.withfn(decorators.command)
\r
179 def user_powers(request, id, action, status):
\r
180 if not request.user.is_superuser:
\r
181 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
183 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
184 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
186 user = get_object_or_404(User, id=id)
\r
187 new_state = action == 'grant'
\r
189 if status == 'super':
\r
190 user.is_superuser = new_state
\r
191 elif status == 'staff':
\r
192 user.is_staff = new_state
\r
197 return decorators.RefreshPageCommand()
\r
200 @decorate.withfn(decorators.command)
\r
201 def award_points(request, id):
\r
202 if not request.POST:
\r
203 return render_to_response('users/karma_bonus.html')
\r
205 if not request.user.is_superuser:
\r
206 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
209 points = int(request.POST['points'])
\r
211 raise decorators.CommandException(_("Invalid number of points to award."))
\r
213 user = get_object_or_404(User, id=id)
\r
215 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
217 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
219 return {'commands': {
\r
220 'update_profile_karma': [user.reputation]
\r
224 @decorate.withfn(decorators.command)
\r
225 def suspend(request, id):
\r
226 user = get_object_or_404(User, id=id)
\r
228 if not request.user.is_superuser:
\r
229 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
231 if not request.POST.get('bantype', None):
\r
232 if user.is_suspended():
\r
233 suspension = user.suspension
\r
234 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
235 return decorators.RefreshPageCommand()
\r
237 return render_to_response('users/suspend_user.html')
\r
240 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
241 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
242 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
246 if data['bantype'] == 'forxdays':
\r
248 data['forxdays'] = int(request.POST['forxdays'])
\r
250 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
252 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
254 return decorators.RefreshPageCommand()
\r
257 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
259 def params(request, id, slug=None):
\r
260 user = get_object_or_404(User, id=id)
\r
261 if private and not (user == request.user or request.user.is_superuser):
\r
262 return HttpResponseUnauthorized(request)
\r
264 if render_to and (not render_to(user)):
\r
265 return HttpResponseRedirect(user.get_profile_url())
\r
267 return [request, user], {}
\r
269 decorated = decorate.params.withfn(params)(fn)
\r
271 def result(context, request, user):
\r
272 rev_page_title = user.username + " - " + tab_description
\r
276 "active_tab" : tab_name,
\r
277 "tab_description" : tab_description,
\r
278 "page_title" : rev_page_title,
\r
279 "can_view_private": (user == request.user) or request.user.is_superuser
\r
281 return render_to_response(template, context, context_instance=RequestContext(request))
\r
283 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
286 def url_getter(vu):
\r
288 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
289 except NoReverseMatch:
\r
290 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
292 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
293 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
300 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
301 def user_profile(request, user):
\r
302 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
303 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
305 up_votes = user.vote_up_count
\r
306 down_votes = user.vote_down_count
\r
307 votes_today = user.get_vote_count_today()
\r
308 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
310 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
311 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
313 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
314 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
316 return pagination.paginated(request, (
\r
317 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),
\r
318 ('answers', UserAnswersPaginatorContext())), {
\r
319 "view_user" : user,
\r
320 "questions" : questions,
\r
321 "answers" : answers,
\r
322 "up_votes" : up_votes,
\r
323 "down_votes" : down_votes,
\r
324 "total_votes": up_votes + down_votes,
\r
325 "votes_today_left": votes_total-votes_today,
\r
326 "votes_total_per_day": votes_total,
\r
327 "user_tags" : user_tags[:50],
\r
329 "total_awards" : len(awards),
\r
332 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
333 def user_recent(request, user):
\r
334 activities = user.actions.exclude(
\r
335 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
336 '-action_date')[:USERS_PAGE_SIZE]
\r
338 return {"view_user" : user, "activities" : activities}
\r
341 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
342 def user_reputation(request, user):
\r
343 rep = list(user.reputes.order_by('date'))
\r
344 values = [r.value for r in rep]
\r
345 redux = lambda x, y: x+y
\r
347 graph_data = simplejson.dumps([
\r
348 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
349 for i in range(len(values))
\r
352 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
354 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
356 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
357 def user_votes(request, user):
\r
358 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
359 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
361 return {"view_user" : user, "votes" : votes}
\r
363 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
364 def user_favorites(request, user):
\r
365 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
367 return {"favorites" : favorites, "view_user" : user}
\r
369 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
\r
370 def user_subscriptions(request, user):
\r
371 enabled = user.subscription_settings.enable_notifications
\r
373 tab = request.GET.get('tab', "settings")
\r
375 if tab == 'settings':
\r
376 manage_open = False
\r
377 if request.method == 'POST':
\r
378 manage_open = False
\r
379 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
381 if form.is_valid():
\r
383 message = _('New subscription settings are now saved')
\r
385 if 'notswitch' in request.POST:
\r
386 enabled = not enabled
\r
389 message = _('Notifications are now enabled')
\r
391 message = _('Notifications are now disabled')
\r
393 user.subscription_settings.enable_notifications = enabled
\r
394 user.subscription_settings.save()
\r
396 request.user.message_set.create(message=message)
\r
398 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
402 'notificatons_on': enabled,
\r
404 'manage_open':manage_open,
\r
407 elif tab == 'manage':
\r
410 auto = request.GET.get('auto', 'True')
\r
413 subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')
\r
416 subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')
\r
418 return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
\r
419 'subscriptions':subscriptions,
\r
422 'manage_open':manage_open,
\r
426 # todo: probably want to throw an error
\r
427 # error = "error to throw"
\r
435 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
436 def user_preferences(request, user):
\r
438 form = UserPreferencesForm(request.POST)
\r
440 if form.is_valid():
\r
441 user.prop.preferences = form.cleaned_data
\r
442 request.user.message_set.create(message=_('New preferences saved'))
\r
445 preferences = user.prop.preferences
\r
448 form = UserPreferencesForm(initial=preferences)
\r
450 form = UserPreferencesForm()
\r
452 return {'view_user': user, 'form': form}
\r