Add report reasons for Links and Comments.

This commit is contained in:
Brian Simpson
2014-06-30 13:15:21 -04:00
parent d2404e4108
commit 85a9223dce
10 changed files with 328 additions and 22 deletions

View File

@@ -1403,10 +1403,15 @@ class ApiController(RedditController):
jquery.refresh()
@require_oauth2_scope("report")
@noresponse(VUser(), VModhash(),
thing = VByName('id'))
@validatedForm(
VUser(),
VModhash(),
thing=VByName('thing_id'),
reason=VLength('reason', max_length=100, empty_error=None),
other_reason=VLength('other_reason', max_length=100, empty_error=None),
)
@api_doc(api_section.links_and_comments)
def POST_report(self, thing):
def POST_report(self, form, jquery, thing, reason, other_reason):
"""Report a link or comment.
Reporting a thing brings it to the attention of the subreddit's
@@ -1417,6 +1422,12 @@ class ApiController(RedditController):
if not thing or thing._deleted:
return
if (form.has_errors("reason", errors.TOO_LONG) or
form.has_errors("other_reason", errors.TOO_LONG)):
return
reason = other_reason if reason == "other" else reason
# if it is a message that is being reported, ban it.
# every user is admin over their own personal inbox
if isinstance(thing, Message):
@@ -1434,12 +1445,21 @@ class ApiController(RedditController):
hooks.get_hook("thing.report").call(thing=thing)
sr = getattr(thing, 'subreddit_slow', None)
if (c.user._spam or
if not (c.user._spam or
c.user.ignorereports or
(sr and sr.is_banned(c.user))):
Report.new(c.user, thing, reason)
admintools.report(thing)
if isinstance(thing, Link):
button = jquery(".id-%s .report-button" % thing._fullname)
elif isinstance(thing, Comment):
button = jquery(".id-%s .entry:first .report-button" % thing._fullname)
else:
return
Report.new(c.user, thing)
admintools.report(thing)
button.text(_("reported"))
form.fadeOut()
@require_oauth2_scope("privatemessages")
@noresponse(VUser(), VModhash(),

View File

@@ -470,6 +470,7 @@ module["reddit"] = LocalizedModule("reddit.js",
"multi.js",
"filter.js",
"recommender.js",
"report.js",
"saved.js",
PermissionsDataSource({
"moderator": ModeratorPermissionSet,

View File

@@ -27,7 +27,7 @@ from wrapped import Wrapped, StringTemplate, CacheStub, CachedVariable, Template
from mako.template import Template
from r2.config.extensions import get_api_subtype
from r2.lib.filters import spaceCompress, safemarkdown
from r2.models import Account
from r2.models import Account, Report
from r2.models.subreddit import SubSR
from r2.models.token import OAuth2Scope, extra_oauth2_scope
import time, pytz
@@ -171,10 +171,13 @@ class ThingJsonTemplate(JsonTemplate):
return None
return distinguished
if attr in ["num_reports", "banned_by", "approved_by"]:
if attr in ["num_reports", "report_reasons", "banned_by", "approved_by"]:
if c.user_is_loggedin and thing.subreddit.is_moderator(c.user):
if attr == "num_reports":
return thing.reported
elif attr == "report_reasons":
return Report.get_reasons(thing)
ban_info = getattr(thing, "ban_info", {})
if attr == "banned_by":
banner = (ban_info.get("banner")
@@ -423,6 +426,7 @@ class LinkJsonTemplate(ThingJsonTemplate):
media_embed="media_embed",
num_comments="num_comments",
num_reports="num_reports",
report_reasons="report_reasons",
over_18="over_18",
permalink="permalink",
saved="saved",
@@ -513,6 +517,7 @@ class CommentJsonTemplate(ThingJsonTemplate):
likes="likes",
link_id="link_id",
num_reports="num_reports",
report_reasons="report_reasons",
parent_id="parent_id",
replies="child",
saved="saved",

View File

@@ -329,8 +329,9 @@ class Reddit(Templated):
clone_template=True,
thing_type="comment",
)
report_form = ReportForm()
self._content = PaneStack([ShareLink(), content,
gold_comment, gold_link])
gold_comment, gold_link, report_form])
else:
self._content = content
@@ -2602,6 +2603,10 @@ class Gilding(Templated):
pass
class ReportForm(Templated):
pass
class Password(Templated):
"""Form encountered when 'recover password' is clicked in the LoginFormWide."""
def __init__(self, success=False):

View File

@@ -23,7 +23,7 @@
from r2.lib.db.thing import NotFound
from r2.lib.menus import Styled
from r2.lib.wrapped import Wrapped
from r2.models import LinkListing, Link, PromotedLink
from r2.models import LinkListing, Link, PromotedLink, Report
from r2.models import make_wrapper, IDBuilder, Thing
from r2.lib.utils import tup
from r2.lib.strings import Score
@@ -132,6 +132,7 @@ class LinkButtons(PrintableButtons):
ignore_reports = thing.ignore_reports,
show_delete = show_delete,
show_report = show_report and c.user_is_loggedin,
report_reasons = Report.get_reasons(thing),
show_distinguish = show_distinguish,
show_marknsfw = show_marknsfw,
show_unmarknsfw = show_unmarknsfw,
@@ -173,6 +174,7 @@ class CommentButtons(PrintableButtons):
parent_permalink = thing.parent_permalink,
can_reply = thing.can_reply,
show_report = show_report,
report_reasons = Report.get_reasons(thing),
show_distinguish = show_distinguish,
show_delete = show_delete,
show_givegold=show_givegold,

View File

@@ -39,7 +39,7 @@ class Report(MultiRelation('report',
_field = 'reported'
@classmethod
def new(cls, user, thing):
def new(cls, user, thing, reason=None):
from r2.lib.db import queries
# check if this report exists already!
@@ -53,7 +53,11 @@ class Report(MultiRelation('report',
g.log.debug("Ignoring duplicate report %s" % oldreport)
return oldreport
r = Report(user, thing, '0')
kw = {}
if reason:
kw['reason'] = reason
r = Report(user, thing, '0', **kw)
if not thing._loaded:
thing._load()
@@ -84,7 +88,7 @@ class Report(MultiRelation('report',
@classmethod
def for_thing(cls, thing):
rel = cls.rel(Account, thing.__class__)
rels = rel._query(rel.c._thing2_id == thing._id)
rels = rel._query(rel.c._thing2_id == thing._id, data=True)
return list(rels)
@@ -118,3 +122,20 @@ class Report(MultiRelation('report',
queries.clear_reports(to_clear, rels)
@classmethod
def get_reasons(cls, wrapped, max_reasons=20):
if wrapped.can_ban and wrapped.reported > 0:
reports = cls.for_thing(wrapped.lookups[0])
reasons = set()
for report in reports:
if len(reasons) >= max_reasons:
break
reason = getattr(report, 'reason', None)
if reason:
reasons.add(reason)
return list(reasons)
else:
return []

View File

@@ -10054,3 +10054,54 @@ body.with-listing-chooser {
}
}
}
.report-form {
display: none;
background-color: #f6e69f;
border: thin solid #d8bb3c;
max-width: 300px;
padding: 5px;
margin: 5px 0;
font-size: larger;
input {
margin: 5px 0;
&[type="radio"] {
margin: 2px 0.5em 0 0;
}
&[name="other_reason"] {
width: 95%;
}
&:disabled {
background: #dddddd;
}
}
}
.reported-stamp.has-reasons {
cursor: pointer;
}
ul.report-reasons {
width: 80%;
background-color: #f6e69f;
border: thin solid black;
display: none;
li {
&.report-reason {
padding: 1px 10px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
&.report-reason-title {
padding: 1px 10px;
font-weight: bold;
}
}
}

View File

@@ -0,0 +1,88 @@
r.report = {
"init": function() {
$('div.content').on(
'click',
'.report-thing, button.cancel-report-thing',
$.proxy(this, 'toggleReportForm')
);
$('div.content').on(
'click',
'button.submit-report',
$.proxy(this, 'submitReport')
);
$('div.content').on(
'change',
'.report-form input[type="radio"]',
$.proxy(this, 'enableReportForm')
);
$('div.content').on(
'click',
'.reported-stamp.has-reasons',
$.proxy(function(event) {
$(event.target).parent().find('.report-reasons').toggle()
}, this)
);
},
toggleReportForm: function(event) {
var element = event.target;
var $thing = $(element).thing();
var $thingForm = $thing.find("> .entry .report-form");
event.stopPropagation();
event.preventDefault();
if ($thingForm.length > 0) {
if ($thingForm.is(":visible")) {
$thingForm.hide();
} else {
$thingForm.show();
}
} else {
var $form = $(".report-form.clonable");
var $clonedForm = $form.clone();
var $insertionPoint = $thing.find("> .entry .buttons");
var thingFullname = $thing.thing_id();
$clonedForm.removeClass("clonable");
$clonedForm.attr("id", "report-thing-" + thingFullname);
$clonedForm.find("input[name='thing_id']").val(thingFullname);
$clonedForm.insertAfter($insertionPoint);
$clonedForm.show();
}
},
submitReport: function(event) {
var $reportForm = $(event.target).parent()
return post_pseudo_form($reportForm, "report");
},
enableReportForm: function(event) {
var $thing = $(event.target).thing();
var $reportForm = $thing.find("> .entry .report-form");
var $submitButton = $reportForm.find('button.submit-report');
var $enabledRadio = $reportForm.find('input[type="radio"]:checked');
var isOther = $enabledRadio.val() == 'other';
var $otherInput = $reportForm.find('input[name="other_reason"]');
event.stopPropagation();
event.preventDefault();
$submitButton.removeAttr("disabled");
if (isOther) {
$otherInput.removeAttr("disabled");
} else {
$otherInput.attr("disabled", "disabled");
}
return false;
}
}
$(function() {
r.report.init();
});

View File

@@ -20,7 +20,7 @@
## reddit Inc. All Rights Reserved.
###############################################################################
<%namespace file="utils.html" import="plain_link, pretty_button, data" />
<%namespace file="utils.html" import="plain_link, pretty_button, data, error_field" />
<%!
from r2.lib.strings import strings
@@ -52,8 +52,14 @@
%endif
%endif
%elif thing.show_report:
<li>
${ynbutton(_("report"), _("reported"), "report", "hide_thing")}
<li class="report-button">
%if thing.style in ("linkbuttons", "commentbuttons"):
<a href="javascript:void(0)" class="report-thing">
${_("report")}
</a>
%else:
${ynbutton(_("report"), _("reported"), "report", "hide_thing")}
%endif
</li>
%endif
%if thing.show_marknsfw:
@@ -174,6 +180,28 @@
</span>
</%def>
<%def name="reports_button()">
<li
%if thing.report_reasons:
class="rounded reported-stamp stamp has-reasons"
title="${_('click to show report reasons')}"
%else:
class="rounded reported-stamp stamp"
%endif
>
${strings.reports % thing.thing.reported}
</li>
</%def>
<%def name="report_reasons()">
<ul class="report-reasons rounded">
<li class="report-reason-title">${_("report reasons:")}</li>
%for reason in thing.report_reasons:
<li class="report-reason" title="${reason}">${reason}</li>
%endfor
</ul>
</%def>
<%def name="linkbuttons()">
%if thing.show_comments:
<li class="first">
@@ -252,9 +280,7 @@
%endif
%if thing.show_reports and not thing.show_spam:
<li class="rounded reported-stamp stamp">
${strings.reports % thing.thing.reported}
</li>
${reports_button()}
%endif
%if getattr(thing.thing, "use_big_modbuttons", False):
@@ -262,6 +288,11 @@
%elif thing.ignore_reports and thing.can_ban:
${ignore_reports_toggle(thing.thing)}
%endif
%if thing.show_reports and not thing.show_spam and thing.report_reasons:
${report_reasons()}
%endif
</%def>
<%def name="commentbuttons()">
@@ -280,6 +311,7 @@
</li>
%endif
%endif
%if c.profilepage:
<li>
${self.bylink_button(_("context"), thing.permalink + "?context=3")}
@@ -292,6 +324,7 @@
a_class="may-blank")}
</li>
%endif
%if not thing.profilepage:
%if thing.parent_permalink:
<li>
@@ -304,24 +337,31 @@
</li>
%endif
%endif
${self.banbuttons()}
${self.distinguish()}
${self.give_gold()}
%if not thing.profilepage and thing.can_reply:
<li>
${self.simple_button(_("reply {verb}"), "reply")}
</li>
%endif
%if thing.show_reports and not thing.show_spam:
<li class="rounded reported-stamp stamp">
${strings.reports % thing.thing.reported}
</li>
${reports_button()}
%endif
%if getattr(thing.thing, "use_big_modbuttons", False):
${big_modbuttons(thing.thing)}
%elif thing.ignore_reports and thing.can_ban:
${ignore_reports_toggle(thing.thing)}
%endif
%if thing.show_reports and not thing.show_spam and thing.report_reasons:
${report_reasons()}
%endif
%endif
</%def>

View File

@@ -0,0 +1,73 @@
## The contents of this file are subject to the Common Public Attribution
## License Version 1.0. (the "License"); you may not use this file except in
## compliance with the License. You may obtain a copy of the License at
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
## software over a computer network and provide for limited attribution for the
## Original Developer. In addition, Exhibit A has been modified to be
## consistent with Exhibit B.
##
## Software distributed under the License is distributed on an "AS IS" basis,
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
## the specific language governing rights and limitations under the License.
##
## The Original Code is reddit.
##
## The Original Developer is the Initial Developer. The Initial Developer of
## the Original Code is reddit Inc.
##
## All portions of the code written by reddit are Copyright (c) 2006-2014
## reddit Inc. All Rights Reserved.
###############################################################################
<%namespace file="utils.html" import="error_field" />
<div id="report-thing-fullname" class="report-form clonable rounded">
<input type="hidden" name="thing_id" value="thing-fullname">
<span class="reason-prompt">
${_('why are you reporting this?')}
</span>
<ol>
<li>
<label>
<input type="radio" name="reason" value="spam">${_("spam")}</input>
</label>
</li>
<li>
<label>
<input type="radio" name="reason" value="vote manipulation">${_("vote manipulation")}</input>
</label>
</li>
<li>
<label>
<input type="radio" name="reason" value="personal information">${_("personal information")}</input>
</label>
</li>
<li>
<label>
<input type="radio" name="reason" value="sexualizing minors">${_("sexualizing minors")}</input>
</label>
</li>
<li>
<label>
<input type="radio" name="reason" value="breaking reddit">${_("breaking reddit")}</input>
</label>
</li>
<li>
<label>
<input type="radio" name="reason" value="other">
${_("other (max %(num)s characters):") % dict(num=100)}
</input>
</label>
<input name="other_reason" value="" maxlength="100" type="text" disabled="disabled"></input>
</li>
</ol>
<button type="button" class="btn submit-report" disabled="disabled">
${_("submit")}
</button>
<button type="button" class="btn cancel-report-thing">
${_("cancel")}
</button>
<span class="status"></span>
${error_field("TOO_LONG", "reason", "span")}
</div>