license_id = db.Column(db.Integer, db.ForeignKey("license.id"))
approved = db.Column(db.Boolean, nullable=False, default=False)
+ soft_deleted = db.Column(db.Boolean, nullable=False, default=False)
# Downloads
repo = db.Column(db.String(200), nullable=True)
return url_for("approve_package_page",
author=self.author.username, name=self.name)
+ def getDeleteURL(self):
+ return url_for("delete_package_page",
+ author=self.author.username, name=self.name)
+
def getNewScreenshotURL(self):
return url_for("create_screenshot_page",
author=self.author.username, name=self.name)
if user is None:
continue
- dep = Package.query.filter_by(author=user, name=value).first()
+ dep = Package.query.filter_by(author=user, name=value, soft_deleted=False).first()
if dep is None:
continue
color: #fff;
}
-.alert-error {
- background: #933;
- border: 1px solid #c44;
+.alert-error, .button-danger {
+ background: #933 !important;
+ border: 1px solid #c44 !important;
}
.alert-warning {
@celery.task()
def importRepoScreenshot(id):
package = Package.query.get(id)
- if package is None:
+ if package is None or package.soft_deleted:
raise Exception("Unexpected none package")
# Get URL Maker
<option value="importusers" selected>Create users from mod list</option>
<option value="importscreenshots">Import screenshots from VCS</option>
</select>
- <input type="submit" value="Start" />
+ <input type="submit" value="Perform" />
+ </form>
+ </div>
+
+ <div class="box box_grey">
+ <h2>Restore Package</h2>
+
+ <form method="post" action="" class="box-body">
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
+ <input type="hidden" name="action" value="restore" />
+ <select name="package">
+ {% for p in deleted_packages %}
+ <option value={{ p.id }}>{{ p.id}}) {{ p.title }} by {{ p.author.display_name }}</option>
+ {% endfor %}
+ </select>
+ <input type="submit" value="Restore" />
</form>
</div>
{% endblock %}
--- /dev/null
+{% extends "base.html" %}
+
+{% block title %}
+ Delete | {{ package.title }}
+{% endblock %}
+
+{% block content %}
+ <form method="POST" action="" class="box box_grey ">
+ <h3>Delete Package</h3>
+
+ <div class="box-body">
+ <p>This action can be undone by the admin, but he'll be very annoyed!</p>
+
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
+ <input type="submit" value="Delete" class="button-danger" />
+ </div>
+ </form>
+{% endblock %}
{% if package.checkPerm(current_user, "MAKE_RELEASE") %}
<li><a href="{{ package.getCreateReleaseURL() }}">Create Release</a></li>
{% endif %}
+ {% if package.checkPerm(current_user, "DELETE_PACKAGE") %}
+ <li><a href="{{ package.getDeleteURL() }}">Delete</a></li>
+ {% endif %}
</ul>
</div>
</aside>
if user is None:
abort(404)
- package = Package.query.filter_by(name=name, author_id=user.id).first()
+ package = Package.query.filter_by(name=name, author_id=user.id, soft_deleted=False).first()
if package is None:
abort(404)
@app.route("/")
@menu.register_menu(app, ".", "Home")
def home_page():
- query = Package.query.filter_by(approved=True)
+ query = Package.query.filter_by(approved=True, soft_deleted=False)
count = query.count()
packages = query.order_by(db.desc(Package.created_at)).limit(15).all()
return render_template("index.html", packages=packages, count=count)
elif action == "importscreenshots":
packages = Package.query \
.outerjoin(PackageScreenshot, Package.id==PackageScreenshot.package_id) \
- .filter(PackageScreenshot.id==None).all()
+ .filter(PackageScreenshot.id==None) \
+ .filter_by(soft_deleted=False).all()
for package in packages:
importRepoScreenshot.delay(package.id)
return redirect(url_for("admin_page"))
+ elif action == "restore":
+ package = Package.query.get(request.form["package"])
+ if package is None:
+ flash("Unknown package", "error")
+ else:
+ package.soft_deleted = False
+ db.session.commit()
+ return redirect(url_for("admin_page"))
else:
flash("Unknown action: " + action, "error")
- return render_template("admin/list.html")
+ deleted_packages = Package.query.filter_by(soft_deleted=True).all()
+ return render_template("admin/list.html", deleted_packages=deleted_packages)
class SwitchUserForm(FlaskForm):
username = StringField("Username")
type = PackageType[type.upper()]
title = "Packages"
- query = Package.query
+ query = Package.query.filter_by(soft_deleted=False)
if type is not None:
title = type.value + "s"
type = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
license = QuerySelectField("License", [InputRequired()], query_factory=lambda: License.query, get_pk=lambda a: a.id, get_label=lambda a: a.name)
tags = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
- harddeps = QuerySelectMultipleField('Dependencies', query_factory=lambda: Package.query.join(User).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
- softdeps = QuerySelectMultipleField('Soft Dependencies', query_factory=lambda: Package.query.join(User).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
+ harddeps = QuerySelectMultipleField('Dependencies', query_factory=lambda: Package.query.join(User).filter_by(soft_deleted=False,approved=True).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
+ softdeps = QuerySelectMultipleField('Soft Dependencies', query_factory=lambda: Package.query.join(User).filter_by(soft_deleted=False,approved=True).order_by(db.asc(Package.title), db.asc(User.display_name)), get_pk=lambda a: a.id, get_label=lambda a: a.title + " by " + a.author.display_name)
repo = StringField("Repo URL", [Optional(), URL()])
website = StringField("Website URL", [Optional(), URL()])
issueTracker = StringField("Issue Tracker URL", [Optional(), URL()])
if not package:
package = Package.query.filter_by(name=form["name"].data, author_id=author.id).first()
if package is not None:
- flash("Package already exists!", "error")
- return redirect(url_for("create_edit_package_page"))
+ if package.soft_deleted:
+ package.delete()
+ else:
+ flash("Package already exists!", "error")
+ return redirect(url_for("create_edit_package_page"))
package = Package()
package.author = author
return redirect(package.getDetailsURL())
+
+@app.route("/packages/<author>/<name>/delete/", methods=["GET", "POST"])
+@login_required
+@is_package_page
+def delete_package_page(package):
+ if request.method == "GET":
+ return render_template("packages/delete.html", package=package)
+
+ if not package.checkPerm(current_user, Permission.DELETE_PACKAGE):
+ flash("You don't have permission to do that.", "error")
+
+ package.soft_deleted = True
+
+ url = url_for("user_profile_page", username=package.author.username)
+ triggerNotif(package.author, current_user,
+ "{} deleted".format(package.title), url)
+ db.session.commit()
+
+ flash("Deleted package", "success")
+
+ return redirect(url)
+
from . import todo, screenshots, editrequests, releases
packages = None
if canApproveNew:
- packages = Package.query.filter_by(approved=False).all()
+ packages = Package.query.filter_by(approved=False, soft_deleted=False).all()
releases = None
if canApproveRel:
--- /dev/null
+"""empty message
+
+Revision ID: c4152f4240ed
+Revises: 3f4d7cd8401f
+Create Date: 2018-05-25 18:27:16.953305
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'c4152f4240ed'
+down_revision = '3f4d7cd8401f'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('package', sa.Column('soft_deleted', sa.Boolean(), nullable=False))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('package', 'soft_deleted')
+ # ### end Alembic commands ###