# HG changeset patch # User Tomas Zeman # Date 1334907951 -7200 # Node ID 4bcb7deedd3f3223af6e6d388d3c580a12e26553 # Parent 077b875a2a0ccba6f5432da04f84bf5eeeb61d24 User contacts diff -r 077b875a2a0c -r 4bcb7deedd3f db/db-schema.sql --- a/db/db-schema.sql Fri Apr 20 08:44:36 2012 +0200 +++ b/db/db-schema.sql Fri Apr 20 09:45:51 2012 +0200 @@ -34,22 +34,6 @@ create sequence "code_list_item_id_seq"; -- indexes on code_list_item create index "code_list_item_code_list_idx" on "code_list_item" ("code_list"); -create table "user" ( - "name" varchar(100) not null, - "updated_at" timestamp not null, - "id" bigint primary key not null, - "note" varchar(10240), - "created_at" timestamp not null, - "created_by" bigint, - "login" varchar(40) not null, - "updated_by" bigint, - "deleted" boolean not null, - "active" boolean not null, - "password" varchar(128) not null - ); -create sequence "user_id_seq"; --- indexes on user -create index "user_login_idx" on "user" ("login"); create table "city" ( "name" varchar(100) not null, "updated_at" timestamp not null, @@ -142,6 +126,26 @@ "contact" bigint not null, "entity" bigint not null ); +create table "user" ( + "name" varchar(100) not null, + "updated_at" timestamp not null, + "id" bigint primary key not null, + "note" varchar(10240), + "created_at" timestamp not null, + "created_by" bigint, + "login" varchar(40) not null, + "updated_by" bigint, + "deleted" boolean not null, + "active" boolean not null, + "password" varchar(128) not null + ); +create sequence "user_id_seq"; +-- indexes on user +create index "user_login_idx" on "user" ("login"); +create table "user_contact" ( + "contact" bigint not null, + "entity" bigint not null + ); -- foreign key constraints : alter table "address" add constraint "addressFK1" foreign key ("city_id") references "city"("id"); alter table "city" add constraint "cityFK2" foreign key ("country_id") references "country"("id"); @@ -150,7 +154,10 @@ alter table "bank_account" add constraint "bank_accountFK5" foreign key ("company_id") references "company"("id") on delete cascade; alter table "company_contact" add constraint "company_contactFK6" foreign key ("entity") references "company"("id") on delete cascade; alter table "company_contact" add constraint "company_contactFK7" foreign key ("contact") references "contact"("id") on delete cascade; +alter table "user_contact" add constraint "user_contactFK8" foreign key ("entity") references "user"("id") on delete cascade; +alter table "user_contact" add constraint "user_contactFK9" foreign key ("contact") references "contact"("id") on delete cascade; -- composite key indexes : alter table "company_contact" add constraint "company_contactCPK" unique("entity","contact"); +alter table "user_contact" add constraint "user_contactCPK" unique("entity","contact"); -- column group indexes : create index "user_deleted_active_idx" on "user" ("deleted","active"); diff -r 077b875a2a0c -r 4bcb7deedd3f src/main/scala/fis/aaa/model/AaaSchema.scala --- a/src/main/scala/fis/aaa/model/AaaSchema.scala Fri Apr 20 08:44:36 2012 +0200 +++ b/src/main/scala/fis/aaa/model/AaaSchema.scala Fri Apr 20 09:45:51 2012 +0200 @@ -16,12 +16,13 @@ package fis.aaa.model import fis.base.model.BaseSchema +import fis.crm.model.{Contact, CrmSchema, EntityContact} import net.liftweb.squerylrecord.RecordTypeMode._ /** * Database schema for users, ... */ -trait AaaSchema extends BaseSchema { +trait AaaSchema extends BaseSchema with CrmSchema { val userT = tableWithSeq[User] on(userT)(t => declare( t.login defineAs(indexed("user_login_idx")), @@ -38,8 +39,28 @@ val activeUsersF = () => from(userT)(u => where(u.deleted === false and u.active === true) select(u) orderBy(u.name asc)) + + val userContacts = manyToManyRelation(userT, contactT). + via[UserContact]((u, c, uc) => ( + u.id === uc.entity, + c.id === uc.contact + )) + userContacts.leftForeignKeyDeclaration.constrainReference(onDelete cascade) + userContacts.rightForeignKeyDeclaration.constrainReference(onDelete cascade) + } object AaaSchema extends AaaSchema +case class UserContact(entity: Long, contact: Long) extends + EntityContact[User] + +object UserContacts { + def apply(u: User): Iterable[Contact] = + from(AaaSchema.userContacts.left(u)) (c => + select(c) orderBy(c.lastName asc, c.firstName asc) + ) +} + + // vim: set ts=2 sw=2 et: diff -r 077b875a2a0c -r 4bcb7deedd3f src/main/scala/fis/aaa/ui/UserSnippet.scala --- a/src/main/scala/fis/aaa/ui/UserSnippet.scala Fri Apr 20 08:44:36 2012 +0200 +++ b/src/main/scala/fis/aaa/ui/UserSnippet.scala Fri Apr 20 09:45:51 2012 +0200 @@ -18,8 +18,11 @@ import fis.base.model._ import fis.base.ui._ import fis.aaa.model._ +import fis.crm.model.{Contact, ContactCrud, CrmSchema} +import fis.crm.ui.ContactTable import net.liftweb.common._ import net.liftweb.http._ +import net.liftweb.http.js.JsCmds.RedirectTo import net.liftweb.sitemap._ import net.liftweb.sitemap.Loc._ import net.liftweb.util._ @@ -41,7 +44,8 @@ private val viewPre = Menu.param[User]("user.view", l10n("User"), parse, encode) / prefix / * >> Title(c => i18n("User %s", c.linkName)) >> - locTpl("entity/view") >> Snippet("panel", panel) >> Hidden + locTpl("user/view") >> Snippet("panel", panel) >> + Snippet("contacts", contacts.contacts) >> Hidden private val editPre = Menu.param[User]("user.edit", l10n("Edit"), parse, encode) / prefix / * / EDIT >> @@ -55,7 +59,8 @@ private val listM = listPre >> SecNav(createPre).build private val createM = createPre >> SecNav(listPre).build - private val viewM = viewPre >> (SecNav(editPre) + deletePre).build + private val viewM = viewPre >> + (SecNav(editPre) + deletePre + contacts.chooseM).build private val editM = editPre >> SecNav(viewPre).build private val deleteM = deletePre >> SecNav(viewPre).build @@ -63,7 +68,8 @@ private lazy val editLoc = editM.toLoc private lazy val deleteLoc = deleteM.toLoc - val menu = listM submenus(viewM, editM, createM, deleteM) + val menu = listM submenus(viewM, editM, createM, deleteM, + contacts.chooseM, contacts.confirmM) private def cur = viewLoc.currentValue or editLoc.currentValue or deleteLoc.currentValue @@ -135,6 +141,79 @@ } } + /* Contacts view + add contact op. */ + private object contacts { + + private val choosePreM = Menu.param[User]("user.addContact", + l10n("Add contact"), parse, encode) / prefix / * / "add-contact" >> + Title(c => i18n("Add contact to %s", c.linkName)) >> + locTpl("entity/add-contact") >> Snippet("panel", chooseContact) >> Hidden + + val confirmM = Menu.params[(User, Contact)]( + "user.addContactId", l10n("Add contact"), { _ match { + case AsLong(uId) :: AsLong(cntId) :: Nil => for { + u <- get(uId) + cnt <- ContactCrud.get(cntId) + } yield (u, cnt) + case _ => Empty + }}, + { p => List(p._1, p._2) map(_.id.toString) }) / prefix / + * / "add-contact" / * >> + Title(p => i18n("Add contact %s to %s", p._2.linkName, p._1.linkName)) >> + locTpl("entity/form") >> Snippet("form", addContact) >> Hidden + + val chooseM = choosePreM >> SecNav(viewPre).build + + private case class RemoveContactLink(u: User, cnt: Contact) extends + ReadOnlyField("actions", "", ConfirmationLink(i18n("Remove"), + l10n("Really remove contact?"), { + AaaSchema.userContacts.left(u).dissociate(cnt) + RedirectTo(viewLoc.calcHref(u)) + }), Empty) + + def contacts: CssTr = "*" #> cur.map { u => + FieldTable[Contact]({ c => ContactTable.fields(c).toSeq :+ + RemoveContactLink(u, c) }, Contact)(UserContacts(u)) } + + private case class AddContactLink(u: User, c: Contact) extends + EntityLink[Contact](c, + { _ => Full(confirmM.toLoc.calcHref((u, c))) }) + + private def chooseContact: CssTr = "*" #> chooseM.toLoc.currentValue.map { + u => + FieldTable[Contact]({ c => + List(AddContactLink(u, c)) }, Contact)(CrmSchema.allContacts) + } + + private object addContact extends HorizontalScreen with CancelButton + with SaveButton { + + override def screenFields: List[BaseField] = (for { + (u, c) <- confirmM.toLoc.currentValue + ul <- EntityLink(u) + cl <- EntityLink(c) + } yield { + List(ReadOnlyField(l10n("User"), ul), + ReadOnlyField(l10n("Contact"), cl)) flatMap(_.allFields) + }) openOr Nil + + def finish() { for { + (u, c) <- confirmM.toLoc.currentValue + } { + val fk = AaaSchema.userContacts.left(u) + fk.exists(_.id == c.id) match { + case true => + S notice l10n("Contact %s is already associated with %s", + c.linkName, u.linkName) + case false => + fk.associate(c) + S notice l10n("Added contact %s", c.linkName) + } + S redirectTo viewLoc.calcHref(u) + }} + } + } + } // vim: set ts=2 sw=2 et: diff -r 077b875a2a0c -r 4bcb7deedd3f src/main/webapp/entity/add-contact.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/entity/add-contact.html Fri Apr 20 09:45:51 2012 +0200 @@ -0,0 +1,18 @@ + + + + + Entity View + + +
+
+
+ +
+
+
+ + + + diff -r 077b875a2a0c -r 4bcb7deedd3f src/main/webapp/user/view.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/user/view.html Fri Apr 20 09:45:51 2012 +0200 @@ -0,0 +1,24 @@ + + + + + Entity View + + +
+
+
+ +
+
+
+
+

+ +
+
+
+ + + +