Skip to content

Commit 5d575b7

Browse files
committed
added alert email for moderators and made moderation queue url common
1 parent 3707e51 commit 5d575b7

File tree

16 files changed

+282
-87
lines changed

16 files changed

+282
-87
lines changed

askbot/const/__init__.py

+11
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@
199199
TYPE_ACTIVITY_VALIDATION_EMAIL_SENT = 28
200200
TYPE_ACTIVITY_POST_SHARED = 29
201201
TYPE_ACTIVITY_ASK_TO_JOIN_GROUP = 30
202+
TYPE_ACTIVITY_MODERATION_ALERT_SENT = 31
202203
#TYPE_ACTIVITY_EDIT_QUESTION = 17
203204
#TYPE_ACTIVITY_EDIT_ANSWER = 18
204205

@@ -257,7 +258,17 @@
257258
TYPE_ACTIVITY_VALIDATION_EMAIL_SENT,
258259
'sent email address validation message'#don't translate, internal
259260
),
261+
(
262+
TYPE_ACTIVITY_MODERATION_ALERT_SENT,
263+
'sent moderation alert'#don't translate, internal
264+
)
265+
)
266+
267+
MODERATED_EDIT_ACTIVITY_TYPES = (
268+
TYPE_ACTIVITY_MODERATED_NEW_POST,
269+
TYPE_ACTIVITY_MODERATED_POST_EDIT
260270
)
271+
MODERATED_ACTIVITY_TYPES = MODERATED_EDIT_ACTIVITY_TYPES + (TYPE_ACTIVITY_MARK_OFFENSIVE,)
261272

262273

263274
#MENTION activity is added implicitly, unfortunately

askbot/doc/source/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changes in Askbot
33

44
Development master branch (only on github)
55
------------------------------------------
6+
* Added email alert for moderators `askbot_send_moderation_alerts`
67
* Implemented Google Plus login
78
* Allowed localized site settings
89
* Added management command `askbot_clear_moderation_queue`

askbot/doc/source/management-commands.rst

+3
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ Any configurable options, related to these commands are accessible via "Email" s
166166
| | of the live settings, as well as the appropriate delay |
167167
| | parameters may be set. |
168168
+-------------------------------------+-------------------------------------------------------------+
169+
| `askbot_send_moderation_alerts` | Sends alerts to moderators when there are items on the |
170+
| | queue. |
171+
+-------------------------------------+-------------------------------------------------------------+
169172

170173
Data repair commands
171174
====================

askbot/mail/__init__.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,19 @@ def _send_mail(subject_line, body_text, sender_email, recipient_list, headers=No
9797
else:
9898
message_class = mail.EmailMessage
9999

100+
from askbot.models import User
101+
email_list = list()
102+
for recipient in recipient_list:
103+
if isinstance(recipient, User):
104+
email_list.append(recipient.email)
105+
else:
106+
email_list.append(recipient)
107+
100108
msg = message_class(
101109
subject_line,
102110
get_text_from_html(body_text),
103111
sender_email,
104-
recipient_list,
112+
email_list,
105113
headers = headers
106114
)
107115
if html_enabled:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from django.core.management.base import NoArgsCommand
2+
from django.template.loader import get_template
3+
from django.utils.translation import ugettext as _
4+
from askbot.conf import settings as askbot_settings
5+
from askbot import const
6+
from askbot import mail
7+
from askbot.models import Activity
8+
from askbot.models import User
9+
10+
def get_moderators():
11+
return User.objects.filter(status__in=('d', 'm'))
12+
13+
def get_last_mod_alert_activity():
14+
atype = const.TYPE_ACTIVITY_MODERATION_ALERT_SENT
15+
acts = Activity.objects.filter(activity_type=atype).order_by('-id')
16+
count = len(acts)
17+
if count == 0:
18+
return None
19+
last_act = acts[0]
20+
21+
if count > 1:
22+
#get last moderation activity and delete all others
23+
acts = acts.exclude(id=last_act.id)
24+
acts.delete()
25+
26+
return last_act
27+
28+
29+
def get_last_notified_user():
30+
last_act = get_last_mod_alert_activity()
31+
if last_act:
32+
return last_act.content_object
33+
return None
34+
35+
36+
def select_moderators_to_notify(candidates, num_needed):
37+
candidates_count = candidates.count()
38+
39+
#special case - if we need to notify the same number of
40+
#moderators that are available, then we don't rotate them
41+
#and notify all, b/c otherwise we would stop notifications
42+
#because there are not enough moderators
43+
if candidates_count <= num_needed:
44+
return list(candidates)
45+
46+
last_notified = get_last_notified_user()
47+
if last_notified is None:
48+
return candidates[:num_needed]
49+
50+
mods = list(candidates.filter(id__gt=last_notified.id))
51+
num_mods = len(mods)
52+
if num_mods >= num_needed:
53+
return mods[:num_needed]
54+
else:
55+
#wrap around the end to the beginning
56+
num_missing = num_needed - num_mods
57+
more_mods = get_moderators().order_by('id')
58+
more_mods = more_mods[:num_missing]
59+
mods.extend(list(more_mods))
60+
return mods
61+
62+
63+
def select_last_moderator(mods):
64+
return max(mods, key=lambda item: item.id)
65+
66+
67+
def remember_last_moderator(user):
68+
act = get_last_mod_alert_activity()
69+
if act:
70+
act.content_object = user
71+
act.save()
72+
else:
73+
act = Activity(
74+
user=user,
75+
content_object=user,
76+
activity_type=const.TYPE_ACTIVITY_MODERATION_ALERT_SENT
77+
)
78+
act.save()
79+
80+
81+
82+
def notify_moderator(user):
83+
template = get_template('email/notify_moderator.html')
84+
subject_line = _('%s moderation alert') % askbot_settings.APP_SHORT_NAME,
85+
mail.send_mail(
86+
subject_line=subject_line,
87+
body_text=template.render({'user': user}),
88+
recipient_list=[user,]
89+
)
90+
91+
92+
class Command(NoArgsCommand):
93+
def handle_noargs(self, *args, **kwargs):
94+
#get size of moderation queue
95+
queue = Activity.objects.filter(activity_type__in=const.MODERATED_ACTIVITY_TYPES)
96+
if queue.count() == 0:
97+
return
98+
99+
#get moderators
100+
mods = get_moderators().order_by('id')
101+
if mods.count() == 0:
102+
return
103+
104+
mods = select_moderators_to_notify(mods, 3)
105+
106+
if len(mods) == 0:
107+
return
108+
109+
for mod in mods:
110+
notify_moderator(mod)
111+
112+
last_mod = select_last_moderator(mods)
113+
remember_last_moderator(last_mod)

askbot/media/js/user.js

-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
var setup_inbox = function(){
2-
var page = $('.inbox-flags');
3-
if (page.length) {
4-
var modControls = new PostModerationControls();
5-
modControls.decorate(page);
6-
}
72
var page = $('.inbox-forum');
83
if (page.length) {
94
var clearNotifs = $('.clear-messages');

askbot/media/style/style.css

+12-10
Original file line numberDiff line numberDiff line change
@@ -1533,8 +1533,7 @@ ul#related-tags li {
15331533
float: left;
15341534
}
15351535
.mod-queue-info {
1536-
margin-top: 12px;
1537-
margin-bottom: 0;
1536+
margin: 12px 0 12px 20px;
15381537
}
15391538
.moderate-tags-page button {
15401539
line-height: 18px;
@@ -2916,34 +2915,37 @@ ul#related-tags li {
29162915
.reject-reason-title {
29172916
margin-bottom: 12px;
29182917
}
2919-
.user-profile-page.inbox-flags .re {
2918+
.moderation-queue-page .re {
29202919
width: 810px;
29212920
}
2922-
.user-profile-page.inbox-flags .post-moderation-controls {
2921+
.moderation-queue-page .post-moderation-controls {
29232922
float: left;
29242923
width: 150px;
29252924
margin-top: 23px;
29262925
text-align: right;
29272926
}
2928-
.user-profile-page.inbox-flags .dropdown {
2927+
.moderation-queue-page .dropdown {
29292928
display: -moz-inline-stack;
29302929
display: inline-block;
29312930
height: 17px;
29322931
}
2933-
.user-profile-page.inbox-flags .dropdown:hover ul.dropdown-menu {
2932+
.moderation-queue-page .dropdown:hover ul.dropdown-menu {
29342933
display: block;
29352934
margin-top: 9px;
29362935
}
2937-
.user-profile-page.inbox-flags .highlight {
2936+
.moderation-queue-page .highlight {
29382937
background: transparent;
29392938
}
2940-
.user-profile-page.inbox-flags .messages {
2939+
.moderation-queue-page .messages {
29412940
margin-bottom: 14px;
29422941
}
2943-
.user-profile-page.inbox-flags .select-items {
2942+
.moderation-queue-page .message {
2943+
margin: 12px 0;
2944+
}
2945+
.moderation-queue-page .select-items {
29442946
margin-bottom: 10px;
29452947
}
2946-
.user-profile-page.inbox-flags #responses div.face {
2948+
.moderation-queue-page #responses div.face {
29472949
display: none;
29482950
}
29492951
.openid-signin form {

askbot/media/style/style.less

+5-3
Original file line numberDiff line numberDiff line change
@@ -1622,8 +1622,7 @@ ul#related-tags li {
16221622
}
16231623

16241624
.mod-queue-info {
1625-
margin-top: 12px;
1626-
margin-bottom: 0;
1625+
margin: 12px 0 12px 20px;
16271626
}
16281627

16291628
.moderate-tags-page {
@@ -3044,7 +3043,7 @@ ul#related-tags li {
30443043
margin-bottom: 12px;
30453044
}
30463045

3047-
.user-profile-page.inbox-flags {
3046+
.moderation-queue-page {
30483047
.re {
30493048
width: 810px;
30503049
}
@@ -3071,6 +3070,9 @@ ul#related-tags li {
30713070
.messages {
30723071
margin-bottom: 14px;
30733072
}
3073+
.message {
3074+
margin: 12px 0;
3075+
}
30743076
.select-items {
30753077
margin-bottom: 10px;
30763078
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% extends "email/base_mail.html" %}
2+
{% block content %}
3+
{% trans mod_url='moderation_queue'|url(), site=settings.APP_SHORT_NAME -%}
4+
There are new items in the {{ site }} moderation queue, <a href="{{ mod_url }}">please have a look</a>
5+
{%- endtrans %}
6+
{% endblock %}
7+
{% block footer %}{% include "email/footer.html" %}{% endblock %}

askbot/templates/macros.html

+1-3
Original file line numberDiff line numberDiff line change
@@ -780,9 +780,7 @@ <h2 class="comment-title">{% trans %}Comments{% endtrans %}</h2>
780780

781781
{%- macro moderation_items_link(user, moderation_items) -%}
782782
{% if moderation_items %}
783-
<a id="ab-responses"
784-
href="{{user.get_absolute_url()}}?sort=inbox&section=flags"
785-
>
783+
<a id="ab-responses" href="{% url moderation_queue %}">
786784
{% if moderation_items['new_count'] > 0 %}
787785
<img src="{{'/images/dialog-warning.png'|media}}"
788786
{% if moderation_items['seen_count'] > 0 %}

askbot/templates/moderation/queue.html

+26-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
{% extends "user_inbox/base.html" %}
1+
{% extends "one_column_body.html" %}
22
{% import "macros.html" as macros %}
3-
{% block profilesection %}
4-
{% trans %}moderation queue{% endtrans %}
3+
{% block title %}
4+
{% trans %}Moderation queue{% endtrans %}
55
{% endblock %}
6-
{% block inbox_content %}
6+
{% block body %}
7+
<h1 class="section-title">{% trans %}Moderation queue{% endtrans %}</h1>
78
<div class="tools">
89
{#<div class="select-items">
910
<strong>{% trans %}Select:{% endtrans %}</strong>
@@ -43,23 +44,33 @@
4344
{% include "moderation/manage_reject_reasons_dialog.html" %}
4445
<div class="action-status"><span></span></div>
4546
<div class="messages">
46-
{% for message in messages %}{# messages are grouped by question, using the "nested_messages" #}
47+
{% for message in messages %}
4748
<div
4849
class="message{% if message.is_new %} highlight new{% else %} seen{% endif %}"
4950
data-message-id="{{ message.id }}"
5051
>
51-
{#<h2>"{{ message.title.strip()|escape}}"</h2>#}
5252
{{ macros.moderation_queue_message(message) }}
5353
</div>
54-
{# "nested" messages are further response messages to the same question #}
55-
{% for followup_message in message.followup_messages %}
56-
<div
57-
class="message{% if followup_message.is_new %} highlight new{% else %} seen{% endif %}"
58-
data-message-id="{{ followup_message.id }}"
59-
>
60-
{{ macros.moderation_queue_message(followup_message) }}
61-
</div>
62-
{% endfor %}
6354
{% endfor %}
6455
</div>
6556
{% endblock %}
57+
{% block endjs %}
58+
{# todo: factor out moderation.js file #}
59+
<script type="text/javascript" src="{{ '/js/user.js'|media }}"></script>
60+
<script type="text/javascript">
61+
(function() {
62+
askbot['urls']['save_post_reject_reason'] = '{% url save_post_reject_reason %}';
63+
askbot['urls']['delete_post_reject_reason'] = '{% url delete_post_reject_reason %}';
64+
{% if request.user.is_administrator_or_moderator() %}
65+
askbot['data']['postRejectReasons'] = [
66+
{% for reason in post_reject_reasons %}
67+
{'id': {{reason.id}}, 'title': '{{reason.title|escapejs}}'},
68+
{% endfor %}
69+
];
70+
{% endif %}
71+
askbot['urls']['moderatePostEdits'] = '{% url moderate_post_edits %}';
72+
var modControls = new PostModerationControls();
73+
modControls.decorate($('body'));
74+
})();
75+
</script>
76+
{% endblock %}

askbot/templates/one_column_body.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{% block body_class %}one-col{% endblock %}
33
{% block body %}
44
<div id="ContentFull">
5-
{% block content%}
6-
{% endblock%}
5+
{% block content %}
6+
{% endblock %}
77
</div>
88
{% endblock %}

askbot/templates/user_inbox/base.html

-10
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,6 @@
5151
askbot['urls'] = askbot['urls'] || {};
5252
askbot['urls']['manageInbox'] = '{% url manage_inbox %}';
5353
askbot['urls']['clearNewNotifications'] = '{% url clear_new_notifications %}';
54-
askbot['urls']['moderatePostEdits'] = '{% url moderate_post_edits %}';
55-
askbot['urls']['save_post_reject_reason'] = '{% url save_post_reject_reason %}';
56-
askbot['urls']['delete_post_reject_reason'] = '{% url delete_post_reject_reason %}';
57-
{% if request.user.is_administrator_or_moderator() %}
58-
askbot['data']['postRejectReasons'] = [
59-
{% for reason in post_reject_reasons %}
60-
{'id': {{reason.id}}, 'title': '{{reason.title|escapejs}}'},
61-
{% endfor %}
62-
];
63-
{% endif %}
6454
$(document).ready(function(){
6555
$('body').addClass('inbox-{{ inbox_section }}');
6656
setup_inbox();

askbot/urls.py

+5
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@
205205
views.commands.moderate_group_join_request,
206206
name='moderate_group_join_request'
207207
),
208+
service_url(
209+
r'^%s$' % _('moderation-queue/'),
210+
views.moderation.moderation_queue,
211+
name='moderation_queue'
212+
),
208213
service_url(
209214
r'^moderate-post-edits/',
210215
views.moderation.moderate_post_edits,

0 commit comments

Comments
 (0)