# HG changeset patch # User Tomas Zeman # Date 1336596783 -7200 # Node ID eac38214183db3ef6df5334926ec2ac381f9adcd # Parent f6bdff1c40ed2d347977dc849a71463d0bb1d5fc 9830b81e1c79d212 Project locations diff -r f6bdff1c40ed -r eac38214183d src/main/resources/db/db-schema.sql --- a/src/main/resources/db/db-schema.sql Thu May 03 22:31:09 2012 +0200 +++ b/src/main/resources/db/db-schema.sql Wed May 09 22:53:03 2012 +0200 @@ -206,6 +206,10 @@ "project" bigint not null, "company" bigint not null ); +create table "project_location" ( + "location" bigint not null, + "project" bigint not null + ); -- foreign key constraints : alter table "address" add foreign key ("city_id") references "city"("id"); alter table "city" add foreign key ("country_id") references "country"("id"); @@ -228,9 +232,12 @@ alter table "user_contact" add foreign key ("contact") references "contact"("id") on delete cascade; alter table "project_company" add foreign key ("project") references "project"("id") on delete cascade; alter table "project_company" add foreign key ("company") references "company"("id") on delete cascade; +alter table "project_location" add foreign key ("project") references "project"("id") on delete cascade; +alter table "project_location" add foreign key ("location") references "location"("id") on delete cascade; -- composite key indexes : alter table "company_contact" add unique("entity","contact"); alter table "user_contact" add unique("entity","contact"); alter table "project_company" add unique("project","company"); +alter table "project_location" add unique("project","location"); -- column group indexes : create index "user_deleted_active_idx" on "user" ("deleted","active"); diff -r f6bdff1c40ed -r eac38214183d src/main/resources/db/schema-changes-0.2-0.3.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/resources/db/schema-changes-0.2-0.3.sql Wed May 09 22:53:03 2012 +0200 @@ -0,0 +1,10 @@ +-- DATABASE SCHEMA CHANGES BETWEEN 0.2 -> 0.3 VERSION + +-- project_location +create table "project_location" ( + "location" bigint not null, + "project" bigint not null + ); +alter table "project_location" add foreign key ("project") references "project"("id") on delete cascade; +alter table "project_location" add foreign key ("location") references "location"("id") on delete cascade; +alter table "project_location" add unique("project","location"); diff -r f6bdff1c40ed -r eac38214183d src/main/scala/fis/geo/model/GeoSchema.scala --- a/src/main/scala/fis/geo/model/GeoSchema.scala Thu May 03 22:31:09 2012 +0200 +++ b/src/main/scala/fis/geo/model/GeoSchema.scala Wed May 09 22:53:03 2012 +0200 @@ -38,6 +38,10 @@ Country.countries.default.set { () => from(countryT)( c => select(c) orderBy(c.name asc)) } + + Location.locations.default.set { () => from(locationT, addressT, cityT)( + (l, a, c) => where(l.address === a.id and a.city === c.id) + select(l) orderBy(c.name asc, l.name asc)) } } object GeoSchema extends GeoSchema diff -r f6bdff1c40ed -r eac38214183d src/main/scala/fis/geo/model/Location.scala --- a/src/main/scala/fis/geo/model/Location.scala Thu May 03 22:31:09 2012 +0200 +++ b/src/main/scala/fis/geo/model/Location.scala Wed May 09 22:53:03 2012 +0200 @@ -19,6 +19,7 @@ import net.liftweb.record.{MetaRecord, Record} import net.liftweb.record.field._ import net.liftweb.squerylrecord.KeyedRecord +import net.liftweb.util.SimpleInjector import net.tz.lift.model.{FieldLabel => FL} import org.squeryl.annotations.Column @@ -32,6 +33,9 @@ } -object Location extends Location with MetaRecord[Location] +object Location extends Location with MetaRecord[Location] with + SimpleInjector { + object locations extends Inject[Iterable[Location]](() => Nil) +} // vim: set ts=2 sw=2 et: diff -r f6bdff1c40ed -r eac38214183d src/main/scala/fis/geo/ui/LocationSnippet.scala --- a/src/main/scala/fis/geo/ui/LocationSnippet.scala Thu May 03 22:31:09 2012 +0200 +++ b/src/main/scala/fis/geo/ui/LocationSnippet.scala Wed May 09 22:53:03 2012 +0200 @@ -18,6 +18,8 @@ import fis.aaa.ui.IfLoggedIn import fis.base.ui._ import fis.geo.model._ +import fis.pm.model.LocationProjects +import fis.pm.ui.ProjectTable import net.liftweb.common._ import net.liftweb.http._ import net.liftweb.sitemap._ @@ -37,7 +39,8 @@ private val viewPre = Menu.param[Location]("location.view", l10n("Location"), parse, encode) / prefix / * >> Title(l => i18n("Location %s", l.linkName)) >> - locTpl("entity/view") >> Snippet("panel", panel) >> Hidden + locTpl("location/view") >> Snippet("panel", panel) >> + Snippet("projects", projects) >> Hidden private val editPre = Menu.param[Location]("location.edit", l10n("Edit"), parse, encode) / prefix / * / EDIT >> @@ -64,6 +67,9 @@ private def panel: CssTr = "*" #> cur.map(LocationPanel(_)) + private def projects: CssTr = "*" #> cur.map { l => + ProjectTable(LocationProjects(l)) } + object url { def view: Location => Box[String] = (viewLoc.calcHref _) andThen (Box !! _) } diff -r f6bdff1c40ed -r eac38214183d src/main/scala/fis/pm/model/PmSchema.scala --- a/src/main/scala/fis/pm/model/PmSchema.scala Thu May 03 22:31:09 2012 +0200 +++ b/src/main/scala/fis/pm/model/PmSchema.scala Wed May 09 22:53:03 2012 +0200 @@ -72,6 +72,15 @@ )) projectCompany.leftForeignKeyDeclaration.constrainReference(onDelete cascade) projectCompany.rightForeignKeyDeclaration.constrainReference(onDelete cascade) + + /* project - location */ + val projectLocation = manyToManyRelation(projectT, locationT). + via[ProjectLocation]((p, l, pl) => ( + p.id === pl.project, + l.id === pl.location + )) + projectLocation.leftForeignKeyDeclaration.constrainReference(onDelete cascade) + projectLocation.rightForeignKeyDeclaration.constrainReference(onDelete cascade) } object PmSchema extends PmSchema @@ -104,6 +113,21 @@ select(p) orderBy(p.name asc)) } +object ProjectLocations { + import fis.geo.model._ + def apply(p: Project): Iterable[Location] = + from(PmSchema.projectLocation.left(p), GeoSchema.addressT, + GeoSchema.cityT)((l, a, c) => + where(l.address === a.id and a.city === c.id) + select(l) orderBy(c.name asc, l.name asc)) +} + +object LocationProjects { + import fis.geo.model._ + def apply(l: Location): Iterable[Project] = + from(PmSchema.projectLocation.right(l))(p => select(p) orderBy(p.name asc)) +} + /* Many-to-many relations */ import org.squeryl.KeyedEntity @@ -114,4 +138,9 @@ def id = CompositeKey2(project, company) } +case class ProjectLocation(val project: Long, val location: Long) + extends KeyedEntity[CompositeKey2[Long, Long]] { + def id = CompositeKey2(project, location) +} + // vim: set ts=2 sw=2 et: diff -r f6bdff1c40ed -r eac38214183d src/main/scala/fis/pm/ui/ProjectSnippet.scala --- a/src/main/scala/fis/pm/ui/ProjectSnippet.scala Thu May 03 22:31:09 2012 +0200 +++ b/src/main/scala/fis/pm/ui/ProjectSnippet.scala Wed May 09 22:53:03 2012 +0200 @@ -16,8 +16,10 @@ package fis.pm.ui import fis.aaa.ui.IfLoggedIn +import fis.base.model.ReadOnlyField import fis.base.ui._ import fis.crm.model._ +import fis.geo.model._ import fis.pm.model._ import net.liftweb.common._ import net.liftweb.http._ @@ -62,19 +64,27 @@ IfLoggedIn.testVal >> locTpl("entity/form") >> Snippet("form", taskF) >> Hidden + private val locationsPre = Menu.param[Project]("project.locations", + l10n("Edit locations"), parse, encode) / prefix / * / "locations" >> + Title(p => i18n("Edit locations of project %s", p.linkName)) >> + IfLoggedIn.testVal >> + locTpl("entity/form") >> Snippet("form", locationsF) >> Hidden + private val listM = listPre >> SecNav(createPre).build private val createM = createPre >> SecNav(listPre).build private val viewM = viewPre >> (SecNav(editPre) + deletePre + - createTaskPre).build + locationsPre + createTaskPre).build private val editM = editPre >> SecNav(viewPre).build private val deleteM = deletePre >> SecNav(viewPre).build private val createTaskM = createTaskPre >> SecNav(viewPre).build + private val locationsM = locationsPre >> SecNav(viewPre).build private lazy val viewLoc = viewM.toLoc private lazy val editLoc = editM.toLoc private lazy val deleteLoc = deleteM.toLoc - val menu = listM submenus(viewM, editM, createM, deleteM, createTaskM) + val menu = listM submenus(viewM, editM, createM, deleteM, createTaskM, + locationsM) private def cur = viewLoc.currentValue or editLoc.currentValue or deleteLoc.currentValue @@ -92,7 +102,7 @@ private def fields(p: Project) = List(p.name, p.identS, p.stateFld, p.createdBy, p.createdAt, p.deadline, p.responsible, p.productLine, - ProjectCompanyField(p), p.description, p.note) + ProjectCompanyField(p), ProjectLocationsField(p), p.description, p.note) private case class ProjectLink(c: Project) extends EntityLink[Project](c, url.view) @@ -105,6 +115,13 @@ PmSchema.projectCompany.left(p).headOption.map(_.id) } + private case class ProjectLocationsField(p: Project) extends ReadOnlyField( + "locations", l10n("project.locations"), NodeSeq.Empty, Empty) { + override def asHtml = { + ProjectLocations(p) flatMap(EntityLink(_)) map { l =>
{l.asHtml}
} + }
+ } + private object form extends HorizontalScreen with CancelButton with SaveButton { @@ -167,6 +184,52 @@ } } + private object locationsF extends HorizontalScreen with CancelButton with + SaveButton { + + private class V(iv: Boolean, val l: Location) { var v: Boolean = iv } + + private object locs extends ScreenVar[Iterable[V]](Nil) + + def v2r(v: V): Box[List[NodeSeq]] = for { + ll <- EntityLink(v.l) + a <- v.l.address.vend + c <- a.city.vend + cl <- EntityLink(c) + } yield { + List(SHtml.checkbox(v.v, v.v = _), ll.asHtml, v.l.address.asHtml, + cl.asHtml) + } + + def locsTbl = + (new DataTable(List(l10n("Check"), l10n("Location"), + Location.address.displayName, l10n("City")), + locs flatMap { v2r _ } toList))(NodeSeq.Empty) + + addFields(() => new ReadOnlyField("locations", l10n("project.locations"), + NodeSeq.Empty, Full(locsTbl))) + + override def localSetup() { + locationsM.toLoc.currentValue.foreach { p => + val ids = ProjectLocations(p) map(_.id) toSet + val ls = Location.locations() map { l => new V(ids contains l.id, l) } + locs(ls) + } + } + + def finish() { locationsM.toLoc.currentValue.foreach { p => + val fk = PmSchema.projectLocation.left(p) + val curIds = fk map(_.id) toSet + val newLocs = locs filter(_.v) map(_.l) + val newIds = newLocs map(_.id) toSet + + fk filterNot(newIds contains _.id) foreach { l => fk.dissociate(l) } + newLocs filterNot(curIds contains _.id) foreach { l => fk.associate(l) } + S notice l10n("Locations saved.") + S redirectTo viewLoc.calcHref(p) + }} + } + } // vim: set ts=2 sw=2 et: diff -r f6bdff1c40ed -r eac38214183d src/main/webapp/css/base.css --- a/src/main/webapp/css/base.css Thu May 03 22:31:09 2012 +0200 +++ b/src/main/webapp/css/base.css Wed May 09 22:53:03 2012 +0200 @@ -28,6 +28,7 @@ font-weight: bold; width: 150px; text-align: justify; + vertical-align: top; } .attr-name-wide { diff -r f6bdff1c40ed -r eac38214183d src/main/webapp/location/view.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/location/view.html Wed May 09 22:53:03 2012 +0200 @@ -0,0 +1,24 @@ + + + + + Entity View + + +
+
+
+ +
+
+
+
+

+ +
+
+
+ + + + diff -r f6bdff1c40ed -r eac38214183d src/main/webapp/templates-hidden/_resources.html --- a/src/main/webapp/templates-hidden/_resources.html Thu May 03 22:31:09 2012 +0200 +++ b/src/main/webapp/templates-hidden/_resources.html Wed May 09 22:53:03 2012 +0200 @@ -16,6 +16,7 @@ @@ -183,6 +184,7 @@ Location %s deleted. Location %s saved. --> + Used in projects Name Note @@ -200,6 +202,9 @@ Project %s saved. Really delete this project? Project %s deleted. + Edit locations + Edit locations of project %s + Locations saved. --> Projects @@ -213,6 +218,7 @@ State Tasks Company + Locations Assigned Paused diff -r f6bdff1c40ed -r eac38214183d src/main/webapp/templates-hidden/_resources_cs.html --- a/src/main/webapp/templates-hidden/_resources_cs.html Thu May 03 22:31:09 2012 +0200 +++ b/src/main/webapp/templates-hidden/_resources_cs.html Wed May 09 22:53:03 2012 +0200 @@ -15,6 +15,7 @@ Vytvořil Aktualizováno Aktualizoval + Označit @@ -173,6 +174,7 @@ Skutečně smazat lokalitu? Lokalita %s smazána. Lokalita %s uložena. + Použito v projektech Název @@ -190,6 +192,9 @@ Projekt %s uložen. Skutečně smazat projekt? Projekt %s smazán. + Upravit lokality + Upravit lokality pro projekt %s + Lokality uloženy. Název Poznámka @@ -201,6 +206,7 @@ Stav Úkoly Společnost + Lokality Přidělen Pozastaven