/*
* Copyright 2012 Tomas Zeman <tzeman@volny.cz>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fis.pm.ui
import fis.aaa.ui.IfLoggedIn
import fis.base.ui._
import fis.cl.model._
import fis.fs.model._
import fis.fs.ui._
import fis.pm.model._
import net.liftweb.common._
import net.liftweb.http._
import net.liftweb.sitemap._
import net.liftweb.sitemap.Loc._
import net.liftweb.squerylrecord.RecordTypeMode._
import net.liftweb.util._
import net.liftweb.util.Helpers._
import net.tz.lift.model._
import net.tz.lift.snippet._
import scala.xml.{Elem, NodeSeq, Text}
object TaskSnippet extends TaskCrud with EntitySnippet[Task] {
val prefix = "task"
private val listM = Menu("task.list", l10n("Tasks")) / prefix >>
Title(_ => i18n("Tasks")) >>
locTpl("task/list") >> Snippet("list", list)
private val viewPre = Menu.param[Task]("task.view", l10n("Task"), parse,
encode) / prefix / * >> Title(t => i18n("Task %s", t.linkName)) >>
locTpl("task/view") >> Snippet("panel", panel) >>
Snippet("attachments", attachmentsTable) >>
Snippet("comments", comments) >> Hidden
private val editPre = Menu.param[Task]("task.edit", l10n("Edit"), parse,
encode) / prefix / * / EDIT >>
Title(t => i18n("Edit task %s", t.linkName)) >> IfLoggedIn.testVal >>
locTpl("entity/form") >> Snippet("form", form) >> Hidden
private val deletePre = Menu.param[Task]("task.delete", l10n("Delete"),
parse, encode) / prefix / * / DELETE >>
Title(t => i18n("Delete task %s", t.linkName)) >> IfLoggedIn.testVal >>
locTpl("entity/delete") >> Snippet("form", deleteF) >> Hidden
private val postCommentPre = Menu.param[Task]("task.post-comment",
l10n("Post comment"), parse, encode) / prefix / * / "post-comment" >>
Title(t => i18n("Post comment for task %s", t.linkName)) >>
IfLoggedIn.testVal >>
locTpl("task/view") >> Snippet("panel", postComment) >>
Snippet("comments", comments) >> Hidden
private val viewM = viewPre >> (SecNav(editPre) + deletePre +
attachments.addPre + postCommentPre).build
private val editM = editPre >> SecNav(viewPre).build
private val deleteM = deletePre >> SecNav(viewPre).build
private val postCommentM = postCommentPre >> SecNav(viewPre).build
private lazy val viewLoc = viewM.toLoc
private lazy val editLoc = editM.toLoc
private lazy val deleteLoc = deleteM.toLoc
private lazy val postCommentLoc = postCommentM.toLoc
val menu = listM submenus(viewM, editM, deleteM, postCommentM,
attachments.menu)
private def cur = viewLoc.currentValue or editLoc.currentValue or
deleteLoc.currentValue or postCommentLoc.currentValue
private def allQp: Boolean = (S param "all") flatMap(asBoolean(_)) openOr false
private def allQp(url: String, all: Boolean) =
all.box(appendQueryParameters(url, List("all" -> "1"))) openOr url
private def list: CssTr = {
"li" #> (List(false, true) map { b =>
val html = <li><a href={allQp(url.list, b)}>{
l10n(if (b) "tasks.all" else "tasks.unfinished")}</a></li>
(b == allQp).box(html % ("class" -> "active")) openOr html
}) &
".content *" #> _TaskTable(allQp.box(PmSchema.tasks) openOr
from(PmSchema.taskT, CodeListSchema.cli)((t, i) =>
where(t.stateFld === i.id and TaskState.unfinishedClause(i))
select(t) orderBy(t.deadline asc)))
}
private def panel: CssTr = "*" #> cur.map { t => ViewPanel(fields(t)) }
private def comments: CssTr = "*" #> cur.map { t =>
CommentTable(TaskComments(t)) }
private def attachmentsTable: CssTr = "*" #> cur.map { t =>
attachments.table(t)(TaskAttachments(t)) }
object url {
def view: Task => Box[String] = (viewLoc.calcHref _) andThen (Box !! _)
def list: String = listM.loc.calcDefaultHref
}
private def fields(t: Task) = List(t.ident, t.name, t.project, t.taskType,
t.stateFld, t.createdBy, t.createdAt, t.deadline, t.responsible, t.note)
private case class TaskLink(t: Task) extends EntityLink[Task](t, url.view)
EntityLink.register[Task](TaskLink(_))
private object _TaskTable extends TaskTable {
override def fields(t: Task) = (Seq(identField(t)) ++ EntityLink(t) ++
Seq(t.project, t.stateFld, t.deadline, t.responsible, t.note)
) map Delayed(t)
import net.datatables.DataTables._
override protected def dataTableOpts =
Options col(ColumnDef dtype(TYPE_EU_DATE) target(4))
}
private class TaskReport(f: () => Iterable[Task]) extends LocInfo[Iterable[Task]] {
def apply() = Full(f)
def tasks = f()
}
private object form extends TaskForm {
override def localSetup() {
cur.foreach(task(_))
}
protected def onSuccess(t: Task) {
S.redirectTo(viewLoc.calcHref(t))
}
}
private object deleteF extends HorizontalScreen with CancelButton with
DeleteButton {
val confirm = field(l10n("Really delete this task?"), false)
def finish() {
for {
t <- deleteLoc.currentValue if confirm
p <- t.project.vend
u <- ProjectSnippet.url.view(p)
r <- delete(t)
n <- r.box(t.linkName)
} {
S notice l10n("Task %s deleted.", n)
S redirectTo u
}
}
}
private object postComment extends HorizontalScreen with CancelButton with
SaveButton {
private object comment extends ScreenVar[Comment](Comment.createRecord)
addFields(() => comment.note)
override def localSetup() {
postCommentM.toLoc.currentValue.foreach { t => comment.task(t.id) }
}
def finish() { for {
c <- CommentCrud.save(comment)
t <- postCommentM.toLoc.currentValue
} {
S notice l10n("Comment saved.")
S redirectTo viewLoc.calcHref(t)
}}
}
/** Task attachments. */
private object attachments {
val addPre = Menu.param[Task]("task.add-attachment",
l10n("Add attachment"), parse, encode) / prefix / * / "attachment" / ADD >>
Title(_ => i18n("Add attachment")) >> IfLoggedIn.testVal >>
locTpl("entity/form") >> Snippet("form", attachmentF) >> Hidden
type TA = (Task, Attachment)
private object taMemo extends RequestMemoize[List[String], Box[TA]]()
private def parseTA(ids: List[String]): Box[TA] = taMemo(ids, ids match {
case AsLong(tid) :: AsLong(aid) :: Nil => for {
t <- get(tid)
a <- AttachmentCrud.get(aid)
} yield (t, a)
case _ => Empty
})
private def encodeTA(ta: TA) = List(ta._1, ta._2) map(_.id.toString)
private val downloadPre = Menu.params[TA]("task.attachment",
LinkText(ta => Text(ta._2.linkName)),
parseTA, encodeTA) / prefix / * / "attachment" / * >>
EarlyResponse(download _)
private val deleteM = Menu.params[TA]("task.delete-attachment",
l10n("Delete"), parseTA, encodeTA) / prefix / * / "attachment" / * / DELETE >>
Title(ta => i18n("Delete attachment %s", ta._2.linkName)) >>
IfLoggedIn.testVal >> locTpl("entity/form") >>
Snippet("form", attachmentD) >> Hidden
private val addM = addPre >> SecNav(viewPre).build
val downloadM = downloadPre submenus(addM, deleteM)
val menu = downloadM
private lazy val downloadLoc = downloadM.toLoc
private def download(): Box[LiftResponse] = for {
(t, a) <- downloadLoc.currentValue
is <- a.getContent
} yield
StreamingResponse(is, () => is.close, a.contentSize, a.headers, Nil, 200)
private object attachmentF extends AttachmentForm {
override protected def onSuccess(a: Attachment) =
addM.toLoc.currentValue.foreach { t =>
PmSchema.taskAttachment.left(t).associate(a)
S notice l10n("Attachment %s saved.", a.name.get)
S redirectTo viewLoc.calcHref(t)
}
}
private object attachmentD extends DeleteAttachmentForm {
protected def getAttachment = deleteM.toLoc.currentValue map(_._2)
protected def onSuccess(d: Boolean, rm: Boolean) {
S notice l10n("Attachment deleted.")
deleteM.toLoc.currentValue map(_._1) foreach { t =>
S redirectTo viewLoc.calcHref(t) }
}
}
def table(t: Task) = AttachmentTable(
{ a => NavLink(downloadLoc)((t, a)) },
{ a => NavLink(deleteM.toLoc)((t, a)) }) _
}
/** Task notifications. */
private object notifications extends TaskNotification {
lazy val viewLoc = TaskSnippet.this.viewLoc
def panel(t: Task) = ViewPanel(fields(t))
def hook(orig: Box[Task], t: Box[Task]): Box[Task] = {
apply(orig, t)
t
}
}
TaskCrud.afterSave = Full(notifications.hook _)
}
// vim: set ts=2 sw=2 et: