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
25 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 UserAnswersPaginatorContext(pagination.PaginatorContext):
\r
45 super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
\r
46 (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
\r
47 (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
\r
48 (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
\r
49 ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
\r
51 USERS_PAGE_SIZE = 35# refactor - move to some constants file
\r
53 @decorators.render('users/users.html', 'users', _('users'), weight=200)
\r
55 suser = request.REQUEST.get('q', "")
\r
56 users = User.objects.all()
\r
59 users = users.filter(username__icontains=suser)
\r
61 return pagination.paginated(request, ('users', UserListPaginatorContext()), {
\r
68 def edit_user(request, id):
\r
69 user = get_object_or_404(User, id=id)
\r
70 if not (request.user.is_superuser or request.user == user):
\r
71 return HttpResponseUnauthorized(request)
\r
72 if request.method == "POST":
\r
73 form = EditUserForm(user, request.POST)
\r
75 new_email = sanitize_html(form.cleaned_data['email'])
\r
77 if new_email != user.email:
\r
78 user.email = new_email
\r
79 user.email_isvalid = False
\r
82 hash = ValidationHash.objects.get(user=request.user, type='email')
\r
87 if settings.EDITABLE_SCREEN_NAME:
\r
88 user.username = sanitize_html(form.cleaned_data['username'])
\r
89 user.real_name = sanitize_html(form.cleaned_data['realname'])
\r
90 user.website = sanitize_html(form.cleaned_data['website'])
\r
91 user.location = sanitize_html(form.cleaned_data['city'])
\r
92 user.date_of_birth = form.cleaned_data['birthday']
\r
93 if user.date_of_birth == "None":
\r
94 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
\r
95 user.about = sanitize_html(form.cleaned_data['about'])
\r
98 EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
\r
100 request.user.message_set.create(message=_("Profile updated."))
\r
101 return HttpResponseRedirect(user.get_profile_url())
\r
103 form = EditUserForm(user)
\r
104 return render_to_response('users/edit.html', {
\r
107 'gravatar_faq_url' : reverse('faq') + '#gravatar',
\r
108 }, context_instance=RequestContext(request))
\r
111 @decorate.withfn(decorators.command)
\r
112 def user_powers(request, id, action, status):
\r
113 if not request.user.is_superuser:
\r
114 raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
\r
116 if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
\r
117 raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
\r
119 user = get_object_or_404(User, id=id)
\r
120 new_state = action == 'grant'
\r
122 if status == 'super':
\r
123 user.is_superuser = new_state
\r
124 elif status == 'staff':
\r
125 user.is_staff = new_state
\r
130 return decorators.RefreshPageCommand()
\r
133 @decorate.withfn(decorators.command)
\r
134 def award_points(request, id):
\r
135 if not request.POST:
\r
136 return render_to_response('users/karma_bonus.html')
\r
138 if not request.user.is_superuser:
\r
139 raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
\r
142 points = int(request.POST['points'])
\r
144 raise decorators.CommandException(_("Invalid number of points to award."))
\r
146 user = get_object_or_404(User, id=id)
\r
148 extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
\r
150 BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
\r
152 return {'commands': {
\r
153 'update_profile_karma': [user.reputation]
\r
157 @decorate.withfn(decorators.command)
\r
158 def suspend(request, id):
\r
159 user = get_object_or_404(User, id=id)
\r
161 if not request.user.is_superuser:
\r
162 raise decorators.CommandException(_("Only superusers can suspend other users"))
\r
164 if not request.POST.get('bantype', None):
\r
165 if user.is_suspended():
\r
166 suspension = user.suspension
\r
167 suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
\r
168 return decorators.RefreshPageCommand()
\r
170 return render_to_response('users/suspend_user.html')
\r
173 'bantype': request.POST.get('bantype', 'indefinetly').strip(),
\r
174 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
\r
175 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
\r
179 if data['bantype'] == 'forxdays':
\r
181 data['forxdays'] = int(request.POST['forxdays'])
\r
183 raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
\r
185 SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
\r
187 return decorators.RefreshPageCommand()
\r
190 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
\r
192 def params(request, id, slug=None):
\r
193 user = get_object_or_404(User, id=id)
\r
194 if private and not (user == request.user or request.user.is_superuser):
\r
195 return HttpResponseUnauthorized(request)
\r
197 if render_to and (not render_to(user)):
\r
198 return HttpResponseRedirect(user.get_profile_url())
\r
200 return [request, user], {}
\r
202 decorated = decorate.params.withfn(params)(fn)
\r
204 def result(context, request, user):
\r
205 rev_page_title = user.username + " - " + tab_description
\r
209 "active_tab" : tab_name,
\r
210 "tab_description" : tab_description,
\r
211 "page_title" : rev_page_title,
\r
212 "can_view_private": (user == request.user) or request.user.is_superuser
\r
214 return render_to_response(template, context, context_instance=RequestContext(request))
\r
216 decorated = decorate.result.withfn(result, needs_params=True)(decorated)
\r
219 def url_getter(vu):
\r
221 return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})
\r
222 except NoReverseMatch:
\r
223 return reverse(fn.__name__, kwargs={'id': vu.id})
\r
225 ui.register(ui.PROFILE_TABS, ui.ProfileTab(
\r
226 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
\r
233 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
\r
234 def user_profile(request, user):
\r
235 questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
236 answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
\r
238 up_votes = user.vote_up_count
\r
239 down_votes = user.vote_down_count
\r
240 votes_today = user.get_vote_count_today()
\r
241 votes_total = int(settings.MAX_VOTES_PER_DAY)
\r
243 user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
\r
244 .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
\r
246 awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
\r
247 Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
\r
249 return pagination.paginated(request, (
\r
250 ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),
\r
251 ('answers', UserAnswersPaginatorContext())), {
\r
252 "view_user" : user,
\r
253 "questions" : questions,
\r
254 "answers" : answers,
\r
255 "up_votes" : up_votes,
\r
256 "down_votes" : down_votes,
\r
257 "total_votes": up_votes + down_votes,
\r
258 "votes_today_left": votes_total-votes_today,
\r
259 "votes_total_per_day": votes_total,
\r
260 "user_tags" : user_tags[:50],
\r
262 "total_awards" : len(awards),
\r
265 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
\r
266 def user_recent(request, user):
\r
267 activities = user.actions.exclude(
\r
268 action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
\r
269 '-action_date')[:USERS_PAGE_SIZE]
\r
271 return {"view_user" : user, "activities" : activities}
\r
274 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))
\r
275 def user_reputation(request, user):
\r
276 rep = list(user.reputes.order_by('date'))
\r
277 values = [r.value for r in rep]
\r
278 redux = lambda x, y: x+y
\r
280 graph_data = simplejson.dumps([
\r
281 (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))
\r
282 for i in range(len(values))
\r
285 rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
\r
287 return {"view_user": user, "reputation": rep, "graph_data": graph_data}
\r
289 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
\r
290 def user_votes(request, user):
\r
291 votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
\r
292 node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
\r
294 return {"view_user" : user, "votes" : votes}
\r
296 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
\r
297 def user_favorites(request, user):
\r
298 favorites = FavoriteAction.objects.filter(canceled=False, user=user)
\r
300 return {"favorites" : favorites, "view_user" : user}
\r
302 @user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True, tabbed=False)
\r
303 def user_subscriptions(request, user):
\r
304 enabled = user.subscription_settings.enable_notifications
\r
306 if request.method == 'POST':
\r
307 form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
\r
309 if form.is_valid():
\r
311 message = _('New subscription settings are now saved')
\r
313 if 'notswitch' in request.POST:
\r
314 enabled = not enabled
\r
317 message = _('Notifications are now enabled')
\r
319 message = _('Notifications are now disabled')
\r
321 user.subscription_settings.enable_notifications = enabled
\r
322 user.subscription_settings.save()
\r
324 request.user.message_set.create(message=message)
\r
326 form = SubscriptionSettingsForm(instance=user.subscription_settings)
\r
328 return {'view_user':user, 'notificatons_on': enabled, 'form':form}
\r
330 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
\r
331 def user_preferences(request, user):
\r
333 form = UserPreferencesForm(request.POST)
\r
335 if form.is_valid():
\r
336 user.prop.preferences = form.cleaned_data
\r
337 request.user.message_set.create(message=_('New preferences saved'))
\r
340 preferences = user.prop.preferences
\r
343 form = UserPreferencesForm(initial=preferences)
\r
345 form = UserPreferencesForm()
\r
347 return {'view_user': user, 'form': form}
\r
350 def account_settings(request):
\r
352 msg = request.GET.get('msg', '')
\r
355 return render_to_response('account_settings.html', {
\r
357 'is_openid': is_openid
\r
358 }, context_instance=RequestContext(request))
\r