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
22 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction
\r
23 from forum.modules import ui
\r
24 from forum.utils import pagination
\r
29 class UserReputationSort(pagination.SimpleSort):
\r
30 def apply(self, objects):
\r
31 return objects.order_by('-is_active', self.order_by)
\r
33 class UserListPaginatorContext(pagination.PaginatorContext):
\r
35 super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
\r
36 (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
\r
37 (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
\r
38 (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
\r
39 (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
\r
40 ), pagesizes=(20, 35, 60))
\r
42 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
44 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
46 suser = request.REQUEST.get('q', "")
\r
47 users = User.objects.all()
\r
50 users = users.filter(username__icontains=suser)
\r
52 return pagination.paginated(request, 'users', UserListPaginatorContext(), {
\r
59 def edit_user(request, id):
\r
60 user = get_object_or_404(User, id=id)
\r
61 if not (request.user.is_superuser or request.user == user):
\r
62 return HttpResponseUnauthorized(request)
\r
63 if request.method == "POST":
\r
64 form = EditUserForm(user, request.POST)
\r
66 new_email = sanitize_html(form.cleaned_data['email'])
\r
68 if new_email != user.email:
\r
69 user.email = new_email
\r
70 user.email_isvalid = False
\r
72 if settings.EDITABLE_SCREEN_NAME:
\r
73 user.username = sanitize_html(form.cleaned_data['username'])
\r
74 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
75 user.website = sanitize_html(form.cleaned_data['website'])
\r
76 user.location = sanitize_html(form.cleaned_data['city'])
\r
77 user.date_of_birth = form.cleaned_data['birthday']
\r
78 if user.date_of_birth == "None":
\r
79 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
80 user.about = sanitize_html(form.cleaned_data['about'])
\r
83 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
85 request.user.message_set.create(message=_("Profile updated."))
\r
86 return HttpResponseRedirect(user.get_profile_url())
\r
88 form = EditUserForm(user)
\r
89 return render_to_response('users/edit.html', {
\r
92 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
93 }, context_instance=RequestContext(request))
\r
96 @decorate.withfn(decorators.command)
\r
97 def user_powers(request, id, action, status):
\r
98 if not request.user.is_superuser:
\r
99 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
101 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
102 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
104 user = get_object_or_404(User, id=id)
\r
105 new_state = action == 'grant'
\r
107 if status == 'super':
\r
108 user.is_superuser = new_state
\r
109 elif status == 'staff':
\r
110 user.is_staff = new_state
\r
115 return decorators.RefreshPageCommand()
\r
118 @decorate.withfn(decorators.command)
\r
119 def award_points(request, id):
\r
120 if not request.POST:
\r
121 return render_to_response('users/karma_bonus.html')
\r
123 if not request.user.is_superuser:
\r
124 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
127 points = int(request.POST['points'])
\r
129 raise decorators.CommandException(_("Invalid number of points to award."))
\r
131 user = get_object_or_404(User, id=id)
\r
133 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
135 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
137 return {'commands': {
\r
138 'update_profile_karma': [user.reputation]
\r
142 @decorate.withfn(decorators.command)
\r
143 def suspend(request, id):
\r
144 user = get_object_or_404(User, id=id)
\r
146 if not request.user.is_superuser:
\r
147 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
149 if not request.POST.get('bantype', None):
\r
150 if user.is_suspended():
\r
151 suspension = user.suspension
\r
152 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
153 return decorators.RefreshPageCommand()
\r
155 return render_to_response('users/suspend_user.html')
\r
158 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
159 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
160 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
164 if data['bantype'] == 'forxdays':
\r
166 data['forxdays'] = int(request.POST['forxdays'])
\r
168 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
170 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
172 return decorators.RefreshPageCommand()
\r
175 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
177 def decorated(fn, request, id, slug=None):
\r
178 user = get_object_or_404(User, id=id)
\r
179 if private and not (user == request.user or request.user.is_superuser):
\r
180 return HttpResponseUnauthorized(request)
\r
182 if render_to and (not render_to(user)):
\r
183 return HttpResponseRedirect(user.get_profile_url())
\r
185 context = fn(request, user)
\r
187 rev_page_title = user.username + " - " + tab_description
\r
191 "active_tab" : tab_name,
\r
192 "tab_description" : tab_description,
\r
193 "page_title" : rev_page_title,
\r
194 "can_view_private": (user == request.user) or request.user.is_superuser
\r
196 return render_to_response(template, context, context_instance=RequestContext(request))
\r
199 def url_getter(vu):
\r
201 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
202 except NoReverseMatch:
\r
203 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
205 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
206 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
209 return decorate.withfn(decorated)(fn)
\r
213 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
214 def user_profile(request, user):
\r
215 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
216 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
218 up_votes = user.vote_up_count
\r
219 down_votes = user.vote_down_count
\r
220 votes_today = user.get_vote_count_today()
\r
221 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
223 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
224 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
226 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
227 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
230 "view_user" : user,
\r
231 "questions" : questions,
\r
232 "answers" : answers,
\r
233 "up_votes" : up_votes,
\r
234 "down_votes" : down_votes,
\r
235 "total_votes": up_votes + down_votes,
\r
236 "votes_today_left": votes_total-votes_today,
\r
237 "votes_total_per_day": votes_total,
\r
238 "user_tags" : user_tags[:50],
\r
240 "total_awards" : len(awards),
\r
243 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
244 def user_recent(request, user):
\r
245 activities = user.actions.exclude(
\r
246 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
247 '-action_date')[:USERS_PAGE_SIZE]
\r
249 return {"view_user" : user, "activities" : activities}
\r
252 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
253 def user_reputation(request, user):
\r
254 rep = list(user.reputes.order_by('date'))
\r
255 values = [r.value for r in rep]
\r
256 redux = lambda x, y: x+y
\r
258 graph_data = simplejson.dumps([
\r
259 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
260 for i in range(len(values))
\r
263 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
265 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
267 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
268 def user_votes(request, user):
\r
269 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
270 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
272 return {"view_user" : user, "votes" : votes}
\r
274 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
275 def user_favorites(request, user):
\r
276 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
278 return {"favorites" : favorites, "view_user" : user}
\r
280 @user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True, tabbed=False)
\r
281 def user_subscriptions(request, user):
\r
282 enabled = user.subscription_settings.enable_notifications
\r
284 if request.method == 'POST':
\r
285 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
287 if form.is_valid():
\r
289 message = _('New subscription settings are now saved')
\r
291 if 'notswitch' in request.POST:
\r
292 enabled = not enabled
\r
295 message = _('Notifications are now enabled')
\r
297 message = _('Notifications are now disabled')
\r
299 user.subscription_settings.enable_notifications = enabled
\r
300 user.subscription_settings.save()
\r
302 request.user.message_set.create(message=message)
\r
304 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
306 return {'view_user':user, 'notificatons_on': enabled, 'form':form}
\r
309 def account_settings(request):
\r
311 msg = request.GET.get('msg', '')
\r
314 return render_to_response('account_settings.html', {
\r
316 'is_openid': is_openid
\r
317 }, context_instance=RequestContext(request))
\r