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
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
85 def edit_user(request, id):
\r
86 user = get_object_or_404(User, id=id)
\r
87 if not (request.user.is_superuser or request.user == user):
\r
88 return HttpResponseUnauthorized(request)
\r
89 if request.method == "POST":
\r
90 form = EditUserForm(user, request.POST)
\r
92 new_email = sanitize_html(form.cleaned_data['email'])
\r
94 if new_email != user.email:
\r
95 user.email = new_email
\r
96 user.email_isvalid = False
\r
99 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
104 if settings.EDITABLE_SCREEN_NAME:
\r
105 user.username = sanitize_html(form.cleaned_data['username'])
\r
106 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
107 user.website = sanitize_html(form.cleaned_data['website'])
\r
108 user.location = sanitize_html(form.cleaned_data['city'])
\r
109 user.date_of_birth = form.cleaned_data['birthday']
\r
110 if user.date_of_birth == "None":
\r
111 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
112 user.about = sanitize_html(form.cleaned_data['about'])
\r
115 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
117 request.user.message_set.create(message=_("Profile updated."))
\r
118 return HttpResponseRedirect(user.get_profile_url())
\r
120 form = EditUserForm(user)
\r
121 return render_to_response('users/edit.html', {
\r
124 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
125 }, context_instance=RequestContext(request))
\r
128 @decorate.withfn(decorators.command)
\r
129 def user_powers(request, id, action, status):
\r
130 if not request.user.is_superuser:
\r
131 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
133 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
134 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
136 user = get_object_or_404(User, id=id)
\r
137 new_state = action == 'grant'
\r
139 if status == 'super':
\r
140 user.is_superuser = new_state
\r
141 elif status == 'staff':
\r
142 user.is_staff = new_state
\r
147 return decorators.RefreshPageCommand()
\r
150 @decorate.withfn(decorators.command)
\r
151 def award_points(request, id):
\r
152 if not request.POST:
\r
153 return render_to_response('users/karma_bonus.html')
\r
155 if not request.user.is_superuser:
\r
156 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
159 points = int(request.POST['points'])
\r
161 raise decorators.CommandException(_("Invalid number of points to award."))
\r
163 user = get_object_or_404(User, id=id)
\r
165 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
167 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
169 return {'commands': {
\r
170 'update_profile_karma': [user.reputation]
\r
174 @decorate.withfn(decorators.command)
\r
175 def suspend(request, id):
\r
176 user = get_object_or_404(User, id=id)
\r
178 if not request.user.is_superuser:
\r
179 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
181 if not request.POST.get('bantype', None):
\r
182 if user.is_suspended():
\r
183 suspension = user.suspension
\r
184 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
185 return decorators.RefreshPageCommand()
\r
187 return render_to_response('users/suspend_user.html')
\r
190 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
191 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
192 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
196 if data['bantype'] == 'forxdays':
\r
198 data['forxdays'] = int(request.POST['forxdays'])
\r
200 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
202 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
204 return decorators.RefreshPageCommand()
\r
207 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
209 def params(request, id, slug=None):
\r
210 user = get_object_or_404(User, id=id)
\r
211 if private and not (user == request.user or request.user.is_superuser):
\r
212 return HttpResponseUnauthorized(request)
\r
214 if render_to and (not render_to(user)):
\r
215 return HttpResponseRedirect(user.get_profile_url())
\r
217 return [request, user], {}
\r
219 decorated = decorate.params.withfn(params)(fn)
\r
221 def result(context, request, user):
\r
222 rev_page_title = user.username + " - " + tab_description
\r
226 "active_tab" : tab_name,
\r
227 "tab_description" : tab_description,
\r
228 "page_title" : rev_page_title,
\r
229 "can_view_private": (user == request.user) or request.user.is_superuser
\r
231 return render_to_response(template, context, context_instance=RequestContext(request))
\r
233 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
236 def url_getter(vu):
\r
238 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
239 except NoReverseMatch:
\r
240 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
242 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
243 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
250 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
251 def user_profile(request, user):
\r
252 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
253 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
255 up_votes = user.vote_up_count
\r
256 down_votes = user.vote_down_count
\r
257 votes_today = user.get_vote_count_today()
\r
258 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
260 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
261 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
263 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
264 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
266 return pagination.paginated(request, (
\r
267 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),
\r
268 ('answers', UserAnswersPaginatorContext())), {
\r
269 "view_user" : user,
\r
270 "questions" : questions,
\r
271 "answers" : answers,
\r
272 "up_votes" : up_votes,
\r
273 "down_votes" : down_votes,
\r
274 "total_votes": up_votes + down_votes,
\r
275 "votes_today_left": votes_total-votes_today,
\r
276 "votes_total_per_day": votes_total,
\r
277 "user_tags" : user_tags[:50],
\r
279 "total_awards" : len(awards),
\r
282 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
283 def user_recent(request, user):
\r
284 activities = user.actions.exclude(
\r
285 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
286 '-action_date')[:USERS_PAGE_SIZE]
\r
288 return {"view_user" : user, "activities" : activities}
\r
291 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
292 def user_reputation(request, user):
\r
293 rep = list(user.reputes.order_by('date'))
\r
294 values = [r.value for r in rep]
\r
295 redux = lambda x, y: x+y
\r
297 graph_data = simplejson.dumps([
\r
298 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
299 for i in range(len(values))
\r
302 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
304 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
306 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
307 def user_votes(request, user):
\r
308 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
309 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
311 return {"view_user" : user, "votes" : votes}
\r
313 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
314 def user_favorites(request, user):
\r
315 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
317 return {"favorites" : favorites, "view_user" : user}
\r
319 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
\r
320 def user_subscriptions(request, user):
\r
321 enabled = user.subscription_settings.enable_notifications
\r
323 tab = request.GET.get('tab', "settings")
\r
325 if tab == 'settings':
\r
326 manage_open = False
\r
327 if request.method == 'POST':
\r
328 manage_open = False
\r
329 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
331 if form.is_valid():
\r
333 message = _('New subscription settings are now saved')
\r
335 if 'notswitch' in request.POST:
\r
336 enabled = not enabled
\r
339 message = _('Notifications are now enabled')
\r
341 message = _('Notifications are now disabled')
\r
343 user.subscription_settings.enable_notifications = enabled
\r
344 user.subscription_settings.save()
\r
346 request.user.message_set.create(message=message)
\r
348 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
352 'notificatons_on': enabled,
\r
354 'manage_open':manage_open,
\r
357 elif tab == 'manage':
\r
360 auto = request.GET.get('auto', 'True')
\r
363 subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')
\r
366 subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')
\r
368 return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
\r
369 'subscriptions':subscriptions,
\r
372 'manage_open':manage_open,
\r
376 # todo: probably want to throw an error
\r
377 # error = "error to throw"
\r
385 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
386 def user_preferences(request, user):
\r
388 form = UserPreferencesForm(request.POST)
\r
390 if form.is_valid():
\r
391 user.prop.preferences = form.cleaned_data
\r
392 request.user.message_set.create(message=_('New preferences saved'))
\r
395 preferences = user.prop.preferences
\r
398 form = UserPreferencesForm(initial=preferences)
\r
400 form = UserPreferencesForm()
\r
402 return {'view_user': user, 'form': form}
\r