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 params(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 return [request, user], {}
\r
187 decorated = decorate.params.withfn(params)(fn)
\r
189 def result(context, request, user):
\r
190 rev_page_title = user.username + " - " + tab_description
\r
194 "active_tab" : tab_name,
\r
195 "tab_description" : tab_description,
\r
196 "page_title" : rev_page_title,
\r
197 "can_view_private": (user == request.user) or request.user.is_superuser
\r
199 return render_to_response(template, context, context_instance=RequestContext(request))
\r
201 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
204 def url_getter(vu):
\r
206 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
207 except NoReverseMatch:
\r
208 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
210 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
211 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
218 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
219 def user_profile(request, user):
\r
220 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
221 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
223 up_votes = user.vote_up_count
\r
224 down_votes = user.vote_down_count
\r
225 votes_today = user.get_vote_count_today()
\r
226 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
228 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
229 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
231 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
232 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
235 "view_user" : user,
\r
236 "questions" : questions,
\r
237 "answers" : answers,
\r
238 "up_votes" : up_votes,
\r
239 "down_votes" : down_votes,
\r
240 "total_votes": up_votes + down_votes,
\r
241 "votes_today_left": votes_total-votes_today,
\r
242 "votes_total_per_day": votes_total,
\r
243 "user_tags" : user_tags[:50],
\r
245 "total_awards" : len(awards),
\r
248 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
249 def user_recent(request, user):
\r
250 activities = user.actions.exclude(
\r
251 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
252 '-action_date')[:USERS_PAGE_SIZE]
\r
254 return {"view_user" : user, "activities" : activities}
\r
257 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
258 def user_reputation(request, user):
\r
259 rep = list(user.reputes.order_by('date'))
\r
260 values = [r.value for r in rep]
\r
261 redux = lambda x, y: x+y
\r
263 graph_data = simplejson.dumps([
\r
264 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
265 for i in range(len(values))
\r
268 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
270 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
272 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
273 def user_votes(request, user):
\r
274 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
275 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
277 return {"view_user" : user, "votes" : votes}
\r
279 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
280 def user_favorites(request, user):
\r
281 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
283 return {"favorites" : favorites, "view_user" : user}
\r
285 @user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True, tabbed=False)
\r
286 def user_subscriptions(request, user):
\r
287 enabled = user.subscription_settings.enable_notifications
\r
289 if request.method == 'POST':
\r
290 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
292 if form.is_valid():
\r
294 message = _('New subscription settings are now saved')
\r
296 if 'notswitch' in request.POST:
\r
297 enabled = not enabled
\r
300 message = _('Notifications are now enabled')
\r
302 message = _('Notifications are now disabled')
\r
304 user.subscription_settings.enable_notifications = enabled
\r
305 user.subscription_settings.save()
\r
307 request.user.message_set.create(message=message)
\r
309 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
311 return {'view_user':user, 'notificatons_on': enabled, 'form':form}
\r
314 def account_settings(request):
\r
316 msg = request.GET.get('msg', '')
\r
319 return render_to_response('account_settings.html', {
\r
321 'is_openid': is_openid
\r
322 }, context_instance=RequestContext(request))
\r