]> git.openstreetmap.org Git - osqa.git/blob - forum/views/users.py
OSQA-266
[osqa.git] / forum / views / users.py
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
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 \r
26 import time\r
27 import datetime\r
28 import decorators\r
29 \r
30 class UserReputationSort(pagination.SimpleSort):\r
31     def apply(self, objects):\r
32         return objects.order_by('-is_active', self.order_by)\r
33 \r
34 class UserListPaginatorContext(pagination.PaginatorContext):\r
35     def __init__(self):\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
42 \r
43 class SubscriptionListPaginatorContext(pagination.PaginatorContext):\r
44     def __init__(self):\r
45         super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)\r
46 \r
47 class UserAnswersPaginatorContext(pagination.PaginatorContext):\r
48     def __init__(self):\r
49         super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(\r
50             (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),\r
51             (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),\r
52             (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),\r
53         ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))\r
54 \r
55 USERS_PAGE_SIZE = 35# refactor - move to some constants file\r
56 \r
57 @decorators.render('users/users.html', 'users', _('users'), weight=200)\r
58 def users(request):\r
59     suser = request.REQUEST.get('q', "")\r
60     users = User.objects.all()\r
61 \r
62     if suser != "":\r
63         users = users.filter(username__icontains=suser)\r
64 \r
65     return pagination.paginated(request, ('users', UserListPaginatorContext()), {\r
66         "users" : users,\r
67         "suser" : suser,\r
68     })\r
69 \r
70 \r
71 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200)\r
72 def online_users(request):\r
73     suser = request.REQUEST.get('q', "")\r
74 \r
75     one_hour_ago = datetime.datetime.now() - datetime.timedelta(hours=1)\r
76     sql_datetime = datetime.datetime.strftime(one_hour_ago, '%Y-%m-%d %H:%M:%S')\r
77     users = User.objects.order_by('-last_seen')\r
78 \r
79     return pagination.paginated(request, ('users', UserListPaginatorContext()), {\r
80         "users" : users,\r
81         "suser" : suser,\r
82     })\r
83 \r
84 \r
85 @login_required\r
86 def edit_user(request, id):\r
87     user = get_object_or_404(User, id=id)\r
88     if not (request.user.is_superuser or request.user == user):\r
89         return HttpResponseUnauthorized(request)\r
90     if request.method == "POST":\r
91         form = EditUserForm(user, request.POST)\r
92         if form.is_valid():\r
93             new_email = sanitize_html(form.cleaned_data['email'])\r
94 \r
95             if new_email != user.email:\r
96                 user.email = new_email\r
97                 user.email_isvalid = False\r
98 \r
99                 try:\r
100                     hash = ValidationHash.objects.get(user=request.user, type='email')\r
101                     hash.delete()\r
102                 except:\r
103                     pass\r
104 \r
105             if settings.EDITABLE_SCREEN_NAME:\r
106                 user.username = sanitize_html(form.cleaned_data['username'])\r
107             user.real_name = sanitize_html(form.cleaned_data['realname'])\r
108             user.website = sanitize_html(form.cleaned_data['website'])\r
109             user.location = sanitize_html(form.cleaned_data['city'])\r
110             user.date_of_birth = form.cleaned_data['birthday']\r
111             if user.date_of_birth == "None":\r
112                 user.date_of_birth = datetime(1900, 1, 1, 0, 0)\r
113             user.about = sanitize_html(form.cleaned_data['about'])\r
114 \r
115             user.save()\r
116             EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()\r
117 \r
118             request.user.message_set.create(message=_("Profile updated."))\r
119             return HttpResponseRedirect(user.get_profile_url())\r
120     else:\r
121         form = EditUserForm(user)\r
122     return render_to_response('users/edit.html', {\r
123     'user': user,\r
124     'form' : form,\r
125     'gravatar_faq_url' : reverse('faq') + '#gravatar',\r
126     }, context_instance=RequestContext(request))\r
127 \r
128 \r
129 @decorate.withfn(decorators.command)\r
130 def user_powers(request, id, action, status):\r
131     if not request.user.is_superuser:\r
132         raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))\r
133 \r
134     if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():\r
135         raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))\r
136 \r
137     user = get_object_or_404(User, id=id)\r
138     new_state = action == 'grant'\r
139 \r
140     if status == 'super':\r
141         user.is_superuser = new_state\r
142     elif status == 'staff':\r
143         user.is_staff = new_state\r
144     else:\r
145         raise Http404()\r
146 \r
147     user.save()\r
148     return decorators.RefreshPageCommand()\r
149 \r
150 \r
151 @decorate.withfn(decorators.command)\r
152 def award_points(request, id):\r
153     if not request.POST:\r
154         return render_to_response('users/karma_bonus.html')\r
155 \r
156     if not request.user.is_superuser:\r
157         raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))\r
158 \r
159     try:\r
160         points = int(request.POST['points'])\r
161     except:\r
162         raise decorators.CommandException(_("Invalid number of points to award."))\r
163 \r
164     user = get_object_or_404(User, id=id)\r
165 \r
166     extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)\r
167 \r
168     BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))\r
169 \r
170     return {'commands': {\r
171             'update_profile_karma': [user.reputation]\r
172         }}\r
173     \r
174 \r
175 @decorate.withfn(decorators.command)\r
176 def suspend(request, id):\r
177     user = get_object_or_404(User, id=id)\r
178 \r
179     if not request.user.is_superuser:\r
180         raise decorators.CommandException(_("Only superusers can suspend other users"))\r
181 \r
182     if not request.POST.get('bantype', None):\r
183         if user.is_suspended():\r
184             suspension = user.suspension\r
185             suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])\r
186             return decorators.RefreshPageCommand()\r
187         else:\r
188             return render_to_response('users/suspend_user.html')\r
189 \r
190     data = {\r
191     'bantype': request.POST.get('bantype', 'indefinetly').strip(),\r
192     'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),\r
193     'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),\r
194     'suspended': user\r
195     }\r
196 \r
197     if data['bantype'] == 'forxdays':\r
198         try:\r
199             data['forxdays'] = int(request.POST['forxdays'])\r
200         except:\r
201             raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))\r
202 \r
203     SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)\r
204 \r
205     return decorators.RefreshPageCommand()\r
206 \r
207 \r
208 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):\r
209     def decorator(fn):\r
210         def params(request, id, slug=None):\r
211             user = get_object_or_404(User, id=id)\r
212             if private and not (user == request.user or request.user.is_superuser):\r
213                 return HttpResponseUnauthorized(request)\r
214 \r
215             if render_to and (not render_to(user)):\r
216                 return HttpResponseRedirect(user.get_profile_url())\r
217 \r
218             return [request, user], {}\r
219 \r
220         decorated = decorate.params.withfn(params)(fn)\r
221 \r
222         def result(context, request, user):\r
223             rev_page_title = user.username + " - " + tab_description\r
224 \r
225             context.update({\r
226                 "tab": "users",\r
227                 "active_tab" : tab_name,\r
228                 "tab_description" : tab_description,\r
229                 "page_title" : rev_page_title,\r
230                 "can_view_private": (user == request.user) or request.user.is_superuser\r
231             })\r
232             return render_to_response(template, context, context_instance=RequestContext(request))\r
233 \r
234         decorated = decorate.result.withfn(result, needs_params=True)(decorated)\r
235 \r
236         if tabbed:\r
237             def url_getter(vu):\r
238                 try:\r
239                     return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})\r
240                 except NoReverseMatch:\r
241                     return reverse(fn.__name__, kwargs={'id': vu.id})\r
242 \r
243             ui.register(ui.PROFILE_TABS, ui.ProfileTab(\r
244                 tab_name, tab_title, tab_description,url_getter, private, render_to, weight\r
245             ))\r
246 \r
247         return decorated\r
248     return decorator\r
249 \r
250 \r
251 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))\r
252 def user_profile(request, user):\r
253     questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
254     answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
255 \r
256     up_votes = user.vote_up_count\r
257     down_votes = user.vote_down_count\r
258     votes_today = user.get_vote_count_today()\r
259     votes_total = int(settings.MAX_VOTES_PER_DAY)\r
260 \r
261     user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \\r
262         .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')\r
263 \r
264     awards = [(Badge.objects.get(id=b['id']), b['count']) for b in\r
265               Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]\r
266 \r
267     return pagination.paginated(request, (\r
268     ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), 15)),\r
269     ('answers', UserAnswersPaginatorContext())), {\r
270     "view_user" : user,\r
271     "questions" : questions,\r
272     "answers" : answers,\r
273     "up_votes" : up_votes,\r
274     "down_votes" : down_votes,\r
275     "total_votes": up_votes + down_votes,\r
276     "votes_today_left": votes_total-votes_today,\r
277     "votes_total_per_day": votes_total,\r
278     "user_tags" : user_tags[:50],\r
279     "awards": awards,\r
280     "total_awards" : len(awards),\r
281     })\r
282     \r
283 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))\r
284 def user_recent(request, user):\r
285     activities = user.actions.exclude(\r
286             action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(\r
287             '-action_date')[:USERS_PAGE_SIZE]\r
288 \r
289     return {"view_user" : user, "activities" : activities}\r
290 \r
291 \r
292 @user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))\r
293 def user_reputation(request, user):\r
294     rep = list(user.reputes.order_by('date'))\r
295     values = [r.value for r in rep]\r
296     redux = lambda x, y: x+y\r
297 \r
298     graph_data = simplejson.dumps([\r
299     (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i], 0))\r
300     for i in range(len(values))\r
301     ])\r
302 \r
303     rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]\r
304 \r
305     return {"view_user": user, "reputation": rep, "graph_data": graph_data}\r
306 \r
307 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)\r
308 def user_votes(request, user):\r
309     votes = user.votes.exclude(node__state_string__contains="(deleted").filter(\r
310             node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]\r
311 \r
312     return {"view_user" : user, "votes" : votes}\r
313 \r
314 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))\r
315 def user_favorites(request, user):\r
316     favorites = FavoriteAction.objects.filter(canceled=False, user=user)\r
317 \r
318     return {"favorites" : favorites, "view_user" : user}\r
319 \r
320 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)\r
321 def user_subscriptions(request, user):\r
322     enabled = user.subscription_settings.enable_notifications\r
323     auto = request.GET.get('auto', 'True')\r
324     show_auto = True\r
325     manage_open = False\r
326     \r
327     if len(request.GET) > 0:\r
328         manage_open = True\r
329         \r
330     if auto == 'True':\r
331         show_auto = True\r
332         subscriptions = user.subscriptions.all().order_by('-questionsubscription__last_view')\r
333     else:\r
334         show_auto = False\r
335         subscriptions = user.subscriptions.filter(questionsubscription__auto_subscription=False).order_by('-questionsubscription__last_view')\r
336 \r
337     if request.method == 'POST':\r
338         manage_open = False\r
339         form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)\r
340 \r
341         if form.is_valid():\r
342             form.save()\r
343             message = _('New subscription settings are now saved')\r
344 \r
345             if 'notswitch' in request.POST:\r
346                 enabled = not enabled\r
347 \r
348                 if enabled:\r
349                     message = _('Notifications are now enabled')\r
350                 else:\r
351                     message = _('Notifications are now disabled')\r
352 \r
353             user.subscription_settings.enable_notifications = enabled\r
354             user.subscription_settings.save()\r
355 \r
356             request.user.message_set.create(message=message)\r
357     else:\r
358         form = SubscriptionSettingsForm(instance=user.subscription_settings)\r
359 \r
360     return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {\r
361         'subscriptions':subscriptions,\r
362         'view_user':user,\r
363         'notificatons_on': enabled,\r
364         'form':form,\r
365         "auto":show_auto,\r
366         "manage_open":manage_open\r
367     })\r
368 \r
369 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)\r
370 def user_preferences(request, user):\r
371     if request.POST:\r
372         form = UserPreferencesForm(request.POST)\r
373 \r
374         if form.is_valid():\r
375             user.prop.preferences = form.cleaned_data\r
376             request.user.message_set.create(message=_('New preferences saved'))\r
377 \r
378     else:\r
379         preferences = user.prop.preferences\r
380 \r
381         if preferences:\r
382             form = UserPreferencesForm(initial=preferences)\r
383         else:\r
384             form = UserPreferencesForm()\r
385             \r
386     return {'view_user': user, 'form': form}\r
387 \r
388 @login_required\r
389 def account_settings(request):\r
390     logging.debug('')\r
391     msg = request.GET.get('msg', '')\r
392     is_openid = False\r
393 \r
394     return render_to_response('account_settings.html', {\r
395     'msg': msg,\r
396     'is_openid': is_openid\r
397     }, context_instance=RequestContext(request))\r