Expose soft delete to users and allow reuploading of deleted torrents. (#331)

Add banning torrents for moderators which disallows reuploading.
New delete UI.
This commit is contained in:
A nyaa developer 2017-08-05 21:41:59 +02:00 committed by Arylide
parent 81d8b0f86b
commit e728ca1818
5 changed files with 160 additions and 14 deletions

View File

@ -45,6 +45,11 @@ def _replace_utf8_values(dict_or_list):
def handle_torrent_upload(upload_form, uploading_user=None, fromAPI=False):
torrent_data = upload_form.torrent_file.parsed_data
# Delete exisiting torrent which is marked as deleted
if torrent_data.db_id is not None:
models.Torrent.query.filter_by(id=torrent_data.db_id).delete()
db.session.commit()
# The torrent has been validated and is safe to access with ['foo'] etc - all relevant
# keys and values have been checked for (see UploadForm in forms.py for details)
info_dict = torrent_data.torrent_dict['info']
@ -62,7 +67,8 @@ def handle_torrent_upload(upload_form, uploading_user=None, fromAPI=False):
# In case no encoding, assume UTF-8.
torrent_encoding = torrent_data.torrent_dict.get('encoding', b'utf-8').decode('utf-8')
torrent = models.Torrent(info_hash=torrent_data.info_hash,
torrent = models.Torrent(id=torrent_data.db_id,
info_hash=torrent_data.info_hash,
display_name=display_name,
torrent_name=torrent_data.filename,
information=information,

View File

@ -8,7 +8,7 @@ from flask_wtf.file import FileField, FileRequired
from flask_wtf.recaptcha import RecaptchaField
from flask_wtf.recaptcha.validators import Recaptcha as RecaptchaValidator
from wtforms import (BooleanField, HiddenField, PasswordField, SelectField, StringField,
TextAreaField)
SubmitField, TextAreaField)
from wtforms.validators import (DataRequired, Email, EqualTo, Length, Optional, Regexp,
StopValidation, ValidationError)
from wtforms.widgets import Select as SelectWidget # For DisabledSelectField
@ -186,6 +186,13 @@ class EditForm(FlaskForm):
])
class DeleteForm(FlaskForm):
delete = SubmitField("Delete")
ban = SubmitField("Delete & Ban")
undelete = SubmitField("Undelete")
unban = SubmitField("Unban")
class UploadForm(FlaskForm):
torrent_file = FileField('Torrent file', [
FileRequired()
@ -281,14 +288,18 @@ class UploadForm(FlaskForm):
# Check if the info_hash exists already in the database
existing_torrent = models.Torrent.by_info_hash(info_hash)
if existing_torrent:
raise ValidationError('That torrent already exists (#{})'.format(existing_torrent.id))
existing_torrent_id = existing_torrent.id if existing_torrent else None
if existing_torrent and not existing_torrent.deleted:
raise ValidationError('This torrent already exists (#{})'.format(existing_torrent.id))
if existing_torrent and existing_torrent.banned:
raise ValidationError('This torrent is banned'.format(existing_torrent.id))
# Torrent is legit, pass original filename and dict along
field.parsed_data = TorrentFileData(filename=os.path.basename(field.data.filename),
torrent_dict=torrent_dict,
info_hash=info_hash,
bencoded_info_dict=bencoded_info_dict)
bencoded_info_dict=bencoded_info_dict,
db_id=existing_torrent_id)
class UserForm(FlaskForm):

View File

@ -98,6 +98,7 @@ class TorrentFlags(IntEnum):
REMAKE = 8
COMPLETE = 16
DELETED = 32
BANNED = 64
class TorrentBase(DeclarativeHelperBase):
@ -250,6 +251,7 @@ class TorrentBase(DeclarativeHelperBase):
anonymous = FlagProperty(TorrentFlags.ANONYMOUS)
hidden = FlagProperty(TorrentFlags.HIDDEN)
deleted = FlagProperty(TorrentFlags.DELETED)
banned = FlagProperty(TorrentFlags.BANNED)
trusted = FlagProperty(TorrentFlags.TRUSTED)
remake = FlagProperty(TorrentFlags.REMAKE)
complete = FlagProperty(TorrentFlags.COMPLETE)

View File

@ -15,7 +15,6 @@
<form method="POST" enctype="multipart/form-data">
{{ form.csrf_token }}
<div class="row">
<div class="col-md-6">
{{ render_field(form.display_name, class_='form-control', placeholder='Display name') }}
@ -46,14 +45,6 @@
<span class="glyphicon glyphicon-unchecked"></span>
Hidden
</label>
{% if g.user.is_moderator %}
<label class="btn btn-primary {% if torrent.deleted %}active{% endif %}">
{{ form.is_deleted }}
<span class="glyphicon glyphicon-check"></span>
<span class="glyphicon glyphicon-unchecked"></span>
Deleted
</label>
{% endif %}
</div>
<div class="hidden-xl"><br></div>
<div class="btn-group" data-toggle="buttons">
@ -93,4 +84,75 @@
</div>
</div>
</form>
<hr>
<div class="row">
<div class="col-md-5"">
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">Danger Zone</h3>
</div>
<div class="panel-body">
<form method="POST" action="{{ url_for('torrents.delete', torrent_id=torrent.id) }}">
{{ delete_form.csrf_token }}
{% if not torrent.deleted %}
<p class="lead">
Delete torrent.
{{ delete_form.delete(class="btn btn-danger pull-right") }}
</p>
<p>
Deleted torrents are retained for backup purposes.<br>
You (or someone else) will be able to reupload the torrent.
</p>
{% if g.user.is_moderator %}
<hr>
<p class="lead">
Delete and ban torrent.
{{ delete_form.ban(class="btn btn-danger pull-right") }}
</p>
<p>
Soft deletes the torrent and disallows reuploading it.
</p>
{% endif %}
{% else %}
<p>This torrent is <strong>deleted</strong>{% if torrent.banned %} and <strong>banned</strong>{% endif %}.</p>
<p class="lead">
Undelete{% if torrent.banned %} and unban{% endif %} torrent.
{{ delete_form.undelete(class="btn btn-info pull-right") }}
</p>
<p>
Undeletes{% if torrent.banned %} and unbans{% endif %} this torrent.
</p>
{% if torrent.banned %}
<hr>
<p class="lead">
Unban torrent.
{{ delete_form.unban(class="btn btn-info pull-right") }}
</p>
<p>
Unbans torrent without undeleting it.<br>
Allows reuploading this torrent again.
</p>
{% else%}
<hr>
<p class="lead">
Ban torrent.
{{ delete_form.ban(value="Ban", class="btn btn-danger pull-right") }}
</p>
<p>
Bans the torrent.<br>
Disallows reuploading this torrent.
</p>
{% endif %}
{% endif %}
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -80,6 +80,7 @@ def view_torrent(torrent_id):
def edit_torrent(torrent_id):
torrent = models.Torrent.by_id(torrent_id)
form = forms.EditForm(flask.request.form)
delete_form = forms.DeleteForm()
form.category.choices = _create_upload_category_choices()
editor = flask.g.user
@ -147,9 +148,73 @@ def edit_torrent(torrent_id):
return flask.render_template('edit.html',
form=form,
delete_form=delete_form,
torrent=torrent)
@bp.route('/view/<int:torrent_id>/delete', endpoint='delete', methods=['POST'])
def delete_torrent(torrent_id):
torrent = models.Torrent.by_id(torrent_id)
form = forms.DeleteForm(flask.request.form)
editor = flask.g.user
if not torrent:
flask.abort(404)
# Only allow admins edit deleted torrents
if torrent.deleted and not (editor and editor.is_moderator):
flask.abort(404)
# Only allow torrent owners or admins edit torrents
if not editor or not (editor is torrent.user or editor.is_moderator):
flask.abort(403)
action = None
url = flask.url_for('main.home')
if form.delete.data and not torrent.deleted:
action = 'deleted'
torrent.deleted = True
db.session.add(torrent)
elif form.ban.data and not torrent.banned and editor.is_moderator:
torrent.banned = True
if not torrent.deleted:
torrent.deleted = True
action = 'deleted and banned'
else:
action = 'banned'
db.session.add(torrent)
elif form.undelete.data and torrent.deleted:
action = 'undeleted'
torrent.deleted = False
torrent.banned = False
db.session.add(torrent)
elif form.unban.data and torrent.banned:
action = 'unbanned'
torrent.banned = False
db.session.add(torrent)
if not action:
flask.flash(flask.Markup('What the fuck are you doing?'), 'danger')
return flask.redirect(flask.url_for('torrents.edit', torrent_id=torrent.id))
if editor.is_moderator:
url = flask.url_for('torrents.view', torrent_id=torrent.id)
if editor is not torrent.user:
log = "Torrent [#{0}]({1}) has been {2}".format(torrent.id, url, action)
adminlog = models.AdminLog(log=log, admin_id=editor.id)
db.session.add(adminlog)
db.session.commit()
flask.flash(flask.Markup('Torrent has been successfully {0}.'.format(action)), 'info')
return flask.redirect(url)
@bp.route('/view/<int:torrent_id>/magnet')
def redirect_magnet(torrent_id):
torrent = models.Torrent.by_id(torrent_id)