forum troll isolation complete
parent
fbf6ff749d
commit
839e2dc163
|
@ -6,14 +6,14 @@ import views._
|
|||
object ForumCateg extends LilaController with ForumController {
|
||||
|
||||
def index = Open { implicit ctx ⇒
|
||||
~ctx.userId.map(teamCache.teamIds.apply) flatMap { teamIds ⇒
|
||||
categApi list teamIds map { html.forum.categ.index(_) }
|
||||
ctx.userId.zmap(teamCache.teamIds.apply) flatMap { teamIds ⇒
|
||||
categApi.list(teamIds, ctx.troll) map { html.forum.categ.index(_) }
|
||||
}
|
||||
}
|
||||
|
||||
def show(slug: String, page: Int) = Open { implicit ctx ⇒
|
||||
CategGrantRead(slug) {
|
||||
OptionOk(categApi.show(slug, page)) {
|
||||
OptionOk(categApi.show(slug, page, ctx.troll)) {
|
||||
case (categ, topics) ⇒ html.forum.categ.show(categ, topics)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ object ForumPost extends LilaController with ForumController {
|
|||
def create(categSlug: String, slug: String, page: Int) = OpenBody { implicit ctx ⇒
|
||||
CategGrantWrite(categSlug) {
|
||||
implicit val req = ctx.body
|
||||
OptionFuResult(topicApi.show(categSlug, slug, page)) {
|
||||
OptionFuResult(topicApi.show(categSlug, slug, page, ctx.troll)) {
|
||||
case (categ, topic, posts) ⇒
|
||||
if (topic.closed) fuccess(BadRequest("This topic is closed"))
|
||||
else forms.post.bindFromRequest.fold(
|
||||
|
|
|
@ -32,7 +32,7 @@ object ForumTopic extends LilaController with ForumController {
|
|||
|
||||
def show(categSlug: String, slug: String, page: Int) = Open { implicit ctx ⇒
|
||||
CategGrantRead(categSlug) {
|
||||
OptionFuOk(topicApi.show(categSlug, slug, page)) {
|
||||
OptionFuOk(topicApi.show(categSlug, slug, page, ctx.troll)) {
|
||||
case (categ, topic, posts) ⇒ isGrantedWrite(categSlug) flatMap { granted ⇒
|
||||
(!posts.hasNextPage && granted && topic.open) ?? forms.postWithCaptcha.map(_.some) map { form ⇒
|
||||
html.forum.topic.show(categ, topic, posts, form)
|
||||
|
@ -45,7 +45,7 @@ object ForumTopic extends LilaController with ForumController {
|
|||
def close(categSlug: String, slug: String) =
|
||||
Secure(_.ModerateForum) { implicit ctx ⇒
|
||||
me ⇒
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1)) {
|
||||
OptionFuRedirect(topicApi.show(categSlug, slug, 1, ctx.troll)) {
|
||||
case (categ, topic, pag) ⇒ topicApi.toggleClose(categ, topic, me) inject
|
||||
routes.ForumTopic.show(categSlug, slug, pag.nbPages)
|
||||
}
|
||||
|
|
|
@ -21,9 +21,37 @@ db.f_topic.find().forEach(function(topic) {
|
|||
}});
|
||||
});
|
||||
|
||||
print("add troll fields to the forum categs")
|
||||
db.f_categ.find().forEach(function(categ) {
|
||||
db.f_categ.update({'_id': categ['_id']}, { $set: {
|
||||
nbTopicsTroll: categ['nbTopics'],
|
||||
nbPostsTroll: categ['nbPosts'],
|
||||
lastPostIdTroll: categ['lastPostId']
|
||||
}});
|
||||
});
|
||||
|
||||
print("remove useless author names in forum posts")
|
||||
db.f_post.update({author:{$exists:true},userId:{$exists:true}},{$unset:{author:true}},{multi:true});
|
||||
|
||||
print("mark all forum posts as not troll");
|
||||
db.f_post.update({},{$set:{troll:false}}, {multi:true});
|
||||
|
||||
print("use troll field in forum post indexes")
|
||||
db.f_post.dropIndex('topicId_1')
|
||||
db.f_post.dropIndex('topicId_1_createdAt_1')
|
||||
db.f_post.dropIndex('categId_1')
|
||||
db.f_post.dropIndex('createdAt_-1')
|
||||
db.f_post.ensureIndex({topicId: 1, troll: 1})
|
||||
db.f_post.ensureIndex({topicId: 1, createdAt: 1, troll: 1})
|
||||
db.f_post.ensureIndex({categId: 1, troll: 1})
|
||||
db.f_post.ensureIndex({createdAt: -1, troll: 1})
|
||||
|
||||
print("use troll field in forum topic indexes")
|
||||
db.f_topic.dropIndex('categId_1')
|
||||
db.f_topic.dropIndex('categId_1_updatedAt_-1')
|
||||
db.f_topic.ensureIndex({categId: 1, troll: 1})
|
||||
db.f_topic.ensureIndex({categId: 1, updatedAt: -1, troll: 1})
|
||||
|
||||
print("user.settings.{chat,sound} should be a string");
|
||||
['settings.chat', 'settings.sound'].forEach(function(name) {
|
||||
[true, false].forEach(function(value) {
|
||||
|
|
|
@ -8,16 +8,26 @@ case class Categ(
|
|||
team: Option[String] = None,
|
||||
nbTopics: Int,
|
||||
nbPosts: Int,
|
||||
lastPostId: String) {
|
||||
lastPostId: String,
|
||||
nbTopicsTroll: Int,
|
||||
nbPostsTroll: Int,
|
||||
lastPostIdTroll: String) {
|
||||
|
||||
def nbTopics(troll: Boolean): Int = troll.fold(nbTopicsTroll, nbTopics)
|
||||
def nbPosts(troll: Boolean): Int = troll.fold(nbPostsTroll, nbPosts)
|
||||
def lastPostId(troll: Boolean): String = troll.fold(lastPostIdTroll, lastPostId)
|
||||
|
||||
def isStaff = slug == "staff"
|
||||
|
||||
def isTeam = team.nonEmpty
|
||||
|
||||
def withTopic(post: Post): Categ = copy(
|
||||
nbTopics = nbTopics + 1,
|
||||
nbPosts = nbPosts + 1,
|
||||
lastPostId = post.id)
|
||||
nbTopics = post.troll.fold(nbTopics, nbTopics + 1),
|
||||
nbPosts = post.troll.fold(nbPosts, nbPosts + 1),
|
||||
lastPostId = post.troll.fold(lastPostId, post.id),
|
||||
nbTopicsTroll = nbTopicsTroll + 1,
|
||||
nbPostsTroll = nbPostsTroll + 1,
|
||||
lastPostIdTroll = post.id)
|
||||
|
||||
def slug = id
|
||||
}
|
||||
|
|
|
@ -11,15 +11,15 @@ import scalaz.{ OptionT, OptionTs }
|
|||
|
||||
private[forum] final class CategApi(env: Env) extends OptionTs {
|
||||
|
||||
def list(teams: List[String]): Fu[List[CategView]] = for {
|
||||
def list(teams: List[String], troll: Boolean): Fu[List[CategView]] = for {
|
||||
categs ← CategRepo withTeams teams
|
||||
views ← (categs map { categ ⇒
|
||||
env.postApi get categ.lastPostId map { topicPost ⇒
|
||||
env.postApi get (categ lastPostId troll) map { topicPost ⇒
|
||||
CategView(categ, topicPost map {
|
||||
_ match {
|
||||
case (topic, post) ⇒ (topic, post, env.postApi lastPageOf topic)
|
||||
}
|
||||
})
|
||||
}, troll)
|
||||
}
|
||||
}).sequence
|
||||
} yield views
|
||||
|
@ -36,7 +36,10 @@ private[forum] final class CategApi(env: Env) extends OptionTs {
|
|||
team = slug.some,
|
||||
nbTopics = 0,
|
||||
nbPosts = 0,
|
||||
lastPostId = "")
|
||||
lastPostId = "",
|
||||
nbTopicsTroll = 0,
|
||||
nbPostsTroll = 0,
|
||||
lastPostIdTroll = "")
|
||||
val topic = Topic.make(
|
||||
categId = categ.slug,
|
||||
slug = slug + "-forum",
|
||||
|
@ -53,19 +56,13 @@ private[forum] final class CategApi(env: Env) extends OptionTs {
|
|||
categId = categ.id)
|
||||
$insert(categ) >>
|
||||
$insert(post) >>
|
||||
$insert(topic.copy(
|
||||
nbPosts = 1,
|
||||
lastPostId = post.id,
|
||||
updatedAt = post.createdAt)) >>
|
||||
$update(categ.copy(
|
||||
nbTopics = categ.nbTopics + 1,
|
||||
nbPosts = categ.nbPosts + 1,
|
||||
lastPostId = post.id))
|
||||
$insert(topic withPost post) >>
|
||||
$update(categ withTopic post)
|
||||
}
|
||||
|
||||
def show(slug: String, page: Int): Fu[Option[(Categ, Paginator[TopicView])]] =
|
||||
def show(slug: String, page: Int, troll: Boolean): Fu[Option[(Categ, Paginator[TopicView])]] =
|
||||
optionT(CategRepo bySlug slug) flatMap { categ ⇒
|
||||
optionT(env.topicApi.paginator(categ, page) map { (categ, _).some })
|
||||
optionT(env.topicApi.paginator(categ, page, troll) map { (categ, _).some })
|
||||
}
|
||||
|
||||
def denormalize(categ: Categ): Funit = for {
|
||||
|
@ -73,10 +70,17 @@ private[forum] final class CategApi(env: Env) extends OptionTs {
|
|||
topicIds = topics map (_.id)
|
||||
nbPosts ← PostRepo countByTopics topicIds
|
||||
lastPost ← PostRepo lastByTopics topicIds
|
||||
topicsTroll ← TopicRepoTroll byCateg categ
|
||||
topicIdsTroll = topicsTroll map (_.id)
|
||||
nbPostsTroll ← PostRepoTroll countByTopics topicIdsTroll
|
||||
lastPostTroll ← PostRepoTroll lastByTopics topicIdsTroll
|
||||
_ ← $update(categ.copy(
|
||||
nbTopics = topics.size,
|
||||
nbPosts = nbPosts,
|
||||
lastPostId = lastPost zmap (_.id)
|
||||
lastPostId = lastPost zmap (_.id),
|
||||
nbTopicsTroll = topicsTroll.size,
|
||||
nbPostsTroll = nbPostsTroll,
|
||||
lastPostIdTroll = lastPostTroll zmap (_.id)
|
||||
))
|
||||
} yield ()
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ case class Post(
|
|||
|
||||
def showAuthor = (author map (_.trim) filter ("" !=)) | User.anonymous
|
||||
|
||||
def showUsernameOrAuthor = userId | showAuthor
|
||||
def showUserIdOrAuthor = userId | showAuthor
|
||||
|
||||
def isTeam = categId startsWith teamSlug("")
|
||||
|
||||
|
|
|
@ -34,14 +34,9 @@ final class PostApi(
|
|||
categId = categ.id)
|
||||
$insert(post) >>
|
||||
// denormalize topic
|
||||
$update(topic.copy(
|
||||
nbPosts = topic.nbPosts + 1,
|
||||
lastPostId = post.id,
|
||||
updatedAt = post.createdAt)) >>
|
||||
$update(topic withPost post) >>
|
||||
// denormalize categ
|
||||
$update(categ.copy(
|
||||
nbPosts = categ.nbPosts + 1,
|
||||
lastPostId = post.id)) >>-
|
||||
$update(categ withTopic post) >>-
|
||||
(indexer ! InsertPost(post)) >>
|
||||
env.recent.invalidate inject post
|
||||
}
|
||||
|
@ -84,9 +79,9 @@ final class PostApi(
|
|||
def lastPageOf(topic: Topic) =
|
||||
math.ceil(topic.nbPosts / maxPerPage.toFloat).toInt
|
||||
|
||||
def paginator(topic: Topic, page: Int): Fu[Paginator[Post]] = Paginator(
|
||||
def paginator(topic: Topic, page: Int, troll: Boolean): Fu[Paginator[Post]] = Paginator(
|
||||
new Adapter(
|
||||
selector = PostRepo selectTopic topic,
|
||||
selector = PostRepo(troll) selectTopic topic,
|
||||
sort = PostRepo.sortQuery :: Nil),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage)
|
||||
|
|
|
@ -6,7 +6,19 @@ import tube.postTube
|
|||
|
||||
import play.api.libs.json.Json
|
||||
|
||||
object PostRepo {
|
||||
object PostRepo extends PostRepo(false) {
|
||||
|
||||
def apply(troll: Boolean): PostRepo = troll.fold(PostRepoTroll, PostRepo)
|
||||
}
|
||||
|
||||
object PostRepoTroll extends PostRepo(true)
|
||||
|
||||
sealed abstract class PostRepo(troll: Boolean) {
|
||||
|
||||
private lazy val trollFilter = troll.fold(
|
||||
Json.obj(),
|
||||
Json.obj("troll" -> false)
|
||||
)
|
||||
|
||||
def isFirstPost(topicId: String, postId: String): Fu[Boolean] =
|
||||
$primitive.one(
|
||||
|
@ -27,10 +39,10 @@ object PostRepo {
|
|||
def removeByTopic(topicId: String): Fu[Unit] =
|
||||
$remove(selectTopic(topicId))
|
||||
|
||||
def selectTopic(topicId: String) = Json.obj("topicId" -> topicId)
|
||||
def selectTopics(topicIds: List[String]) = Json.obj("topicId" -> $in(topicIds))
|
||||
def selectTopic(topicId: String) = Json.obj("topicId" -> topicId) ++ trollFilter
|
||||
def selectTopics(topicIds: List[String]) = Json.obj("topicId" -> $in(topicIds)) ++ trollFilter
|
||||
|
||||
def selectCategs(categIds: List[String]) = Json.obj("categId" -> $in(categIds))
|
||||
def selectCategs(categIds: List[String]) = Json.obj("categId" -> $in(categIds)) ++ trollFilter
|
||||
|
||||
def sortQuery = $sort.createdAsc
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ case class Topic(
|
|||
troll: Boolean,
|
||||
closed: Boolean) {
|
||||
|
||||
def updatedAt(troll: Boolean): DateTime = troll.fold(updatedAtTroll, updatedAt)
|
||||
def nbPosts(troll: Boolean): Int = troll.fold(nbPostsTroll, nbPosts)
|
||||
def lastPostId(troll: Boolean): String = troll.fold(lastPostIdTroll, lastPostId)
|
||||
|
||||
def open = !closed
|
||||
|
||||
def withPost(post: Post): Topic = copy(
|
||||
|
|
|
@ -16,12 +16,15 @@ private[forum] final class TopicApi(
|
|||
maxPerPage: Int,
|
||||
modLog: lila.mod.ModlogApi) extends OptionTs {
|
||||
|
||||
def show(categSlug: String, slug: String, page: Int): Fu[Option[(Categ, Topic, Paginator[Post])]] =
|
||||
def show(categSlug: String, slug: String, page: Int, troll: Boolean): Fu[Option[(Categ, Topic, Paginator[Post])]] =
|
||||
for {
|
||||
data ← get(categSlug, slug)
|
||||
data ← (for {
|
||||
categ ← optionT(CategRepo bySlug categSlug)
|
||||
topic ← optionT(TopicRepo(troll).byTree(categSlug, slug))
|
||||
} yield categ -> topic).value
|
||||
res ← data zmap {
|
||||
case (categ, topic) ⇒ (TopicRepo incViews topic) >>
|
||||
(env.postApi.paginator(topic, page) map { (categ, topic, _).some })
|
||||
(env.postApi.paginator(topic, page, troll) map { (categ, topic, _).some })
|
||||
}
|
||||
} yield res
|
||||
|
||||
|
@ -50,18 +53,13 @@ private[forum] final class TopicApi(
|
|||
env.recent.invalidate inject topic
|
||||
}
|
||||
|
||||
def get(categSlug: String, slug: String): Fu[Option[(Categ, Topic)]] = for {
|
||||
categ ← optionT(CategRepo bySlug categSlug)
|
||||
topic ← optionT(TopicRepo.byTree(categSlug, slug))
|
||||
} yield categ -> topic
|
||||
|
||||
def paginator(categ: Categ, page: Int): Fu[Paginator[TopicView]] = Paginator(
|
||||
def paginator(categ: Categ, page: Int, troll: Boolean): Fu[Paginator[TopicView]] = Paginator(
|
||||
adapter = new Adapter[Topic](
|
||||
selector = TopicRepo byCategQuery categ,
|
||||
selector = TopicRepo(troll) byCategQuery categ,
|
||||
sort = Seq($sort.createdDesc)
|
||||
) mapFuture { topic ⇒
|
||||
$find.byId[Post](topic.lastPostId) map { post ⇒
|
||||
TopicView(categ, topic, post, env.postApi lastPageOf topic)
|
||||
$find.byId[Post](topic lastPostId troll) map { post ⇒
|
||||
TopicView(categ, topic, post, env.postApi lastPageOf topic, troll)
|
||||
}
|
||||
},
|
||||
currentPage = page,
|
||||
|
@ -81,10 +79,15 @@ private[forum] final class TopicApi(
|
|||
def denormalize(topic: Topic): Funit = for {
|
||||
nbPosts ← PostRepo countByTopics List(topic)
|
||||
lastPost ← PostRepo lastByTopics List(topic)
|
||||
nbPostsTroll ← PostRepoTroll countByTopics List(topic)
|
||||
lastPostTroll ← PostRepoTroll lastByTopics List(topic)
|
||||
_ ← $update(topic.copy(
|
||||
nbPosts = nbPosts,
|
||||
lastPostId = lastPost zmap (_.id),
|
||||
updatedAt = lastPost.fold(topic.updatedAt)(_.createdAt)
|
||||
updatedAt = lastPost.fold(topic.updatedAt)(_.createdAt),
|
||||
nbPostsTroll = nbPostsTroll,
|
||||
lastPostIdTroll = lastPostTroll zmap (_.id),
|
||||
updatedAtTroll = lastPostTroll.fold(topic.updatedAtTroll)(_.createdAt)
|
||||
))
|
||||
} yield ()
|
||||
|
||||
|
|
|
@ -6,7 +6,19 @@ import tube.topicTube
|
|||
|
||||
import play.api.libs.json.Json
|
||||
|
||||
object TopicRepo {
|
||||
object TopicRepo extends TopicRepo(false) {
|
||||
|
||||
def apply(troll: Boolean): TopicRepo = troll.fold(TopicRepoTroll, TopicRepo)
|
||||
}
|
||||
|
||||
object TopicRepoTroll extends TopicRepo(true)
|
||||
|
||||
sealed abstract class TopicRepo(troll: Boolean) {
|
||||
|
||||
private lazy val trollFilter = troll.fold(
|
||||
Json.obj(),
|
||||
Json.obj("troll" -> false)
|
||||
)
|
||||
|
||||
def close(id: String, value: Boolean): Funit =
|
||||
$update.field(id, "closed", value)
|
||||
|
@ -15,11 +27,12 @@ object TopicRepo {
|
|||
$find(byCategQuery(categ))
|
||||
|
||||
def byTree(categSlug: String, slug: String): Fu[Option[Topic]] =
|
||||
$find one Json.obj("categId" -> categSlug, "slug" -> slug)
|
||||
$find.one(Json.obj("categId" -> categSlug, "slug" -> slug) ++ trollFilter)
|
||||
|
||||
def nextSlug(categ: Categ, name: String, it: Int = 1): Fu[String] = {
|
||||
val slug = lila.common.String.slugify(name) + ~(it == 1).option("-" + it)
|
||||
byTree(categ.slug, slug) flatMap {
|
||||
// also take troll topic into accounts
|
||||
TopicRepoTroll.byTree(categ.slug, slug) flatMap {
|
||||
_.isDefined.fold(
|
||||
nextSlug(categ, name, it + 1),
|
||||
fuccess(slug)
|
||||
|
@ -30,5 +43,5 @@ object TopicRepo {
|
|||
def incViews(topic: Topic): Funit =
|
||||
$update($select(topic.id), $inc("views" -> 1))
|
||||
|
||||
def byCategQuery(categ: Categ) = Json.obj("categId" -> categ.slug)
|
||||
def byCategQuery(categ: Categ) = Json.obj("categId" -> categ.slug) ++ trollFilter
|
||||
}
|
||||
|
|
|
@ -2,27 +2,34 @@ package lila.forum
|
|||
|
||||
case class CategView(
|
||||
categ: Categ,
|
||||
lastPost: Option[(Topic, Post, Int)]) {
|
||||
lastPost: Option[(Topic, Post, Int)],
|
||||
troll: Boolean) {
|
||||
|
||||
def nbTopics = categ nbTopics troll
|
||||
def nbPosts = categ nbPosts troll
|
||||
def lastPostId = categ lastPostId troll
|
||||
|
||||
def slug = categ.slug
|
||||
def name = categ.name
|
||||
def desc = categ.desc
|
||||
def nbTopics = categ.nbTopics
|
||||
def nbPosts = categ.nbPosts
|
||||
}
|
||||
|
||||
case class TopicView(
|
||||
categ: Categ,
|
||||
topic: Topic,
|
||||
lastPost: Option[Post],
|
||||
lastPage: Int) {
|
||||
lastPage: Int,
|
||||
troll: Boolean) {
|
||||
|
||||
def updatedAt = topic updatedAt troll
|
||||
def nbPosts = topic nbPosts troll
|
||||
def lastPostId = topic lastPostId troll
|
||||
|
||||
def id = topic.id
|
||||
def slug = topic.slug
|
||||
def name = topic.name
|
||||
def views = topic.views
|
||||
def createdAt = topic.createdAt
|
||||
def nbPosts = topic.nbPosts
|
||||
}
|
||||
|
||||
case class PostView(
|
||||
|
@ -31,7 +38,7 @@ case class PostView(
|
|||
categ: Categ,
|
||||
topicLastPage: Int) {
|
||||
|
||||
def show = post.showUsernameOrAuthor + " @ " + topic.name + " - " + post.text.take(80)
|
||||
def show = post.showUserIdOrAuthor + " @ " + topic.name + " - " + post.text.take(80)
|
||||
}
|
||||
|
||||
case class PostLiteView(post: Post, topic: Topic, topicLastPage: Int)
|
||||
|
|
|
@ -742,6 +742,9 @@ div.checkmateSection {
|
|||
float: left;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
div.checkmateSection label {
|
||||
width: 310px;
|
||||
}
|
||||
div.checkmateSection div.result {
|
||||
display: none;
|
||||
height: 48px;
|
||||
|
|
Loading…
Reference in New Issue