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
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
30 class UserReputationSort(pagination.SimpleSort):
\r
31 def apply(self, objects):
\r
32 return objects.order_by('-is_active', self.order_by)
\r
34 class UserListPaginatorContext(pagination.PaginatorContext):
\r
36 super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
\r
37 (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
\r
38 (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
\r
39 (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
\r
40 (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
\r
41 ), pagesizes=(20, 35, 60))
\r
43 class SubscriptionListPaginatorContext(pagination.PaginatorContext):
\r
45 super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)
\r
47 class UserAnswersPaginatorContext(pagination.PaginatorContext):
\r
49 super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
\r
50 (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
\r
51 (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
\r
52 (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
\r
53 ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
\r
55 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
57 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
59 suser = request.REQUEST.get('q', "")
\r
60 users = User.objects.all()
\r
63 users = users.filter(username__icontains=suser)
\r
65 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
71 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200)
\r
72 def online_users(request):
\r
73 suser = request.REQUEST.get('q', "")
\r
75 one_hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
\r
76 sql_datetime = datetime.datetime.strftime(one_hour_ago, '%Y-%m-%d %H:%M:%S')
\r
77 users = User.objects.order_by('-last_seen')
\r
79 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
86 def edit_user(request, id):
\r
87 user = get_object_or_404(User, id=id)
\r
88 if not (request.user.is_superuser or request.user == user):
\r
89 return HttpResponseUnauthorized(request)
\r
90 if request.method == "POST":
\r
91 form = EditUserForm(user, request.POST)
\r
93 new_email = sanitize_html(form.cleaned_data['email'])
\r
95 if new_email != user.email:
\r
96 user.email = new_email
\r
97 user.email_isvalid = False
\r
100 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
105 if settings.EDITABLE_SCREEN_NAME:
\r
106 user.username = sanitize_html(form.cleaned_data['username'])
\r
107 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
108 user.website = sanitize_html(form.cleaned_data['website'])
\r
109 user.location = sanitize_html(form.cleaned_data['city'])
\r
110 user.date_of_birth = form.cleaned_data['birthday']
\r
111 if user.date_of_birth == "None":
\r
112 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
113 user.about = sanitize_html(form.cleaned_data['about'])
\r
116 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
118 request.user.message_set.create(message=_("Profile updated."))
\r
119 return HttpResponseRedirect(user.get_profile_url())
\r
121 form = EditUserForm(user)
\r
122 return render_to_response('users/edit.html', {
\r
125 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
126 }, context_instance=RequestContext(request))
\r
129 @decorate.withfn(decorators.command)
\r
130 def user_powers(request, id, action, status):
\r
131 if not request.user.is_superuser:
\r
132 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
134 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
135 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
137 user = get_object_or_404(User, id=id)
\r
138 new_state = action == 'grant'
\r
140 if status == 'super':
\r
141 user.is_superuser = new_state
\r
142 elif status == 'staff':
\r
143 user.is_staff = new_state
\r
148 return decorators.RefreshPageCommand()
\r
151 @decorate.withfn(decorators.command)
\r
152 def award_points(request, id):
\r
153 if not request.POST:
\r
154 return render_to_response('users/karma_bonus.html')
\r
156 if not request.user.is_superuser:
\r
157 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
160 points = int(request.POST['points'])
\r
162 raise decorators.CommandException(_("Invalid number of points to award."))
\r
164 user = get_object_or_404(User, id=id)
\r
166 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
168 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
170 return {'commands': {
\r
171 'update_profile_karma': [user.reputation]
\r
175 @decorate.withfn(decorators.command)
\r
176 def suspend(request, id):
\r
177 user = get_object_or_404(User, id=id)
\r
179 if not request.user.is_superuser:
\r
180 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
182 if not request.POST.get('bantype', None):
\r
183 if user.is_suspended():
\r
184 suspension = user.suspension
\r
185 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
186 return decorators.RefreshPageCommand()
\r
188 return render_to_response('users/suspend_user.html')
\r
191 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
192 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
193 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
197 if data['bantype'] == 'forxdays':
\r
199 data['forxdays'] = int(request.POST['forxdays'])
\r
201 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
203 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
205 return decorators.RefreshPageCommand()
\r
208 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
210 def params(request, id, slug=None):
\r
211 user = get_object_or_404(User, id=id)
\r
212 if private and not (user == request.user or request.user.is_superuser):
\r
213 return HttpResponseUnauthorized(request)
\r
215 if render_to and (not render_to(user)):
\r
216 return HttpResponseRedirect(user.get_profile_url())
\r
218 return [request, user], {}
\r
220 decorated = decorate.params.withfn(params)(fn)
\r
222 def result(context, request, user):
\r
223 rev_page_title = user.username + " - " + tab_description
\r
227 "active_tab" : tab_name,
\r
228 "tab_description" : tab_description,
\r
229 "page_title" : rev_page_title,
\r
230 "can_view_private": (user == request.user) or request.user.is_superuser
\r
232 return render_to_response(template, context, context_instance=RequestContext(request))
\r
234 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
237 def url_getter(vu):
\r
239 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
240 except NoReverseMatch:
\r
241 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
243 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
244 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
251 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
252 def user_profile(request, user):
\r
253 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
254 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
256 up_votes = user.vote_up_count
\r
257 down_votes = user.vote_down_count
\r
258 votes_today = user.get_vote_count_today()
\r
259 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
261 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
262 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
264 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
265 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
267 return pagination.paginated(request, (
\r
268 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),
\r
269 ('answers', UserAnswersPaginatorContext())), {
\r
270 "view_user" : user,
\r
271 "questions" : questions,
\r
272 "answers" : answers,
\r
273 "up_votes" : up_votes,
\r
274 "down_votes" : down_votes,
\r
275 "total_votes": up_votes + down_votes,
\r
276 "votes_today_left": votes_total-votes_today,
\r
277 "votes_total_per_day": votes_total,
\r
278 "user_tags" : user_tags[:50],
\r
280 "total_awards" : len(awards),
\r
283 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
284 def user_recent(request, user):
\r
285 activities = user.actions.exclude(
\r
286 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
287 '-action_date')[:USERS_PAGE_SIZE]
\r
289 return {"view_user" : user, "activities" : activities}
\r
292 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
293 def user_reputation(request, user):
\r
294 rep = list(user.reputes.order_by('date'))
\r
295 values = [r.value for r in rep]
\r
296 redux = lambda x, y: x+y
\r
298 graph_data = simplejson.dumps([
\r
299 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
300 for i in range(len(values))
\r
303 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
305 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
307 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
308 def user_votes(request, user):
\r
309 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
310 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
312 return {"view_user" : user, "votes" : votes}
\r
314 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
315 def user_favorites(request, user):
\r
316 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
318 return {"favorites" : favorites, "view_user" : user}
\r
320 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
\r
321 def user_subscriptions(request, user):
\r
322 enabled = user.subscription_settings.enable_notifications
\r
323 auto = request.GET.get('auto', 'True')
\r
325 manage_open = False
\r
327 if len(request.GET) > 0:
\r
332 subscriptions = user.subscriptions.all().order_by('-questionsubscription__last_view')
\r
335 subscriptions = user.subscriptions.filter(questionsubscription__auto_subscription=False).order_by('-questionsubscription__last_view')
\r
337 if request.method == 'POST':
\r
338 manage_open = False
\r
339 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
341 if form.is_valid():
\r
343 message = _('New subscription settings are now saved')
\r
345 if 'notswitch' in request.POST:
\r
346 enabled = not enabled
\r
349 message = _('Notifications are now enabled')
\r
351 message = _('Notifications are now disabled')
\r
353 user.subscription_settings.enable_notifications = enabled
\r
354 user.subscription_settings.save()
\r
356 request.user.message_set.create(message=message)
\r
358 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
360 return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
\r
361 'subscriptions':subscriptions,
\r
363 'notificatons_on': enabled,
\r
366 "manage_open":manage_open
\r
369 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
370 def user_preferences(request, user):
\r
372 form = UserPreferencesForm(request.POST)
\r
374 if form.is_valid():
\r
375 user.prop.preferences = form.cleaned_data
\r
376 request.user.message_set.create(message=_('New preferences saved'))
\r
379 preferences = user.prop.preferences
\r
382 form = UserPreferencesForm(initial=preferences)
\r
384 form = UserPreferencesForm()
\r
386 return {'view_user': user, 'form': form}
\r
389 def account_settings(request):
\r
391 msg = request.GET.get('msg', '')
\r
394 return render_to_response('account_settings.html', {
\r
396 'is_openid': is_openid
\r
397 }, context_instance=RequestContext(request))
\r