diff --git a/app/models.py b/app/models.py
index c0c9a095..9118f78c 100644
--- a/app/models.py
+++ b/app/models.py
@@ -261,6 +261,7 @@ class Package(db.Model):
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)
@@ -329,6 +330,10 @@ class Package(db.Model):
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)
@@ -546,7 +551,7 @@ class EditRequestChange(db.Model):
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
diff --git a/app/scss/components.scss b/app/scss/components.scss
index 3a145bc5..226ef6b2 100644
--- a/app/scss/components.scss
+++ b/app/scss/components.scss
@@ -301,9 +301,9 @@ select:not([multiple]) {
color: #fff;
}
-.alert-error {
- background: #933;
- border: 1px solid #c44;
+.alert-error, .button-danger {
+ background: #933 !important;
+ border: 1px solid #c44 !important;
}
.alert-warning {
diff --git a/app/tasks/importtasks.py b/app/tasks/importtasks.py
index 352736e7..a20bbee8 100644
--- a/app/tasks/importtasks.py
+++ b/app/tasks/importtasks.py
@@ -231,7 +231,7 @@ def makeVCSRelease(id, branch):
@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
diff --git a/app/templates/admin/list.html b/app/templates/admin/list.html
index 7b664584..373f18b5 100644
--- a/app/templates/admin/list.html
+++ b/app/templates/admin/list.html
@@ -19,7 +19,22 @@
-
+
+
+
+
+
+
Restore Package
+
+
{% endblock %}
diff --git a/app/templates/packages/delete.html b/app/templates/packages/delete.html
new file mode 100644
index 00000000..95709c39
--- /dev/null
+++ b/app/templates/packages/delete.html
@@ -0,0 +1,18 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Delete | {{ package.title }}
+{% endblock %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/app/templates/packages/view.html b/app/templates/packages/view.html
index c3dbb34b..8d329ef7 100644
--- a/app/templates/packages/view.html
+++ b/app/templates/packages/view.html
@@ -91,6 +91,9 @@
{% if package.checkPerm(current_user, "MAKE_RELEASE") %}
Create Release
{% endif %}
+ {% if package.checkPerm(current_user, "DELETE_PACKAGE") %}
+ Delete
+ {% endif %}
diff --git a/app/utils.py b/app/utils.py
index e42c3cc0..9be70d6c 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -116,7 +116,7 @@ def getPackageByInfo(author, name):
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)
diff --git a/app/views/__init__.py b/app/views/__init__.py
index a296e5df..abaacd71 100644
--- a/app/views/__init__.py
+++ b/app/views/__init__.py
@@ -38,7 +38,7 @@ def send_upload(path):
@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)
diff --git a/app/views/admin.py b/app/views/admin.py
index d0dcf733..6c8c4ef0 100644
--- a/app/views/admin.py
+++ b/app/views/admin.py
@@ -37,15 +37,25 @@ def admin_page():
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")
diff --git a/app/views/packages/__init__.py b/app/views/packages/__init__.py
index c2806665..340dab7a 100644
--- a/app/views/packages/__init__.py
+++ b/app/views/packages/__init__.py
@@ -43,7 +43,7 @@ def packages_page():
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"
@@ -107,8 +107,8 @@ class PackageForm(FlaskForm):
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()])
@@ -151,8 +151,11 @@ def create_edit_package_page(author=None, name=None):
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
@@ -198,4 +201,26 @@ def approve_package_page(package):
return redirect(package.getDetailsURL())
+
+@app.route("/packages///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
diff --git a/app/views/packages/todo.py b/app/views/packages/todo.py
index 53fec737..63f843b7 100644
--- a/app/views/packages/todo.py
+++ b/app/views/packages/todo.py
@@ -29,7 +29,7 @@ def todo_page():
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:
diff --git a/migrations/versions/c4152f4240ed_.py b/migrations/versions/c4152f4240ed_.py
new file mode 100644
index 00000000..de729a81
--- /dev/null
+++ b/migrations/versions/c4152f4240ed_.py
@@ -0,0 +1,28 @@
+"""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 ###