From ca88510e4dd14206f623cababfda1e8a9daf70d4 Mon Sep 17 00:00:00 2001 From: hernani Date: Fri, 14 May 2010 23:58:15 +0000 Subject: [PATCH] Closing OSQA 35. Give admins and moderators an easy way to award karma points. New user management interface. git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@284 0cfe37f9-358a-4d5e-be75-b63607b5c754 --- forum/actions/user.py | 19 +++ forum/forms.py | 4 + forum/skins/default/media/js/osqa.user.js | 90 ++++++++++++++ forum/skins/default/media/style/style.css | 14 +++ forum/skins/default/media/style/user.css | 110 ++++++++++++++++++ .../default/templates/auth/auth_settings.html | 8 +- forum/skins/default/templates/user.html | 10 +- forum/skins/default/templates/users/info.html | 22 +--- forum/skins/default/templates/users/menu.html | 96 +++++++++++++++ .../default/templates/users/moderation.html | 18 --- forum/skins/default/templates/users/tabs.html | 5 - forum/templatetags/user_tags.py | 7 +- forum/urls.py | 3 +- forum/views/auth.py | 3 +- forum/views/decorators.py | 4 +- forum/views/users.py | 20 +++- 16 files changed, 378 insertions(+), 55 deletions(-) create mode 100644 forum/skins/default/media/js/osqa.user.js create mode 100644 forum/skins/default/media/style/user.css create mode 100644 forum/skins/default/templates/users/menu.html delete mode 100644 forum/skins/default/templates/users/moderation.html diff --git a/forum/actions/user.py b/forum/actions/user.py index 1ceae09..aef541a 100644 --- a/forum/actions/user.py +++ b/forum/actions/user.py @@ -23,6 +23,25 @@ class EditProfileAction(ActionProxy): 'profile_link': self.hyperlink(self.user.get_profile_url(), _('profile')), } +class BonusRepAction(ActionProxy): + def process_data(self, value): + self._value = value + + def repute_users(self): + self.repute(self.user, self._value) + self.user.message_set.create(message=_("Congratulations, you have been awarded an extra %s reputation points.") % self._value + + '
%s' % self.extra.get('message', _('Thank you'))) + + def describe(self, viewer=None): + value = self.extra.get('value', _('unknown')) + message = self.extra.get('message', '') + + return _("%(user)s %(was_were)s awarded %(value)s reputation points: %(message)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'was_were': self.viewer_or_user_verb(viewer, self.user, _('were'), _('was')), + 'value': value, 'message': message + } + class AwardAction(ActionProxy): def process_data(self, badge, trigger): self.__dict__['_badge'] = badge diff --git a/forum/forms.py b/forum/forms.py index 509c125..b54d65b 100644 --- a/forum/forms.py +++ b/forum/forms.py @@ -251,3 +251,7 @@ class SubscriptionSettingsForm(forms.Form): notify_comments = forms.BooleanField(required=False, initial=False) notify_accepted = forms.BooleanField(required=False, initial=False) + +class AwardPointsForm(forms.Form): + points = forms.IntegerField(min_value=1, initial=50, label=_('Points to award')) + message = forms.CharField(widget=forms.Textarea(), label=_('Message'), required=False) diff --git a/forum/skins/default/media/js/osqa.user.js b/forum/skins/default/media/js/osqa.user.js new file mode 100644 index 0000000..4a168a2 --- /dev/null +++ b/forum/skins/default/media/js/osqa.user.js @@ -0,0 +1,90 @@ + +function show_dialog (html, extra_class, pos, dim, yes, no_text) { + $dialog = $('
' + + '
' + html + '
' + + '' + + '' + + '
'); + + $('body').append($dialog); + + $dialog.css({ + top: pos.y, + left: pos.x + }); + + $dialog.animate({ + top: "-=" + (dim.h / 2), + left: "-=" + (dim.w / 2), + width: dim.w, + height: dim.h + }, 200, function() { + $dialog.find('.dialog-no').click(function() { + $dialog.fadeOut('fast'); + }); + $dialog.find('.dialog-yes').click(function() { + yes.callback($dialog); + }); + }); + +} + + +$().ready(function() { + var $dropdown = $('#user-menu-dropdown'); + + $('#user-menu').click(function(){ + $('.dialog').fadeOut('fast'); + $dropdown.slideToggle('fast'); + }); + + $('.confirm').each(function() { + var $link = $(this); + + $link.click(function(e) { + $dropdown.slideUp('fast'); + var html = messages.confirm; + + show_dialog(html, 'confirm', {x: e.pageX, y: e.pageY}, {w: 200, h: 100}, { + text: messages.yes, + callback: function() { + window.location = $link.attr('href'); + } + }, messages.no); + + return false; + }); + }); + + $('#award-rep-points').click(function(e) { + $dropdown.slideUp('fast'); + + var html = '' + + '
' + messages.points + '
' + messages.message + '
'; + + show_dialog(html, 'award-rep-points', {x: e.pageX, y: e.pageY}, {w: 300, h: 125}, { + text: messages.award, + callback: function($dialog) { + var $points_input = $('#points-to-award'); + var _points = parseInt($points_input.val()); + + if(!isNaN(_points)) { + $dialog.fadeOut('fast'); + var _message = $('#award-message').val(); + $.post($('#award-rep-points').attr('href'), {points: _points, message: _message}, function(data) { + if (data.success) { + $('#user-reputation').css('background', 'yellow'); + $('#user-reputation').html(data.reputation); + + $('#user-reputation').animate({ backgroundColor: "transparent" }, 1000); + + } + }, 'json') + } + } + }, messages.cancel); + + + return false; + }); +}); \ No newline at end of file diff --git a/forum/skins/default/media/style/style.css b/forum/skins/default/media/style/style.css index b2653d3..360e477 100644 --- a/forum/skins/default/media/style/style.css +++ b/forum/skins/default/media/style/style.css @@ -1375,4 +1375,18 @@ div.comment-tools a:hover { .moderation #action_status { font-weight: bold; text-align: center; +} + +.moderation-table input[type=text], .moderation-table textarea { + width: 150px; + max-height: 50px; +} + +.moderation-table th { + vertical-align: top; + text-align: left; +} + +.moderation-table-footer { + text-align: right; } \ No newline at end of file diff --git a/forum/skins/default/media/style/user.css b/forum/skins/default/media/style/user.css new file mode 100644 index 0000000..5804226 --- /dev/null +++ b/forum/skins/default/media/style/user.css @@ -0,0 +1,110 @@ +#user-menu-container { + position: relative; + text-align: right; +} + +#user-menu { + cursor: pointer; + height: 1em; + font-size: 120%; + font-weight: bold; + color: #3060A8; +} + +#user-menu-dropdown, div.dialog { + position: absolute; + background-color: #B6C4E2; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -moz-box-shadow: 2px 2px 5px #3060A8; + -webkit-box-shadow: 2px 2px 5px #3060A8; +} + +div.dialog .dialog-buttons { + margin: 0px; + height: 25px; + text-align: center; + position: absolute; + bottom: 0px; + left: 0px; + width: 100%; +} + +.dialog-yes, .dialog-no { + margin: 0 3px 5px 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + background-color: #3060A8; + color: white; + height: 20px; + line-height: 20px; + font-weight: bold; + border: 0; +} + +div.dialog.confirm { + text-align: center; + line-height: 75px; + font-size: 140%; + font-weight: bold; +} + +div.dialog.award-rep-points table { + margin: auto; + margin-top: 8px; +} + +div.dialog.award-rep-points table th { + text-align: left; +} + +div.dialog.award-rep-points table input, div.dialog.award-rep-points table textarea { + width: 150px; + max-height: 35px; +} + +#user-menu-dropdown { + display: none; + right: 0px; + top: 1.5em; + text-align: left; + list-style-type: none; +} + +#user-menu-dropdown li.item { + padding: 4px 8px 4px 8px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +#user-menu-dropdown li.item a { + color: inherit; + white-space: nowrap; + text-decoration: none; +} + +#user-menu-dropdown li.separator { + text-align: center; + padding: 10px 0 4px 0; + font-size: 120%; + font-weight: bold; +} + +#user-menu-dropdown li.item:hover { + background-color: #3060A8; + color: white; +} + +#user-menu-dropdown span { + margin-right: 4px; + float: left; + width: 16px; + height: 16px; +} + +.user-auth { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 0; } +.user-award_rep { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -17px; } +.user-edit { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -34px; } +.user-moderator { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -51px; } +.user-subscriptions { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -68px; } +.user-superuser { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -85px; } diff --git a/forum/skins/default/templates/auth/auth_settings.html b/forum/skins/default/templates/auth/auth_settings.html index e19a10c..3e32007 100644 --- a/forum/skins/default/templates/auth/auth_settings.html +++ b/forum/skins/default/templates/auth/auth_settings.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "user.html" %} {% load i18n %} {% block head %}{% endblock %} {% block title %}{% spaceless %}{% trans "Authentication settings" %}{% endspaceless %}{% endblock %} -{% block content %} -
{% trans "Authentication settings" %}
+{% block usercontent %} +

{% trans "Authentication settings" %}

{% if auth_keys %}

{% blocktrans %}These are the external authentication providers currently associated with your account.{% endblocktrans %}

@@ -16,7 +16,7 @@ {% if not auth_keys %}

{% blocktrans %}You currently have no external authentication provider associated with your account.{% endblocktrans %}

{% endif %} -{% ifequal user request.user %} +{% ifequal view_user request.user %} {% endifequal %} {% if has_password %} diff --git a/forum/skins/default/templates/user.html b/forum/skins/default/templates/user.html index 5ff0d80..263f91f 100644 --- a/forum/skins/default/templates/user.html +++ b/forum/skins/default/templates/user.html @@ -3,6 +3,8 @@ {% load extra_tags %} {% load extra_filters %} {% load humanize %} +{% load smart_if %} + {% block title %}{% spaceless %}{{ page_title }}{% endspaceless %}{% endblock %} {% block forestyle%} {% endblock %} {% block forejs %} - {% if request.user.is_superuser %} + {% if request.user.is_superuser or request.user == view_user %} + + + + + + {% endif %} + +
+ {% trans "User tools" %} ▼ + +
+ +{% comment %} +

{% trans "Moderation tools" %}

+

{% trans "Reputation bonus" %}

+
+ + {{ awardform.as_table }} + + +
+ +{% if not user.is_superuser %} +

{% trans "Grant super user status" %}

+ {% if not user.is_staff %} +

{% trans "Grant moderator status" %}

+ {% else %} +

{% trans "Remove moderator status" %}

+ {% endif %} +{% else %} + {% ifequal moderator.id 1 %} + {% ifnotequal user.id 1 %} +

{% trans "Remove super user status" %}

+ {% endifnotequal %} + {% endifequal %} +{% endif %} + +{% endcomment %} diff --git a/forum/skins/default/templates/users/moderation.html b/forum/skins/default/templates/users/moderation.html deleted file mode 100644 index 8def633..0000000 --- a/forum/skins/default/templates/users/moderation.html +++ /dev/null @@ -1,18 +0,0 @@ -{% load i18n %} - -

{% trans "Moderation tools" %}

-

{% trans "Reputation bonus" %}

-{% if not user.is_superuser %} -

{% trans "Grant super user status" %}

- {% if not user.is_staff %} -

{% trans "Grant moderator status" %}

- {% else %} -

{% trans "Remove moderator status" %}

- {% endif %} -{% else %} - {% ifequal moderator.id 1 %} - {% ifnotequal user.id 1 %} -

{% trans "Remove super user status" %}

- {% endifnotequal %} - {% endifequal %} -{% endif %} diff --git a/forum/skins/default/templates/users/tabs.html b/forum/skins/default/templates/users/tabs.html index 78d0c33..1815c24 100644 --- a/forum/skins/default/templates/users/tabs.html +++ b/forum/skins/default/templates/users/tabs.html @@ -17,11 +17,6 @@ {% trans "favorites" %} - {% if can_view_private %} - {% trans "subscriptions" %} - {% endif %}
{% endwith %} diff --git a/forum/templatetags/user_tags.py b/forum/templatetags/user_tags.py index d69c1bc..27535f6 100644 --- a/forum/templatetags/user_tags.py +++ b/forum/templatetags/user_tags.py @@ -1,6 +1,7 @@ from django import template from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe +from forum.forms import AwardPointsForm import logging register = template.Library() @@ -54,7 +55,7 @@ def activity_item(parser, token): return ActivityNode(activity, viewer) -@register.inclusion_tag('users/moderation.html') -def user_moderation(moderator, user): - return dict(moderator=moderator, user=user) +@register.inclusion_tag('users/menu.html') +def user_menu(request, user): + return dict(viewer=request.user, user=user) diff --git a/forum/urls.py b/forum/urls.py index c808bb8..c173bcf 100644 --- a/forum/urls.py +++ b/forum/urls.py @@ -94,8 +94,9 @@ urlpatterns += patterns('', url(r'^%s$' % _('users/'),app.users.users, name='users'), url(r'^%s(?P\d+)/%s$' % (_('users/'), _('edit/')), app.users.edit_user, name='edit_user'), + url(r'^%s(?P\d+)/%s$' % (_('users/'), _('award/')), app.users.award_points, name='user_award_points'), url(r'^%s(?P\d+)/%s(?P[a-z]+)/(?P[a-z]+)/$' % (_('users/'), _('powers/')), app.users.user_powers, name='user_powers'), - url(r'^%s(?P\d+)/(?P.+)/%s$' % (_('users/'), _('subscriptions/')), app.users.user_subscriptions, name='user_subscriptions'), + url(r'^%s(?P\d+)/%s$' % (_('users/'), _('subscriptions/')), app.users.user_subscriptions, name='user_subscriptions'), url(r'^%s(?P\d+)/(?P.+)/%s$' % (_('users/'), _('favorites/')), app.users.user_favorites, name='user_favorites'), url(r'^%s(?P\d+)/(?P.+)/%s$' % (_('users/'), _('reputation/')), app.users.user_reputation, name='user_reputation'), url(r'^%s(?P\d+)/(?P.+)/%s$' % (_('users/'), _('votes/')), app.users.user_votes, name='user_votes'), diff --git a/forum/views/auth.py b/forum/views/auth.py index 23059e2..6b7f5d1 100644 --- a/forum/views/auth.py +++ b/forum/views/auth.py @@ -304,7 +304,8 @@ def auth_settings(request, id): }) return render_to_response('auth/auth_settings.html', { - 'user': user_, + 'view_user': user_, + "can_view_private": (user_ == request.user) or request.user.is_superuser, 'form': form, 'has_password': user_.has_usable_password(), 'auth_keys': auth_keys_list, diff --git a/forum/views/decorators.py b/forum/views/decorators.py index f9929ff..90bb2b2 100644 --- a/forum/views/decorators.py +++ b/forum/views/decorators.py @@ -75,8 +75,8 @@ def command(func): response['success'] = True except Exception, e: - #import sys, traceback - #traceback.print_exc(file=sys.stdout) + import sys, traceback + traceback.print_exc(file=sys.stdout) if isinstance(e, CommandException): response = { diff --git a/forum/views/users.py b/forum/views/users.py index 3e624ea..6818974 100644 --- a/forum/views/users.py +++ b/forum/views/users.py @@ -17,7 +17,7 @@ from forum.forms import * from forum.utils.html import sanitize_html from datetime import date import decorators -from forum.actions import EditProfileAction, FavoriteAction +from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction import time @@ -133,6 +133,24 @@ def user_powers(request, id, action, status): return HttpResponseRedirect(user.get_profile_url()) +@decorators.command +def award_points(request, id): + if (not request.POST) and request.POST.get('points', None): + raise decorators.CommandException(_("Invalid request type")) + + if not request.user.is_superuser: + raise decorators.CommandException(_("Only superusers are allowed to award reputation points")) + + user = get_object_or_404(User, id=id) + points = int(request.POST['points']) + + extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points) + + BonusRepAction(user=user, extra=extra).save(data=dict(value=points)) + + return dict(reputation=user.reputation) + + def user_view(template, tab_name, tab_description, page_title, private=False): def decorator(fn): def decorated(request, id, slug=None): -- 2.45.2