1 from forum.models import User
 
   2 from django.db.models import Q, Count
 
   3 from django.core.paginator import Paginator, EmptyPage, InvalidPage
 
   4 from django.template.defaultfilters import slugify
 
   5 from django.contrib.contenttypes.models import ContentType
 
   6 from django.core.urlresolvers import reverse
 
   7 from django.shortcuts import render_to_response, get_object_or_404
 
   8 from django.template import RequestContext
 
   9 from django.http import HttpResponse, HttpResponseRedirect, Http404
 
  10 from forum.http_responses import HttpResponseUnauthorized
 
  11 from django.utils.translation import ugettext as _
 
  12 from django.utils.http import urlquote_plus
 
  13 from django.utils.html import strip_tags
 
  14 from django.utils.encoding import smart_unicode
 
  15 from django.core.urlresolvers import reverse, NoReverseMatch
 
  16 from forum.forms import *
 
  17 from forum.utils.html import sanitize_html
 
  18 from forum.modules import decorate, ReturnImediatelyException
 
  19 from datetime import datetime, date
 
  20 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction, ReportAction
 
  21 from forum.modules import ui
 
  22 from forum.utils import pagination
 
  23 from forum.views.readers import QuestionListPaginatorContext, AnswerPaginatorContext
 
  24 from forum.settings import ONLINE_USERS
 
  26 from django.contrib import messages
 
  33 class UserReputationSort(pagination.SimpleSort):
 
  34     def apply(self, objects):
 
  35         return objects.order_by('-is_active', self.order_by)
 
  37 class UserListPaginatorContext(pagination.PaginatorContext):
 
  38     def __init__(self, pagesizes=(20, 35, 60), default_pagesize=35):
 
  39         super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=(
 
  40             (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))),
 
  41             (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))),
 
  42             (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))),
 
  43             (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))),
 
  44         ), pagesizes=pagesizes, default_pagesize=default_pagesize)
 
  46 class SubscriptionListPaginatorContext(pagination.PaginatorContext):
 
  48         super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20)
 
  50 class UserAnswersPaginatorContext(pagination.PaginatorContext):
 
  52         super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=(
 
  53             (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))),
 
  54             (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))),
 
  55             (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))),
 
  56         ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers'))
 
  58 USERS_PAGE_SIZE = 35# refactor - move to some constants file
 
  60 @decorators.render('users/users.html', 'users', _('users'), weight=200)
 
  62     suser = request.REQUEST.get('q', "")
 
  63     users = User.objects.all()
 
  66         users = users.filter(username__icontains=suser)
 
  68     return pagination.paginated(request, ('users', UserListPaginatorContext()), {
 
  74 @decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False)
 
  75 def online_users(request):
 
  76     suser = request.REQUEST.get('q', "")
 
  79     if request.GET.get("sort", None):
 
  81             sort = int(request.GET["sort"])
 
  83             logging.error('Found invalid sort "%s", loading %s, refered by %s' % (
 
  84                 request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
 
  89     if request.GET.get("page", None):
 
  91             page = int(request.GET["page"])
 
  93             logging.error('Found invalid page "%s", loading %s, refered by %s' % (
 
  94                 request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
 
  99     if request.GET.get("pagesize", None):
 
 101             pagesize = int(request.GET["pagesize"])
 
 103             logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % (
 
 104                 request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN')
 
 110     if sort == "reputation":
 
 111         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation)
 
 112     elif sort == "newest" :
 
 113         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
 
 115         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last)
 
 117         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name)
 
 118     elif sort == "oldest":
 
 119         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest)
 
 120     elif sort == "newest":
 
 121         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest)
 
 122     elif sort == "votes":
 
 123         users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes)
 
 125         users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1])
 
 127     return render_to_response('users/online_users.html', {
 
 132         "pageSize" : pagesize,
 
 136 def edit_user(request, id, slug):
 
 137     user = get_object_or_404(User, id=id)
 
 138     if not (request.user.is_superuser or request.user == user):
 
 139         return HttpResponseUnauthorized(request)
 
 140     if request.method == "POST":
 
 141         form = EditUserForm(user, request.POST)
 
 143             new_email = sanitize_html(form.cleaned_data['email'])
 
 145             if new_email != user.email:
 
 146                 user.email = new_email
 
 147                 user.email_isvalid = False
 
 150                     hash = ValidationHash.objects.get(user=request.user, type='email')
 
 155             if settings.EDITABLE_SCREEN_NAME:
 
 156                 user.username = sanitize_html(form.cleaned_data['username'])
 
 157             user.real_name = sanitize_html(form.cleaned_data['realname'])
 
 158             user.website = sanitize_html(form.cleaned_data['website'])
 
 159             user.location = sanitize_html(form.cleaned_data['city'])
 
 160             user.date_of_birth = form.cleaned_data['birthday']
 
 161             if user.date_of_birth == "None":
 
 162                 user.date_of_birth = datetime(1900, 1, 1, 0, 0)
 
 163             user.about = sanitize_html(form.cleaned_data['about'])
 
 166             EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save()
 
 168             messages.info(request, _("Profile updated."))
 
 169             return HttpResponseRedirect(user.get_profile_url())
 
 171         form = EditUserForm(user)
 
 172     return render_to_response('users/edit.html', {
 
 175     'gravatar_faq_url' : reverse('faq') + '#gravatar',
 
 176     }, context_instance=RequestContext(request))
 
 179 @decorate.withfn(decorators.command)
 
 180 def user_powers(request, id, action, status):
 
 181     if not request.user.is_superuser:
 
 182         raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions."))
 
 184     if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner():
 
 185         raise decorators.CommandException(_("Only the site owner can remove the super user status from other user."))
 
 187     user = get_object_or_404(User, id=id)
 
 188     new_state = action == 'grant'
 
 190     if status == 'super':
 
 191         user.is_superuser = new_state
 
 192     elif status == 'staff':
 
 193         user.is_staff = new_state
 
 198     return decorators.RefreshPageCommand()
 
 201 @decorate.withfn(decorators.command)
 
 202 def award_points(request, id):
 
 204         return render_to_response('users/karma_bonus.html')
 
 206     if not request.user.is_superuser:
 
 207         raise decorators.CommandException(_("Only superusers are allowed to award reputation points"))
 
 210         points = int(request.POST['points'])
 
 212         raise decorators.CommandException(_("Invalid number of points to award."))
 
 214     awarding_user = get_object_or_404(User, id=request.user.pk)
 
 216     if points > awarding_user.reputation:
 
 217         raise decorators.CommandException(_("Invalid number of points to award."))
 
 219     user = get_object_or_404(User, id=id)
 
 221     extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
 
 223     BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user))
 
 225     return {'commands': {
 
 226             'update_profile_karma': [user.reputation]
 
 230 @decorate.withfn(decorators.command)
 
 231 def suspend(request, id):
 
 232     user = get_object_or_404(User, id=id)
 
 234     if not request.user.is_superuser:
 
 235         raise decorators.CommandException(_("Only superusers can suspend other users"))
 
 237     if not request.POST.get('bantype', None):
 
 238         if user.is_suspended():
 
 239             suspension = user.suspension
 
 240             suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR'])
 
 241             return decorators.RefreshPageCommand()
 
 243             return render_to_response('users/suspend_user.html')
 
 246         'bantype': request.POST.get('bantype', 'Indefinitely').strip(),
 
 247         'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')),
 
 248         'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''),
 
 252     if data['bantype'] == 'forxdays':
 
 254             data['forxdays'] = int(request.POST['forxdays'])
 
 256             raise decorators.CommandException(_('Invalid numeric argument for the number of days.'))
 
 258     SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
 
 260     return decorators.RefreshPageCommand()
 
 262 @decorate.withfn(decorators.command)
 
 263 def report_user(request, id):
 
 264     user = get_object_or_404(User, id=id)
 
 266     if not request.POST.get('publicmsg', None):
 
 267         return render_to_response('users/report_user.html')
 
 270         'publicmsg': request.POST.get('publicmsg', _('N/A')),
 
 274     ReportAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data)
 
 277     return decorators.RefreshPageCommand()
 
 281 def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500):
 
 283         def params(request, id=None, slug=None):
 
 284             # Get the user object by id if the id parameter has been passed
 
 286                 user = get_object_or_404(User, id=id)
 
 287             # ...or by slug if the slug has been given
 
 288             elif slug is not None:
 
 290                     user = User.objects.get(username__iexact=slug)
 
 291                 except User.DoesNotExist:
 
 294             if private and not (user == request.user or request.user.is_superuser):
 
 295                 raise ReturnImediatelyException(HttpResponseUnauthorized(request))
 
 297             if render_to and (not render_to(user)):
 
 298                 raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url()))
 
 300             return [request, user], { 'slug' : slug, }
 
 302         decorated = decorate.params.withfn(params)(fn)
 
 304         def result(context_or_response, request, user, **kwargs):
 
 305             rev_page_title = smart_unicode(user.username) + " - " + tab_description
 
 307             # Check whether the return type of the decorated function is a context or Http Response
 
 308             if isinstance(context_or_response, HttpResponse):
 
 309                 response = context_or_response
 
 311                 # If it is a response -- show it
 
 314                 # ...if it is a context move forward, update it and render it to response
 
 315                 context = context_or_response
 
 319                 "active_tab" : tab_name,
 
 320                 "tab_description" : tab_description,
 
 321                 "page_title" : rev_page_title,
 
 322                 "can_view_private": (user == request.user) or request.user.is_superuser
 
 324             return render_to_response(template, context, context_instance=RequestContext(request))
 
 326         decorated = decorate.result.withfn(result, needs_params=True)(decorated)
 
 331                     return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))})
 
 332                 except NoReverseMatch:
 
 334                         return reverse(fn.__name__, kwargs={'id': vu.id})
 
 335                     except NoReverseMatch:
 
 336                         return reverse(fn.__name__, kwargs={'slug': slugify(smart_unicode(vu.username))})
 
 338             ui.register(ui.PROFILE_TABS, ui.ProfileTab(
 
 339                 tab_name, tab_title, tab_description,url_getter, private, render_to, weight
 
 346 @user_view('users/stats.html', 'stats', _('overview'), _('user overview'))
 
 347 def user_profile(request, user, **kwargs):
 
 348     questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
 
 349     answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')
 
 351     # Check whether the passed slug matches the one for the user object
 
 352     slug = kwargs['slug']
 
 353     if slug != slugify(smart_unicode(user.username)):
 
 354         return HttpResponseRedirect(user.get_absolute_url())
 
 356     up_votes = user.vote_up_count
 
 357     down_votes = user.vote_down_count
 
 358     votes_today = user.get_vote_count_today()
 
 359     votes_total = user.can_vote_count_today()
 
 361     user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \
 
 362         .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count')
 
 364     awards = [(Badge.objects.get(id=b['id']), b['count']) for b in
 
 365               Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')]
 
 367     return pagination.paginated(request, (
 
 368     ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), default_pagesize=15)),
 
 369     ('answers', UserAnswersPaginatorContext())), {
 
 371     "questions" : questions,
 
 373     "up_votes" : up_votes,
 
 374     "down_votes" : down_votes,
 
 375     "total_votes": up_votes + down_votes,
 
 376     "votes_today_left": votes_total-votes_today,
 
 377     "votes_total_per_day": votes_total,
 
 378     "user_tags" : user_tags[:50],
 
 380     "total_awards" : len(awards),
 
 383 @user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))
 
 384 def user_recent(request, user, **kwargs):
 
 385     activities = user.actions.exclude(
 
 386             action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(
 
 387             '-action_date')[:USERS_PAGE_SIZE]
 
 389     return {"view_user" : user, "activities" : activities}
 
 392 @user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma'))
 
 393 def user_reputation(request, user, **kwargs):
 
 394     rep = list(user.reputes.order_by('date'))
 
 395     values = [r.value for r in rep]
 
 396     redux = lambda x, y: x+y
 
 398     graph_data = json.dumps([
 
 399     (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i+1], 0))
 
 400     for i in range(len(values))
 
 403     rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20]
 
 405     return {"view_user": user, "reputation": rep, "graph_data": graph_data}
 
 407 @user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)
 
 408 def user_votes(request, user, **kwargs):
 
 409     votes = user.votes.exclude(node__state_string__contains="(deleted").filter(
 
 410             node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]
 
 412     return {"view_user" : user, "votes" : votes}
 
 414 @user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))
 
 415 def user_favorites(request, user, **kwargs):
 
 416     favorites = FavoriteAction.objects.filter(canceled=False, user=user)
 
 418     return {"favorites" : favorites, "view_user" : user}
 
 420 @user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False)
 
 421 def user_subscriptions(request, user, **kwargs):
 
 422     return _user_subscriptions(request, user, **kwargs)
 
 424 def _user_subscriptions(request, user, **kwargs):
 
 427     tab = request.GET.get('tab', "settings")
 
 433         auto = request.GET.get('auto', 'True')
 
 436             subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view')
 
 439             subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view')
 
 441         return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), {
 
 442             'subscriptions':subscriptions,
 
 445             'manage_open':manage_open,
 
 447     # Settings Tab and everything else
 
 450         if request.method == 'POST':
 
 452             form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings)
 
 456                 message = _('New subscription settings are now saved')
 
 458                 user.subscription_settings.enable_notifications = enabled
 
 459                 user.subscription_settings.save()
 
 461                 messages.info(request, message)
 
 463             form = SubscriptionSettingsForm(instance=user.subscription_settings)
 
 467             'notificatons_on': enabled,
 
 469             'manage_open':manage_open,
 
 472 @user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False)
 
 473 def user_preferences(request, user, **kwargs):
 
 475         form = UserPreferencesForm(request.POST)
 
 478             user.prop.preferences = form.cleaned_data
 
 479             messages.info(request, _('New preferences saved'))
 
 482         preferences = user.prop.preferences
 
 485             form = UserPreferencesForm(initial=preferences)
 
 487             form = UserPreferencesForm()
 
 489     return {'view_user': user, 'form': form}