Permalink
Browse files

shadowban button (without using admin gui)

  • Loading branch information...
1 parent d7cdf45 commit 502026010122fbea118a9953dc06b3f1df5b5f76 Justine Tunney committed Jan 15, 2012
View
@@ -270,6 +270,9 @@ def _check_modify_article(user, article):
if not user.userinfo.can_moderate():
if article.author != user:
raise APIException(_("you didn't post that"))
+ if user.userinfo.can_moderate() and not user.is_staff:
+ if article.author.is_staff:
+ raise APIException(_("insufficient privileges"))
def _check_modify_comment(user, comment, mods_only=False):
@@ -281,6 +284,31 @@ def _check_modify_comment(user, comment, mods_only=False):
if not user.userinfo.can_moderate():
if comment.user != user:
raise APIException(_("you didn't post that"))
+ if user.userinfo.can_moderate() and not user.is_staff:
+ if comment.user.is_staff:
+ raise APIException(_("insufficient privileges"))
+
+
+def shadowban(user, username, action, **kwargs):
+ """Ban a user through nefarious means"""
+ if not (user and user.id):
+ raise APIException(_("you're not logged in"))
+ if not user.userinfo.can_moderate():
+ raise APIException(_("insufficient permissions"))
+ try:
+ user2 = db.User.objects.get(username=username)
+ except db.User.DoesNotExist:
+ raise APIException(_("user not found"))
+ if user2.userinfo.can_moderate():
+ raise APIException(_("cannot ban privileged users"))
+ if action == 'ban' and not user2.userinfo.is_shadow_banned:
+ user2.userinfo.is_shadow_banned = True
+ elif action == 'unban' and user2.userinfo.is_shadow_banned:
+ user2.userinfo.is_shadow_banned = False
+ else:
+ raise APIException(_("invalid action"))
+ user2.userinfo.save()
+ return []
def article_edit(user, article_slug, title, content, **kwargs):
@@ -13,11 +13,12 @@ jQuery.fn.numberAdd = function(delta) {
var penguin = 50;
function init(root) {
+ init_shadowban(root);
+ init_comment_form($(".postcommentform", root),
+ $("#comment-list", root), "");
$(".article", root).each(function() {
init_article($(this));
});
- init_comment_form($(".postcommentform", root),
- $("#comment-list", root), "");
$(".comment", root).each(function() {
init_comment($(this));
});
@@ -27,6 +28,30 @@ jQuery.fn.numberAdd = function(delta) {
}
}
+ function init_shadowban(root) {
+ $(".ban, .unban", root).click(function(ev) {
+ ev.preventDefault();
+ var self = $(this);
+ var action = self.is(".ban") ? "ban" : "unban";
+ api("/api/shadowban/", {
+ "username": self.attr("id"),
+ "action": action
+ }, function(data) {
+ if (data.status != "ERROR") {
+ if (action == "ban") {
+ $(".ban", self.parent()).hide();
+ $(".unban", self.parent()).show();
+ } else {
+ $(".ban", self.parent()).show();
+ $(".unban", self.parent()).hide();
+ }
+ } else {
+ alert(data.message);
+ }
+ });
+ });
+ }
+
function init_article(article) {
var slug = article.attr("id");
@@ -31,7 +31,8 @@
{% if user and user.userinfo.can_moderate %}
<br />
{% trans 'Mod:' %}
- <a href="/admin/auth/user/{{ article.author.id }}/shadowban/">{% if article.author.userinfo.is_shadow_banned %}un{% endif %}ban</a>
+ <a class="ban" id="{{ article.author.username }}" href="#" style="{% if article.author.userinfo.is_shadow_banned %}display:none;{% endif %}">{% trans 'ban' %}</a>
+ <a class="unban" id="{{ article.author.username }}" href="#" style="{% if not article.author.userinfo.is_shadow_banned %}display:none;{% endif %}">{% trans 'unban' %}</a>
<a href="/admin/occupywallst/comment/?user={{ article.author.id }}">comments</a>
<a href="/admin/occupywallst/forumpost/?author={{ article.author.id }}">threads</a>
<a href="/admin/occupywallst/{% if article.is_forum %}forumpost{% else %}newsarticle{% endif %}/{{ article.id }}/">{% trans 'admin' %}</a>
@@ -99,10 +99,10 @@
{% endblock forms %}
<script src="{{ MEDIA_URL }}js/jquery-1.6.2{% if not DEBUG %}.min{% endif %}.js" type="text/javascript"></script>
{% if not DEBUG and OWS_SCRIPTS_MINIFIED %}
- <script src="{{ MEDIA_URL }}{{ OWS_SCRIPTS_MINIFIED }}?v=38" type="text/javascript"></script>
+ <script src="{{ MEDIA_URL }}{{ OWS_SCRIPTS_MINIFIED }}?v=39" type="text/javascript"></script>
{% else %}
{% for script in OWS_SCRIPTS %}
- <script src="{{ MEDIA_URL }}{{ script }}?v=38" type="text/javascript"></script>
+ <script src="{{ MEDIA_URL }}{{ script }}?v=39" type="text/javascript"></script>
{% endfor %}
{% endif %}
<!-- <script src="//chat.{{ request.get_host }}/socket.io/socket.io.js" type="text/javascript"></script> -->
@@ -38,7 +38,8 @@
<a class="permalink" href="{{ comment.get_absolute_url }}">{% trans 'permalink' %}</a>
{% if user and user.userinfo.can_moderate %}
| {% trans 'Mod:' %}
- <a href="/admin/auth/user/{{ comment.user.id }}/shadowban/">{% if comment.user.userinfo.is_shadow_banned %}un{% endif %}ban</a>
+ <a class="ban" id="{{ comment.user.username }}" href="#" style="{% if comment.user.userinfo.is_shadow_banned %}display:none;{% endif %}">{% trans 'ban' %}</a>
+ <a class="unban" id="{{ comment.user.username }}" href="#" style="{% if not comment.user.userinfo.is_shadow_banned %}display:none;{% endif %}">{% trans 'unban' %}</a>
<a href="/admin/occupywallst/comment/?user={{ comment.user.id }}">comments</a>
<a href="/admin/occupywallst/forumpost/?author={{ comment.user.id }}">threads</a>
<a href="/admin/occupywallst/comment/{{ comment.id }}/">{% trans 'admin' %}</a>
@@ -10,10 +10,14 @@
{{ comment_count }} comments {{ created }} ago by {{ username }} ({{ karma }}) last comment {{ last_comment }} ago
{% endblocktrans %}
{% if user and user.userinfo.can_moderate %}
- | {% trans 'Mod:' %} <a class="remove" href="#">{% if not article.is_visible %}{% trans 'unremove' %}{% else %}{% trans 'remove' %}{% endif %}</a>
+ | {% trans 'Mod:' %}
+ <a class="remove" href="#">{% if not article.is_visible %}{% trans 'unremove' %}{% else %}{% trans 'remove' %}{% endif %}</a>
+ <a class="ban" id="{{ article.author.username }}" href="#" style="{% if article.author.userinfo.is_shadow_banned %}display:none;{% endif %}">{% trans 'ban' %}</a>
+ <a class="unban" id="{{ article.author.username }}" href="#" style="{% if not article.author.userinfo.is_shadow_banned %}display:none;{% endif %}">{% trans 'unban' %}</a>
{% if user.is_superuser %}
- <a href="/admin/occupywallst/forumpost/?ip=127.0.0.1">{{ article.ip }} {{ article.ip|ipcountry }}</a>
+ <a href="/admin/occupywallst/forumpost/?ip=127.0.0.1">{{ article.ip }}</a>
{% endif %}
+ {{ article.ip|ipcountry }}
{% endif %}
</div>
</div>
View
@@ -126,9 +126,20 @@ def create_users(self):
self.red_user = User.objects.create_user('red', '', 'red')
self.green_user = User.objects.create_user('green', '', 'green')
self.blue_user = User.objects.create_user('blue', '', 'blue')
- for u in [self.red_user, self.green_user, self.blue_user]:
+ self.admin_user = User.objects.create_user('admin', '', 'admin')
+ self.staff_user = User.objects.create_user('staff', '', 'staff')
+ self.mod_user = User.objects.create_user('mod', '', 'mod')
+ for u in [self.red_user, self.green_user, self.blue_user,
+ self.admin_user, self.staff_user, self.mod_user]:
ui = db.UserInfo(user=u)
ui.save()
+ self.admin_user.is_staff = True
+ self.admin_user.is_admin = True
+ self.admin_user.save()
+ self.staff_user.is_staff = True
+ self.staff_user.save()
+ self.mod_user.userinfo.is_moderator = True
+ self.mod_user.userinfo.save()
def setUp(self):
settings.OWS_LIMIT_THREAD = -1
@@ -159,6 +170,29 @@ def setUp(self):
self.photo = db.Photo(carousel=self.carousel, caption='hello, world')
self.photo.save()
+ def invoke(api, *args, **kwargs):
+ resp = self.client.post(api, *args, **kwargs)
+ if response.status_code != 200:
+ raise Exception("%s gave unexpected status code: %d"
+ % (api, response.status_code))
+ return json.loads(resp.content)
+
+ def good(api, *args, **kwargs):
+ data = self.invoke(api, *args, **kwargs)
+ if data['status'] == 'ERROR':
+ raise Exception("%s failed: %s" % (api, jdump(data)))
+ return data
+
+ def bad(msg, api, *args, **kwargs):
+ data = self.invoke(api, *args, **kwargs)
+ data = json.loads(resp.content)
+ if data['status'] != 'ERROR':
+ raise Exception("%s didn't fail: %s" % (api, jdump(data)))
+ if data['message'] != msg:
+ raise Exception("%s didn't fail with '%s': %s" % (api, msg, jdump(data)))
+ return data
+
+
######################################################################
# tests of models
@@ -804,6 +838,31 @@ def test_api_article(self):
j = assert_and_get_valid_json(response)
assert j['status'] == 'ERROR' # TODO: confirm that this is correct
+ def test_api_shadowban(self):
+ # normal users can't ban
+ self.good('/api/login/', {'username': 'red', 'password': 'red'})
+ self.bad("insufficient permissions", '/api/shadowban/', {'username': 'red', 'action': 'ban'})
+
+ # but moderators can
+ self.good('/api/login/', {'username': 'mod', 'password': 'mod'})
+ self.good('/api/shadowban/', {'username': 'red', 'action': 'ban'})
+ self.good('/api/shadowban/', {'username': 'red', 'action': 'unban'})
+
+ # same goes for staff
+ self.good('/api/login/', {'username': 'staff', 'password': 'staff'})
+ self.good('/api/shadowban/', {'username': 'red', 'action': 'ban'})
+ self.good('/api/shadowban/', {'username': 'red', 'action': 'unban'})
+
+ # but you can't ban important people
+ self.good('/api/login/', {'username': 'mod', 'password': 'mod'})
+ self.bad("cannot ban privileged users", '/api/shadowban/', {'username': 'mod', 'action': 'mod'})
+
+ # ban if already banned raises error
+ self.good('/api/login/', {'username': 'staff', 'password': 'staff'})
+ self.good('/api/shadowban/', {'username': 'red', 'action': 'ban'})
+ self.bad("invalid action", '/api/shadowban/', {'username': 'red', 'action': 'ban'})
+ self.good('/api/shadowban/', {'username': 'red', 'action': 'unban'})
+
def test_api_comment(self):
settings.OWS_LIMIT_COMMENT = -1 # turn off limit for testing
content = random_words(20)
View
@@ -64,6 +64,7 @@
url(r'^api/message_send/$', require_POST(utils.api_view(api.message_send))),
url(r'^api/message_delete/$', require_POST(utils.api_view(api.message_delete))),
url(r'^api/check_username/$', require_POST(utils.api_view(api.check_username))),
+ url(r'^api/shadowban/$', require_POST(utils.api_view(api.shadowban))),
url(r'^api/signup/$', require_POST(utils.api_view(api.signup))),
url(r'^api/login/$', require_POST(utils.api_view(api.login))),
url(r'^api/logout/$', require_POST(utils.api_view(api.logout))),

0 comments on commit 5020260

Please sign in to comment.