257 lines
8.3 KiB
Scala
257 lines
8.3 KiB
Scala
package controllers
|
|
|
|
import play.api.libs.json._
|
|
import play.api.mvc._
|
|
|
|
import lila.api.Context
|
|
import lila.app._
|
|
import lila.common.{ HTTPRequest, MaxPerSecond }
|
|
import lila.hub.lightTeam._
|
|
import lila.security.Granter
|
|
import lila.team.{ Joined, Motivate, Team => TeamModel, TeamRepo, MemberRepo }
|
|
import lila.user.{ User => UserModel }
|
|
import views._
|
|
|
|
object Team extends LilaController {
|
|
|
|
private def forms = Env.team.forms
|
|
private def api = Env.team.api
|
|
private def paginator = Env.team.paginator
|
|
private lazy val teamInfo = Env.current.teamInfo
|
|
|
|
def all(page: Int) = Open { implicit ctx =>
|
|
paginator popularTeams page map { html.team.list.all(_) }
|
|
}
|
|
|
|
def home(page: Int) = Open { implicit ctx =>
|
|
ctx.me.??(api.hasTeams) map {
|
|
case true => Redirect(routes.Team.mine)
|
|
case false => Redirect(routes.Team.all(page))
|
|
}
|
|
}
|
|
|
|
def show(id: String, page: Int) = Open { implicit ctx =>
|
|
OptionFuOk(api team id) { renderTeam(_, page) }
|
|
}
|
|
|
|
def search(text: String, page: Int) = OpenBody { implicit ctx =>
|
|
if (text.trim.isEmpty) paginator popularTeams page map { html.team.list.all(_) }
|
|
else Env.teamSearch(text, page) map { html.team.list.search(text, _) }
|
|
}
|
|
|
|
private def renderTeam(team: TeamModel, page: Int = 1)(implicit ctx: Context) = for {
|
|
info <- teamInfo(team, ctx.me)
|
|
members <- paginator.teamMembers(team, page)
|
|
_ <- Env.user.lightUserApi preloadMany info.userIds
|
|
} yield html.team.show(team, members, info)
|
|
|
|
def users(teamId: String) = Action.async { req =>
|
|
import Api.limitedDefault
|
|
Env.team.api.team(teamId) flatMap {
|
|
_ ?? { team =>
|
|
Api.GlobalLinearLimitPerIP(HTTPRequest lastRemoteAddress req) {
|
|
import play.api.libs.iteratee._
|
|
Api.jsonStream {
|
|
Env.team.memberStream(team, MaxPerSecond(20)) &>
|
|
Enumeratee.map(Env.api.userApi.one)
|
|
} |> fuccess
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def edit(id: String) = Auth { implicit ctx => me =>
|
|
OptionFuResult(api team id) { team =>
|
|
Owner(team) { fuccess(html.team.form.edit(team, forms edit team)) }
|
|
}
|
|
}
|
|
|
|
def update(id: String) = AuthBody { implicit ctx => implicit me =>
|
|
OptionFuResult(api team id) { team =>
|
|
Owner(team) {
|
|
implicit val req = ctx.body
|
|
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))
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
def kickForm(id: String) = Auth { implicit ctx => me =>
|
|
OptionFuResult(api team id) { team =>
|
|
Owner(team) {
|
|
MemberRepo userIdsByTeam team.id map { userIds =>
|
|
html.team.admin.kick(team, userIds - me.id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
def kick(id: String) = AuthBody { implicit ctx => me =>
|
|
OptionFuResult(api team id) { team =>
|
|
Owner(team) {
|
|
implicit val req = ctx.body
|
|
forms.selectMember.bindFromRequest.value ?? { api.kick(team, _, me) } inject Redirect(routes.Team.show(team.id))
|
|
}
|
|
}
|
|
}
|
|
def kickUser(teamId: String, userId: String) = Scoped(_.Team.Write) { req => me =>
|
|
api team teamId flatMap {
|
|
_ ?? { team =>
|
|
if (team isCreator me.id) api.kick(team, userId, me) inject jsonOkResult
|
|
else Forbidden(jsonError("Not your team")).fuccess
|
|
}
|
|
}
|
|
}
|
|
|
|
def changeOwnerForm(id: String) = Auth { implicit ctx => me =>
|
|
OptionFuResult(api team id) { team =>
|
|
Owner(team) {
|
|
MemberRepo userIdsByTeam team.id map { userIds =>
|
|
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
|
|
forms.selectMember.bindFromRequest.value ?? { api.changeOwner(team, _, me) } inject Redirect(routes.Team.show(team.id))
|
|
}
|
|
}
|
|
}
|
|
|
|
def close(id: String) = Secure(_.ManageTeam) { implicit ctx => me =>
|
|
OptionFuResult(api team id) { team =>
|
|
(api delete team) >>
|
|
Env.mod.logApi.deleteTeam(me.id, team.name, team.description) inject
|
|
Redirect(routes.Team all 1)
|
|
}
|
|
}
|
|
|
|
def form = Auth { implicit ctx => me =>
|
|
OnePerWeek(me) {
|
|
forms.anyCaptcha map { captcha =>
|
|
Ok(html.team.form.create(forms.create, captcha))
|
|
}
|
|
}
|
|
}
|
|
|
|
def create = AuthBody { implicit ctx => implicit me =>
|
|
OnePerWeek(me) {
|
|
implicit val req = ctx.body
|
|
forms.create.bindFromRequest.fold(
|
|
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 }
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
def mine = Auth { implicit ctx => me =>
|
|
api mine me map { html.team.list.mine(_) }
|
|
}
|
|
|
|
def join(id: String) = AuthOrScoped(_.Team.Write)(
|
|
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"))
|
|
}
|
|
)
|
|
|
|
def requests = Auth { implicit ctx => me =>
|
|
Env.team.cached.nbRequests invalidate me.id
|
|
api requestsWithUsers me map { html.team.request.all(_) }
|
|
}
|
|
|
|
def requestForm(id: String) = Auth { implicit ctx => me =>
|
|
OptionFuOk(api.requestable(id, me)) { team =>
|
|
forms.anyCaptcha map { html.team.request.requestForm(team, forms.request, _) }
|
|
}
|
|
}
|
|
|
|
def requestCreate(id: String) = AuthBody { implicit ctx => me =>
|
|
OptionFuResult(api.requestable(id, me)) { team =>
|
|
implicit val req = ctx.body
|
|
forms.request.bindFromRequest.fold(
|
|
err => forms.anyCaptcha map { captcha =>
|
|
BadRequest(html.team.request.requestForm(team, err, captcha))
|
|
},
|
|
setup => api.createRequest(team, setup, me) inject Redirect(routes.Team.show(team.id))
|
|
)
|
|
}
|
|
}
|
|
|
|
def requestProcess(requestId: String) = AuthBody { implicit ctx => me =>
|
|
OptionFuRedirectUrl(for {
|
|
requestOption ← api request requestId
|
|
teamOption ← requestOption.??(req => TeamRepo.owned(req.team, me.id))
|
|
} 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
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
def quit(id: String) = AuthOrScoped(_.Team.Write)(
|
|
auth = ctx => me => OptionResult(api.quit(id, me)) { team =>
|
|
Redirect(routes.Team.show(team.id))
|
|
}(ctx),
|
|
scoped = req => me => api.quit(id, me) flatMap {
|
|
_.fold(notFoundJson())(_ => jsonOkResult.fuccess)
|
|
}
|
|
)
|
|
|
|
def autocomplete = Action.async { req =>
|
|
get("term", req).filter(_.nonEmpty) match {
|
|
case None => BadRequest("No search term provided").fuccess
|
|
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
|
|
}
|
|
}
|
|
|
|
private def OnePerWeek[A <: Result](me: UserModel)(a: => Fu[A])(implicit ctx: Context): Fu[Result] =
|
|
api.hasCreatedRecently(me) flatMap { did =>
|
|
if (did && !Granter(_.ManageTeam)(me)) Forbidden(views.html.site.message.teamCreateLimit).fuccess
|
|
else a
|
|
}
|
|
|
|
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(_) }
|
|
|
|
private[controllers] def teamsIBelongTo(me: lila.user.User): Fu[List[LightTeam]] =
|
|
api mine me map { _.map(_.light) }
|
|
}
|