diff --git a/app/core/CoreEnv.scala b/app/core/CoreEnv.scala index 968210043e..a81d582ce7 100644 --- a/app/core/CoreEnv.scala +++ b/app/core/CoreEnv.scala @@ -57,6 +57,7 @@ final class CoreEnv private (application: Application, val settings: Settings) { sendMessage = message.api.lichessThread, makeForum = forum.categApi.makeTeam, getForumNbPosts = forum.categApi.getTeamNbPosts, + getForumPosts = forum.recent.team, mongodb = mongodb.apply _) lazy val lobby = new lila.lobby.LobbyEnv( diff --git a/app/forum/Categ.scala b/app/forum/Categ.scala index d86f551a90..480df70428 100644 --- a/app/forum/Categ.scala +++ b/app/forum/Categ.scala @@ -16,4 +16,6 @@ case class Categ( def isStaff = slug == "staff" def isTeam = team.nonEmpty + + def id = slug } diff --git a/app/forum/CategApi.scala b/app/forum/CategApi.scala index fe8ffd18cd..5e68eb29d1 100644 --- a/app/forum/CategApi.scala +++ b/app/forum/CategApi.scala @@ -40,7 +40,8 @@ final class CategApi(env: ForumEnv) { userId = "lichess".some, ip = none, text = "Welcome to the %s forum!\nOnly members of the team can post here" format name, - number = 1) + number = 1, + categId = categ.id) _ ← env.categRepo saveIO categ _ ← env.postRepo saveIO post // denormalize topic diff --git a/app/forum/CategRepo.scala b/app/forum/CategRepo.scala index 071f10760c..edab57ee17 100644 --- a/app/forum/CategRepo.scala +++ b/app/forum/CategRepo.scala @@ -14,6 +14,10 @@ final class CategRepo( findOneById(slug) } + def byIds(ids: Iterable[String]): IO[List[Categ]] = io { + find("_id" $in ids).toList + } + val all: IO[List[Categ]] = io { find(DBObject()).sort(DBObject("pos" -> 1)).toList } diff --git a/app/forum/Post.scala b/app/forum/Post.scala index 5a1d890840..abf5ccfbcd 100644 --- a/app/forum/Post.scala +++ b/app/forum/Post.scala @@ -10,6 +10,7 @@ import user.User case class Post( @Key("_id") id: String, topicId: String, + categId: String, author: Option[String], userId: Option[String], ip: Option[String], @@ -26,6 +27,7 @@ object Post { def apply( topicId: String, + categId: String, author: Option[String], userId: Option[String], ip: Option[String], @@ -38,5 +40,6 @@ object Post { ip = ip, text = text, number = number, - createdAt = DateTime.now) + createdAt = DateTime.now, + categId = categId) } diff --git a/app/forum/PostApi.scala b/app/forum/PostApi.scala index 7c5129867e..3a1bbbc825 100644 --- a/app/forum/PostApi.scala +++ b/app/forum/PostApi.scala @@ -33,7 +33,8 @@ final class PostApi( userId = ctx.me map (_.id), ip = ctx.isAnon option ctx.req.remoteAddress, text = data.text, - number = number + 1) + number = number + 1, + categId = categ.id) _ ← env.postRepo saveIO post // denormalize topic _ ← env.topicRepo saveIO topic.copy( @@ -55,15 +56,18 @@ final class PostApi( ) } yield (topicOption |@| postOption).tupled - def view(post: Post): IO[Option[PostView]] = for { - topicOption ← env.topicRepo byId post.topicId - categOption ← topicOption.fold( - topic ⇒ env.categRepo bySlug topic.categId, - io(none[Categ]) - ) - } yield topicOption |@| categOption apply { - case (topic, categ) ⇒ PostView(post, topic, categ, lastPageOf(topic)) - } + def views(posts: List[Post]): IO[List[PostView]] = for { + topics ← env.topicRepo byIds posts.map(_.topicId).distinct + categs ← env.categRepo byIds topics.map(_.categId).distinct + } yield (for { + post ← posts + } yield for { + topic ← topics find (_.id == post.topicId) + categ ← categs find (_.slug == topic.categId) + } yield PostView(post, topic, categ, lastPageOf(topic)) + ).flatten + + def view(post: Post): IO[Option[PostView]] = views(List(post)) map (_.headOption) def lastNumberOf(topic: Topic): IO[Int] = env.postRepo lastByTopics List(topic) map (_.number) @@ -96,7 +100,7 @@ final class PostApi( } yield () ) post = view.post - _ ← modLog.deletePost(mod, post.userId, post.author, post.ip, + _ ← modLog.deletePost(mod, post.userId, post.author, post.ip, text = "%s / %s / %s".format(view.categ.name, view.topic.name, post.text)) } yield (), io() diff --git a/app/forum/PostRepo.scala b/app/forum/PostRepo.scala index c8727e81c1..bbc8d830d5 100644 --- a/app/forum/PostRepo.scala +++ b/app/forum/PostRepo.scala @@ -26,8 +26,8 @@ final class PostRepo( find(DBObject()).toList } - def recent(nb: Int): IO[List[Post]] = io { - find(DBObject()).sort(sortQuery(-1)).limit(nb).toList.reverse + def recentInCategs(nb: Int)(categIds: List[String]): IO[List[Post]] = io { + find("categId" $in categIds).sort(sortQuery(-1)).limit(nb).toList } val sortQuery: DBObject = sortQuery(1) diff --git a/app/forum/Recent.scala b/app/forum/Recent.scala index f5f6ae92e4..874225a7d5 100644 --- a/app/forum/Recent.scala +++ b/app/forum/Recent.scala @@ -9,22 +9,40 @@ import scalaz.effects._ final class Recent(postRepo: PostRepo, postApi: PostApi, timeout: Int) { - val nb = 30 + private val nb = 20 - private val cache = Builder.cache[Boolean, List[PostView]](timeout, staff ⇒ - fetch(staff).unsafePerformIO + private val Public = "$public" + private val Staff = "$staff" + private def teamSlug(id: String) = "team-" + id + + private lazy val publicCategIds = List( + "game-analysis", + "general-chess-discussion", + "lichess-feedback", + "off-topic-discussion") + + private lazy val staffCategIds = "staff" :: publicCategIds + + private val cache = Builder.cache[String, List[PostView]](timeout, key ⇒ + fetch(key).unsafePerformIO ) def apply(user: Option[User]): IO[List[PostView]] = io { - cache get Granter.option(Permission.StaffForum)(user) + cache get Granter.option(Permission.StaffForum)(user).fold(Staff, Public) + } + + def team(teamId: String): IO[List[PostView]] = io { + cache get teamId } val invalidate: IO[Unit] = io(cache.invalidateAll) - private def fetch(staff: Boolean): IO[List[PostView]] = for { - posts ← postRepo.recent(nb) - views ← (posts map postApi.view).sequence - } yield views collect { - case Some(v) if (staff || v.categ.slug != "staff") ⇒ v - } + private def fetch(key: String): IO[List[PostView]] = for { + posts ← postRepo.recentInCategs(nb)(key match { + case Public ⇒ publicCategIds + case Staff ⇒ staffCategIds + case teamId ⇒ List(teamSlug(teamId)) + }) + views ← postApi views posts + } yield views } diff --git a/app/forum/TopicApi.scala b/app/forum/TopicApi.scala index 611acdcd36..f02d913987 100644 --- a/app/forum/TopicApi.scala +++ b/app/forum/TopicApi.scala @@ -33,7 +33,8 @@ final class TopicApi(env: ForumEnv, maxPerPage: Int) { userId = ctx.me map (_.id), ip = ctx.isAnon option ctx.req.remoteAddress, text = data.post.text, - number = 1) + number = 1, + categId = categ.id) _ ← env.postRepo saveIO post // denormalize topic _ ← env.topicRepo saveIO topic.copy( diff --git a/app/forum/TopicRepo.scala b/app/forum/TopicRepo.scala index a80ee8a906..eff85cb069 100644 --- a/app/forum/TopicRepo.scala +++ b/app/forum/TopicRepo.scala @@ -14,6 +14,10 @@ final class TopicRepo( findOneById(id) } + def byIds(ids: Iterable[String]): IO[List[Topic]] = io { + find("_id" $in ids).toList + } + def byCateg(categ: Categ): IO[List[Topic]] = io { find(DBObject("categId" -> categ.slug)).toList } diff --git a/app/team/TeamEnv.scala b/app/team/TeamEnv.scala index a893520f42..501fb68146 100644 --- a/app/team/TeamEnv.scala +++ b/app/team/TeamEnv.scala @@ -5,7 +5,7 @@ import core.Settings import site.Captcha import user.UserRepo import message.LichessThread -import forum.Categ +import forum.{ Categ, PostView } import com.mongodb.casbah.MongoCollection import scalaz.effects._ @@ -17,6 +17,7 @@ final class TeamEnv( sendMessage: LichessThread ⇒ IO[Unit], makeForum: (String, String) ⇒ IO[Unit], getForumNbPosts: String ⇒ IO[Int], + getForumPosts: String ⇒ IO[List[PostView]], mongodb: String ⇒ MongoCollection) { import settings._ @@ -51,7 +52,8 @@ final class TeamEnv( memberRepo = memberRepo, requestRepo = requestRepo, userRepo = userRepo, - getForumNbPosts = getForumNbPosts) _ + getForumNbPosts = getForumNbPosts, + getForumPosts = getForumPosts) _ lazy val forms = new DataForm(teamRepo, captcha) diff --git a/app/team/TeamInfo.scala b/app/team/TeamInfo.scala index 86b4825a3a..0be0ac69b4 100644 --- a/app/team/TeamInfo.scala +++ b/app/team/TeamInfo.scala @@ -3,7 +3,7 @@ package team import user.{ User, UserRepo } import game.{ GameRepo, DbGame } -import forum.Categ +import forum.PostView import http.Context import scalaz.effects._ @@ -15,15 +15,8 @@ case class TeamInfo( requests: List[RequestWithUser], bestPlayers: List[User], averageElo: Int, - forumNbPosts: Int) { - // rank: Option[(Int, Int)], - // nbPlaying: Int, - // nbWithMe: Option[Int], - // nbBookmark: Int, - // eloWithMe: Option[List[(String, Int)]], - // eloChart: Option[EloChart], - // winChart: Option[WinChart], - // spy: Option[TeamSpy]) { + forumNbPosts: Int, + forumPosts: List[PostView]) { def hasRequests = requests.nonEmpty } @@ -35,7 +28,8 @@ object TeamInfo { memberRepo: MemberRepo, requestRepo: RequestRepo, userRepo: UserRepo, - getForumNbPosts: String ⇒ IO[Int])(team: Team, me: Option[User]): IO[TeamInfo] = for { + getForumNbPosts: String ⇒ IO[Int], + getForumPosts: String ⇒ IO[List[PostView]])(team: Team, me: Option[User]): IO[TeamInfo] = for { mine ← ~me.map(api.belongsTo(team, _)) requestedByMe ← ~me.map(m ⇒ requestRepo.exists(team.id, m.id)) doUnless mine requests ← api.requestsWithUsers(team) doIf { @@ -45,6 +39,7 @@ object TeamInfo { bestPlayers ← userRepo.byIdsSortByElo(userIds, 5) averageElo ← userRepo.idsAverageElo(userIds) forumNbPosts ← getForumNbPosts(team.id) + forumPosts ← getForumPosts(team.id) } yield TeamInfo( mine = mine, createdByMe = ~me.map(m ⇒ team.isCreator(m.id)), @@ -52,5 +47,6 @@ object TeamInfo { requests = requests, bestPlayers = bestPlayers, averageElo = averageElo, - forumNbPosts = forumNbPosts) + forumNbPosts = forumNbPosts, + forumPosts = forumPosts) } diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala index c3899c2064..44dd20106f 100644 --- a/app/templating/AssetHelper.scala +++ b/app/templating/AssetHelper.scala @@ -7,7 +7,7 @@ import play.api.templates.Html trait AssetHelper { - val assetVersion = 17 + val assetVersion = 18 def cssTag(name: String) = css("stylesheets/" + name) diff --git a/app/views/forum/post/recent.scala.html b/app/views/forum/post/recent.scala.html index 5fc2e040e8..35fa6d7630 100644 --- a/app/views/forum/post/recent.scala.html +++ b/app/views/forum/post/recent.scala.html @@ -1,5 +1,5 @@ @(posts: List[lila.forum.PostView])(implicit ctx: Context) -@for(v <- posts) { +@for(v <- posts.reverse) { @defining(routes.ForumTopic.show(v.categ.slug, v.topic.slug, v.topicLastPage) + "#" + v.post.number) { postUrl =>
@trans.averageElo(): @info.averageElo
@trans.teamLeader(): @userIdLinkMini(t.createdBy)
-@shorten(v.post.text, 200)
+