]> git.openstreetmap.org Git - osqa.git/blob - forum/views/users.py
dc865a236ed3e8824903419c78b88fdb609ce9ba
[osqa.git] / forum / views / users.py
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.encoding import smart_unicode\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, ReturnImediatelyException\r
20 from datetime import datetime, date\r
21 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction, ReportAction\r
22 from forum.modules import ui\r
23 from forum.utils import pagination\r
24 from forum.views.readers import QuestionListPaginatorContext, AnswerPaginatorContext\r
25 from forum.settings import ONLINE_USERS\r
26  \r
27 import time\r
28 import datetime\r
29 import decorators\r
30 \r
31 class UserReputationSort(pagination.SimpleSort):\r
32     def apply(self, objects):\r
33         return objects.order_by('-is_active', self.order_by)\r
34 \r
35 class UserListPaginatorContext(pagination.PaginatorContext):\r
36     def __init__(self, pagesizes=(20, 35, 60), default_pagesize=35):\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=pagesizes, default_pagesize=default_pagesize)\r
43 \r
44 class SubscriptionListPaginatorContext(pagination.PaginatorContext):\r
45     def __init__(self):\r
46         super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)\r
47 \r
48 class UserAnswersPaginatorContext(pagination.PaginatorContext):\r
49     def __init__(self):\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
55 \r
56 USERS_PAGE_SIZE = 35# refactor - move to some constants file\r
57 \r
58 @decorators.render('users/users.html', 'users', _('users'), weight=200)\r
59 def users(request):\r
60     suser = request.REQUEST.get('q', "")\r
61     users = User.objects.all()\r
62 \r
63     if suser != "":\r
64         users = users.filter(username__icontains=suser)\r
65 \r
66     return pagination.paginated(request, ('users', UserListPaginatorContext()), {\r
67         "users" : users,\r
68         "suser" : suser,\r
69     })\r
70 \r
71 \r
72 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False)\r
73 def online_users(request):\r
74     suser = request.REQUEST.get('q', "")\r
75 \r
76     sort = ""\r
77     if request.GET.get("sort", None):\r
78         try:\r
79             sort = int(request.GET["sort"])\r
80         except ValueError:\r
81             logging.error('Found invalid sort "%s", loading %s, refered by %s' % (\r
82                 request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')\r
83             ))\r
84             raise Http404()\r
85 \r
86     page = 0\r
87     if request.GET.get("page", None):\r
88         try:\r
89             page = int(request.GET["page"])\r
90         except ValueError:\r
91             logging.error('Found invalid page "%s", loading %s, refered by %s' % (\r
92                 request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')\r
93             ))\r
94             raise Http404()\r
95 \r
96     pagesize = 10\r
97     if request.GET.get("pagesize", None):\r
98         try:\r
99             pagesize = int(request.GET["pagesize"])\r
100         except ValueError:\r
101             logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % (\r
102                 request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')\r
103             ))\r
104             raise Http404()\r
105 \r
106 \r
107     users = None\r
108     if sort == "reputation":\r
109         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation)\r
110     elif sort == "newest" :\r
111         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)\r
112     elif sort == "last":\r
113         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last)\r
114     elif sort == "name":\r
115         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name)\r
116     elif sort == "oldest":\r
117         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest)\r
118     elif sort == "newest":\r
119         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)\r
120     elif sort == "votes":\r
121         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes)\r
122     else:\r
123         users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1])\r
124 \r
125     return render_to_response('users/online_users.html', {\r
126         "users" : users,\r
127         "suser" : suser,\r
128         "sort" : sort,\r
129         "page" : page,\r
130         "pageSize" : pagesize,\r
131     })\r
132 \r
133 \r
134 def edit_user(request, id, slug):\r
135     user = get_object_or_404(User, id=id)\r
136     if not (request.user.is_superuser or request.user == user):\r
137         return HttpResponseUnauthorized(request)\r
138     if request.method == "POST":\r
139         form = EditUserForm(user, request.POST)\r
140         if form.is_valid():\r
141             new_email = sanitize_html(form.cleaned_data['email'])\r
142 \r
143             if new_email != user.email:\r
144                 user.email = new_email\r
145                 user.email_isvalid = False\r
146 \r
147                 try:\r
148                     hash = ValidationHash.objects.get(user=request.user, type='email')\r
149                     hash.delete()\r
150                 except:\r
151                     pass\r
152 \r
153             if settings.EDITABLE_SCREEN_NAME:\r
154                 user.username = sanitize_html(form.cleaned_data['username'])\r
155             user.real_name = sanitize_html(form.cleaned_data['realname'])\r
156             user.website = sanitize_html(form.cleaned_data['website'])\r
157             user.location = sanitize_html(form.cleaned_data['city'])\r
158             user.date_of_birth = form.cleaned_data['birthday']\r
159             if user.date_of_birth == "None":\r
160                 user.date_of_birth = datetime(1900, 1, 1, 0, 0)\r
161             user.about = sanitize_html(form.cleaned_data['about'])\r
162 \r
163             user.save()\r
164             EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()\r
165 \r
166             request.user.message_set.create(message=_("Profile updated."))\r
167             return HttpResponseRedirect(user.get_profile_url())\r
168     else:\r
169         form = EditUserForm(user)\r
170     return render_to_response('users/edit.html', {\r
171     'user': user,\r
172     'form' : form,\r
173     'gravatar_faq_url' : reverse('faq') + '#gravatar',\r
174     }, context_instance=RequestContext(request))\r
175 \r
176 \r
177 @decorate.withfn(decorators.command)\r
178 def user_powers(request, id, action, status):\r
179     if not request.user.is_superuser:\r
180         raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))\r
181 \r
182     if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():\r
183         raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))\r
184 \r
185     user = get_object_or_404(User, id=id)\r
186     new_state = action == 'grant'\r
187 \r
188     if status == 'super':\r
189         user.is_superuser = new_state\r
190     elif status == 'staff':\r
191         user.is_staff = new_state\r
192     else:\r
193         raise Http404()\r
194 \r
195     user.save()\r
196     return decorators.RefreshPageCommand()\r
197 \r
198 \r
199 @decorate.withfn(decorators.command)\r
200 def award_points(request, id):\r
201     if not request.POST:\r
202         return render_to_response('users/karma_bonus.html')\r
203 \r
204     if not request.user.is_superuser:\r
205         raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))\r
206 \r
207     try:\r
208         points = int(request.POST['points'])\r
209     except:\r
210         raise decorators.CommandException(_("Invalid number of points to award."))\r
211 \r
212     user = get_object_or_404(User, id=id)\r
213 \r
214     extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)\r
215 \r
216     BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))\r
217 \r
218     return {'commands': {\r
219             'update_profile_karma': [user.reputation]\r
220         }}\r
221     \r
222 \r
223 @decorate.withfn(decorators.command)\r
224 def suspend(request, id):\r
225     user = get_object_or_404(User, id=id)\r
226 \r
227     if not request.user.is_superuser:\r
228         raise decorators.CommandException(_("Only superusers can suspend other users"))\r
229 \r
230     if not request.POST.get('bantype', None):\r
231         if user.is_suspended():\r
232             suspension = user.suspension\r
233             suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])\r
234             return decorators.RefreshPageCommand()\r
235         else:\r
236             return render_to_response('users/suspend_user.html')\r
237 \r
238     data = {\r
239         'bantype': request.POST.get('bantype', 'Indefinitely').strip(),\r
240         'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),\r
241         'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),\r
242         'suspended': user\r
243     }\r
244 \r
245     if data['bantype'] == 'forxdays':\r
246         try:\r
247             data['forxdays'] = int(request.POST['forxdays'])\r
248         except:\r
249             raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))\r
250 \r
251     SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)\r
252 \r
253     return decorators.RefreshPageCommand()\r
254 \r
255 @decorate.withfn(decorators.command)\r
256 def report_user(request, id):\r
257     user = get_object_or_404(User, id=id)\r
258 \r
259     if not request.POST.get('publicmsg', None):\r
260         return render_to_response('users/report_user.html')\r
261 \r
262     data = {\r
263         'publicmsg': request.POST.get('publicmsg', _('N/A')),\r
264         'reported': user\r
265     }\r
266 \r
267     ReportAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)\r
268 \r
269 \r
270     return decorators.RefreshPageCommand()\r
271 \r
272 \r
273 \r
274 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):\r
275     def decorator(fn):\r
276         def params(request, id=None, slug=None):\r
277             # Get the user object by id if the id parameter has been passed\r
278             if id is not None:\r
279                 user = get_object_or_404(User, id=id)\r
280             # ...or by slug if the slug has been given\r
281             elif slug is not None:\r
282                 try:\r
283                     user = User.objects.get(username__iexact=slug)\r
284                 except User.DoesNotExist:\r
285                     raise Http404\r
286 \r
287             if private and not (user == request.user or request.user.is_superuser):\r
288                 raise ReturnImediatelyException(HttpResponseUnauthorized(request))\r
289 \r
290             if render_to and (not render_to(user)):\r
291                 raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url()))\r
292 \r
293             return [request, user], { 'slug' : slug, }\r
294 \r
295         decorated = decorate.params.withfn(params)(fn)\r
296 \r
297         def result(context_or_response, request, user, **kwargs):\r
298             rev_page_title = smart_unicode(user.username) + " - " + tab_description\r
299 \r
300             # Check whether the return type of the decorated function is a context or Http Response\r
301             if isinstance(context_or_response, HttpResponse):\r
302                 response = context_or_response\r
303 \r
304                 # If it is a response -- show it\r
305                 return response\r
306             else:\r
307                 # ...if it is a context move forward, update it and render it to response\r
308                 context = context_or_response\r
309 \r
310             context.update({\r
311                 "tab": "users",\r
312                 "active_tab" : tab_name,\r
313                 "tab_description" : tab_description,\r
314                 "page_title" : rev_page_title,\r
315                 "can_view_private": (user == request.user) or request.user.is_superuser\r
316             })\r
317             return render_to_response(template, context, context_instance=RequestContext(request))\r
318 \r
319         decorated = decorate.result.withfn(result, needs_params=True)(decorated)\r
320 \r
321         if tabbed:\r
322             def url_getter(vu):\r
323                 try:\r
324                     return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))})\r
325                 except NoReverseMatch:\r
326                     try:\r
327                         return reverse(fn.__name__, kwargs={'id': vu.id})\r
328                     except NoReverseMatch:\r
329                         return reverse(fn.__name__, kwargs={'slug': slugify(smart_unicode(vu.username))})\r
330 \r
331             ui.register(ui.PROFILE_TABS, ui.ProfileTab(\r
332                 tab_name, tab_title, tab_description,url_getter, private, render_to, weight\r
333             ))\r
334 \r
335         return decorated\r
336     return decorator\r
337 \r
338 \r
339 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))\r
340 def user_profile(request, user, **kwargs):\r
341     questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
342     answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
343 \r
344     # Check whether the passed slug matches the one for the user object\r
345     slug = kwargs['slug']\r
346     if slug != slugify(smart_unicode(user.username)):\r
347         return HttpResponseRedirect(user.get_absolute_url())\r
348 \r
349     up_votes = user.vote_up_count\r
350     down_votes = user.vote_down_count\r
351     votes_today = user.get_vote_count_today()\r
352     votes_total = user.can_vote_count_today()\r
353 \r
354     user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \\r
355         .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')\r
356 \r
357     awards = [(Badge.objects.get(id=b['id']), b['count']) for b in\r
358               Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]\r
359 \r
360     return pagination.paginated(request, (\r
361     ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), default_pagesize=15)),\r
362     ('answers', UserAnswersPaginatorContext())), {\r
363     "view_user" : user,\r
364     "questions" : questions,\r
365     "answers" : answers,\r
366     "up_votes" : up_votes,\r
367     "down_votes" : down_votes,\r
368     "total_votes": up_votes + down_votes,\r
369     "votes_today_left": votes_total-votes_today,\r
370     "votes_total_per_day": votes_total,\r
371     "user_tags" : user_tags[:50],\r
372     "awards": awards,\r
373     "total_awards" : len(awards),\r
374     })\r
375     \r
376 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))\r
377 def user_recent(request, user, **kwargs):\r
378     activities = user.actions.exclude(\r
379             action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(\r
380             '-action_date')[:USERS_PAGE_SIZE]\r
381 \r
382     return {"view_user" : user, "activities" : activities}\r
383 \r
384 \r
385 @user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma'))\r
386 def user_reputation(request, user, **kwargs):\r
387     rep = list(user.reputes.order_by('date'))\r
388     values = [r.value for r in rep]\r
389     redux = lambda x, y: x+y\r
390 \r
391     graph_data = simplejson.dumps([\r
392     (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i+1], 0))\r
393     for i in range(len(values))\r
394     ])\r
395 \r
396     rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]\r
397 \r
398     return {"view_user": user, "reputation": rep, "graph_data": graph_data}\r
399 \r
400 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)\r
401 def user_votes(request, user, **kwargs):\r
402     votes = user.votes.exclude(node__state_string__contains="(deleted").filter(\r
403             node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]\r
404 \r
405     return {"view_user" : user, "votes" : votes}\r
406 \r
407 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))\r
408 def user_favorites(request, user, **kwargs):\r
409     favorites = FavoriteAction.objects.filter(canceled=False, user=user)\r
410 \r
411     return {"favorites" : favorites, "view_user" : user}\r
412 \r
413 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)\r
414 def user_subscriptions(request, user, **kwargs):\r
415     return _user_subscriptions(request, user, **kwargs)\r
416 \r
417 def _user_subscriptions(request, user, **kwargs):\r
418     enabled = True\r
419 \r
420     tab = request.GET.get('tab', "settings")\r
421 \r
422     # Manage tab\r
423     if tab == 'manage':\r
424         manage_open = True\r
425 \r
426         auto = request.GET.get('auto', 'True')\r
427         if auto == 'True':\r
428             show_auto = True\r
429             subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')\r
430         else:\r
431             show_auto = False\r
432             subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')\r
433 \r
434         return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {\r
435             'subscriptions':subscriptions,\r
436             'view_user':user,\r
437             "auto":show_auto,\r
438             'manage_open':manage_open,\r
439         })\r
440     # Settings Tab and everything else\r
441     else:\r
442         manage_open = False\r
443         if request.method == 'POST':\r
444             manage_open = False\r
445             form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)\r
446 \r
447             if form.is_valid():\r
448                 form.save()\r
449                 message = _('New subscription settings are now saved')\r
450 \r
451                 user.subscription_settings.enable_notifications = enabled\r
452                 user.subscription_settings.save()\r
453 \r
454                 request.user.message_set.create(message=message)\r
455         else:\r
456             form = SubscriptionSettingsForm(instance=user.subscription_settings)\r
457 \r
458         return {\r
459             'view_user':user,\r
460             'notificatons_on': enabled,\r
461             'form':form,\r
462             'manage_open':manage_open,\r
463         }\r
464 \r
465 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)\r
466 def user_preferences(request, user, **kwargs):\r
467     if request.POST:\r
468         form = UserPreferencesForm(request.POST)\r
469 \r
470         if form.is_valid():\r
471             user.prop.preferences = form.cleaned_data\r
472             request.user.message_set.create(message=_('New preferences saved'))\r
473 \r
474     else:\r
475         preferences = user.prop.preferences\r
476 \r
477         if preferences:\r
478             form = UserPreferencesForm(initial=preferences)\r
479         else:\r
480             form = UserPreferencesForm()\r
481             \r
482     return {'view_user': user, 'form': form}\r
483 \r
484 \r