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