mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-24 06:18:08 -05:00
Add report reasons for Links and Comments.
This commit is contained in:
@@ -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(),
|
||||
|
||||
@@ -470,6 +470,7 @@ module["reddit"] = LocalizedModule("reddit.js",
|
||||
"multi.js",
|
||||
"filter.js",
|
||||
"recommender.js",
|
||||
"report.js",
|
||||
"saved.js",
|
||||
PermissionsDataSource({
|
||||
"moderator": ModeratorPermissionSet,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 []
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
88
r2/r2/public/static/js/report.js
Normal file
88
r2/r2/public/static/js/report.js
Normal 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();
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
|
||||
73
r2/r2/templates/reportform.html
Normal file
73
r2/r2/templates/reportform.html
Normal 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>
|
||||
Reference in New Issue
Block a user