lila/app/controllers/Team.scala

278 lines
8.5 KiB
Scala
Raw Normal View History

package controllers
2019-10-02 10:50:09 -06:00
import play.api.libs.json._
import play.api.mvc._
2015-04-12 00:59:13 -06:00
import lila.api.Context
import lila.app._
2019-12-04 23:52:53 -07:00
import lila.common.config.MaxPerSecond
2019-12-08 10:35:26 -07:00
import lila.hub.LightTeam
2015-04-12 00:59:13 -06:00
import lila.security.Granter
2019-12-04 23:52:53 -07:00
import lila.team.{ Joined, Motivate, Team => TeamModel }
2014-02-17 02:12:19 -07:00
import lila.user.{ User => UserModel }
import views._
2019-12-04 23:52:53 -07:00
final class Team(
env: Env,
2019-12-05 14:51:18 -07:00
apiC: => Api
2019-12-04 23:52:53 -07:00
) extends LilaController(env) {
2019-12-13 07:30:20 -07:00
private def forms = env.team.forms
private def api = env.team.api
2019-12-04 16:39:16 -07:00
private def paginator = env.team.paginator
2013-04-09 12:58:34 -06:00
2015-09-09 09:32:49 -06:00
def all(page: Int) = Open { implicit ctx =>
2019-10-01 10:11:01 -06:00
paginator popularTeams page map { html.team.list.all(_) }
2013-05-06 14:49:12 -06:00
}
2015-09-09 09:32:49 -06:00
def home(page: Int) = Open { implicit ctx =>
2019-10-01 10:11:01 -06:00
ctx.me.??(api.hasTeams) map {
2019-12-13 07:30:20 -07:00
case true => Redirect(routes.Team.mine)
2019-10-01 10:11:01 -06:00
case false => Redirect(routes.Team.all(page))
2015-09-09 09:32:49 -06:00
}
}
2014-02-17 02:12:19 -07:00
def show(id: String, page: Int) = Open { implicit ctx =>
2019-10-01 10:11:01 -06:00
OptionFuOk(api team id) { renderTeam(_, page) }
2013-05-06 14:49:12 -06:00
}
2014-02-17 02:12:19 -07:00
def search(text: String, page: Int) = OpenBody { implicit ctx =>
2019-12-13 07:30:20 -07:00
if (text.trim.isEmpty) paginator popularTeams page map { html.team.list.all(_) } else
env.teamSearch(text, page) map { html.team.list.search(text, _) }
2013-05-06 14:49:12 -06:00
}
2019-12-13 07:30:20 -07:00
private def renderTeam(team: TeamModel, page: Int = 1)(implicit ctx: Context) =
for {
info <- env.teamInfo(team, ctx.me)
members <- paginator.teamMembers(team, page)
_ <- env.user.lightUserApi preloadMany info.userIds
} yield html.team.show(team, members, info)
2013-05-06 14:49:12 -06:00
2020-01-21 17:52:24 -07:00
def users(teamId: String) = Action.async { implicit req =>
2019-12-04 23:52:53 -07:00
api.team(teamId) flatMap {
2018-05-08 20:38:45 -06:00
_ ?? { team =>
2020-01-21 17:52:24 -07:00
apiC.jsonStream {
env.team
.memberStream(team, MaxPerSecond(20))
.map(env.api.userApi.one)
}.fuccess
}
}
}
2019-12-08 11:12:00 -07:00
def edit(id: String) = Auth { implicit ctx => _ =>
2017-01-26 04:05:21 -07:00
OptionFuResult(api team id) { team =>
2019-04-17 08:52:54 -06:00
Owner(team) { fuccess(html.team.form.edit(team, forms edit team)) }
2017-01-26 04:05:21 -07:00
}
2013-05-06 14:49:12 -06:00
}
2017-01-26 04:05:21 -07:00
def update(id: String) = AuthBody { implicit ctx => implicit me =>
OptionFuResult(api team id) { team =>
Owner(team) {
implicit val req = ctx.body
2019-12-13 07:30:20 -07:00
forms
.edit(team)
.bindFromRequest
.fold(
err => BadRequest(html.team.form.edit(team, err)).fuccess,
data => api.update(team, data, me) inject Redirect(routes.Team.show(team.id))
)
2017-01-26 04:05:21 -07:00
}
2013-05-06 14:49:12 -06:00
}
}
2017-01-26 04:05:21 -07:00
def kickForm(id: String) = Auth { implicit ctx => me =>
OptionFuResult(api team id) { team =>
Owner(team) {
2019-12-04 23:52:53 -07:00
env.team.memberRepo userIdsByTeam team.id map { userIds =>
2019-04-17 08:52:54 -06:00
html.team.admin.kick(team, userIds - me.id)
2017-01-26 04:05:21 -07:00
}
2013-05-06 14:49:12 -06:00
}
}
}
2017-01-26 04:05:21 -07:00
def kick(id: String) = AuthBody { implicit ctx => me =>
OptionFuResult(api team id) { team =>
Owner(team) {
implicit val req = ctx.body
2019-12-13 07:30:20 -07:00
forms.selectMember.bindFromRequest.value ?? { api.kick(team, _, me) } inject Redirect(
routes.Team.show(team.id)
)
2017-01-26 04:05:21 -07:00
}
2013-05-06 14:49:12 -06:00
}
}
2019-12-08 11:12:00 -07:00
def kickUser(teamId: String, userId: String) = Scoped(_.Team.Write) { _ => me =>
2019-07-23 06:39:25 -06:00
api team teamId flatMap {
_ ?? { team =>
if (team isCreator me.id) api.kick(team, userId, me) inject jsonOkResult
else Forbidden(jsonError("Not your team")).fuccess
}
}
}
2013-05-06 14:49:12 -06:00
2019-12-08 11:12:00 -07:00
def changeOwnerForm(id: String) = Auth { implicit ctx => _ =>
OptionFuResult(api team id) { team =>
Owner(team) {
2019-12-04 23:52:53 -07:00
env.team.memberRepo userIdsByTeam team.id map { userIds =>
2019-04-17 08:52:54 -06:00
html.team.admin.changeOwner(team, userIds - team.createdBy)
}
}
}
}
def changeOwner(id: String) = AuthBody { implicit ctx => me =>
OptionFuResult(api team id) { team =>
Owner(team) {
implicit val req = ctx.body
2019-12-13 07:30:20 -07:00
forms.selectMember.bindFromRequest.value ?? { api.changeOwner(team, _, me) } inject Redirect(
routes.Team.show(team.id)
)
}
}
}
2018-07-14 04:48:37 -06:00
def close(id: String) = Secure(_.ManageTeam) { implicit ctx => me =>
2017-01-15 05:26:08 -07:00
OptionFuResult(api team id) { team =>
(api delete team) >>
2019-12-04 16:39:16 -07:00
env.mod.logApi.deleteTeam(me.id, team.name, team.description) inject
2017-01-15 05:26:08 -07:00
Redirect(routes.Team all 1)
}
}
2017-01-15 05:26:08 -07:00
def form = Auth { implicit ctx => me =>
2019-10-01 10:11:01 -06:00
OnePerWeek(me) {
forms.anyCaptcha map { captcha =>
Ok(html.team.form.create(forms.create, captcha))
2013-05-06 14:49:12 -06:00
}
2017-01-15 05:26:08 -07:00
}
2013-05-06 14:49:12 -06:00
}
2017-01-26 04:05:21 -07:00
def create = AuthBody { implicit ctx => implicit me =>
OnePerWeek(me) {
implicit val req = ctx.body
forms.create.bindFromRequest.fold(
2019-12-13 07:30:20 -07:00
err =>
forms.anyCaptcha map { captcha =>
BadRequest(html.team.form.create(err, captcha))
},
data =>
api.create(data, me) map { team =>
Redirect(routes.Team.show(team.id)): Result
2019-12-13 07:30:20 -07:00
}
2017-01-26 04:05:21 -07:00
)
}
2013-05-06 14:49:12 -06:00
}
2017-01-26 04:05:21 -07:00
def mine = Auth { implicit ctx => me =>
2019-04-17 08:52:54 -06:00
api mine me map { html.team.list.mine(_) }
2013-05-06 14:49:12 -06:00
}
2019-07-16 12:03:10 -06:00
def join(id: String) = AuthOrScoped(_.Team.Write)(
2019-12-13 07:30:20 -07:00
auth = ctx =>
me =>
api.join(id, me) flatMap {
case Some(Joined(team)) => Redirect(routes.Team.show(team.id)).fuccess
case Some(Motivate(team)) => Redirect(routes.Team.requestForm(team.id)).fuccess
case _ => notFound(ctx)
},
scoped = req =>
me =>
env.oAuth.server.fetchAppAuthor(req) flatMap {
_ ?? { api.joinApi(id, me, _) }
} map {
case Some(Joined(_)) => jsonOkResult
case Some(Motivate(_)) =>
Forbidden(jsonError("This team requires confirmation, and is not owned by the oAuth app owner."))
case _ => NotFound(jsonError("Team not found"))
}
2019-07-16 12:03:10 -06:00
)
2013-05-06 14:49:12 -06:00
2017-01-26 04:05:21 -07:00
def requests = Auth { implicit ctx => me =>
2019-12-23 13:19:37 -07:00
import lila.memo.CacheApi._
2019-12-04 16:39:16 -07:00
env.team.cached.nbRequests invalidate me.id
2019-04-17 08:52:54 -06:00
api requestsWithUsers me map { html.team.request.all(_) }
2013-05-06 14:49:12 -06:00
}
2017-01-26 04:05:21 -07:00
def requestForm(id: String) = Auth { implicit ctx => me =>
OptionFuOk(api.requestable(id, me)) { team =>
2019-04-17 08:52:54 -06:00
forms.anyCaptcha map { html.team.request.requestForm(team, forms.request, _) }
2017-01-26 04:05:21 -07:00
}
2017-01-15 05:26:08 -07:00
}
2017-01-26 04:05:21 -07:00
def requestCreate(id: String) = AuthBody { implicit ctx => me =>
OptionFuResult(api.requestable(id, me)) { team =>
2013-05-06 14:49:12 -06:00
implicit val req = ctx.body
2017-01-26 04:05:21 -07:00
forms.request.bindFromRequest.fold(
2019-12-13 07:30:20 -07:00
err =>
forms.anyCaptcha map { captcha =>
BadRequest(html.team.request.requestForm(team, err, captcha))
},
2017-01-26 04:05:21 -07:00
setup => api.createRequest(team, setup, me) inject Redirect(routes.Team.show(team.id))
2013-05-06 14:49:12 -06:00
)
}
}
2017-01-26 04:05:21 -07:00
def requestProcess(requestId: String) = AuthBody { implicit ctx => me =>
OptionFuRedirectUrl(for {
2019-11-28 11:20:59 -07:00
requestOption <- api request requestId
2019-12-13 07:30:20 -07:00
teamOption <- requestOption.??(req => env.team.teamRepo.owned(req.team, me.id))
2017-01-26 04:05:21 -07:00
} yield (teamOption |@| requestOption).tupled) {
case (team, request) => {
implicit val req = ctx.body
forms.processRequest.bindFromRequest.fold(
_ => fuccess(routes.Team.show(team.id).toString), {
case (decision, url) =>
api.processRequest(team, request, (decision === "accept")) inject url
}
)
}
}
2017-01-15 05:26:08 -07:00
}
2017-01-26 04:05:21 -07:00
2019-07-16 14:45:14 -06:00
def quit(id: String) = AuthOrScoped(_.Team.Write)(
2019-12-13 07:30:20 -07:00
auth = ctx =>
me =>
OptionResult(api.quit(id, me)) { team =>
Redirect(routes.Team.show(team.id))
}(ctx),
scoped = _ =>
me =>
api.quit(id, me) flatMap {
_.fold(notFoundJson())(_ => jsonOkResult.fuccess)
}
2019-07-16 14:45:14 -06:00
)
2013-05-06 14:49:12 -06:00
2019-10-02 10:50:09 -06:00
def autocomplete = Action.async { req =>
get("term", req).filter(_.nonEmpty) match {
case None => BadRequest("No search term provided").fuccess
2019-12-13 07:30:20 -07:00
case Some(term) =>
for {
teams <- api.autocomplete(term, 10)
_ <- env.user.lightUserApi preloadMany teams.map(_.createdBy)
} yield Ok {
JsArray(teams map { team =>
Json.obj(
"id" -> team.id,
"name" -> team.name,
"owner" -> env.user.lightUserApi.sync(team.createdBy).fold(team.createdBy)(_.name),
"members" -> team.nbMembers
)
})
} as JSON
2019-10-02 10:50:09 -06:00
}
}
private def OnePerWeek[A <: Result](me: UserModel)(a: => Fu[A])(implicit ctx: Context): Fu[Result] =
2014-02-17 02:12:19 -07:00
api.hasCreatedRecently(me) flatMap { did =>
if (did && !Granter(_.ManageTeam)(me)) Forbidden(views.html.site.message.teamCreateLimit).fuccess
2018-07-20 04:21:06 -06:00
else a
2013-05-06 14:49:12 -06:00
}
2018-07-20 04:21:06 -06:00
private def Owner(team: TeamModel)(a: => Fu[Result])(implicit ctx: Context): Fu[Result] =
if (ctx.me.??(me => team.isCreator(me.id) || isGranted(_.ManageTeam))) a
else renderTeam(team) map { Forbidden(_) }
2019-10-02 04:20:44 -06:00
private[controllers] def teamsIBelongTo(me: lila.user.User): Fu[List[LightTeam]] =
api mine me map { _.map(_.light) }
}