--- a/src/main/scala/bootstrap/liftweb/Boot.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala Tue Apr 03 14:39:58 2012 +0200
@@ -16,7 +16,7 @@
package bootstrap.liftweb
import fis.base.model._
-import fis.crm.ui.{ContactSnippet, ContactSnippet2}
+import fis.crm.ui.ContactSnippet
import fis.db.SquerylTxMgr
import net.liftweb.common._
import net.liftweb.http._
@@ -37,7 +37,7 @@
import Loc._
val menus = List(Menu("/", "FIS Main page") / "index" >> Hidden,
- Menu.i("Home") / "" , ContactSnippet.menu, ContactSnippet2.menu)
+ Menu.i("Home") / "" , ContactSnippet.menu)
LiftRules.setSiteMap(SiteMap(menus:_*))
}
--- a/src/main/scala/fis/base/ui/EntitySnippet.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/main/scala/fis/base/ui/EntitySnippet.scala Tue Apr 03 14:39:58 2012 +0200
@@ -15,8 +15,10 @@
*/
package fis.base.ui
+import fis.base.model.RecordCrud
import net.liftweb.common._
import net.liftweb.http._
+import net.liftweb.record._
import net.liftweb.sitemap._
import net.liftweb.util._
import net.liftweb.util.Helpers._
@@ -85,17 +87,27 @@
def deleteMenu = deletePreMenu >> LocTpl(tplDelete)
}
-trait EntitySnippet[T <: KeyedRecord[Long]] extends SnippetHelpers with
- CrudMenu[T] {
+trait EntitySnippet[T <: Record[T] with KeyedRecord[Long]] extends
+ RecordCrud[T] {
+
+ def prefix: String
- object cur extends RequestVar[EntityLoc[T]](ListEntity[T]())
+ protected object memo extends RequestMemoize[String, Box[T]]()
+ lazy val parse = { id: String => memo(id, get(id)) }
+ lazy val encode = { v: T => v.id.toString }
+ val ADD = "add"
+ val EDIT = "edit"
+ val DELETE = "delete"
+
+ /*
def listsMenu = Menu(prefix + "s", titleList) / (prefix + "s") >>
EarlyResponse(() => Full(RedirectResponse("/" + prefix)))
lazy val menu: Menu = listsMenu submenus(
listMenu, addMenu, showMenu, editMenu, deleteMenu
)
+ */
}
// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/crm/model/Contact.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/main/scala/fis/crm/model/Contact.scala Tue Apr 03 14:39:58 2012 +0200
@@ -17,73 +17,33 @@
import fis.base.model.{Entity, MetaEntity}
import net.liftweb.common._
-import net.liftweb.http.S
import net.liftweb.record.{MetaRecord, Record}
import net.liftweb.record.field._
-import net.liftweb.util.FieldError
-import net.tz.lift.model.OptionalFieldDisplay
+import net.liftweb.squerylrecord.RecordTypeMode._
+import net.tz.lift.model.{FieldLabel => FL}
+import net.tz.lift.model.{OptionalFieldDisplay => OptDisp}
import scala.xml.Text
-class OptionalEmailField[OwnerType <: Record[OwnerType]](rec: OwnerType,
- maxLength: Int) extends OptionalStringField[OwnerType](rec, maxLength) {
- override def validations = validateEmail _ :: Nil
- protected def validateEmail(email: ValueType): List[FieldError] =
- toBoxMyType(email) match {
- case Empty | Full("") => Nil
- case Full(v) if EmailField.validEmailAddr_?(v) => Nil
- case _ => Text(S.??("invalid.email.address"))
- }
-}
-
-
class Contact private() extends Record[Contact] with Entity[Contact] {
def meta = Contact
- val firstName = new StringField(this, 80, "")
- val lastName = new StringField(this, 80, "")
- val position = new OptionalStringField(this, 40) with OptionalFieldDisplay
- val workMail = new EmailField(this, 256)
- val privateMail = new OptionalEmailField(this, 256) with OptionalFieldDisplay
- val otherMail = new OptionalEmailField(this, 256) with OptionalFieldDisplay
- val workMobile = new StringField(this, 40)
- val privateMobile = new OptionalStringField(this, 40) with
- OptionalFieldDisplay
- val otherMobile = new OptionalStringField(this, 40) with OptionalFieldDisplay
- val workPhone = new OptionalStringField(this, 40) with OptionalFieldDisplay
- val privatePhone = new OptionalStringField(this, 40) with OptionalFieldDisplay
- val fax = new OptionalStringField(this, 40) with OptionalFieldDisplay
+ val firstName = new StringField(this, 80, "") with FL
+ val lastName = new StringField(this, 80, "") with FL
+ val position = new OptionalStringField(this, 40) with OptDisp with FL
+ val workMail = new EmailField(this, 256) with FL
+ val privateMail = new OptionalEmailField(this, 256) with OptDisp with FL
+ val otherMail = new OptionalEmailField(this, 256) with OptDisp with FL
+ val workMobile = new StringField(this, 40) with FL
+ val privateMobile = new OptionalStringField(this, 40) with OptDisp with FL
+ val otherMobile = new OptionalStringField(this, 40) with OptDisp with FL
+ val workPhone = new OptionalStringField(this, 40) with OptDisp with FL
+ val privatePhone = new OptionalStringField(this, 40) with OptDisp with FL
+ val fax = new OptionalStringField(this, 40) with OptDisp with FL
override def linkName = lastName.get + " " + firstName.get
-
- def fieldsForView = List(lastName, firstName, position, workMail, workMobile,
- workPhone, privateMail, privateMobile, privatePhone, fax,
- otherMail, otherMobile, note)
}
-import net.liftweb.squerylrecord.RecordTypeMode._
-
-object Contact extends Contact with MetaRecord[Contact] with
- MetaEntity[Contact] {
-
- def getTable = CrmSchema.contacts
- def contacts: List[Contact] = from(getTable) (c =>
- select(c) orderBy(c.lastName asc, c.firstName asc)
- ) toList
-
- def fieldsForList = List(position, workMail,
- workMobile, note)
-
- def delete(c: Contact) = getTable.delete(c.id)
-}
-
-import org.squeryl.KeyedEntity
-import org.squeryl.dsl.CompositeKey3
-
-case class EntityContact(val entityId: Long, val contactId: Long,
- val typeId: Long) extends KeyedEntity[CompositeKey3[Long, Long, Long]] {
-
- def id = CompositeKey3(entityId, contactId, typeId)
-}
+object Contact extends Contact with MetaRecord[Contact]
// vim: set ts=2 sw=2 et:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/model/ContactCrud.scala Tue Apr 03 14:39:58 2012 +0200
@@ -0,0 +1,26 @@
+/*
+ * 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.crm.model
+
+import fis.base.model.RecordCrud
+
+trait ContactCrud extends RecordCrud[Contact] {
+ val table = CrmSchema.contactT
+}
+
+object ContactCrud extends ContactCrud
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/crm/model/CrmSchema.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/main/scala/fis/crm/model/CrmSchema.scala Tue Apr 03 14:39:58 2012 +0200
@@ -21,7 +21,11 @@
trait CrmSchema extends BaseSchema with GeoSchema {
- val contacts = table[Contact]("contact")
+ val contactT = tableWithSeq[Contact]
+
+ def allContacts: Iterable[Contact] = from(contactT) (c =>
+ select(c) orderBy(c.lastName asc, c.firstName asc)
+ )
/*
val entityContacts = manyToManyRelation(entities, contacts).
@@ -52,4 +56,19 @@
object CrmSchema extends CrmSchema
+import org.squeryl.KeyedEntity
+import org.squeryl.dsl._
+
+case class EntityContact[T](val entityId: Long, val contactId: Long) extends
+ KeyedEntity[CompositeKey2[Long, Long]] {
+
+ def id = CompositeKey2(entityId, contactId)
+}
+
+case class EntityContactWithType[T](val entityId: Long, val contactId: Long,
+ val typeId: Long) extends KeyedEntity[CompositeKey3[Long, Long, Long]] {
+
+ def id = CompositeKey3(entityId, contactId, typeId)
+}
+
// vim: set ts=2 sw=2 et:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/ui/ContactPanel.scala Tue Apr 03 14:39:58 2012 +0200
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011-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.crm.ui
+
+import fis.base.ui.ViewPanel
+import fis.crm.model._
+import net.liftweb.util.BaseField
+
+object ContactPanel {
+
+ def fields(c: Contact): List[BaseField] = List(c.lastName, c.firstName,
+ c.position, c.workMail, c.workMobile, c.workPhone, c.privateMail,
+ c.privateMobile, c.privatePhone, c.fax, c.otherMail, c.otherMobile, c.note)
+
+ def apply(c: Contact) = ViewPanel(fields(c))
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/crm/ui/ContactSnippet.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/main/scala/fis/crm/ui/ContactSnippet.scala Tue Apr 03 14:39:58 2012 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ * Copyright 2011-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.
@@ -15,160 +15,89 @@
*/
package fis.crm.ui
+import fis.base.ui._
import fis.crm.model._
import net.liftweb.common._
import net.liftweb.http._
import net.liftweb.sitemap._
+import net.liftweb.sitemap.Loc._
import net.liftweb.util._
import net.liftweb.util.Helpers._
-import net.tz.lift.snippet.{A, CssTr, DataTable, LocTpl, TplPanel, SnippetHelpers}
+import net.tz.lift.model._
+import net.tz.lift.snippet._
import scala.xml.{Elem, NodeSeq, Text}
-sealed trait ContactLoc {
- def title: String
- def url: List[String]
- def tpl: String
-}
-
-case object ListContacts extends ContactLoc {
- def title = "Contacts"
- def url = Nil
- def tpl = "entity/list"
-}
-
-case object AddContact extends ContactLoc {
- def title = "Add contact"
- def url = List("add")
- def tpl = "entity/form"
-}
-
-case class ShowContact(c: Contact) extends ContactLoc {
- def title = "Contact " + c.linkName
- def url = List(c.id.toString)
- def tpl = "entity/view"
-}
-
-case class EditContact(c: Contact) extends ContactLoc {
- def title = "Edit contact " + c.linkName
- def url = List(c.id.toString, "edit")
- def tpl = "entity/form"
-}
-
-case class DeleteContact(c: Contact) extends ContactLoc {
- def title = "Delete contact " + c.linkName
- def url = List(c.id.toString, "delete")
- def tpl = "entity/form"
-}
-
-object AsContactLoc extends Loggable {
- def apply(pars: List[String]): Box[ContactLoc] = {
- logger.debug("Params: " + pars)
- pars match {
- case List("add") => Full(AddContact)
- case AsLong(id) :: xs => Contact.findByKey(id) map { c => xs match {
- case List("edit") => EditContact(c)
- case List("delete") => DeleteContact(c)
- case _ => ShowContact(c)
- }}
- case Nil => Full(ListContacts)
- case _ => Empty
- }
- }
-}
-
-object ContactSnippet extends SnippetHelpers {
- import Loc._
-
+object ContactSnippet extends ContactCrud with EntitySnippet[Contact] {
val prefix = "contact"
- lazy val menu: Menu = Menu("contact.list", ListContacts.title) / "contacts" >>
- EarlyResponse(() => Full(RedirectResponse(url(ListContacts)))) submenus(
- Menu("contact.add", AddContact.title) / prefix / "add",
- Menu.params[ContactLoc]("contact.edit", "Edit contact", parMenu.parser,
- parMenu.encoder) / prefix / * / "edit" >> CalcValue(() => cur.is match {
- case ShowContact(c) => Full(ShowContact(c))
- case _ => Empty
- }) ,
- Menu.params[ContactLoc]("contact.delete", "Delete contact", parMenu.parser,
- parMenu.encoder) / prefix / * / "delete" >> CalcValue(() => cur.is match {
- case ShowContact(c) => Full(ShowContact(c))
- case _ => Empty
- }) ,
- menu1)
+ val listOp = Menu("contact.list", l10n("Contacts")) / prefix >>
+ Title(_ => i18n("Contacts")) >>
+ locTpl("entity/list") >> Snippet("list", list)
+
+ val createOp = Menu("contact.create", l10n("Create")) / prefix / ADD >>
+ Title(_ => i18n("Create contact")) >>
+ locTpl("entity/form") >> Snippet("form", form) >> Hidden
+
+ val viewOp = Menu.param[Contact]("contact.view", l10n("Contact"), parse,
+ encode) / prefix / * >> Title(c => i18n("Contact %s", c.linkName)) >>
+ locTpl("entity/view") >> Snippet("panel", panel) >>
+ Hidden
+
+ val editOp = Menu.param[Contact]("contact.edit", l10n("Edit"), parse,
+ encode) / prefix / * / EDIT >>
+ Title(c => i18n("Edit contact %s", c.linkName)) >>
+ locTpl("entity/form") >> Snippet("form", form) >> Hidden
+
+ val deleteOp = Menu.param[Contact]("contact.delete", l10n("Delete"),
+ parse, encode) / prefix / * / DELETE >>
+ Title(c => i18n("Delete contact %s", c.linkName)) >>
+ locTpl("entity/delete") >> Snippet("form", deleteF) >> Hidden
- lazy val menu1: Menu = parMenu >> new DispatchLocSnippets {
- def dispatch = { n => (n, cur.is) match {
- case ("list", ListContacts) => listContacts
- case ("panel", ShowContact(c)) => ContactPanel(c)
- case ("form", EditContact(c)) => ContactForm(c).dispatch("")
- case ("form", AddContact) => ContactForm(Contact.createRecord).
- dispatch("")
- case ("form", DeleteContact(c)) => DeleteContactForm(c).dispatch("")
- }}
- } >> LocTpl(_.tpl) >> Hidden
+ lazy val viewLoc: Loc[Contact] = viewOp.toLoc
+ lazy val editLoc = editOp.toLoc
+ lazy val deleteLoc = deleteOp.toLoc
+
+ val menu = listOp submenus (viewOp, editOp, createOp, deleteOp)
+
+ def list: CssTr = /*ContactTable(from(CrmSchema.contacts)(c =>
+ select(c) orderBy(c.lastName asc, c.firstName asc)))*/ ClearNodes
+
+ def panel: CssTr = "*" #> viewLoc.currentValue.map { ContactPanel(_) }
- lazy val parMenu = Menu.params[ContactLoc]("contact",
- Loc.LinkText(l => Text(l.title)),
- AsContactLoc(_) pass { _.foreach { cur(_) } }, _.url) / prefix / **
+ object form extends LiftScreen {
+ object c extends ScreenVar[Contact](Contact.createRecord)
- def url(l: ContactLoc) = parMenu.toLoc.calcHref(l)
-
- object cur extends RequestVar[ContactLoc](ListContacts)
+ override def screenFields: List[BaseField] = ContactPanel.fields(c)
- def listContacts: CssTr = {
- val cols = Contact.fieldsForList.map { _.displayName }
- val cells = Contact.contacts.map { c =>
- A(url(ShowContact(c)), Text(c.linkName)) ::
- (Contact.fieldsForList.map { f =>
- c.fieldByName(f.name).map { _.asHtml } openOr NodeSeq.Empty
- }.toList) }.toList
- new DataTable((S ? "Full name") :: cols, cells)
+ override def localSetup() {
+ editLoc.currentValue.foreach(c(_))
+ }
+
+ def finish() { save(c) foreach { v =>
+ S notice l10n("Contact %s saved", v.linkName)
+ S.redirectTo(viewLoc.calcHref(v))
+ }}
}
- case class ContactPanel(c: Contact) extends TplPanel(c.fieldsForView.map
- { f => (f.displayHtml, f.asHtml) })
+ object deleteF extends LiftScreen {
+ val confirm = field(l10n("Really delete this contact?"), false)
+
+ override def finishButton: Elem = <button>{l10n("Delete")}</button>
- case class ContactForm(c: Contact) extends LiftScreen {
- object contact extends ScreenVar(c)
- contact.is.fieldsForView.map { f => addFields(() => f) }
- override def finishButton: Elem = <button>{S ? "Save"}</button>
- protected def finish() {
- val c = contact.is
- Contact.getTable.insertOrUpdate(c)
- S.notice("Contact " + c.linkName + " saved")
- S.redirectTo(url(ShowContact(c)))
- ()
+ def finish() {
+ for {
+ c <- deleteLoc.currentValue if confirm
+ r <- delete(c)
+ n <- r.box(c.linkName)
+ } {
+ S notice l10n("Contact %s deleted", n)
+ S redirectTo listOp.loc.calcDefaultHref
+ }
}
}
- case class DeleteContactForm(c: Contact) extends LiftScreen {
- val confirm = field("Really delete this contact?", false)
- override def finishButton: Elem = <button>{S ? "Delete"}</button>
- protected def finish() {
- confirm.is match {
- case true =>
- Contact.delete(c)
- S.notice("Contact " + c.linkName + " deleted")
- S.redirectTo(url(ListContacts))
- case false =>
- S.redirectTo(url(ShowContact(c)))
- }
- ()
- }
- }
+
}
-import fis.base.ui._
-
-object ContactSnippet2 extends EntitySnippet[Contact] {
- implicit def str2ns(s: String): NodeSeq = Text(s)
- def prefix = "contact2"
- def asEntity(id: Long): Box[Contact] = Contact.findByKey(id)
- def titleList = "Contacts"
- def titleAdd = "Create a contact"
- def titleShow(c: Contact) = "Contact " + c.linkName
- def titleEdit(c: Contact) = "Edit contact " + c.linkName
- def titleDelete(c: Contact) = "Delete contact " + c.linkName
-}
// vim: set ts=2 sw=2 et:
--- a/src/main/scala/net/tz/lift/snippet/package.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/main/scala/net/tz/lift/snippet/package.scala Tue Apr 03 14:39:58 2012 +0200
@@ -29,6 +29,7 @@
def a(href: Box[String])(cnt: NodeSeq): NodeSeq =
href.dmap(cnt) { l => <a href={l}>{cnt}</a> }
+ def locTpl(p: String): Loc.Template = LocTpl(p)
}
--- a/src/test/scala/fis/crm/model/ContactSpec.scala Tue Apr 03 14:31:12 2012 +0200
+++ b/src/test/scala/fis/crm/model/ContactSpec.scala Tue Apr 03 14:39:58 2012 +0200
@@ -22,7 +22,6 @@
import org.scalatest._
class ContactSpec extends AbstractTest with BeforeAndAfterAll {
- import CrmSchema._
override def beforeAll() {
super.beforeAll
@@ -31,13 +30,13 @@
"EntityContact" should "create" in { doInDB {
val c1 = Contact.createRecord.name("c1")
- contacts.insert(c1)
+ ContactCrud.save(c1) should be ('defined)
/*
val t1 = CodeListItem.createRecord.name("t1")
codeListItems.insert(t1)
*/
val c2 = Contact.createRecord.name("c2") // acts here as entity
- contacts.insert(c2)
+ ContactCrud.save(c2) should be ('defined)
/*
entityContacts.insert(EntityContact(c2.id, c1.id, t1.id))