aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/models.py32
-rw-r--r--app/templates/macros/threads.html17
-rw-r--r--app/views/threads.py38
-rw-r--r--docker-compose.yml2
-rw-r--r--runprodguni.sh2
5 files changed, 64 insertions, 27 deletions
diff --git a/app/models.py b/app/models.py
index b1cfbb5..20c3e60 100644
--- a/app/models.py
+++ b/app/models.py
@@ -20,10 +20,9 @@ from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from urllib.parse import urlparse
from app import app, gravatar
-from datetime import datetime
from sqlalchemy.orm import validates
from flask_user import login_required, UserManager, UserMixin, SQLAlchemyAdapter
-import enum
+import enum, datetime
# Initialise database
db = SQLAlchemy(app)
@@ -129,8 +128,6 @@ class User(db.Model, UserMixin):
replies = db.relationship("ThreadReply", backref="author", lazy="dynamic")
def __init__(self, username, active=False, email=None, password=None):
- import datetime
-
self.username = username
self.confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000)
self.display_name = username
@@ -172,6 +169,16 @@ class User(db.Model, UserMixin):
else:
raise Exception("Permission {} is not related to users".format(perm.name))
+ def canCommentRL(self):
+ hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
+ return ThreadReply.query.filter_by(author=self) \
+ .filter(ThreadReply.created_at > hour_ago).count() < 4
+
+ def canOpenThreadRL(self):
+ hour_ago = datetime.datetime.utcnow() - datetime.timedelta(hours=1)
+ return Thread.query.filter_by(author=self) \
+ .filter(Thread.created_at > hour_ago).count() < 2
+
class UserEmailVerification(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
@@ -347,7 +354,7 @@ class Package(db.Model):
shortDesc = db.Column(db.String(200), nullable=False)
desc = db.Column(db.Text, nullable=True)
type = db.Column(db.Enum(PackageType))
- created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
+ created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
license_id = db.Column(db.Integer, db.ForeignKey("license.id"), nullable=False, default=1)
license = db.relationship("License", foreign_keys=[license_id])
@@ -496,8 +503,11 @@ class Package(db.Model):
isOwner = user == self.author
+ if perm == Permission.CREATE_THREAD:
+ return user.rank.atLeast(UserRank.MEMBER)
+
# Members can edit their own packages, and editors can edit any packages
- if perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS or perm == Permission.CREATE_THREAD:
+ if perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS:
return isOwner or user.rank.atLeast(UserRank.EDITOR)
if perm == Permission.EDIT_PACKAGE or perm == Permission.APPROVE_CHANGES:
@@ -522,8 +532,6 @@ class Package(db.Model):
raise Exception("Permission {} is not related to packages".format(perm.name))
def recalcScore(self):
- import datetime
-
self.score = 10
if self.forums is not None:
@@ -630,7 +638,7 @@ class PackageRelease(db.Model):
def __init__(self):
- self.releaseDate = datetime.now()
+ self.releaseDate = datetime.datetime.now()
class PackageReview(db.Model):
@@ -762,7 +770,7 @@ class Thread(db.Model):
title = db.Column(db.String(100), nullable=False)
private = db.Column(db.Boolean, server_default="0")
- created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
+ created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
replies = db.relationship("ThreadReply", backref="thread", lazy="dynamic")
@@ -800,7 +808,7 @@ class ThreadReply(db.Model):
thread_id = db.Column(db.Integer, db.ForeignKey("thread.id"), nullable=False)
comment = db.Column(db.String(500), nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
- created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
+ created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
REPO_BLACKLIST = [".zip", "mediafire.com", "dropbox.com", "weebly.com", \
@@ -824,7 +832,7 @@ class ForumTopic(db.Model):
posts = db.Column(db.Integer, nullable=False)
views = db.Column(db.Integer, nullable=False)
- created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
+ created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
def getRepoURL(self):
if self.link is None:
diff --git a/app/templates/macros/threads.html b/app/templates/macros/threads.html
index 65f02f4..fd7b648 100644
--- a/app/templates/macros/threads.html
+++ b/app/templates/macros/threads.html
@@ -42,11 +42,18 @@
<a name="reply"></a>
</div>
- <form method="post" action="{{ url_for('thread_page', id=thread.id)}}" class="card-body">
- <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
- <textarea class="form-control markdown" required maxlength=500 name="comment"></textarea><br />
- <input class="btn btn-primary" type="submit" value="Comment" />
- </form>
+ {% if current_user.canCommentRL() %}
+ <form method="post" action="{{ url_for('thread_page', id=thread.id)}}" class="card-body">
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
+ <textarea class="form-control markdown" required maxlength=500 name="comment"></textarea><br />
+ <input class="btn btn-primary" type="submit" value="Comment" />
+ </form>
+ {% else %}
+ <div class="card-body">
+ <textarea class="form-control" readonly disabled>Please wait before commenting again.</textarea><br />
+ <input class="btn btn-primary" type="submit" disabled value="Comment" />
+ </div>
+ {% endif %}
</div>
</div>
</div>
diff --git a/app/views/threads.py b/app/views/threads.py
index 37ac3d1..c168a23 100644
--- a/app/views/threads.py
+++ b/app/views/threads.py
@@ -21,6 +21,8 @@ from app import app
from app.models import *
from app.utils import triggerNotif, clearNotifications
+import datetime
+
from flask_wtf import FlaskForm
from wtforms import *
from wtforms.validators import *
@@ -78,6 +80,13 @@ def thread_page(id):
if current_user.is_authenticated and request.method == "POST":
comment = request.form["comment"]
+ if not current_user.canCommentRL():
+ flash("Please wait before commenting again", "danger")
+ if package:
+ return redirect(package.getDetailsURL())
+ else:
+ return redirect(url_for("home_page"))
+
if len(comment) <= 500 and len(comment) > 3:
reply = ThreadReply()
reply.author = current_user
@@ -126,15 +135,15 @@ def new_thread_page():
if package is None:
flash("Unable to find that package!", "error")
- # Don't allow making threads on approved packages for now
+ # Don't allow making orphan threads on approved packages for now
if package is None:
abort(403)
def_is_private = request.args.get("private") or False
- if not package.approved:
+ if package is None or not package.approved:
def_is_private = True
- allow_change = package.approved
- is_review_thread = package is not None and not package.approved
+ allow_change = package and package.approved
+ is_review_thread = package and not package.approved
# Check that user can make the thread
if not package.checkPerm(current_user, Permission.CREATE_THREAD):
@@ -144,8 +153,15 @@ def new_thread_page():
# Only allow creating one thread when not approved
elif is_review_thread and package.review_thread is not None:
flash("A review thread already exists!", "error")
- if request.method == "GET":
- return redirect(url_for("thread_page", id=package.review_thread.id))
+ return redirect(url_for("thread_page", id=package.review_thread.id))
+
+ elif not current_user.canOpenThreadRL():
+ flash("Please wait before opening another thread", "danger")
+
+ if package:
+ return redirect(package.getDetailsURL())
+ else:
+ return redirect(url_for("home_page"))
# Set default values
elif request.method == "GET":
@@ -178,9 +194,15 @@ def new_thread_page():
if is_review_thread:
package.review_thread = thread
+ notif_msg = None
if package is not None:
- triggerNotif(package.author, current_user,
- "New thread '{}' on package {}".format(thread.title, package.title), url_for("thread_page", id=thread.id))
+ notif_msg = "New thread '{}' on package {}".format(thread.title, package.title)
+ triggerNotif(package.author, current_user, notif_msg, url_for("thread_page", id=thread.id))
+ else:
+ notif_msg = "New thread '{}'".format(thread.title)
+
+ for user in User.query.filter(User.rank >= UserRank.EDITOR).all():
+ triggerNotif(user, current_user, notif_msg, url_for("thread_page", id=thread.id))
db.session.commit()
diff --git a/docker-compose.yml b/docker-compose.yml
index 4c2752e..93e86bc 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,7 +2,6 @@ version: '3'
services:
db:
image: "postgres:9.6.5"
- restart: always
volumes:
- "./data/db:/var/lib/postgresql/data"
env_file:
@@ -21,6 +20,7 @@ services:
- 5123:5123
volumes:
- "./data/uploads:/home/cdb/app/public/uploads"
+ - "./app:/home/cdb/app"
depends_on:
- db
- redis
diff --git a/runprodguni.sh b/runprodguni.sh
index fca01c0..c7e8bb8 100644
--- a/runprodguni.sh
+++ b/runprodguni.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-gunicorn -w 4 -b :5123 -e FLASK_APP=app/__init__.py -e FLASK_CONFIG=../config.prod.cfg -e FLASK_DEBUG=0 app:app
+gunicorn -w 4 -b :5123 -e FLASK_APP=app/__init__.py -e FLASK_CONFIG=../config.prod.cfg -e FLASK_DEBUG=1 app:app