]> git.openstreetmap.org Git - osqa.git/blob - forum/views/users.py
be able to have user links without ids in the url, wrapped in a setting of the URL...
[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\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)):\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)\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):\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', 'indefinetly').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 \r
256 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):\r
257     def decorator(fn):\r
258         def params(request, id=None, slug=None):\r
259             # Get the user object by id if the id parameter has been passed\r
260             if id is not None:\r
261                 user = get_object_or_404(User, id=id)\r
262             # ...or by slug if the slug has been given\r
263             elif slug is not None:\r
264                 user = get_object_or_404(User, username=slug)\r
265 \r
266             if private and not (user == request.user or request.user.is_superuser):\r
267                 raise ReturnImediatelyException(HttpResponseUnauthorized(request))\r
268 \r
269             if render_to and (not render_to(user)):\r
270                 raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url()))\r
271 \r
272             return [request, user], {}\r
273 \r
274         decorated = decorate.params.withfn(params)(fn)\r
275 \r
276         def result(context, request, user):\r
277             rev_page_title = smart_unicode(user.username) + " - " + tab_description\r
278 \r
279             context.update({\r
280                 "tab": "users",\r
281                 "active_tab" : tab_name,\r
282                 "tab_description" : tab_description,\r
283                 "page_title" : rev_page_title,\r
284                 "can_view_private": (user == request.user) or request.user.is_superuser\r
285             })\r
286             return render_to_response(template, context, context_instance=RequestContext(request))\r
287 \r
288         decorated = decorate.result.withfn(result, needs_params=True)(decorated)\r
289 \r
290         if tabbed:\r
291             def url_getter(vu):\r
292                 try:\r
293                     return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))})\r
294                 except NoReverseMatch:\r
295                     return reverse(fn.__name__, kwargs={'id': vu.id})\r
296 \r
297             ui.register(ui.PROFILE_TABS, ui.ProfileTab(\r
298                 tab_name, tab_title, tab_description,url_getter, private, render_to, weight\r
299             ))\r
300 \r
301         return decorated\r
302     return decorator\r
303 \r
304 \r
305 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))\r
306 def user_profile(request, user):\r
307     questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
308     answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
309 \r
310     up_votes = user.vote_up_count\r
311     down_votes = user.vote_down_count\r
312     votes_today = user.get_vote_count_today()\r
313     votes_total = user.can_vote_count_today()\r
314 \r
315     user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \\r
316         .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')\r
317 \r
318     awards = [(Badge.objects.get(id=b['id']), b['count']) for b in\r
319               Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]\r
320 \r
321     return pagination.paginated(request, (\r
322     ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), default_pagesize=15)),\r
323     ('answers', UserAnswersPaginatorContext())), {\r
324     "view_user" : user,\r
325     "questions" : questions,\r
326     "answers" : answers,\r
327     "up_votes" : up_votes,\r
328     "down_votes" : down_votes,\r
329     "total_votes": up_votes + down_votes,\r
330     "votes_today_left": votes_total-votes_today,\r
331     "votes_total_per_day": votes_total,\r
332     "user_tags" : user_tags[:50],\r
333     "awards": awards,\r
334     "total_awards" : len(awards),\r
335     })\r
336     \r
337 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))\r
338 def user_recent(request, user):\r
339     activities = user.actions.exclude(\r
340             action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(\r
341             '-action_date')[:USERS_PAGE_SIZE]\r
342 \r
343     return {"view_user" : user, "activities" : activities}\r
344 \r
345 \r
346 @user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma'))\r
347 def user_reputation(request, user):\r
348     rep = list(user.reputes.order_by('date'))\r
349     values = [r.value for r in rep]\r
350     redux = lambda x, y: x+y\r
351 \r
352     graph_data = simplejson.dumps([\r
353     (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))\r
354     for i in range(len(values))\r
355     ])\r
356 \r
357     rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]\r
358 \r
359     return {"view_user": user, "reputation": rep, "graph_data": graph_data}\r
360 \r
361 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)\r
362 def user_votes(request, user):\r
363     votes = user.votes.exclude(node__state_string__contains="(deleted").filter(\r
364             node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]\r
365 \r
366     return {"view_user" : user, "votes" : votes}\r
367 \r
368 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))\r
369 def user_favorites(request, user):\r
370     favorites = FavoriteAction.objects.filter(canceled=False, user=user)\r
371 \r
372     return {"favorites" : favorites, "view_user" : user}\r
373 \r
374 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)\r
375 def user_subscriptions(request, user):\r
376     enabled = True\r
377 \r
378     tab = request.GET.get('tab', "settings")\r
379 \r
380     if tab == 'settings':\r
381         manage_open = False\r
382         if request.method == 'POST':\r
383             manage_open = False\r
384             form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)\r
385 \r
386             if form.is_valid():\r
387                 form.save()\r
388                 message = _('New subscription settings are now saved')\r
389 \r
390                 user.subscription_settings.enable_notifications = enabled\r
391                 user.subscription_settings.save()\r
392 \r
393                 request.user.message_set.create(message=message)\r
394         else:\r
395             form = SubscriptionSettingsForm(instance=user.subscription_settings)\r
396 \r
397         return {\r
398             'view_user':user,\r
399             'notificatons_on': enabled,\r
400             'form':form,\r
401             'manage_open':manage_open,\r
402         }\r
403 \r
404     elif tab == 'manage':\r
405         manage_open = True\r
406 \r
407         auto = request.GET.get('auto', 'True')\r
408         if auto == 'True':\r
409             show_auto = True\r
410             subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')\r
411         else:\r
412             show_auto = False\r
413             subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')\r
414 \r
415         return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {\r
416             'subscriptions':subscriptions,\r
417             'view_user':user,\r
418             "auto":show_auto,\r
419             'manage_open':manage_open,\r
420         })\r
421 \r
422     # else:\r
423         # todo: probably want to throw an error\r
424         # error = "error to throw"\r
425 \r
426 \r
427 \r
428 \r
429 \r
430 \r
431 \r
432 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)\r
433 def user_preferences(request, user):\r
434     if request.POST:\r
435         form = UserPreferencesForm(request.POST)\r
436 \r
437         if form.is_valid():\r
438             user.prop.preferences = form.cleaned_data\r
439             request.user.message_set.create(message=_('New preferences saved'))\r
440 \r
441     else:\r
442         preferences = user.prop.preferences\r
443 \r
444         if preferences:\r
445             form = UserPreferencesForm(initial=preferences)\r
446         else:\r
447             form = UserPreferencesForm()\r
448             \r
449     return {'view_user': user, 'form': form}\r
450 \r
451 \r