moar team stuff
This commit is contained in:
parent
2e5d7adbee
commit
51c4a3c0da
|
@ -14,16 +14,21 @@ import play.api.templates.Html
|
|||
|
||||
object Team extends LilaController {
|
||||
|
||||
private def repo = env.team.repo
|
||||
private def teamRepo = env.team.teamRepo
|
||||
private def forms = env.team.forms
|
||||
private def api = env.team.api
|
||||
private def paginator = env.team.paginator
|
||||
|
||||
def home(page: Int) = Open { implicit ctx ⇒
|
||||
Ok(html.team.home(api popular page))
|
||||
Ok(html.team.home(paginator popularTeams page))
|
||||
}
|
||||
|
||||
def show(id: String) = Open { implicit ctx ⇒
|
||||
IOptionOk(repo byId id) { html.team.show(_) }
|
||||
def show(id: String, page: Int) = Open { implicit ctx ⇒
|
||||
IOptionIOk(teamRepo byId id) { team ⇒
|
||||
api isMine team map { isMine ⇒
|
||||
html.team.show(team, paginator.teamMembers(team, page), isMine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def form = Auth { implicit ctx ⇒
|
||||
|
@ -46,11 +51,22 @@ object Team extends LilaController {
|
|||
}
|
||||
|
||||
def mine = Auth { implicit ctx ⇒
|
||||
me ⇒ IOk(repo byUser me map { html.team.mine(_) })
|
||||
me ⇒ IOk(teamRepo byUser me map { html.team.mine(_) })
|
||||
}
|
||||
|
||||
def join(id: String) = TODO
|
||||
def leave(id: String) = TODO
|
||||
def join(id: String) = Auth { implicit ctx ⇒
|
||||
implicit me ⇒ IOResult(api join id map {
|
||||
case Some(team) ⇒ Redirect(routes.Team.show(team.id))
|
||||
case _ ⇒ notFound
|
||||
})
|
||||
}
|
||||
|
||||
def quit(id: String) = Auth { implicit ctx ⇒
|
||||
implicit me ⇒ IOResult(api quit id map {
|
||||
case Some(team) ⇒ Redirect(routes.Team.show(team.id))
|
||||
case _ ⇒ notFound
|
||||
})
|
||||
}
|
||||
|
||||
private def OnePerWeek[A <: Result](me: UserModel)(a: ⇒ A)(implicit ctx: Context): Result = {
|
||||
!Granter.superAdmin(me) &&
|
||||
|
|
|
@ -53,6 +53,7 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
lazy val team = new lila.team.TeamEnv(
|
||||
settings = settings,
|
||||
captcha = site.captcha,
|
||||
userRepo = user.userRepo,
|
||||
mongodb = mongodb.apply _)
|
||||
|
||||
lazy val lobby = new lila.lobby.LobbyEnv(
|
||||
|
|
|
@ -124,6 +124,7 @@ final class Settings(config: Config, val IsDev: Boolean) {
|
|||
val WikiGitUrl = getString("wiki.git_url")
|
||||
|
||||
val TeamCollectionTeam = getString("team.collection.team")
|
||||
val TeamCollectionMember = getString("team.collection.member")
|
||||
val TeamPaginatorMaxPerPage = getInt("team.paginator.max_per_page")
|
||||
|
||||
val BookmarkCollectionBookmark = getString("bookmark.collection.bookmark")
|
||||
|
|
|
@ -171,7 +171,7 @@ final class I18nKeys(translator: Translator) {
|
|||
val myTeams = new Key("myTeams")
|
||||
val noTeamFound = new Key("noTeamFound")
|
||||
val joinTeam = new Key("joinTeam")
|
||||
val leaveTeam = new Key("leaveTeam")
|
||||
val quitTeam = new Key("quitTeam")
|
||||
|
||||
def keys = List(playWithAFriend, inviteAFriendToPlayWithYou, playWithTheMachine, challengeTheArtificialIntelligence, toInviteSomeoneToPlayGiveThisUrl, gameOver, waitingForOpponent, waiting, yourTurn, aiNameLevelAiLevel, level, toggleTheChat, toggleSound, chat, resign, checkmate, stalemate, white, black, createAGame, noGameAvailableRightNowCreateOne, whiteIsVictorious, blackIsVictorious, playWithTheSameOpponentAgain, newOpponent, playWithAnotherOpponent, yourOpponentWantsToPlayANewGameWithYou, joinTheGame, whitePlays, blackPlays, theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim, makeYourOpponentResign, forceResignation, talkInChat, theFirstPersonToComeOnThisUrlWillPlayWithYou, whiteCreatesTheGame, blackCreatesTheGame, whiteJoinsTheGame, blackJoinsTheGame, whiteResigned, blackResigned, whiteLeftTheGame, blackLeftTheGame, shareThisUrlToLetSpectatorsSeeTheGame, youAreViewingThisGameAsASpectator, replayAndAnalyse, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, signUp, people, games, forum, chessPlayers, minutesPerSide, variant, timeControl, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, searchAPlayer, whoIsOnline, allPlayers, namedPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, advancedSearch, tournament, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents, teams, nbMembers, allTeams, newTeam, myTeams, noTeamFound, joinTeam, leaveTeam)
|
||||
def keys = List(playWithAFriend, inviteAFriendToPlayWithYou, playWithTheMachine, challengeTheArtificialIntelligence, toInviteSomeoneToPlayGiveThisUrl, gameOver, waitingForOpponent, waiting, yourTurn, aiNameLevelAiLevel, level, toggleTheChat, toggleSound, chat, resign, checkmate, stalemate, white, black, createAGame, noGameAvailableRightNowCreateOne, whiteIsVictorious, blackIsVictorious, playWithTheSameOpponentAgain, newOpponent, playWithAnotherOpponent, yourOpponentWantsToPlayANewGameWithYou, joinTheGame, whitePlays, blackPlays, theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim, makeYourOpponentResign, forceResignation, talkInChat, theFirstPersonToComeOnThisUrlWillPlayWithYou, whiteCreatesTheGame, blackCreatesTheGame, whiteJoinsTheGame, blackJoinsTheGame, whiteResigned, blackResigned, whiteLeftTheGame, blackLeftTheGame, shareThisUrlToLetSpectatorsSeeTheGame, youAreViewingThisGameAsASpectator, replayAndAnalyse, viewGameStats, flipBoard, threefoldRepetition, claimADraw, offerDraw, draw, nbConnectedPlayers, talkAboutChessAndDiscussLichessFeaturesInTheForum, seeTheGamesBeingPlayedInRealTime, gamesBeingPlayedRightNow, viewAllNbGames, viewNbCheckmates, nbBookmarks, nbPopularGames, nbAnalysedGames, bookmarkedByNbPlayers, viewInFullSize, logOut, signIn, signUp, people, games, forum, chessPlayers, minutesPerSide, variant, timeControl, start, username, password, haveAnAccount, allYouNeedIsAUsernameAndAPassword, learnMoreAboutLichess, rank, gamesPlayed, declineInvitation, cancel, timeOut, drawOfferSent, drawOfferDeclined, drawOfferAccepted, drawOfferCanceled, yourOpponentOffersADraw, accept, decline, playingRightNow, abortGame, gameAborted, standard, unlimited, mode, casual, rated, thisGameIsRated, rematch, rematchOfferSent, rematchOfferAccepted, rematchOfferCanceled, rematchOfferDeclined, cancelRematchOffer, viewRematch, play, inbox, chatRoom, spectatorRoom, composeMessage, sentMessages, incrementInSeconds, freeOnlineChess, spectators, nbWins, nbLosses, nbDraws, exportGames, color, eloRange, giveNbSeconds, searchAPlayer, whoIsOnline, allPlayers, namedPlayers, premoveEnabledClickAnywhereToCancel, thisPlayerUsesChessComputerAssistance, opening, takeback, proposeATakeback, takebackPropositionSent, takebackPropositionDeclined, takebackPropositionAccepted, takebackPropositionCanceled, yourOpponentProposesATakeback, bookmarkThisGame, toggleBackground, advancedSearch, tournament, freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents, teams, nbMembers, allTeams, newTeam, myTeams, noTeamFound, joinTeam, quitTeam)
|
||||
}
|
||||
|
|
|
@ -1,22 +1,35 @@
|
|||
package lila
|
||||
package team
|
||||
|
||||
import org.joda.time.{ DateTime, Duration }
|
||||
import com.novus.salat.annotations.Key
|
||||
import org.joda.time.DateTime
|
||||
import org.scala_tools.time.Imports._
|
||||
import scalaz.effects._
|
||||
|
||||
import user.User
|
||||
import user.{ User, UserRepo }
|
||||
|
||||
case class Member(
|
||||
id: String,
|
||||
createdAt: DateTime) {
|
||||
@Key("_id") id: String,
|
||||
team: String,
|
||||
user: String,
|
||||
date: DateTime) {
|
||||
|
||||
def is(userId: String): Boolean = id == userId
|
||||
def is(userId: String): Boolean = user == userId
|
||||
def is(user: User): Boolean = is(user.id)
|
||||
}
|
||||
|
||||
object Member {
|
||||
|
||||
def apply(user: User): Member = apply(user.id)
|
||||
def makeId(team: String, user: String) = user + "@" + team
|
||||
|
||||
def apply(user: String): Member = new Member(id = user, createdAt = DateTime.now)
|
||||
def apply(team: String, user: String): Member = new Member(
|
||||
id = makeId(team, user),
|
||||
user = user,
|
||||
team = team,
|
||||
date = DateTime.now)
|
||||
}
|
||||
|
||||
case class MemberWithUser(member: Member, user: User) {
|
||||
def team = member.team
|
||||
def date = member.date
|
||||
}
|
||||
|
|
55
app/team/MemberRepo.scala
Normal file
55
app/team/MemberRepo.scala
Normal file
|
@ -0,0 +1,55 @@
|
|||
package lila
|
||||
package team
|
||||
|
||||
import com.novus.salat._
|
||||
import com.novus.salat.dao._
|
||||
import com.mongodb.casbah.MongoCollection
|
||||
import com.mongodb.casbah.Imports._
|
||||
|
||||
import scalaz.effects._
|
||||
import org.joda.time.DateTime
|
||||
|
||||
// db.member.ensureIndex({t:1})
|
||||
// db.member.ensureIndex({u:1})
|
||||
// db.member.ensureIndex({d: -1})
|
||||
final class MemberRepo(collection: MongoCollection)
|
||||
extends SalatDAO[Member, String](collection) {
|
||||
|
||||
// def byTeamId(teamId: String): IO[List[Member]] = io {
|
||||
// find(teamIdQuery(teamId)).toList
|
||||
// }
|
||||
|
||||
def userIdsByTeamId(teamId: String): IO[List[String]] = io {
|
||||
(collection find teamIdQuery(teamId) sort sortQuery(1) map { obj ⇒
|
||||
obj.getAs[String]("user")
|
||||
}).flatten.toList
|
||||
}
|
||||
|
||||
def teamIdsByUserId(userId: String): IO[Set[String]] = io {
|
||||
(collection find userIdQuery(userId) map { obj ⇒
|
||||
obj.getAs[String]("team")
|
||||
}).flatten.toSet
|
||||
}
|
||||
|
||||
def removeByteamId(teamId: String): IO[Unit] = io {
|
||||
remove(teamIdQuery(teamId))
|
||||
}
|
||||
|
||||
def exists(teamId: String, userId: String): IO[Boolean] = io {
|
||||
collection.find(idQuery(teamId, userId)).limit(1).size != 0
|
||||
}
|
||||
|
||||
def idQuery(teamId: String, userId: String) = DBObject("_id" -> id(teamId, userId))
|
||||
def id(teamId: String, userId: String) = Member.makeId(teamId, userId)
|
||||
def teamIdQuery(teamId: String) = DBObject("team" -> teamId)
|
||||
def userIdQuery(userId: String) = DBObject("user" -> userId)
|
||||
def sortQuery(order: Int = -1) = DBObject("date" -> order)
|
||||
|
||||
def add(teamId: String, userId: String): IO[Unit] = io {
|
||||
insert(Member(team = teamId, user = userId))
|
||||
}
|
||||
|
||||
def remove(teamId: String, userId: String): IO[Unit] = io {
|
||||
remove(idQuery(teamId, userId))
|
||||
}
|
||||
}
|
48
app/team/PaginatorBuilder.scala
Normal file
48
app/team/PaginatorBuilder.scala
Normal file
|
@ -0,0 +1,48 @@
|
|||
package lila
|
||||
package team
|
||||
|
||||
import user.{ User, UserRepo }
|
||||
import mongodb.CachedAdapter
|
||||
|
||||
import com.github.ornicar.paginator._
|
||||
import com.mongodb.casbah.Imports._
|
||||
import org.joda.time.DateTime
|
||||
|
||||
final class PaginatorBuilder(
|
||||
memberRepo: MemberRepo,
|
||||
teamRepo: TeamRepo,
|
||||
userRepo: UserRepo,
|
||||
maxPerPage: Int) {
|
||||
|
||||
def popularTeams(page: Int): Paginator[Team] = paginator(
|
||||
SalatAdapter(
|
||||
dao = teamRepo,
|
||||
query = teamRepo.enabledQuery,
|
||||
sort = teamRepo.sortPopular), page)
|
||||
|
||||
def teamMembers(team: Team, page: Int): Paginator[MemberWithUser] =
|
||||
paginator(new TeamAdapter(team), page)
|
||||
|
||||
private def paginator[A](adapter: Adapter[A], page: Int): Paginator[A] =
|
||||
Paginator(
|
||||
adapter,
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage
|
||||
) | paginator(adapter, 1)
|
||||
|
||||
final class TeamAdapter(team: Team) extends Adapter[MemberWithUser] {
|
||||
|
||||
val nbResults = team.nbMembers
|
||||
|
||||
def slice(offset: Int, length: Int): Seq[MemberWithUser] = {
|
||||
val members = (memberRepo find query sort sort skip offset limit length).toList
|
||||
val users = (userRepo byOrderedIds members.map(_.user)).unsafePerformIO
|
||||
members zip users map {
|
||||
case (member, user) ⇒ MemberWithUser(member, user)
|
||||
}
|
||||
}
|
||||
|
||||
private def query = memberRepo teamIdQuery team.id
|
||||
private def sort = memberRepo sortQuery -1
|
||||
}
|
||||
}
|
|
@ -6,13 +6,13 @@ import user.User
|
|||
import org.joda.time.DateTime
|
||||
import com.novus.salat.annotations.Key
|
||||
import java.text.Normalizer
|
||||
import scalaz.effects._
|
||||
|
||||
case class Team(
|
||||
@Key("_id") id: String, // also the url slug
|
||||
name: String,
|
||||
location: Option[String],
|
||||
description: String,
|
||||
members: List[Member],
|
||||
nbMembers: Int,
|
||||
enabled: Boolean,
|
||||
createdAt: DateTime,
|
||||
|
@ -20,11 +20,6 @@ case class Team(
|
|||
|
||||
def slug = id
|
||||
|
||||
def contains(userId: String): Boolean = members exists (_ is userId)
|
||||
def contains(user: User): Boolean = contains(user.id)
|
||||
|
||||
def canJoin(user: User) = true
|
||||
|
||||
def disabled = !enabled
|
||||
}
|
||||
|
||||
|
@ -39,11 +34,10 @@ object Team {
|
|||
name = name,
|
||||
location = location,
|
||||
description = description,
|
||||
members = Member(createdBy) :: Nil,
|
||||
nbMembers = 1,
|
||||
enabled = true,
|
||||
createdAt = DateTime.now,
|
||||
createdBy = createdBy.id)
|
||||
|
||||
def nameToId(name: String) = templating.StringHelper slugify name
|
||||
def nameToId(name: String) = templating.StringHelper slugify name
|
||||
}
|
||||
|
|
|
@ -2,36 +2,62 @@ package lila
|
|||
package team
|
||||
|
||||
import scalaz.effects._
|
||||
import com.github.ornicar.paginator._
|
||||
import org.scala_tools.time.Imports._
|
||||
import com.github.ornicar.paginator.Paginator
|
||||
|
||||
import user.User
|
||||
import user.{ User, UserRepo }
|
||||
import http.Context
|
||||
|
||||
final class TeamApi(
|
||||
repo: TeamRepo,
|
||||
maxPerPage: Int) {
|
||||
teamRepo: TeamRepo,
|
||||
memberRepo: MemberRepo,
|
||||
userRepo: UserRepo,
|
||||
paginator: PaginatorBuilder) {
|
||||
|
||||
val creationPeriod = 1 week
|
||||
|
||||
def popular(page: Int): Paginator[Team] = Paginator(
|
||||
SalatAdapter(
|
||||
dao = repo,
|
||||
query = repo.enabledQuery,
|
||||
sort = repo.sortPopular),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage
|
||||
) | popular(1)
|
||||
|
||||
def create(setup: TeamSetup, me: User): IO[Team] = setup.trim |> { s ⇒
|
||||
Team(
|
||||
name = s.name,
|
||||
location = s.location,
|
||||
description = s.description,
|
||||
createdBy = me) |> { team ⇒
|
||||
repo saveIO team inject team
|
||||
teamRepo saveIO team inject team
|
||||
}
|
||||
}
|
||||
|
||||
def hasCreatedRecently(me: User): IO[Boolean] =
|
||||
repo.userHasCreatedSince(me.id, creationPeriod)
|
||||
teamRepo.userHasCreatedSince(me.id, creationPeriod)
|
||||
|
||||
def isMine(team: Team)(implicit ctx: Context): IO[Boolean] =
|
||||
~ctx.me.map(me ⇒ belongsTo(team, me))
|
||||
|
||||
def join(teamId: String)(implicit ctx: Context): IO[Option[Team]] = for {
|
||||
teamOption ← teamRepo byId teamId
|
||||
result ← ~(teamOption |@| ctx.me).tupled.map({
|
||||
case (team, user) ⇒ for {
|
||||
exists ← belongsTo(team, user)
|
||||
_ ← (for {
|
||||
_ ← memberRepo.add(team.id, user.id)
|
||||
_ ← teamRepo.incMembers(team.id, +1)
|
||||
} yield ()) doUnless exists
|
||||
} yield team.some
|
||||
})
|
||||
} yield result
|
||||
|
||||
def quit(teamId: String)(implicit ctx: Context): IO[Option[Team]] = for {
|
||||
teamOption ← teamRepo byId teamId
|
||||
result ← ~(teamOption |@| ctx.me).tupled.map({
|
||||
case (team, user) ⇒ for {
|
||||
exists ← belongsTo(team, user)
|
||||
_ ← (for {
|
||||
_ ← memberRepo.remove(team.id, user.id)
|
||||
_ ← teamRepo.incMembers(team.id, -1)
|
||||
} yield ()) doIf exists
|
||||
} yield team.some
|
||||
})
|
||||
} yield result
|
||||
|
||||
def belongsTo(team: Team, user: User): IO[Boolean] =
|
||||
memberRepo.exists(teamId = team.id, userId = user.id)
|
||||
}
|
||||
|
|
|
@ -3,21 +3,33 @@ package team
|
|||
|
||||
import core.Settings
|
||||
import site.Captcha
|
||||
import user.UserRepo
|
||||
|
||||
import com.mongodb.casbah.MongoCollection
|
||||
|
||||
final class TeamEnv(
|
||||
settings: Settings,
|
||||
captcha: Captcha,
|
||||
userRepo: UserRepo,
|
||||
mongodb: String ⇒ MongoCollection) {
|
||||
|
||||
import settings._
|
||||
|
||||
lazy val repo = new TeamRepo(mongodb(TeamCollectionTeam))
|
||||
lazy val teamRepo = new TeamRepo(mongodb(TeamCollectionTeam))
|
||||
|
||||
lazy val api = new TeamApi(
|
||||
repo = repo,
|
||||
lazy val memberRepo = new MemberRepo(mongodb(TeamCollectionMember))
|
||||
|
||||
lazy val paginator = new PaginatorBuilder(
|
||||
memberRepo = memberRepo,
|
||||
teamRepo = teamRepo,
|
||||
userRepo = userRepo,
|
||||
maxPerPage = TeamPaginatorMaxPerPage)
|
||||
|
||||
lazy val forms = new DataForm(repo, captcha)
|
||||
lazy val api = new TeamApi(
|
||||
teamRepo = teamRepo,
|
||||
memberRepo = memberRepo,
|
||||
userRepo = userRepo,
|
||||
paginator = paginator)
|
||||
|
||||
lazy val forms = new DataForm(teamRepo, captcha)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,10 @@ final class TeamRepo(collection: MongoCollection)
|
|||
).limit(1).size > 0
|
||||
}
|
||||
|
||||
def incMembers(teamId: String, by: Int): IO[Unit] = io {
|
||||
update(selectId(teamId), $inc("nbMembers" -> by))
|
||||
}
|
||||
|
||||
def userQuery(user: User) = DBObject("members.id" -> user.id)
|
||||
|
||||
def selectId(id: String) = DBObject("_id" -> id)
|
||||
|
|
|
@ -9,7 +9,7 @@ currentTab = "mine".some) {
|
|||
@if(teams.size > 0) {
|
||||
<tbody>
|
||||
@teams.map { team =>
|
||||
<tr class="paginated_element">
|
||||
<tr>
|
||||
<td class="subject">
|
||||
<a class="team-name" href="@routes.Team.show(team.id)">@team.name</a>
|
||||
@shorten(team.description, 200)
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
@(t: lila.team.Team)(implicit ctx: Context)
|
||||
@(t: lila.team.Team, members: Paginator[lila.team.MemberWithUser], isMine: Boolean)(implicit ctx: Context)
|
||||
|
||||
@team.layout(
|
||||
title = t.name) {
|
||||
<div class="content_box no_padding team_show">
|
||||
<div id="team" class="content_box no_padding team_show">
|
||||
<div class="content_box_top">
|
||||
@if(~ctx.me.map(t.contains)) {
|
||||
<a href="@routes.Team.leave(t.id)" class="send_message">@trans.leaveTeam()</a>
|
||||
} else {
|
||||
if (~ctx.me.map(t.canJoin)) {
|
||||
<a href="@routes.Team.join(t.id)" class="send_message">@trans.joinTeam()</a>
|
||||
}
|
||||
}
|
||||
<span class="righty">@trans.nbMembers(t.nbMembers)</span>
|
||||
<div class="icon"></div>
|
||||
<h1 class="lichess_title">@t.name</h1>
|
||||
<span class="rank">
|
||||
@trans.nbMembers(t.nbMembers)
|
||||
</span>
|
||||
@if(t.disabled) {
|
||||
<span class="staff">CLOSED</span>
|
||||
}
|
||||
|
@ -22,5 +14,28 @@ title = t.name) {
|
|||
<div class="content_box_content clearfix">
|
||||
@autoLink(t.description)
|
||||
</div>
|
||||
@if(t.enabled && !isMine) {
|
||||
<form class="inline" method="post" action="@routes.Team.join(t.id)">
|
||||
<input class="submit button" type="submit" value="@trans.joinTeam()" />
|
||||
</form>
|
||||
}
|
||||
@if(isMine) {
|
||||
<form class="inline" method="post" action="@routes.Team.quit(t.id)">
|
||||
<input class="submit button" type="submit" value="@trans.quitTeam()" />
|
||||
</form>
|
||||
}
|
||||
<div class="team-members">
|
||||
<table class="slist">
|
||||
<tbody>
|
||||
@members.currentPageResults.map { member =>
|
||||
<tr class="paginated_element">
|
||||
<td class="subject">
|
||||
@userLink(member.user)
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ evenMoreCss = evenMoreCss) {
|
|||
<form id="exportform" action="@routes.User.export(u.username)" method="POST">
|
||||
<a onclick="document.getElementById('exportform').submit();">@trans.exportGames()</a>
|
||||
</form>
|
||||
<div class="status @isUsernameOnline(u.username).fold("connected", "")"></div>
|
||||
<div class="icon status @isUsernameOnline(u.username).fold("connected", "")"></div>
|
||||
<h1 class="lichess_title">@u.usernameWithElo</h1>
|
||||
@info.rank.map { r =>
|
||||
<span class="rank">
|
||||
|
|
|
@ -147,4 +147,4 @@ newTeam=New team
|
|||
myTeams=My teams
|
||||
noTeamFound=No team found
|
||||
joinTeam=Join team
|
||||
leaveTeam=Leave team
|
||||
quitTeam=Quit team
|
||||
|
|
|
@ -55,9 +55,9 @@ GET /team controllers.Team.home(page: Int ?=
|
|||
GET /team/new controllers.Team.form
|
||||
POST /team/new controllers.Team.create
|
||||
GET /team/me controllers.Team.mine
|
||||
GET /team/:id controllers.Team.show(id: String)
|
||||
GET /team/:id controllers.Team.show(id: String, page: Int ?= 1)
|
||||
POST /team/:id/join controllers.Team.join(id: String)
|
||||
POST /team/:id/leave controllers.Team.leave(id: String)
|
||||
POST /team/:id/quit controllers.Team.quit(id: String)
|
||||
|
||||
# Analyse
|
||||
GET /analyse/$gameId<[\w\-]{8}> controllers.Analyse.replay(gameId: String, color: String = "white")
|
||||
|
|
|
@ -17,7 +17,7 @@ trait Resolvers {
|
|||
trait Dependencies {
|
||||
val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.4"
|
||||
val salat = "com.novus" %% "salat-core" % "1.9.1"
|
||||
val scalalib = "com.github.ornicar" %% "scalalib" % "2.5"
|
||||
val scalalib = "com.github.ornicar" %% "scalalib" % "2.7"
|
||||
val config = "com.typesafe" % "config" % "0.4.1"
|
||||
val guava = "com.google.guava" % "guava" % "13.0"
|
||||
val apache = "org.apache.commons" % "commons-lang3" % "3.1"
|
||||
|
|
|
@ -174,6 +174,7 @@ div.content_box.no_padding h1 {
|
|||
.content_box_title {
|
||||
padding: 20px 25px 0 25px;
|
||||
}
|
||||
|
||||
div.content_box_top {
|
||||
padding-left: 25px;
|
||||
height: 40px;
|
||||
|
@ -193,6 +194,18 @@ div.content_box_top a {
|
|||
div.content_box_top .link:hover {
|
||||
background: #fff;
|
||||
}
|
||||
div.content_box_top h1.lichess_title {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
div.content_box_top .icon {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
float: left;
|
||||
background: transparent url(/assets/images/s16.png) top left no-repeat;
|
||||
margin: 12px 8px 0 -10px;
|
||||
}
|
||||
.content_box_content {
|
||||
padding: 20px 25px 25px 25px;
|
||||
}
|
||||
|
|
|
@ -46,3 +46,13 @@ form.new-team p.error {
|
|||
font-size: 0.9em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#team div.content_box_top .icon {
|
||||
background-position: 0 -336px;
|
||||
}
|
||||
#team.team_show .lichess_title {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
#team.team_show .content_box_top .righty {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
div.user_show .lichess_title {
|
||||
display: inline;
|
||||
}
|
||||
div.user_show div.content_box_top .status {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
float: left;
|
||||
background: transparent url(/assets/images/s16.png) top left no-repeat;
|
||||
div.user_show div.content_box_top .icon {
|
||||
background-position: 0 -160px;
|
||||
margin: 12px 8px 0 -10px;
|
||||
}
|
||||
div.user_show div.content_box_top .status.connected {
|
||||
div.user_show div.content_box_top .icon.connected {
|
||||
background-position: 0 -208px;
|
||||
}
|
||||
div.user_show .staff {
|
||||
|
|
Loading…
Reference in a new issue