mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-22 21:38:11 -05:00
Add listing for promotions suspected of fraud
This commit is contained in:
@@ -227,7 +227,7 @@ def make_map():
|
||||
mc('/sponsor/promoted/:sort', controller='sponsorlisting', action='listing',
|
||||
requirements=dict(sort="future_promos|pending_promos|unpaid_promos|"
|
||||
"rejected_promos|live_promos|underdelivered|"
|
||||
"reported|house|all"))
|
||||
"reported|house|fraud|all"))
|
||||
mc('/sponsor', controller='sponsorlisting', action="listing",
|
||||
sort="all")
|
||||
mc('/sponsor/promoted/', controller='sponsorlisting', action="listing",
|
||||
@@ -371,7 +371,8 @@ def make_map():
|
||||
"freebie|promote_note|update_pay|"
|
||||
"edit_campaign|delete_campaign|"
|
||||
"add_roadblock|rm_roadblock|check_inventory|"
|
||||
"refund_campaign|terminate_campaign")))
|
||||
"refund_campaign|terminate_campaign|"
|
||||
"review_fraud")))
|
||||
mc('/api/:action', controller='apiminimal',
|
||||
requirements=dict(action="new_captcha"))
|
||||
mc('/api/:type', controller='api',
|
||||
|
||||
@@ -373,6 +373,7 @@ class SponsorListingController(PromoteListingController):
|
||||
'underdelivered': N_('underdelivered promoted links'),
|
||||
'reported': N_('reported promoted links'),
|
||||
'house': N_('house promoted links'),
|
||||
'fraud': N_('fraud suspected promoted links'),
|
||||
}.items())
|
||||
base_path = '/sponsor/promoted'
|
||||
|
||||
@@ -382,7 +383,7 @@ class SponsorListingController(PromoteListingController):
|
||||
|
||||
@property
|
||||
def menus(self):
|
||||
if self.sort in {'underdelivered', 'reported', 'house'}:
|
||||
if self.sort in {'underdelivered', 'reported', 'house', 'fraud'}:
|
||||
menus = []
|
||||
else:
|
||||
menus = super(SponsorListingController, self).menus
|
||||
@@ -465,6 +466,8 @@ class SponsorListingController(PromoteListingController):
|
||||
return [Link._fullname_from_id36(to36(id)) for id in link_ids]
|
||||
elif self.sort == 'reported':
|
||||
return queries.get_reported_links(Subreddit.get_promote_srid())
|
||||
elif self.sort == 'fraud':
|
||||
return queries.get_payment_flagged_links()
|
||||
elif self.sort == 'house':
|
||||
return self.get_house_link_names()
|
||||
elif self.sort == 'all':
|
||||
@@ -548,6 +551,21 @@ class PromoteApiController(ApiController):
|
||||
form.find(".notes").children(":last").after(
|
||||
"<p>" + websafe(text) + "</p>")
|
||||
|
||||
@validatedForm(
|
||||
VSponsorAdmin(),
|
||||
VModhash(),
|
||||
thing = VByName("thing_id"),
|
||||
is_fraud=VBoolean("fraud"),
|
||||
)
|
||||
def POST_review_fraud(self, form, jquery, thing, is_fraud):
|
||||
if not promote.is_promo(thing):
|
||||
return
|
||||
|
||||
promote.review_fraud(thing, is_fraud)
|
||||
|
||||
button = jquery(".id-%s .fraud-button" % thing._fullname)
|
||||
button.text(_("fraud" if is_fraud else "not fraud"))
|
||||
form.fadeOut()
|
||||
|
||||
@noresponse(VSponsorAdmin(),
|
||||
VModhash(),
|
||||
|
||||
@@ -793,6 +793,23 @@ def get_all_accepted_links():
|
||||
return _promoted_link_query(None, 'accepted')
|
||||
|
||||
|
||||
@cached_query(UserQueryCache)
|
||||
def get_payment_flagged_links():
|
||||
return FakeQuery(sort=[desc("_date")])
|
||||
|
||||
|
||||
def set_payment_flagged_link(link):
|
||||
with CachedQueryMutator() as m:
|
||||
q = get_payment_flagged_links()
|
||||
m.insert(q, [link])
|
||||
|
||||
|
||||
def unset_payment_flagged_link(link):
|
||||
with CachedQueryMutator() as m:
|
||||
q = get_payment_flagged_links()
|
||||
m.delete(q, [link])
|
||||
|
||||
|
||||
@cached_query(UserQueryCache)
|
||||
def get_underdelivered_campaigns():
|
||||
return FakeQuery(sort=[desc("_date")])
|
||||
|
||||
@@ -459,7 +459,7 @@ module["reddit"] = LocalizedModule("reddit.js",
|
||||
"multi.js",
|
||||
"filter.js",
|
||||
"recommender.js",
|
||||
"report.js",
|
||||
"action-forms.js",
|
||||
"saved.js",
|
||||
"messages.js",
|
||||
PermissionsDataSource({
|
||||
|
||||
@@ -338,6 +338,10 @@ class Reddit(Templated):
|
||||
panes = [ShareLink(), content, report_form]
|
||||
if self.show_sidebar:
|
||||
panes.extend([gold_comment, gold_link])
|
||||
|
||||
if c.user_is_sponsor:
|
||||
panes.append(FraudForm())
|
||||
|
||||
self._content = PaneStack(panes)
|
||||
else:
|
||||
self._content = content
|
||||
@@ -2732,6 +2736,10 @@ class ReportForm(Templated):
|
||||
pass
|
||||
|
||||
|
||||
class FraudForm(Templated):
|
||||
pass
|
||||
|
||||
|
||||
class Password(Templated):
|
||||
"""Form encountered when 'recover password' is clicked in the LoginFormWide."""
|
||||
def __init__(self, success=False):
|
||||
@@ -3728,6 +3736,7 @@ class PromotePage(Reddit):
|
||||
NavButton('underdelivered', '/sponsor/promoted/underdelivered'),
|
||||
NavButton('house ads', '/sponsor/promoted/house'),
|
||||
NavButton('reported links', '/sponsor/promoted/reported'),
|
||||
NavButton('fraud', '/sponsor/promoted/fraud'),
|
||||
NavButton('lookup user', '/sponsor/lookup_user'),
|
||||
]
|
||||
return NavMenu(buttons, type='flatlist')
|
||||
|
||||
@@ -112,8 +112,12 @@ class LinkButtons(PrintableButtons):
|
||||
kw = dict(promo_url = promo_edit_url(thing),
|
||||
promote_status = getattr(thing, "promote_status", 0),
|
||||
user_is_sponsor = c.user_is_sponsor,
|
||||
traffic_url = promo_traffic_url(thing),
|
||||
is_author = thing.is_author)
|
||||
traffic_url = promo_traffic_url(thing),
|
||||
is_author = thing.is_author,
|
||||
)
|
||||
|
||||
if c.user_is_sponsor:
|
||||
kw["is_awaiting_fraud_review"] = is_awaiting_fraud_review(thing)
|
||||
|
||||
PrintableButtons.__init__(self, 'linkbuttons', thing,
|
||||
# user existence and preferences
|
||||
|
||||
@@ -41,11 +41,7 @@ from r2.lib import (
|
||||
hooks,
|
||||
)
|
||||
from r2.lib.db.operators import not_
|
||||
from r2.lib.db.queries import (
|
||||
set_promote_status,
|
||||
set_underdelivered_campaigns,
|
||||
unset_underdelivered_campaigns,
|
||||
)
|
||||
from r2.lib.db import queries
|
||||
from r2.lib.cache import sgm
|
||||
from r2.lib.memoize import memoize
|
||||
from r2.lib.strings import strings
|
||||
@@ -137,6 +133,9 @@ def refund_url(link, campaign):
|
||||
|
||||
# booleans
|
||||
|
||||
def is_awaiting_fraud_review(link):
|
||||
return link.payment_flagged_reason and link.fraud == None
|
||||
|
||||
def is_promo(link):
|
||||
return (link and not link._deleted and link.promoted is not None
|
||||
and hasattr(link, "promote_status"))
|
||||
@@ -216,7 +215,7 @@ def add_trackers(items, sr):
|
||||
|
||||
|
||||
def update_promote_status(link, status):
|
||||
set_promote_status(link, status)
|
||||
queries.set_promote_status(link, status)
|
||||
hooks.get_hook('promote.edit_promotion').call(link=link)
|
||||
|
||||
|
||||
@@ -487,10 +486,25 @@ def accept_promotion(link):
|
||||
all_live_promo_srnames(_update=True)
|
||||
|
||||
|
||||
def flag_payment(link, reason="Unknown reason."):
|
||||
link.payment_flagged = reason
|
||||
def flag_payment(link, reason):
|
||||
# already determined to be fraud.
|
||||
if link.payment_flagged_reason and link.fraud:
|
||||
return
|
||||
|
||||
link.payment_flagged_reason = reason
|
||||
link._commit()
|
||||
PromotionLog.add(link, "payment flagged: %s" % reason)
|
||||
queries.set_payment_flagged_link(link)
|
||||
|
||||
|
||||
def review_fraud(link, is_fraud):
|
||||
link.fraud = is_fraud
|
||||
link._commit()
|
||||
PromotionLog.add(link, "marked as fraud" if is_fraud else "resolved as not fraud")
|
||||
queries.unset_payment_flagged_link(link)
|
||||
|
||||
if is_fraud:
|
||||
hooks.get_hook("promote.fraud_identified").call(link=link, sponsor=c.user)
|
||||
|
||||
|
||||
def reject_promotion(link, reason=None):
|
||||
@@ -724,7 +738,7 @@ def finalize_completed_campaigns(daysago=1):
|
||||
underdelivered_campaigns.append(camp)
|
||||
|
||||
if underdelivered_campaigns:
|
||||
set_underdelivered_campaigns(underdelivered_campaigns)
|
||||
queries.set_underdelivered_campaigns(underdelivered_campaigns)
|
||||
|
||||
|
||||
def get_refund_amount(camp, billable):
|
||||
@@ -758,7 +772,7 @@ def refund_campaign(link, camp, billable_amount, billable_impressions):
|
||||
PromotionLog.add(link, text)
|
||||
camp.refund_amount = refund_amount
|
||||
camp._commit()
|
||||
unset_underdelivered_campaigns(camp)
|
||||
queries.unset_underdelivered_campaigns(camp)
|
||||
emailer.refunded_promo(link)
|
||||
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ class Link(Thing, Printable):
|
||||
media_autoplay=False,
|
||||
domain_override=None,
|
||||
promoted=None,
|
||||
payment_flagged=None,
|
||||
payment_flagged_reason=None,
|
||||
fraud=None,
|
||||
managed_promo=False,
|
||||
pending=False,
|
||||
disable_comments=False,
|
||||
|
||||
@@ -10414,7 +10414,7 @@ body.with-listing-chooser {
|
||||
}
|
||||
}
|
||||
|
||||
.report-form {
|
||||
.action-form {
|
||||
display: none;
|
||||
background-color: #f6e69f;
|
||||
border: thin solid #d8bb3c;
|
||||
@@ -10424,13 +10424,13 @@ body.with-listing-chooser {
|
||||
font-size: larger;
|
||||
|
||||
input {
|
||||
margin: 5px 0;
|
||||
|
||||
&[type="radio"] {
|
||||
margin: 2px 0.5em 0 0;
|
||||
}
|
||||
|
||||
&[name="other_reason"] {
|
||||
&[type="text"] {
|
||||
margin-top: 5px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
@@ -10438,6 +10438,10 @@ body.with-listing-chooser {
|
||||
background: #dddddd;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.reported-stamp.has-reasons {
|
||||
|
||||
125
r2/r2/public/static/js/action-forms.js
Normal file
125
r2/r2/public/static/js/action-forms.js
Normal file
@@ -0,0 +1,125 @@
|
||||
r.actionForm = {
|
||||
init: function() {
|
||||
$('div.content').on(
|
||||
'click',
|
||||
'.action-thing, .cancel-action-thing',
|
||||
this.toggleActionForm.bind(this)
|
||||
);
|
||||
|
||||
$('div.content').on(
|
||||
'submit',
|
||||
'.action-form',
|
||||
this.submitAction.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
toggleActionForm: function(e) {
|
||||
var el = e.target;
|
||||
var $el = $(el);
|
||||
var $thing = $el.thing();
|
||||
var $thingForm = $thing.find('> .entry .action-form');
|
||||
var formSelector = $el.data('action-form');
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if ($thingForm.length > 0) {
|
||||
$thingForm.toggle();
|
||||
} else {
|
||||
var $form = $(formSelector);
|
||||
var $clonedForm = $form.clone();
|
||||
var $insertionPoint = $thing.find('> .entry .buttons');
|
||||
var thingFullname = $thing.thing_id();
|
||||
|
||||
$clonedForm.attr('id', 'action-thing-' + thingFullname);
|
||||
$clonedForm.find('input[name="thing_id"]').val(thingFullname);
|
||||
$clonedForm.insertAfter($insertionPoint);
|
||||
$clonedForm.show();
|
||||
}
|
||||
},
|
||||
|
||||
submitAction: function(e) {
|
||||
var $actionForm = $(e.target).thing().find('.action-form');
|
||||
var action = $actionForm.data('form-action');
|
||||
|
||||
return post_pseudo_form($actionForm, action);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
r.fraud = {
|
||||
|
||||
init: function() {
|
||||
$('div.content').on(
|
||||
'change',
|
||||
'.fraud-action-form input',
|
||||
this.validate.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
validate: function(e) {
|
||||
var $el = $(e.target);
|
||||
var $form = $el.parents('form');
|
||||
var $submit = $form.find('[type="submit"]');
|
||||
var $refund = $form.find('input[name=refund]');
|
||||
var fraud = $form.find('input[name=fraud]:checked').val();
|
||||
var allowRefund = fraud === 'True';
|
||||
|
||||
if (allowRefund) {
|
||||
$refund.removeAttr('disabled').focus();
|
||||
} else {
|
||||
$refund.prop('checked', false).attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
if (!!fraud) {
|
||||
$submit.removeAttr('disabled');
|
||||
} else {
|
||||
$submit.attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
r.report = {
|
||||
|
||||
init: function() {
|
||||
$('div.content').on(
|
||||
'change',
|
||||
'.report-action-form input',
|
||||
this.validate.bind(this)
|
||||
);
|
||||
|
||||
$('div.content').on(
|
||||
'click',
|
||||
'.reported-stamp.has-reasons',
|
||||
this.toggleReasons.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
toggleReasons: function(e) {
|
||||
$(e.target).parent().find('.report-reasons').toggle();
|
||||
},
|
||||
|
||||
validate: function(e) {
|
||||
var $thing = $(e.target).thing();
|
||||
var $form = $thing.find('> .entry .report-action-form');
|
||||
var $submit = $form.find('[type="submit"]');
|
||||
var $reason = $form.find('[name=reason]:checked');
|
||||
var $other = $form.find('[name="other_reason"]');
|
||||
var isOther = $reason.val() === 'other';
|
||||
|
||||
$submit.removeAttr('disabled');
|
||||
|
||||
if (isOther) {
|
||||
$other.removeAttr('disabled').focus();
|
||||
} else {
|
||||
$other.attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$(function () {
|
||||
r.actionForm.init();
|
||||
r.fraud.init();
|
||||
r.report.init();
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
r.report = {
|
||||
"init": function() {
|
||||
$('div.content').on(
|
||||
'click',
|
||||
'.report-thing, button.cancel-report-thing',
|
||||
$.proxy(this, 'toggleReportForm')
|
||||
);
|
||||
|
||||
$('div.content').on(
|
||||
'submit',
|
||||
'form.report-form',
|
||||
$.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).thing().find(".report-form")
|
||||
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").focus();
|
||||
} else {
|
||||
$otherInput.attr("disabled", "disabled");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
r.report.init();
|
||||
});
|
||||
47
r2/r2/templates/fraudform.html
Normal file
47
r2/r2/templates/fraudform.html
Normal file
@@ -0,0 +1,47 @@
|
||||
## 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.
|
||||
###############################################################################
|
||||
|
||||
<form id="fraud-action-form" class="action-form fraud-action-form rounded" data-form-action="review_fraud">
|
||||
<input type="hidden" name="thing_id" value="thing-fullname">
|
||||
<span class="reason-prompt">
|
||||
${_('is this fraud?')}
|
||||
</span>
|
||||
<ol>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="fraud" value="True">${_("yes")}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="fraud" value="False">${_("no")}
|
||||
</label>
|
||||
</li>
|
||||
</ol>
|
||||
<button type="submit" class="btn submit-action-thing" disabled>
|
||||
${_("submit")}
|
||||
</button>
|
||||
<button type="button" class="btn cancel-action-thing">
|
||||
${_("cancel")}
|
||||
</button>
|
||||
<span class="status"></span>
|
||||
</form>
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
%if thing.show_report:
|
||||
<li class="report-button">
|
||||
<a href="javascript:void(0)" class="report-thing">
|
||||
<a href="javascript:void(0)" class="action-thing" data-action-form="#report-action-form">
|
||||
${_("report")}
|
||||
</a>
|
||||
</li>
|
||||
@@ -292,6 +292,13 @@
|
||||
${ynbutton(_("accept"), _("accepted"), "promote")}
|
||||
</li>
|
||||
%endif
|
||||
%if thing.is_awaiting_fraud_review:
|
||||
<li class="fraud-button">
|
||||
<a href="javascript:void(0)" class="action-thing" data-action-form="#fraud-action-form">
|
||||
${_("fraud")}
|
||||
</a>
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
%if thing.user_is_sponsor or thing.is_author:
|
||||
<li>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<%namespace file="utils.html" import="error_field" />
|
||||
|
||||
<form id="report-thing-fullname" class="report-form clonable rounded">
|
||||
<form id="report-action-form" class="action-form report-action-form rounded" data-form-action="report">
|
||||
<input type="hidden" name="thing_id" value="thing-fullname">
|
||||
<span class="reason-prompt">
|
||||
${_('why are you reporting this?')}
|
||||
@@ -30,42 +30,41 @@
|
||||
<ol>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="reason" value="spam">${_("spam")}</input>
|
||||
<input type="radio" name="reason" value="spam">${_("spam")}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="reason" value="vote manipulation">${_("vote manipulation")}</input>
|
||||
<input type="radio" name="reason" value="vote manipulation">${_("vote manipulation")}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="reason" value="personal information">${_("personal information")}</input>
|
||||
<input type="radio" name="reason" value="personal information">${_("personal information")}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="reason" value="sexualizing minors">${_("sexualizing minors")}</input>
|
||||
<input type="radio" name="reason" value="sexualizing minors">${_("sexualizing minors")}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="reason" value="breaking reddit">${_("breaking reddit")}</input>
|
||||
<input type="radio" name="reason" value="breaking reddit">${_("breaking reddit")}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" name="reason" value="other">
|
||||
${_("other (max %(num)s characters):") % dict(num=100)}
|
||||
</input>
|
||||
${_("other (max %(num)s characters):") % dict(num=100)}
|
||||
</label>
|
||||
<input name="other_reason" value="" maxlength="100" type="text" disabled="disabled"></input>
|
||||
<input name="other_reason" value="" maxlength="100" type="text" disabled>
|
||||
</li>
|
||||
</ol>
|
||||
<button type="submit" class="btn submit-report" disabled="disabled">
|
||||
<button type="submit" class="btn submit-action-thing" disabled>
|
||||
${_("submit")}
|
||||
</button>
|
||||
<button type="button" class="btn cancel-report-thing">
|
||||
<button type="button" class="btn cancel-action-thing">
|
||||
${_("cancel")}
|
||||
</button>
|
||||
<span class="status"></span>
|
||||
|
||||
Reference in New Issue
Block a user