]> git.lizzy.rs Git - cheatdb.git/blob - app/views/packages/__init__.py
Finish screenshot approval feature
[cheatdb.git] / app / views / packages / __init__.py
1 # Content DB
2 # Copyright (C) 2018  rubenwardy
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17
18 from flask import *
19 from flask_user import *
20 from flask.ext import menu
21 from app import app
22 from app.models import *
23 from app.tasks.importtasks import importRepoScreenshot, makeVCSRelease
24
25 from app.utils import *
26
27 from celery import uuid
28 from flask_wtf import FlaskForm
29 from wtforms import *
30 from wtforms.validators import *
31 from wtforms.ext.sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
32
33 # TODO: the following could be made into one route, except I"m not sure how
34 # to do the menu
35
36 @menu.register_menu(app, ".mods", "Mods", order=11, endpoint_arguments_constructor=lambda: { 'type': 'mod' })
37 @menu.register_menu(app, ".games", "Games", order=12, endpoint_arguments_constructor=lambda: { 'type': 'game' })
38 @menu.register_menu(app, ".txp", "Texture Packs", order=13, endpoint_arguments_constructor=lambda: { 'type': 'txp' })
39 @app.route("/packages/")
40 def packages_page():
41         type = request.args.get("type")
42         if type is not None:
43                 type = PackageType[type.upper()]
44
45         title = "Packages"
46         query = Package.query.filter_by(soft_deleted=False)
47
48         if type is not None:
49                 title = type.value + "s"
50                 query = query.filter_by(type=type, approved=True)
51
52         search = request.args.get("q")
53         if search is not None:
54                 query = query.filter(Package.title.contains(search))
55
56         if shouldReturnJson():
57                 pkgs = [package.getAsDictionary(app.config["BASE_URL"]) \
58                                 for package in query.all() if package.getDownloadRelease() is not None]
59                 return jsonify(pkgs)
60         else:
61                 tags = Tag.query.all()
62                 return render_template("packages/list.html", title=title, packages=query.all(), \
63                                 query=search, tags=tags, type=None if type is None else type.toName())
64
65
66 def getReleases(package):
67         if package.checkPerm(current_user, Permission.MAKE_RELEASE):
68                 return package.releases
69         else:
70                 return [rel for rel in package.releases if rel.approved]
71
72
73 @app.route("/packages/<author>/<name>/")
74 @is_package_page
75 def package_page(package):
76         if shouldReturnJson():
77                 return jsonify(package.getAsDictionary(app.config["BASE_URL"]))
78         else:
79                 clearNotifications(package.getDetailsURL())
80
81                 releases = getReleases(package)
82                 requests = [r for r in package.requests if r.status == 0]
83                 return render_template("packages/view.html", package=package, releases=releases, requests=requests)
84
85
86 @app.route("/packages/<author>/<name>/download/")
87 @is_package_page
88 def package_download_page(package):
89         release = package.getDownloadRelease()
90
91         if release is None:
92                 if "application/zip" in request.accept_mimetypes and \
93                                 not "text/html" in request.accept_mimetypes:
94                         return "", 204
95                 else:
96                         flash("No download available.", "error")
97                         return redirect(package.getDetailsURL())
98         else:
99                 return redirect(release.url, code=302)
100
101
102 class PackageForm(FlaskForm):
103         name          = StringField("Name", [InputRequired(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
104         title         = StringField("Title", [InputRequired(), Length(3, 50)])
105         shortDesc     = StringField("Short Description", [InputRequired(), Length(1,200)])
106         desc          = TextAreaField("Long Description", [Optional(), Length(0,10000)])
107         type          = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
108         license       = QuerySelectField("License", [InputRequired()], query_factory=lambda: License.query, get_pk=lambda a: a.id, get_label=lambda a: a.name)
109         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)
110         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)
111         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)
112         repo          = StringField("Repo URL", [Optional(), URL()])
113         website       = StringField("Website URL", [Optional(), URL()])
114         issueTracker  = StringField("Issue Tracker URL", [Optional(), URL()])
115         forums        = IntegerField("Forum Topic ID", [Optional(), NumberRange(0,999999)])
116         submit        = SubmitField("Save")
117
118 @app.route("/packages/new/", methods=["GET", "POST"])
119 @app.route("/packages/<author>/<name>/edit/", methods=["GET", "POST"])
120 @login_required
121 def create_edit_package_page(author=None, name=None):
122         package = None
123         form = None
124         if author is None:
125                 form = PackageForm(formdata=request.form)
126                 author = request.args.get("author")
127                 if author is None or author == current_user.username:
128                         author = current_user
129                 else:
130                         author = User.query.filter_by(username=author).first()
131                         if author is None:
132                                 flash("Unable to find that user", "error")
133                                 return redirect(url_for("create_edit_package_page"))
134
135                         if not author.checkPerm(current_user, Permission.CHANGE_AUTHOR):
136                                 flash("Permission denied", "error")
137                                 return redirect(url_for("create_edit_package_page"))
138
139         else:
140                 package = getPackageByInfo(author, name)
141                 if not package.checkPerm(current_user, Permission.EDIT_PACKAGE):
142                         return redirect(package.getDetailsURL())
143
144                 author = package.author
145
146                 form = PackageForm(formdata=request.form, obj=package)
147
148         # Initial form class from post data and default data
149         if request.method == "POST" and form.validate():
150                 wasNew = False
151                 if not package:
152                         package = Package.query.filter_by(name=form["name"].data, author_id=author.id).first()
153                         if package is not None:
154                                 if package.soft_deleted:
155                                         Package.query.filter_by(name=form["name"].data, author_id=author.id).delete()
156                                 else:
157                                         flash("Package already exists!", "error")
158                                         return redirect(url_for("create_edit_package_page"))
159
160                         package = Package()
161                         package.author = author
162                         wasNew = True
163                 else:
164                         triggerNotif(package.author, current_user,
165                                         "{} edited".format(package.title), package.getDetailsURL())
166
167                 form.populate_obj(package) # copy to row
168
169                 package.tags.clear()
170                 for tag in form.tags.raw_data:
171                         package.tags.append(Tag.query.get(tag))
172
173                 db.session.commit() # save
174
175                 if wasNew and package.canImportScreenshot():
176                         task = importRepoScreenshot.delay(package.id)
177                         return redirect(url_for("check_task", id=task.id, r=package.getDetailsURL()))
178
179                 return redirect(package.getDetailsURL())
180
181         enableWizard = name is None and request.method != "POST"
182         return render_template("packages/create_edit.html", package=package, \
183                         form=form, author=author, enable_wizard=enableWizard)
184
185 @app.route("/packages/<author>/<name>/approve/", methods=["POST"])
186 @login_required
187 @is_package_page
188 def approve_package_page(package):
189         if not package.checkPerm(current_user, Permission.APPROVE_NEW):
190                 flash("You don't have permission to do that.", "error")
191
192         elif package.approved:
193                 flash("Package has already been approved", "error")
194
195         else:
196                 package.approved = True
197
198                 screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all()
199                 for s in screenshots:
200                         screenshots.approved = True
201
202                 triggerNotif(package.author, current_user,
203                                 "{} approved".format(package.title), package.getDetailsURL())
204                 db.session.commit()
205
206         return redirect(package.getDetailsURL())
207
208
209 @app.route("/packages/<author>/<name>/delete/", methods=["GET", "POST"])
210 @login_required
211 @is_package_page
212 def delete_package_page(package):
213         if request.method == "GET":
214                 return render_template("packages/delete.html", package=package)
215
216         if not package.checkPerm(current_user, Permission.DELETE_PACKAGE):
217                 flash("You don't have permission to do that.", "error")
218
219         package.soft_deleted = True
220
221         url = url_for("user_profile_page", username=package.author.username)
222         triggerNotif(package.author, current_user,
223                         "{} deleted".format(package.title), url)
224         db.session.commit()
225
226         flash("Deleted package", "success")
227
228         return redirect(url)
229
230 from . import todo, screenshots, editrequests, releases