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
31 class UserReputationSort(pagination.SimpleSort):
\r
32 def apply(self, objects):
\r
33 return objects.order_by('-is_active', self.order_by)
\r
35 class UserListPaginatorContext(pagination.PaginatorContext):
\r
37 super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
\r
38 (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
\r
39 (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
\r
40 (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
\r
41 (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
\r
42 ), pagesizes=(20, 35, 60))
\r
44 class SubscriptionListPaginatorContext(pagination.PaginatorContext):
\r
46 super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)
\r
48 class UserAnswersPaginatorContext(pagination.PaginatorContext):
\r
50 super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
\r
51 (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
\r
52 (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
\r
53 (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
\r
54 ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
\r
56 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
58 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
60 suser = request.REQUEST.get('q', "")
\r
61 users = User.objects.all()
\r
64 users = users.filter(username__icontains=suser)
\r
66 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
72 #@decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200)
\r
73 def online_users(request):
\r
74 suser = request.REQUEST.get('q', "")
\r
76 one_hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)
\r
77 sql_datetime = datetime.datetime.strftime(one_hour_ago, '%Y-%m-%d %H:%M:%S')
\r
78 users = User.objects.order_by('-last_seen')
\r
80 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
87 def edit_user(request, id):
\r
88 user = get_object_or_404(User, id=id)
\r
89 if not (request.user.is_superuser or request.user == user):
\r
90 return HttpResponseUnauthorized(request)
\r
91 if request.method == "POST":
\r
92 form = EditUserForm(user, request.POST)
\r
94 new_email = sanitize_html(form.cleaned_data['email'])
\r
96 if new_email != user.email:
\r
97 user.email = new_email
\r
98 user.email_isvalid = False
\r
101 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
106 if settings.EDITABLE_SCREEN_NAME:
\r
107 user.username = sanitize_html(form.cleaned_data['username'])
\r
108 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
109 user.website = sanitize_html(form.cleaned_data['website'])
\r
110 user.location = sanitize_html(form.cleaned_data['city'])
\r
111 user.date_of_birth = form.cleaned_data['birthday']
\r
112 if user.date_of_birth == "None":
\r
113 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
114 user.about = sanitize_html(form.cleaned_data['about'])
\r
117 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
119 request.user.message_set.create(message=_("Profile updated."))
\r
120 return HttpResponseRedirect(user.get_profile_url())
\r
122 form = EditUserForm(user)
\r
123 return render_to_response('users/edit.html', {
\r
126 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
127 }, context_instance=RequestContext(request))
\r
130 @decorate.withfn(decorators.command)
\r
131 def user_powers(request, id, action, status):
\r
132 if not request.user.is_superuser:
\r
133 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
135 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
136 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
138 user = get_object_or_404(User, id=id)
\r
139 new_state = action == 'grant'
\r
141 if status == 'super':
\r
142 user.is_superuser = new_state
\r
143 elif status == 'staff':
\r
144 user.is_staff = new_state
\r
149 return decorators.RefreshPageCommand()
\r
152 @decorate.withfn(decorators.command)
\r
153 def award_points(request, id):
\r
154 if not request.POST:
\r
155 return render_to_response('users/karma_bonus.html')
\r
157 if not request.user.is_superuser:
\r
158 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
161 points = int(request.POST['points'])
\r
163 raise decorators.CommandException(_("Invalid number of points to award."))
\r
165 user = get_object_or_404(User, id=id)
\r
167 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
169 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
171 return {'commands': {
\r
172 'update_profile_karma': [user.reputation]
\r
176 @decorate.withfn(decorators.command)
\r
177 def suspend(request, id):
\r
178 user = get_object_or_404(User, id=id)
\r
180 if not request.user.is_superuser:
\r
181 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
183 if not request.POST.get('bantype', None):
\r
184 if user.is_suspended():
\r
185 suspension = user.suspension
\r
186 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
187 return decorators.RefreshPageCommand()
\r
189 return render_to_response('users/suspend_user.html')
\r
192 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
193 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
194 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
198 if data['bantype'] == 'forxdays':
\r
200 data['forxdays'] = int(request.POST['forxdays'])
\r
202 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
204 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
206 return decorators.RefreshPageCommand()
\r
209 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
211 def params(request, id, slug=None):
\r
212 user = get_object_or_404(User, id=id)
\r
213 if private and not (user == request.user or request.user.is_superuser):
\r
214 return HttpResponseUnauthorized(request)
\r
216 if render_to and (not render_to(user)):
\r
217 return HttpResponseRedirect(user.get_profile_url())
\r
219 return [request, user], {}
\r
221 decorated = decorate.params.withfn(params)(fn)
\r
223 def result(context, request, user):
\r
224 rev_page_title = user.username + " - " + tab_description
\r
228 "active_tab" : tab_name,
\r
229 "tab_description" : tab_description,
\r
230 "page_title" : rev_page_title,
\r
231 "can_view_private": (user == request.user) or request.user.is_superuser
\r
233 return render_to_response(template, context, context_instance=RequestContext(request))
\r
235 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
238 def url_getter(vu):
\r
240 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
241 except NoReverseMatch:
\r
242 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
244 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
245 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
252 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
253 def user_profile(request, user):
\r
254 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
255 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
257 up_votes = user.vote_up_count
\r
258 down_votes = user.vote_down_count
\r
259 votes_today = user.get_vote_count_today()
\r
260 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
262 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
263 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
265 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
266 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
268 return pagination.paginated(request, (
\r
269 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),
\r
270 ('answers', UserAnswersPaginatorContext())), {
\r
271 "view_user" : user,
\r
272 "questions" : questions,
\r
273 "answers" : answers,
\r
274 "up_votes" : up_votes,
\r
275 "down_votes" : down_votes,
\r
276 "total_votes": up_votes + down_votes,
\r
277 "votes_today_left": votes_total-votes_today,
\r
278 "votes_total_per_day": votes_total,
\r
279 "user_tags" : user_tags[:50],
\r
281 "total_awards" : len(awards),
\r
284 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
285 def user_recent(request, user):
\r
286 activities = user.actions.exclude(
\r
287 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
288 '-action_date')[:USERS_PAGE_SIZE]
\r
290 return {"view_user" : user, "activities" : activities}
\r
293 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
294 def user_reputation(request, user):
\r
295 rep = list(user.reputes.order_by('date'))
\r
296 values = [r.value for r in rep]
\r
297 redux = lambda x, y: x+y
\r
299 graph_data = simplejson.dumps([
\r
300 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
301 for i in range(len(values))
\r
304 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
306 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
308 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
309 def user_votes(request, user):
\r
310 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
311 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
313 return {"view_user" : user, "votes" : votes}
\r
315 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
316 def user_favorites(request, user):
\r
317 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
319 return {"favorites" : favorites, "view_user" : user}
\r
321 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
\r
322 def user_subscriptions(request, user):
\r
323 enabled = user.subscription_settings.enable_notifications
\r
325 tab = request.GET.get('tab', "settings")
\r
327 if tab == 'settings':
\r
328 manage_open = False
\r
329 if request.method == 'POST':
\r
330 manage_open = False
\r
331 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
333 if form.is_valid():
\r
335 message = _('New subscription settings are now saved')
\r
337 if 'notswitch' in request.POST:
\r
338 enabled = not enabled
\r
341 message = _('Notifications are now enabled')
\r
343 message = _('Notifications are now disabled')
\r
345 user.subscription_settings.enable_notifications = enabled
\r
346 user.subscription_settings.save()
\r
348 request.user.message_set.create(message=message)
\r
350 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
354 'notificatons_on': enabled,
\r
356 'manage_open':manage_open,
\r
359 elif tab == 'manage':
\r
362 auto = request.GET.get('auto', 'True')
\r
365 subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')
\r
368 subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')
\r
370 return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
\r
371 'subscriptions':subscriptions,
\r
374 'manage_open':manage_open,
\r
378 # todo: probably want to throw an error
\r
379 # error = "error to throw"
\r
387 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
388 def user_preferences(request, user):
\r
390 form = UserPreferencesForm(request.POST)
\r
392 if form.is_valid():
\r
393 user.prop.preferences = form.cleaned_data
\r
394 request.user.message_set.create(message=_('New preferences saved'))
\r
397 preferences = user.prop.preferences
\r
400 form = UserPreferencesForm(initial=preferences)
\r
402 form = UserPreferencesForm()
\r
404 return {'view_user': user, 'form': form}
\r
407 def account_settings(request):
\r
409 msg = request.GET.get('msg', '')
\r
412 return render_to_response('account_settings.html', {
\r
414 'is_openid': is_openid
\r
415 }, context_instance=RequestContext(request))
\r