2 # Copyright (C) 2018 rubenwardy
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.
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.
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/>.
19 from flask_user import *
21 from app.models import *
22 from app.utils import triggerNotif, clearNotifications
24 from flask_wtf import FlaskForm
26 from wtforms.validators import *
28 @app.route("/threads/")
31 if not Permission.SEE_THREAD.check(current_user):
32 query = query.filter_by(private=False)
33 return render_template("threads/list.html", threads=query.all())
36 @app.route("/threads/<int:id>/subscribe/", methods=["POST"])
38 def thread_subscribe_page(id):
39 thread = Thread.query.get(id)
40 if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD):
43 if current_user in thread.watchers:
44 flash("Already subscribed!", "success")
46 flash("Subscribed to thread", "success")
47 thread.watchers.append(current_user)
50 return redirect(url_for("thread_page", id=id))
53 @app.route("/threads/<int:id>/unsubscribe/", methods=["POST"])
55 def thread_unsubscribe_page(id):
56 thread = Thread.query.get(id)
57 if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD):
60 if current_user in thread.watchers:
61 flash("Unsubscribed!", "success")
62 thread.watchers.remove(current_user)
65 flash("Not subscribed to thread", "success")
67 return redirect(url_for("thread_page", id=id))
70 @app.route("/threads/<int:id>/", methods=["GET", "POST"])
72 clearNotifications(url_for("thread_page", id=id))
74 thread = Thread.query.get(id)
75 if thread is None or not thread.checkPerm(current_user, Permission.SEE_THREAD):
78 if current_user.is_authenticated and request.method == "POST":
79 comment = request.form["comment"]
81 if len(comment) <= 500 and len(comment) > 3:
83 reply.author = current_user
84 reply.comment = comment
87 thread.replies.append(reply)
88 if not current_user in thread.watchers:
89 thread.watchers.append(current_user)
92 if thread.package is None:
93 msg = "New comment on '{}'".format(thread.title)
95 msg = "New comment on '{}' on package {}".format(thread.title, thread.package.title)
98 for user in thread.watchers:
99 if user != current_user:
100 triggerNotif(user, current_user, msg, url_for("thread_page", id=thread.id))
104 return redirect(url_for("thread_page", id=id))
107 flash("Comment needs to be between 3 and 500 characters.")
109 return render_template("threads/view.html", thread=thread)
112 class ThreadForm(FlaskForm):
113 title = StringField("Title", [InputRequired(), Length(3,100)])
114 comment = TextAreaField("Comment", [InputRequired(), Length(10, 500)])
115 private = BooleanField("Private")
116 submit = SubmitField("Open Thread")
118 @app.route("/threads/new/", methods=["GET", "POST"])
120 def new_thread_page():
121 form = ThreadForm(formdata=request.form)
124 if "pid" in request.args:
125 package = Package.query.get(int(request.args.get("pid")))
127 flash("Unable to find that package!", "error")
129 # Don't allow making threads on approved packages for now
133 def_is_private = request.args.get("private") or False
134 if not package.approved:
135 def_is_private = True
136 allow_change = package.approved
137 is_review_thread = package is not None and not package.approved
139 # Check that user can make the thread
140 if not package.checkPerm(current_user, Permission.CREATE_THREAD):
141 flash("Unable to create thread!", "error")
142 return redirect(url_for("home_page"))
144 # Only allow creating one thread when not approved
145 elif is_review_thread and package.review_thread is not None:
146 flash("A review thread already exists!", "error")
147 if request.method == "GET":
148 return redirect(url_for("thread_page", id=package.review_thread.id))
151 elif request.method == "GET":
152 form.private.data = def_is_private
153 form.title.data = request.args.get("title") or ""
155 # Validate and submit
156 elif request.method == "POST" and form.validate():
158 thread.author = current_user
159 thread.title = form.title.data
160 thread.private = form.private.data if allow_change else def_is_private
161 thread.package = package
162 db.session.add(thread)
164 thread.watchers.append(current_user)
165 if package is not None and package.author != current_user:
166 thread.watchers.append(package.author)
168 reply = ThreadReply()
169 reply.thread = thread
170 reply.author = current_user
171 reply.comment = form.comment.data
172 db.session.add(reply)
174 thread.replies.append(reply)
179 package.review_thread = thread
181 if package is not None:
182 triggerNotif(package.author, current_user,
183 "New thread '{}' on package {}".format(thread.title, package.title), url_for("thread_page", id=thread.id))
187 return redirect(url_for("thread_page", id=thread.id))
190 return render_template("threads/new.html", form=form, allow_private_change=allow_change)