mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 22:12:24 +01:00
parent
a47e6e8998
commit
7f5656df08
66
app/blueprints/report/__init__.py
Normal file
66
app/blueprints/report/__init__.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# ContentDB
|
||||||
|
# Copyright (C) 2022 rubenwardy
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from flask import Blueprint, request, render_template, url_for
|
||||||
|
from flask_babel import lazy_gettext
|
||||||
|
from flask_login import current_user
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from werkzeug.utils import redirect
|
||||||
|
from wtforms import TextAreaField, SubmitField, BooleanField
|
||||||
|
from wtforms.fields.html5 import URLField
|
||||||
|
from wtforms.validators import InputRequired, Optional, Length
|
||||||
|
|
||||||
|
from app.models import User, UserRank
|
||||||
|
from app.tasks.emails import send_user_email
|
||||||
|
from app.tasks.webhooktasks import post_discord_webhook
|
||||||
|
from app.utils import isYes, isNo
|
||||||
|
|
||||||
|
bp = Blueprint("report", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportForm(FlaskForm):
|
||||||
|
url = URLField(lazy_gettext("URL"), [Optional()])
|
||||||
|
message = TextAreaField(lazy_gettext("Message"), [InputRequired(), Length(10, 10000)])
|
||||||
|
submit = SubmitField(lazy_gettext("Report"))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/report/", methods=["GET", "POST"])
|
||||||
|
def report():
|
||||||
|
is_anon = not current_user.is_authenticated or not isNo(request.args.get("anon"))
|
||||||
|
|
||||||
|
form = ReportForm(formdata=request.form)
|
||||||
|
if request.method == "GET":
|
||||||
|
if "url" in request.args:
|
||||||
|
form.url.data = request.args["url"]
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
user_info = f"{current_user.username}"
|
||||||
|
else:
|
||||||
|
user_info = request.headers.get("X-Forwarded-For") or request.remote_addr
|
||||||
|
|
||||||
|
url = request.args.get("url") or form.url.data or "?"
|
||||||
|
text = f"{url}\n\n{form.message.data}"
|
||||||
|
|
||||||
|
task = None
|
||||||
|
for admin in User.query.filter_by(rank=UserRank.ADMIN).all():
|
||||||
|
task = send_user_email.delay(admin.email, f"User report from {user_info}", text)
|
||||||
|
|
||||||
|
post_discord_webhook.delay(None if is_anon else current_user.username, f"**New Report**\n`{url}`\n\n{form.message.data}", True)
|
||||||
|
|
||||||
|
return redirect(url_for("tasks.check", id=task.id, r=url_for("homepage.home")))
|
||||||
|
|
||||||
|
return render_template("report/index.html", form=form, url=request.args.get("url"), is_anon=is_anon)
|
@ -9,7 +9,7 @@ toc: False
|
|||||||
* [Non-free Licenses](non_free)
|
* [Non-free Licenses](non_free)
|
||||||
* [Why WTFPL is a terrible license](wtfpl)
|
* [Why WTFPL is a terrible license](wtfpl)
|
||||||
* [Ranks and Permissions](ranks_permissions)
|
* [Ranks and Permissions](ranks_permissions)
|
||||||
* [Reporting Content](reporting)
|
* [Contact Us](contact_us)
|
||||||
* [Top Packages Algorithm](top_packages)
|
* [Top Packages Algorithm](top_packages)
|
||||||
* [Featured Packages](featured)
|
* [Featured Packages](featured)
|
||||||
|
|
||||||
|
14
app/flatpages/help/contact_us.md
Normal file
14
app/flatpages/help/contact_us.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
title: Contact Us
|
||||||
|
|
||||||
|
## Reports
|
||||||
|
|
||||||
|
Please let us know if anything on the ContentDB violates our rules or any applicable
|
||||||
|
laws.
|
||||||
|
|
||||||
|
We take copyright violation and other offenses very seriously.
|
||||||
|
|
||||||
|
<a href="/report/" class="btn btn-primary">Report</a>
|
||||||
|
|
||||||
|
## Other
|
||||||
|
|
||||||
|
<a href="https://rubenwardy.com/contact/" class="btn btn-primary">Contact the admin</a>
|
@ -1,8 +0,0 @@
|
|||||||
title: Reporting Content
|
|
||||||
|
|
||||||
Please let us know if anything on the ContentDB violates our rules or any applicable
|
|
||||||
laws.
|
|
||||||
|
|
||||||
We take copyright violation and other offenses very seriously.
|
|
||||||
|
|
||||||
<a href="https://rubenwardy.com/contact/" class="btn btn-success">Contact</a>
|
|
@ -27,7 +27,7 @@ including ones not covered by this document, and to ban users who abuse this ser
|
|||||||
### 2.1. Acceptable Content
|
### 2.1. Acceptable Content
|
||||||
|
|
||||||
Sexually-orientated content is not permitted.
|
Sexually-orientated content is not permitted.
|
||||||
If in doubt at what this means, [contact us by raising a report](/help/reporting/).
|
If in doubt at what this means, [contact us by raising a report](/report/).
|
||||||
|
|
||||||
Mature content is permitted providing that it is labelled correctly.
|
Mature content is permitted providing that it is labelled correctly.
|
||||||
See [Content Flags](/help/content_flags/).
|
See [Content Flags](/help/content_flags/).
|
||||||
@ -153,4 +153,4 @@ Doing so may result in temporary or permanent suspension from ContentDB.
|
|||||||
|
|
||||||
## 7. Reporting Violations
|
## 7. Reporting Violations
|
||||||
|
|
||||||
See the [Reporting Content](/help/reporting/) page.
|
Please click "Report" on the package page.
|
||||||
|
@ -72,7 +72,7 @@ requested. See below.
|
|||||||
|
|
||||||
## Removal Requests
|
## Removal Requests
|
||||||
|
|
||||||
Please [raise a report](https://content.minetest.net/help/reporting/) if you
|
Please [raise a report](https://content.minetest.net/report/?anon=0) if you
|
||||||
wish to remove your personal information.
|
wish to remove your personal information.
|
||||||
|
|
||||||
ContentDB keeps a record of each username and forum topic on the forums,
|
ContentDB keeps a record of each username and forum topic on the forums,
|
||||||
|
@ -124,6 +124,9 @@ class ThreadReply(db.Model):
|
|||||||
|
|
||||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
def get_url(self):
|
||||||
|
return url_for('threads.view', id=self.thread.id) + "#reply-" + str(self.id)
|
||||||
|
|
||||||
def checkPerm(self, user, perm):
|
def checkPerm(self, user, perm):
|
||||||
if not user.is_authenticated:
|
if not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from flask import render_template
|
from flask import render_template, escape
|
||||||
from flask_mail import Message
|
from flask_mail import Message
|
||||||
from app import mail
|
from app import mail
|
||||||
from app.models import Notification, db, EmailSubscription, User
|
from app.models import Notification, db, EmailSubscription, User
|
||||||
@ -86,7 +86,7 @@ def send_email_with_reason(email, subject, text, html, reason):
|
|||||||
msg = Message(subject, recipients=[email])
|
msg = Message(subject, recipients=[email])
|
||||||
|
|
||||||
msg.body = text
|
msg.body = text
|
||||||
html = html or text
|
html = html or f"<pre>{escape(text)}</pre>"
|
||||||
msg.html = render_template("emails/base.html", subject=subject, content=html, reason=reason, sub=sub)
|
msg.html = render_template("emails/base.html", subject=subject, content=html, reason=reason, sub=sub)
|
||||||
mail.send(msg)
|
mail.send(msg)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from . import app, utils
|
from . import app, utils
|
||||||
from .models import Permission, Package, PackageState, PackageRelease
|
from .models import Permission, Package, PackageState, PackageRelease
|
||||||
from .utils import abs_url_for, url_set_query, url_set_anchor
|
from .utils import abs_url_for, url_set_query, url_set_anchor, url_current
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_babel import format_timedelta, gettext
|
from flask_babel import format_timedelta, gettext
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@ -17,7 +17,7 @@ def inject_debug():
|
|||||||
def inject_functions():
|
def inject_functions():
|
||||||
check_global_perm = Permission.checkPerm
|
check_global_perm = Permission.checkPerm
|
||||||
return dict(abs_url_for=abs_url_for, url_set_query=url_set_query, url_set_anchor=url_set_anchor,
|
return dict(abs_url_for=abs_url_for, url_set_query=url_set_query, url_set_anchor=url_set_anchor,
|
||||||
check_global_perm=check_global_perm, get_headings=get_headings)
|
check_global_perm=check_global_perm, get_headings=get_headings, url_current=url_current)
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_todo():
|
def inject_todo():
|
||||||
|
@ -234,7 +234,7 @@
|
|||||||
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='policy_and_guidance') }}">{{ _("Policy and Guidance") }}</a></li>
|
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='policy_and_guidance') }}">{{ _("Policy and Guidance") }}</a></li>
|
||||||
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='help/api') }}">{{ _("API") }}</a></li>
|
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='help/api') }}">{{ _("API") }}</a></li>
|
||||||
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='privacy_policy') }}">{{ _("Privacy Policy") }}</a></li>
|
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='privacy_policy') }}">{{ _("Privacy Policy") }}</a></li>
|
||||||
<li class="list-inline-item"><a href="{{ url_for('flatpage', path='help/reporting') }}">{{ _("Report / DMCA") }}</a></li>
|
<li class="list-inline-item"><a href="{{ url_for('report.report', url=url_current(True)) }}">{{ _("Report") }}</a></li>
|
||||||
<li class="list-inline-item"><a href="https://monitor.rubenwardy.com/d/3ELzFy3Wz/contentdb">{{ _("Stats / Monitoring") }}</a></li>
|
<li class="list-inline-item"><a href="https://monitor.rubenwardy.com/d/3ELzFy3Wz/contentdb">{{ _("Stats / Monitoring") }}</a></li>
|
||||||
<li class="list-inline-item"><a href="{{ url_for('users.list_all') }}">{{ _("User List") }}</a></li>
|
<li class="list-inline-item"><a href="{{ url_for('users.list_all') }}">{{ _("User List") }}</a></li>
|
||||||
<li class="list-inline-item"><a href="https://github.com/minetest/contentdb">{{ _("Source Code") }}</a></li>
|
<li class="list-inline-item"><a href="https://github.com/minetest/contentdb">{{ _("Source Code") }}</a></li>
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<a name="reply-{{ r.id }}" class="text-muted float-right"
|
<a name="reply-{{ r.id }}" class="text-muted float-right"
|
||||||
href="{{ url_for('threads.view', id=thread.id) }}#reply-{{ r.id }}">
|
href="{{ r.get_url() }}">
|
||||||
{{ r.created_at | datetime }}
|
{{ r.created_at | datetime }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -48,10 +48,17 @@
|
|||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if current_user != r.author %}
|
||||||
|
<a class="float-right btn-secondary btn-sm ml-2"
|
||||||
|
title="{{ _('Report') }}"
|
||||||
|
href="{{ url_for('report.report', url=r.get_url()) }}">
|
||||||
|
<i class="fas fa-flag mr-1"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if current_user == thread.author and thread.review and thread.replies[0] == r %}
|
{% if current_user == thread.author and thread.review and thread.replies[0] == r %}
|
||||||
<a class="float-right btn btn-primary btn-sm ml-2"
|
<a class="float-right btn btn-primary btn-sm ml-2"
|
||||||
href="{{ thread.review.package.getURL("packages.review") }}">
|
href="{{ thread.review.package.getURL('packages.review') }}">
|
||||||
<i class="fas fa-pen"></i>
|
<i class="fas fa-pen"></i>
|
||||||
</a>
|
</a>
|
||||||
{% elif r.checkPerm(current_user, "EDIT_REPLY") %}
|
{% elif r.checkPerm(current_user, "EDIT_REPLY") %}
|
||||||
|
@ -473,13 +473,16 @@
|
|||||||
|
|
||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
{% if package.approved and current_user != package.author %}
|
{% if package.approved and current_user != package.author %}
|
||||||
<a class="float-right"
|
<a href="{{ url_for('report.report', url=url_current(True)) }}">
|
||||||
href="{{ url_for('threads.new', pid=package.id) }}">
|
<i class="fas fa-flag mr-1"></i>
|
||||||
{{ _("Report a problem with this listing") }}
|
{{ _("Report") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if package.checkPerm(current_user, "EDIT_PACKAGE") or package.checkPerm(current_user, "APPROVE_NEW") %}
|
{% if package.checkPerm(current_user, "EDIT_PACKAGE") or package.checkPerm(current_user, "APPROVE_NEW") %}
|
||||||
<a class="float-right" href="{{ package.getURL("packages.audit") }}">
|
{% if package.approved and current_user != package.author %}
|
||||||
|
|
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ package.getURL("packages.audit") }}">
|
||||||
{{ _("See audit log") }}
|
{{ _("See audit log") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
32
app/templates/report/index.html
Normal file
32
app/templates/report/index.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{{ _("Report") }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% from "macros/forms.html" import render_field, render_submit_field, render_checkbox_field %}
|
||||||
|
<h1>{{ _("Report") }}</h1>
|
||||||
|
|
||||||
|
<form method="POST" action="" enctype="multipart/form-data">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
{% if url %}
|
||||||
|
<p>
|
||||||
|
URL: <code>{{ url }}</code>
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
{{ render_field(form.url, hint=_("URL to the thing you're reporting")) }}
|
||||||
|
{% endif %}
|
||||||
|
{{ render_field(form.message, hint=_("What are you reporting? Why are you reporting it?")) }}
|
||||||
|
{{ render_submit_field(form.submit) }}
|
||||||
|
|
||||||
|
<p class="mt-5 text-muted">
|
||||||
|
{{ _("Reports will be shared with ContentDB stuff.") }}
|
||||||
|
{% if is_anon %}
|
||||||
|
{{ _("Only the admin will be able to see who made the report.") }}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -21,7 +21,12 @@
|
|||||||
<i class="fas fa-tasks mr-1"></i>
|
<i class="fas fa-tasks mr-1"></i>
|
||||||
{{ _("To Do List") }}
|
{{ _("To Do List") }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<a class="btn btn-secondary float-right mr-3" href="{{ url_for('report.report', url=url_current(True)) }}">
|
||||||
|
<i class="fas fa-flag mr-1"></i>
|
||||||
|
{{ _("Report") }}
|
||||||
|
</a>
|
||||||
|
|
||||||
{% if current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.MODERATOR) and user.email %}
|
{% if current_user.is_authenticated and current_user.rank.atLeast(current_user.rank.MODERATOR) and user.email %}
|
||||||
<a class="btn btn-secondary float-right mr-3" href="{{ url_for('admin.send_single_email', username=user.username) }}">
|
<a class="btn btn-secondary float-right mr-3" href="{{ url_for('admin.send_single_email', username=user.username) }}">
|
||||||
|
@ -40,6 +40,15 @@ def abs_url_for(path, **kwargs):
|
|||||||
def abs_url(path):
|
def abs_url(path):
|
||||||
return urljoin(app.config["BASE_URL"], path)
|
return urljoin(app.config["BASE_URL"], path)
|
||||||
|
|
||||||
|
def url_current(abs=False):
|
||||||
|
args = MultiDict(request.args)
|
||||||
|
dargs = dict(args.lists())
|
||||||
|
dargs.update(request.view_args)
|
||||||
|
if abs:
|
||||||
|
return abs_url_for(request.endpoint, **dargs)
|
||||||
|
else:
|
||||||
|
return url_for(request.endpoint, **dargs)
|
||||||
|
|
||||||
def url_set_anchor(anchor):
|
def url_set_anchor(anchor):
|
||||||
args = MultiDict(request.args)
|
args = MultiDict(request.args)
|
||||||
dargs = dict(args.lists())
|
dargs = dict(args.lists())
|
||||||
|
Loading…
Reference in New Issue
Block a user