2020-12-10 12:37:15 +01:00
|
|
|
# ContentDB
|
2021-01-30 17:59:42 +01:00
|
|
|
# Copyright (C) 2018-21 rubenwardy
|
2020-12-10 12:37:15 +01:00
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
2021-01-30 17:59:42 +01:00
|
|
|
# it under the terms of the GNU Affero General Public License as published by
|
2020-12-10 12:37:15 +01:00
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2021-01-30 17:59:42 +01:00
|
|
|
# GNU Affero General Public License for more details.
|
2020-12-10 12:37:15 +01:00
|
|
|
#
|
2021-01-30 17:59:42 +01:00
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
2020-12-10 12:37:15 +01:00
|
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import datetime
|
2021-08-18 23:09:41 +02:00
|
|
|
from typing import Tuple, List
|
2020-12-10 12:37:15 +01:00
|
|
|
|
|
|
|
from flask import url_for
|
|
|
|
|
|
|
|
from . import db
|
|
|
|
from .users import Permission, UserRank
|
|
|
|
from .packages import Package
|
|
|
|
|
|
|
|
watchers = db.Table("watchers",
|
|
|
|
db.Column("user_id", db.Integer, db.ForeignKey("user.id"), primary_key=True),
|
|
|
|
db.Column("thread_id", db.Integer, db.ForeignKey("thread.id"), primary_key=True)
|
|
|
|
)
|
|
|
|
|
2020-12-10 17:49:37 +01:00
|
|
|
|
2020-12-10 12:37:15 +01:00
|
|
|
class Thread(db.Model):
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
|
|
|
|
package_id = db.Column(db.Integer, db.ForeignKey("package.id"), nullable=True)
|
|
|
|
package = db.relationship("Package", foreign_keys=[package_id], back_populates="threads")
|
|
|
|
|
|
|
|
is_review_thread = db.relationship("Package", foreign_keys=[Package.review_thread_id], back_populates="review_thread")
|
|
|
|
|
|
|
|
review_id = db.Column(db.Integer, db.ForeignKey("package_review.id"), nullable=True)
|
|
|
|
review = db.relationship("PackageReview", foreign_keys=[review_id], cascade="all, delete")
|
|
|
|
|
|
|
|
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
|
|
|
|
author = db.relationship("User", back_populates="threads", foreign_keys=[author_id])
|
|
|
|
|
|
|
|
title = db.Column(db.String(100), nullable=False)
|
|
|
|
private = db.Column(db.Boolean, server_default="0", nullable=False)
|
|
|
|
|
|
|
|
locked = db.Column(db.Boolean, server_default="0", nullable=False)
|
|
|
|
|
|
|
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
|
|
|
|
|
|
|
replies = db.relationship("ThreadReply", back_populates="thread", lazy="dynamic",
|
|
|
|
order_by=db.asc("thread_reply_id"), cascade="all, delete, delete-orphan")
|
|
|
|
|
2020-12-10 18:46:10 +01:00
|
|
|
watchers = db.relationship("User", secondary=watchers, backref="watching")
|
2020-12-10 12:37:15 +01:00
|
|
|
|
2021-08-17 22:16:43 +02:00
|
|
|
def get_description(self):
|
|
|
|
comment = self.replies[0].comment.replace("\r\n", " ").replace("\n", " ").replace(" ", " ")
|
|
|
|
if len(comment) > 100:
|
|
|
|
return comment[:97] + "..."
|
|
|
|
else:
|
|
|
|
return comment
|
|
|
|
|
|
|
|
def getViewURL(self, absolute=False):
|
|
|
|
if absolute:
|
|
|
|
from ..utils import abs_url_for
|
|
|
|
return abs_url_for("threads.view", id=self.id)
|
|
|
|
else:
|
|
|
|
return url_for("threads.view", id=self.id, _external=False)
|
2020-12-10 12:37:15 +01:00
|
|
|
|
|
|
|
def getSubscribeURL(self):
|
|
|
|
return url_for("threads.subscribe", id=self.id)
|
|
|
|
|
|
|
|
def getUnsubscribeURL(self):
|
|
|
|
return url_for("threads.unsubscribe", id=self.id)
|
|
|
|
|
|
|
|
def checkPerm(self, user, perm):
|
|
|
|
if not user.is_authenticated:
|
|
|
|
return perm == Permission.SEE_THREAD and not self.private
|
|
|
|
|
|
|
|
if type(perm) == str:
|
|
|
|
perm = Permission[perm]
|
|
|
|
elif type(perm) != Permission:
|
|
|
|
raise Exception("Unknown permission given to Thread.checkPerm()")
|
|
|
|
|
|
|
|
isMaintainer = user == self.author or (self.package is not None and self.package.author == user)
|
|
|
|
if self.package:
|
|
|
|
isMaintainer = isMaintainer or user in self.package.maintainers
|
|
|
|
|
2021-08-16 19:57:05 +02:00
|
|
|
canSee = not self.private or isMaintainer or user.rank.atLeast(UserRank.APPROVER)
|
2020-12-10 12:37:15 +01:00
|
|
|
|
|
|
|
if perm == Permission.SEE_THREAD:
|
|
|
|
return canSee
|
|
|
|
|
|
|
|
elif perm == Permission.COMMENT_THREAD:
|
|
|
|
return canSee and (not self.locked or user.rank.atLeast(UserRank.MODERATOR))
|
|
|
|
|
2021-07-20 01:25:55 +02:00
|
|
|
elif perm == Permission.LOCK_THREAD:
|
2020-12-10 12:37:15 +01:00
|
|
|
return user.rank.atLeast(UserRank.MODERATOR)
|
|
|
|
|
2021-07-20 01:25:55 +02:00
|
|
|
elif perm == Permission.DELETE_THREAD:
|
|
|
|
from app.utils.models import get_system_user
|
|
|
|
return (self.author == get_system_user() and self.package and
|
|
|
|
user in self.package.maintainers) or user.rank.atLeast(UserRank.MODERATOR)
|
|
|
|
|
2020-12-10 12:37:15 +01:00
|
|
|
else:
|
|
|
|
raise Exception("Permission {} is not related to threads".format(perm.name))
|
|
|
|
|
2020-12-22 13:22:52 +01:00
|
|
|
def get_latest_reply(self):
|
|
|
|
return ThreadReply.query.filter_by(thread_id=self.id).order_by(db.desc(ThreadReply.id)).first()
|
|
|
|
|
2020-12-10 17:49:37 +01:00
|
|
|
|
2020-12-10 12:37:15 +01:00
|
|
|
class ThreadReply(db.Model):
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
|
|
|
|
thread_id = db.Column(db.Integer, db.ForeignKey("thread.id"), nullable=False)
|
|
|
|
thread = db.relationship("Thread", back_populates="replies", foreign_keys=[thread_id])
|
|
|
|
|
|
|
|
comment = db.Column(db.String(2000), nullable=False)
|
|
|
|
|
|
|
|
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
|
|
|
|
author = db.relationship("User", back_populates="replies", foreign_keys=[author_id])
|
|
|
|
|
|
|
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
|
|
|
|
|
|
|
def checkPerm(self, user, perm):
|
|
|
|
if not user.is_authenticated:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if type(perm) == str:
|
|
|
|
perm = Permission[perm]
|
|
|
|
elif type(perm) != Permission:
|
|
|
|
raise Exception("Unknown permission given to ThreadReply.checkPerm()")
|
|
|
|
|
|
|
|
if perm == Permission.EDIT_REPLY:
|
|
|
|
return user == self.author and user.rank.atLeast(UserRank.MEMBER) and not self.thread.locked
|
|
|
|
|
|
|
|
elif perm == Permission.DELETE_REPLY:
|
|
|
|
return user.rank.atLeast(UserRank.MODERATOR) and self.thread.replies[0] != self
|
|
|
|
|
|
|
|
else:
|
|
|
|
raise Exception("Permission {} is not related to threads".format(perm.name))
|
|
|
|
|
|
|
|
|
|
|
|
class PackageReview(db.Model):
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
|
|
|
|
|
|
package_id = db.Column(db.Integer, db.ForeignKey("package.id"), nullable=True)
|
|
|
|
package = db.relationship("Package", foreign_keys=[package_id], back_populates="reviews")
|
|
|
|
|
|
|
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|
|
|
|
|
|
|
|
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
|
|
|
|
author = db.relationship("User", foreign_keys=[author_id], back_populates="reviews")
|
|
|
|
|
|
|
|
recommends = db.Column(db.Boolean, nullable=False)
|
|
|
|
|
2020-12-10 17:49:37 +01:00
|
|
|
thread = db.relationship("Thread", uselist=False, back_populates="review")
|
2021-08-21 23:55:11 +02:00
|
|
|
votes = db.relationship("PackageReviewVote", back_populates="review", cascade="all, delete, delete-orphan")
|
2021-08-18 23:09:41 +02:00
|
|
|
|
|
|
|
score = db.Column(db.Integer, nullable=False, default=1)
|
|
|
|
|
|
|
|
def get_totals(self, current_user = None) -> Tuple[int,int,bool]:
|
|
|
|
votes: List[PackageReviewVote] = self.votes
|
|
|
|
pos = sum([ 1 for vote in votes if vote.is_positive ])
|
|
|
|
neg = sum([ 1 for vote in votes if not vote.is_positive])
|
|
|
|
user_vote = next(filter(lambda vote: vote.user == current_user, votes), None)
|
|
|
|
return pos, neg, user_vote.is_positive if user_vote else None
|
2020-12-10 12:37:15 +01:00
|
|
|
|
|
|
|
def asSign(self):
|
|
|
|
return 1 if self.recommends else -1
|
|
|
|
|
|
|
|
def getEditURL(self):
|
2021-07-24 05:30:14 +02:00
|
|
|
return self.package.getURL("packages.review")
|
2020-12-10 12:37:15 +01:00
|
|
|
|
|
|
|
def getDeleteURL(self):
|
|
|
|
return url_for("packages.delete_review",
|
|
|
|
author=self.package.author.username,
|
|
|
|
name=self.package.name)
|
2021-08-18 23:09:41 +02:00
|
|
|
|
|
|
|
def getVoteUrl(self, next_url=None):
|
|
|
|
return url_for("packages.review_vote",
|
|
|
|
author=self.package.author.username,
|
|
|
|
name=self.package.name,
|
|
|
|
review_id=self.id,
|
|
|
|
r=next_url)
|
|
|
|
|
|
|
|
def update_score(self):
|
|
|
|
(pos, neg, _) = self.get_totals()
|
|
|
|
self.score = 3 * (pos - neg) + 1
|
|
|
|
|
|
|
|
|
|
|
|
class PackageReviewVote(db.Model):
|
|
|
|
review_id = db.Column(db.Integer, db.ForeignKey("package_review.id"), primary_key=True)
|
|
|
|
review = db.relationship("PackageReview", foreign_keys=[review_id], back_populates="votes")
|
|
|
|
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
|
|
|
|
user = db.relationship("User", foreign_keys=[user_id], back_populates="review_votes")
|
|
|
|
|
|
|
|
is_positive = db.Column(db.Boolean, nullable=False)
|
|
|
|
|
|
|
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
|