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