]> git.lizzy.rs Git - cheatdb.git/blob - app/blueprints/threads/__init__.py
Refactor endpoints to use blueprints instead
[cheatdb.git] / app / blueprints / threads / __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
20 bp = Blueprint("threads", __name__)
21
22 from flask_user import *
23 from app.models import *
24 from app.utils import triggerNotif, clearNotifications
25
26 import datetime
27
28 from flask_wtf import FlaskForm
29 from wtforms import *
30 from wtforms.validators import *
31
32 @bp.route("/threads/")
33 def list_all():
34         query = Thread.query
35         if not Permission.SEE_THREAD.check(current_user):
36                 query = query.filter_by(private=False)
37         return render_template("threads/list.html", threads=query.all())
38
39
40 @bp.route("/threads/<int:id>/subscribe/", methods=["POST"])
41 @login_required
42 def subscribe(id):
43         thread = Thread.query.get(id)
44         if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD):
45                 abort(404)
46
47         if current_user in thread.watchers:
48                 flash("Already subscribed!", "success")
49         else:
50                 flash("Subscribed to thread", "success")
51                 thread.watchers.append(current_user)
52                 db.session.commit()
53
54         return redirect(url_for("threads.view", id=id))
55
56
57 @bp.route("/threads/<int:id>/unsubscribe/", methods=["POST"])
58 @login_required
59 def unsubscribe(id):
60         thread = Thread.query.get(id)
61         if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD):
62                 abort(404)
63
64         if current_user in thread.watchers:
65                 flash("Unsubscribed!", "success")
66                 thread.watchers.remove(current_user)
67                 db.session.commit()
68         else:
69                 flash("Not subscribed to thread", "success")
70
71         return redirect(url_for("threads.view", id=id))
72
73
74 @bp.route("/threads/<int:id>/", methods=["GET", "POST"])
75 def view(id):
76         clearNotifications(url_for("threads.view", id=id))
77
78         thread = Thread.query.get(id)
79         if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD):
80                 abort(404)
81
82         if current_user.is_authenticated and request.method == "POST":
83                 comment = request.form["comment"]
84
85                 if not current_user.canCommentRL():
86                         flash("Please wait before commenting again", "danger")
87                         if package:
88                                 return redirect(package.getDetailsURL())
89                         else:
90                                 return redirect(url_for("home_page"))
91
92                 if len(comment) <= 500 and len(comment) > 3:
93                         reply = ThreadReply()
94                         reply.author = current_user
95                         reply.comment = comment
96                         db.session.add(reply)
97
98                         thread.replies.append(reply)
99                         if not current_user in thread.watchers:
100                                 thread.watchers.append(current_user)
101
102                         msg = None
103                         if thread.package is None:
104                                 msg = "New comment on '{}'".format(thread.title)
105                         else:
106                                 msg = "New comment on '{}' on package {}".format(thread.title, thread.package.title)
107
108
109                         for user in thread.watchers:
110                                 if user != current_user:
111                                         triggerNotif(user, current_user, msg, url_for("threads.view", id=thread.id))
112
113                         db.session.commit()
114
115                         return redirect(url_for("threads.view", id=id))
116
117                 else:
118                         flash("Comment needs to be between 3 and 500 characters.")
119
120         return render_template("threads/view.html", thread=thread)
121
122
123 class ThreadForm(FlaskForm):
124         title   = StringField("Title", [InputRequired(), Length(3,100)])
125         comment = TextAreaField("Comment", [InputRequired(), Length(10, 500)])
126         private = BooleanField("Private")
127         submit  = SubmitField("Open Thread")
128
129 @bp.route("/threads/new/", methods=["GET", "POST"])
130 @login_required
131 def new():
132         form = ThreadForm(formdata=request.form)
133
134         package = None
135         if "pid" in request.args:
136                 package = Package.query.get(int(request.args.get("pid")))
137                 if package is None:
138                         flash("Unable to find that package!", "error")
139
140         # Don't allow making orphan threads on approved packages for now
141         if package is None:
142                 abort(403)
143
144         def_is_private   = request.args.get("private") or False
145         if package is None:
146                 def_is_private = True
147         allow_change     = package and package.approved
148         is_review_thread = package and not package.approved
149
150         # Check that user can make the thread
151         if not package.checkPerm(current_user, Permission.CREATE_THREAD):
152                 flash("Unable to create thread!", "error")
153                 return redirect(url_for("home_page"))
154
155         # Only allow creating one thread when not approved
156         elif is_review_thread and package.review_thread is not None:
157                 flash("A review thread already exists!", "error")
158                 return redirect(url_for("threads.view", id=package.review_thread.id))
159
160         elif not current_user.canOpenThreadRL():
161                 flash("Please wait before opening another thread", "danger")
162
163                 if package:
164                         return redirect(package.getDetailsURL())
165                 else:
166                         return redirect(url_for("home_page"))
167
168         # Set default values
169         elif request.method == "GET":
170                 form.private.data = def_is_private
171                 form.title.data   = request.args.get("title") or ""
172
173         # Validate and submit
174         elif request.method == "POST" and form.validate():
175                 thread = Thread()
176                 thread.author  = current_user
177                 thread.title   = form.title.data
178                 thread.private = form.private.data if allow_change else def_is_private
179                 thread.package = package
180                 db.session.add(thread)
181
182                 thread.watchers.append(current_user)
183                 if package is not None and package.author != current_user:
184                         thread.watchers.append(package.author)
185
186                 reply = ThreadReply()
187                 reply.thread  = thread
188                 reply.author  = current_user
189                 reply.comment = form.comment.data
190                 db.session.add(reply)
191
192                 thread.replies.append(reply)
193
194                 db.session.commit()
195
196                 if is_review_thread:
197                         package.review_thread = thread
198
199                 notif_msg = None
200                 if package is not None:
201                         notif_msg = "New thread '{}' on package {}".format(thread.title, package.title)
202                         triggerNotif(package.author, current_user, notif_msg, url_for("threads.view", id=thread.id))
203                 else:
204                         notif_msg = "New thread '{}'".format(thread.title)
205
206                 for user in User.query.filter(User.rank >= UserRank.EDITOR).all():
207                         triggerNotif(user, current_user, notif_msg, url_for("threads.view", id=thread.id))
208
209                 db.session.commit()
210
211                 return redirect(url_for("threads.view", id=thread.id))
212
213
214         return render_template("threads/new.html", form=form, allow_private_change=allow_change, package=package)