]> git.openstreetmap.org Git - osqa.git/blob - forum/views/users.py
some cleanup on the listing of subscriptions and changing how we fetch the subscriptions
[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 = QuestionSubscription.objects.filter(user=user).order_by('-last_view')        \r
333         #subscriptions = user.subscriptions.all().order_by('-questionsubscription__last_view')\r
334     else:\r
335         show_auto = False\r
336         subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')\r
337         #subscriptions = user.subscriptions.filter(questionsubscription__auto_subscription=False).order_by('-questionsubscription__last_view')\r
338 \r
339     if request.method == 'POST':\r
340         manage_open = False\r
341         form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)\r
342 \r
343         if form.is_valid():\r
344             form.save()\r
345             message = _('New subscription settings are now saved')\r
346 \r
347             if 'notswitch' in request.POST:\r
348                 enabled = not enabled\r
349 \r
350                 if enabled:\r
351                     message = _('Notifications are now enabled')\r
352                 else:\r
353                     message = _('Notifications are now disabled')\r
354 \r
355             user.subscription_settings.enable_notifications = enabled\r
356             user.subscription_settings.save()\r
357 \r
358             request.user.message_set.create(message=message)\r
359     else:\r
360         form = SubscriptionSettingsForm(instance=user.subscription_settings)\r
361 \r
362     return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {\r
363         'subscriptions':subscriptions,\r
364         'view_user':user,\r
365         'notificatons_on': enabled,\r
366         'form':form,\r
367         "auto":show_auto,\r
368         "manage_open":manage_open\r
369     })\r
370 \r
371 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)\r
372 def user_preferences(request, user):\r
373     if request.POST:\r
374         form = UserPreferencesForm(request.POST)\r
375 \r
376         if form.is_valid():\r
377             user.prop.preferences = form.cleaned_data\r
378             request.user.message_set.create(message=_('New preferences saved'))\r
379 \r
380     else:\r
381         preferences = user.prop.preferences\r
382 \r
383         if preferences:\r
384             form = UserPreferencesForm(initial=preferences)\r
385         else:\r
386             form = UserPreferencesForm()\r
387             \r
388     return {'view_user': user, 'form': form}\r
389 \r
390 @login_required\r
391 def account_settings(request):\r
392     logging.debug('')\r
393     msg = request.GET.get('msg', '')\r
394     is_openid = False\r
395 \r
396     return render_to_response('account_settings.html', {\r
397     'msg': msg,\r
398     'is_openid': is_openid\r
399     }, context_instance=RequestContext(request))\r