study cloneable setting
parent
ae19121f3b
commit
7d7be8629b
|
@ -190,20 +190,19 @@ object Study extends LilaController {
|
|||
name = "clone study per IP",
|
||||
key = "clone_study.ip")
|
||||
|
||||
def cloneApply(id: String) = Auth { implicit ctx =>
|
||||
me =>
|
||||
implicit val default = ornicar.scalalib.Zero.instance[Fu[Result]](notFound)
|
||||
CloneLimitPerUser(me.id, cost = 1) {
|
||||
CloneLimitPerIP(HTTPRequest lastRemoteAddress ctx.req, cost = 1) {
|
||||
OptionFuResult(env.api.byId(id)) { prev =>
|
||||
CanViewResult(prev) {
|
||||
env.api.clone(me, prev) map { study =>
|
||||
Redirect(routes.Study.show(study.id))
|
||||
}
|
||||
def cloneApply(id: String) = Auth { implicit ctx => me =>
|
||||
implicit val default = ornicar.scalalib.Zero.instance[Fu[Result]](notFound)
|
||||
CloneLimitPerUser(me.id, cost = 1) {
|
||||
CloneLimitPerIP(HTTPRequest lastRemoteAddress ctx.req, cost = 1) {
|
||||
OptionFuResult(env.api.byId(id)) { prev =>
|
||||
CanViewResult(prev) {
|
||||
env.api.clone(me, prev) map { study =>
|
||||
Redirect(routes.Study.show((study | prev).id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val PgnRateLimitGlobal = new lila.memo.RateLimit(
|
||||
|
|
|
@ -191,7 +191,6 @@ private object BSONHandlers {
|
|||
def read(b: BSONInteger): Variant = Variant(b.value) err s"No such variant: ${b.value}"
|
||||
def write(x: Variant) = BSONInteger(x.id)
|
||||
}
|
||||
private implicit val StudyViewsBSONHandler = intAnyValHandler[Study.Views](_.value, Study.Views.apply)
|
||||
|
||||
private implicit val PgnTagBSONHandler = new BSONHandler[BSONString, Tag] {
|
||||
def read(b: BSONString): Tag = b.value.split(":", 2) match {
|
||||
|
@ -253,7 +252,14 @@ private object BSONHandlers {
|
|||
def read(bs: BSONString) = UserSelection.byKey get bs.value err s"Invalid user selection ${bs.value}"
|
||||
def write(x: UserSelection) = BSONString(x.key)
|
||||
}
|
||||
implicit val SettingsBSONHandler = Macros.handler[Settings]
|
||||
implicit val SettingsBSONHandler = new BSON[Settings] {
|
||||
def reads(r: Reader) = Settings(
|
||||
computer = r.get[UserSelection]("computer"),
|
||||
explorer = r.get[UserSelection]("explorer"),
|
||||
cloneable = r.getO[UserSelection]("cloneable") | UserSelection.Everyone)
|
||||
private val writer = Macros.writer[Settings]
|
||||
def writes(w: Writer, s: Settings) = writer write s
|
||||
}
|
||||
|
||||
import Study.Likes
|
||||
private[study] implicit val LikesBSONHandler = intAnyValHandler[Likes](_.value, Likes.apply)
|
||||
|
|
|
@ -25,11 +25,18 @@ final class JsonView(
|
|||
chapters: List[Chapter.Metadata],
|
||||
currentChapter: Chapter,
|
||||
me: Option[User]) = {
|
||||
|
||||
def allowed(selection: Settings.UserSelection): Boolean =
|
||||
Settings.UserSelection.allows(selection, study, me.map(_.id))
|
||||
|
||||
currentChapter.setup.gameId.??(GameRepo.gameWithInitialFen) zip
|
||||
me.?? { studyRepo.liked(study, _) } map {
|
||||
case (gameOption, liked) =>
|
||||
studyWrites.writes(study) ++ Json.obj(
|
||||
"liked" -> liked,
|
||||
"features" -> Json.obj(
|
||||
"cloneable" -> allowed(study.settings.cloneable)
|
||||
),
|
||||
"chapters" -> chapters.map(chapterMetadataWrites.writes),
|
||||
"chapter" -> Json.obj(
|
||||
"ownerId" -> currentChapter.ownerId,
|
||||
|
@ -42,8 +49,8 @@ final class JsonView(
|
|||
"game" -> gameOption,
|
||||
"conceal" -> currentChapter.conceal,
|
||||
"features" -> Json.obj(
|
||||
"computer" -> Settings.UserSelection.allows(study.settings.computer, study, me.map(_.id)),
|
||||
"explorer" -> Settings.UserSelection.allows(study.settings.explorer, study, me.map(_.id))
|
||||
"computer" -> allowed(study.settings.computer),
|
||||
"explorer" -> allowed(study.settings.explorer)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -3,16 +3,16 @@ package lila.study
|
|||
import lila.user.User
|
||||
|
||||
case class Settings(
|
||||
computer: Settings.UserSelection,
|
||||
explorer: Settings.UserSelection) {
|
||||
|
||||
}
|
||||
computer: Settings.UserSelection,
|
||||
explorer: Settings.UserSelection,
|
||||
cloneable: Settings.UserSelection)
|
||||
|
||||
object Settings {
|
||||
|
||||
val init = Settings(
|
||||
computer = UserSelection.Everyone,
|
||||
explorer = UserSelection.Everyone)
|
||||
explorer = UserSelection.Everyone,
|
||||
cloneable = UserSelection.Everyone)
|
||||
|
||||
sealed trait UserSelection {
|
||||
lazy val key = toString.toLowerCase
|
||||
|
|
|
@ -62,8 +62,6 @@ object Study {
|
|||
|
||||
def toName(str: String) = str.trim take 100
|
||||
|
||||
case class Views(value: Int) extends AnyVal
|
||||
|
||||
sealed trait Visibility {
|
||||
lazy val key = toString.toLowerCase
|
||||
}
|
||||
|
@ -98,13 +96,15 @@ object Study {
|
|||
name: String,
|
||||
visibility: String,
|
||||
computer: String,
|
||||
explorer: String) {
|
||||
explorer: String,
|
||||
cloneable: String) {
|
||||
import Settings._
|
||||
def vis = Visibility.byKey get visibility getOrElse Visibility.Public
|
||||
def settings = for {
|
||||
comp <- UserSelection.byKey get computer
|
||||
expl <- UserSelection.byKey get explorer
|
||||
} yield Settings(comp, expl)
|
||||
clon <- UserSelection.byKey get cloneable
|
||||
} yield Settings(comp, expl, clon)
|
||||
}
|
||||
|
||||
case class WithChapter(study: Study, chapter: Chapter)
|
||||
|
|
|
@ -64,20 +64,21 @@ final class StudyApi(
|
|||
scheduleTimeline(res.study.id) inject res
|
||||
}
|
||||
|
||||
def clone(me: User, prev: Study): Fu[Study] = {
|
||||
chapterRepo.orderedByStudy(prev.id).flatMap { chapters =>
|
||||
val study1 = prev.cloneFor(me)
|
||||
val newChapters = chapters.map(_ cloneFor study1)
|
||||
val study = study1.withChapter(newChapters.headOption.err {
|
||||
s"Cloning study ${study1.id} from ${prev.id} has no first chapter!"
|
||||
})
|
||||
studyRepo.insert(study) >>
|
||||
newChapters.map(chapterRepo.insert).sequenceFu >>- {
|
||||
chat ! lila.chat.actorApi.SystemTalk(study.id,
|
||||
s"Cloned from lichess.org/study/${prev.id}")
|
||||
} inject study
|
||||
def clone(me: User, prev: Study): Fu[Option[Study]] =
|
||||
Settings.UserSelection.allows(prev.settings.cloneable, prev, me.id.some) ?? {
|
||||
chapterRepo.orderedByStudy(prev.id).flatMap { chapters =>
|
||||
val study1 = prev.cloneFor(me)
|
||||
val newChapters = chapters.map(_ cloneFor study1)
|
||||
newChapters.headOption.map(study1.withChapter) ?? { study =>
|
||||
studyRepo.insert(study) >>
|
||||
newChapters.map(chapterRepo.insert).sequenceFu >>- {
|
||||
chat ! lila.chat.actorApi.SystemTalk(
|
||||
study.id,
|
||||
s"Cloned from lichess.org/study/${prev.id}")
|
||||
} inject study.some
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def resetIfOld(study: Study, chapters: List[Chapter.Metadata]): Fu[Study] =
|
||||
chapters.headOption match {
|
||||
|
|
|
@ -87,15 +87,10 @@ module.exports = {
|
|||
if (data.visibility === 'public' && s.visibility === 'private' && !members.myMember())
|
||||
return lichess.reload();
|
||||
if (s.position !== data.position) commentForm.close();
|
||||
data.position = s.position;
|
||||
data.name = document.title = s.name;
|
||||
data.visibility = s.visibility;
|
||||
data.settings = s.settings;
|
||||
data.visibility = s.visibility;
|
||||
data.views = s.views;
|
||||
data.chapter = s.chapter;
|
||||
data.likes = s.likes;
|
||||
data.liked = s.liked;
|
||||
['position', 'name', 'visibility', 'features', 'settings', 'chapter', 'likes', 'liked'].forEach(function(key) {
|
||||
data[key] = s[key];
|
||||
});
|
||||
document.title = data.name;
|
||||
members.dict(s.members);
|
||||
chapters.list(s.chapters);
|
||||
ctrl.reloadData(d.analysis);
|
||||
|
|
|
@ -67,7 +67,8 @@ module.exports = {
|
|||
name: e.target.querySelector('#study-name').value,
|
||||
visibility: e.target.querySelector('#study-visibility').value,
|
||||
computer: e.target.querySelector('#study-computer').value,
|
||||
explorer: e.target.querySelector('#study-explorer').value
|
||||
explorer: e.target.querySelector('#study-explorer').value,
|
||||
cloneable: e.target.querySelector('#study-cloneable').value
|
||||
}, isNew);
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
|
@ -89,13 +90,19 @@ module.exports = {
|
|||
m('label.control-label[for=study-name]', 'Name'),
|
||||
m('i.bar')
|
||||
]),
|
||||
m('div.game.form-group', select({
|
||||
key: 'visibility',
|
||||
name: 'Visibility',
|
||||
choices: visibilityChoices,
|
||||
selected: data.visibility
|
||||
})),
|
||||
m('div', [
|
||||
m('div.game.form-group.half', select({
|
||||
key: 'visibility',
|
||||
name: 'Visibility',
|
||||
choices: visibilityChoices,
|
||||
selected: data.visibility
|
||||
})),
|
||||
m('div.game.form-group.half', select({
|
||||
key: 'cloneable',
|
||||
name: 'Allow cloning',
|
||||
choices: userSelectionChoices,
|
||||
selected: data.settings.cloneable
|
||||
})),
|
||||
m('div.game.form-group.half', select({
|
||||
key: 'computer',
|
||||
name: 'Computer analysis',
|
||||
|
@ -107,7 +114,7 @@ module.exports = {
|
|||
name: 'Opening explorer',
|
||||
choices: userSelectionChoices,
|
||||
selected: data.settings.explorer
|
||||
}))
|
||||
})),
|
||||
]),
|
||||
dialog.button(isNew ? 'Start' : 'Save')
|
||||
]),
|
||||
|
|
|
@ -39,12 +39,12 @@ function buttons(root) {
|
|||
'data-hint': 'Download as PGN',
|
||||
href: '/study/' + ctrl.data.id + '.pgn'
|
||||
}, m('i[data-icon=x]')),
|
||||
m('a.button.hint--top', {
|
||||
ctrl.data.features.cloneable ? m('a.button.hint--top', {
|
||||
'data-hint': 'Clone this study',
|
||||
href: '/study/' + ctrl.data.id + '/clone'
|
||||
}, m('i', {
|
||||
'data-icon': '{'
|
||||
})),
|
||||
})) : null,
|
||||
canContribute ? [
|
||||
(function(enabled) {
|
||||
return m('a.button.comment.hint--top', {
|
||||
|
|
Loading…
Reference in New Issue