Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Designer Friendly Templates

  • Loading branch information...
commit 22add5868572d47eba2b792cfcb4acc0944f8b45 1 parent 536a919
unknown authored
Showing with 1,390 additions and 2,158 deletions.
  1. +4 −0 .gitignore
  2. +2 −30 README
  3. +22 −2 pom.xml
  4. +14 −7 spa/pom.xml
  5. +2 −12 spa/src/main/resources/META-INF/persistence.xml
  6. +0 −3  spa/src/main/scala/TravelCompanionScala/model/BlogEntry.scala
  7. +2 −2 spa/src/main/scala/TravelCompanionScala/model/Member.scala
  8. +0 −3  spa/src/main/scala/TravelCompanionScala/model/Tour.scala
  9. +25 −26 spa/src/test/scala/TravelCompanionScala/model/TestJPAWeb.scala
  10. +8 −115 web/pom.xml
  11. +2 −0  web/src/main/resources/Picture_de.properties
  12. +2 −0  web/src/main/resources/Picture_en.properties
  13. +2 −2 web/src/main/resources/TravelCompanion_de.properties
  14. +2 −2 web/src/main/resources/TravelCompanion_en.properties
  15. +2 −2 web/src/main/resources/default.props
  16. +50 −49 web/src/main/scala/TravelCompanionScala/api/GridAPI.scala
  17. +1 −1  web/src/main/scala/TravelCompanionScala/api/PCData.scala
  18. +23 −92 web/src/main/scala/TravelCompanionScala/api/RestAPI.scala
  19. +16 −87 web/src/main/scala/TravelCompanionScala/comet/DynamicBlogViews.scala
  20. +17 −52 web/src/main/scala/TravelCompanionScala/controller/BlogCache.scala
  21. +21 −53 web/src/main/scala/TravelCompanionScala/model/EntityConverter.scala
  22. +14 −24 web/src/main/scala/TravelCompanionScala/model/GeoCoder.scala
  23. +6 −31 web/src/main/scala/TravelCompanionScala/model/ImageLogic.scala
  24. +17 −30 web/src/main/scala/TravelCompanionScala/model/Model.scala
  25. +27 −82 web/src/main/scala/TravelCompanionScala/model/UserManagement.scala
  26. +24 −0 web/src/main/scala/TravelCompanionScala/model/Util.scala
  27. +157 −311 web/src/main/scala/TravelCompanionScala/snippet/BlogSnippet.scala
  28. +8 −17 web/src/main/scala/TravelCompanionScala/snippet/Language.scala
  29. +62 −110 web/src/main/scala/TravelCompanionScala/snippet/PictureSnippet.scala
  30. +22 −0 web/src/main/scala/TravelCompanionScala/snippet/PropsView.scala
  31. +48 −75 web/src/main/scala/TravelCompanionScala/snippet/StageSnippet.scala
  32. +6 −13 web/src/main/scala/TravelCompanionScala/snippet/TableSorter.scala
  33. +39 −73 web/src/main/scala/TravelCompanionScala/snippet/TourSnippet.scala
  34. +6 −10 web/src/main/scala/TravelCompanionScala/snippet/TravelDistance.scala
  35. +9 −27 web/src/main/scala/TravelCompanionScala/snippet/UsrMgtHelper.scala
  36. +6 −23 web/src/main/scala/TravelCompanionScala/widget/Gauge.scala
  37. +55 −42 web/src/main/scala/bootstrap/liftweb/Boot.scala
  38. +8 −6 web/src/main/webapp/accessrestricted.html
  39. +21 −0 web/src/main/webapp/blog/_chooseCommentForm.html
  40. +31 −0 web/src/main/webapp/blog/_chooseComments.html
  41. +35 −0 web/src/main/webapp/blog/_chooseEntry.html
  42. +35 −0 web/src/main/webapp/blog/_chooseForm.html
  43. +88 −243 web/src/main/webapp/blog/list.html
  44. +51 −60 web/src/main/webapp/blog/view.html
  45. +6 −7 web/src/main/webapp/index.html
  46. +33 −38 web/src/main/webapp/picture/create.html
  47. +86 −87 web/src/main/webapp/picture/list.html
  48. +12 −11 web/src/main/webapp/picture/view.html
  49. +1 −1  web/src/main/webapp/scripts/jquery.jqGrid.min.js
  50. +18 −28 web/src/main/webapp/templates-hidden/default.html
  51. +37 −40 web/src/main/webapp/tour/edit.html
  52. +34 −39 web/src/main/webapp/tour/list.html
  53. +42 −45 web/src/main/webapp/tour/stage/edit.html
  54. +32 −33 web/src/main/webapp/tour/stage/view.html
  55. +97 −112 web/src/main/webapp/tour/view.html
View
4 .gitignore
@@ -0,0 +1,4 @@
+*.iml
+*.ipr
+.idea
+target
View
32 README
@@ -1,35 +1,7 @@
-Welcome to TravelCompanion in Scala / Lift.
+Welcome to the Lift JPA Archetype. To run the sample app:
-This project was created during the bachelor thesis by Ralf Muri and Daniel Hobi in spring 2010.
-
-The following technical aspects are implemented:
-- JPA (based on lift-jpa-archetype)
-- User Management (copy & paste from ProtoUser without mapper functionality)
-- Validating (JSR 303)
-- GUI Widgets (DistanceTraveler)
-- Ajax & Comet (Blog as Single Page Application)
-- REST
-
-Please feel free to download & study our document in the "doc" directory (german!)
-You will get some additional stuff like:
-- Best practices in Lift & HowTos
-- Working with IntelliJ IDEA & github.com
-- General information about TravelCompanion
-- Experience made by the authors
-
-A running demo is deployed on stax.net:
-http://travelcompanion.ralfmuri.staxapps.net
-
-To run the app:
mvn install
cd web
mvn jetty:run
-Then point your favorite browser to http://localhost:9090/
-
-To create an offline version of the app (web/target/TravelCompanionScala-offline-1.0/):
-mvn install
-cd web
-mvn package -Pjetty-offline
-
-Notice: TravelCompanion is intended for demo purposes only.
+Then point your favorite browser to http://localhost:9090/
View
24 pom.xml
@@ -11,7 +11,7 @@
<packaging>pom</packaging>
<inceptionYear>2010</inceptionYear>
<properties>
- <scala.version>2.8.0.RC5</scala.version>
+ <scala.version>2.8.1</scala.version>
</properties>
<!-- Set up repo for ScalaJPA -->
@@ -64,12 +64,32 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.apache.derby</groupId>
+ <artifactId>derby</artifactId>
+ <version>10.4.2.0</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
-
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.16</version>
+ </dependency>
</dependencies>
<build>
View
21 spa/pom.xml
@@ -28,15 +28,20 @@
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>2.1.0</version>
<scope>runtime</scope>
</dependency>
+ <!--<dependency>-->
+ <!--<groupId>javax.persistence</groupId>-->
+ <!--<artifactId>persistence-api</artifactId>-->
+ <!--<version>1.0</version>-->
+ <!--</dependency>-->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.0</version>
</dependency>
- <dependency>
+ <dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
@@ -48,11 +53,13 @@
<version>1.0.1B-rc4</version>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.0.8</version>
- </dependency>
+ <!--<dependency>-->
+ <!--<groupId>org.slf4j</groupId>-->
+ <!--<artifactId>slf4j-simple</artifactId>-->
+ <!--<version>1.4.2</version>-->
+ <!--<scope>runtime</scope>-->
+ <!--</dependency>-->
+
</dependencies>
View
14 spa/src/main/resources/META-INF/persistence.xml
@@ -11,23 +11,13 @@
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
-
- <!--Configuration for deployment on stax.net on MySql database-->
- <!--<property name="javax.persistence.jdbc.password" value="tc"/>-->
- <!--<property name="javax.persistence.jdbc.user" value="tc"/> -->
- <!--<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>-->
- <!--<property name="javax.persistence.jdbc.url"
- value="jdbc:mysql://ec2-75-101-156-134.compute-1.amazonaws.com:3306/travelcompanion"/> -->
- <!--stax.net deployment configuration end-->
-
- <!--Configuration for local file based H2 Database-->
<property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.H2Platform"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:file:~/TravelCompanion;FILE_LOCK=NO"/>
- <!--H2 configuration end-->
-
+ <!--<property name="eclipselink.jdbc.url" value="jdbc:h2:tcp://localhost/~/aulaweb/db/database"/>-->
+ <!--<property name="eclipselink.ddl-generation" value="drop-and-create-tables" />-->
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="database"/>
<!-- To print SQL statements -->
View
3  spa/src/main/scala/TravelCompanionScala/model/BlogEntry.scala
@@ -46,9 +46,6 @@ class BlogEntry {
@OneToMany(mappedBy = "blogEntry", cascade = Array(CascadeType.ALL), targetEntity = classOf[Comment])
var comments: java.util.List[Comment] = new ArrayList[Comment]()
-
- @OneToMany(mappedBy = "blogEntry", cascade = Array(CascadeType.ALL), targetEntity = classOf[Picture])
- var pictures: java.util.List[Picture] = new ArrayList[Picture]()
}
}
View
4 spa/src/main/scala/TravelCompanionScala/model/Member.scala
@@ -15,7 +15,7 @@ import org.hibernate.validator.constraints._
@Entity
@Table(name = "members")
-class Member {
+class Member() {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id: Long = _
@@ -57,7 +57,7 @@ class Member {
val pictures: List[Picture] = new ArrayList[Picture]()
@ManyToMany(cascade = Array(CascadeType.ALL))
- @JoinTable(name = "member_roles", joinColumns = Array(new JoinColumn(name = "member", referencedColumnName = "ID")), inverseJoinColumns = Array(new JoinColumn(name = "roles", referencedColumnName = "ID")))
+ @JoinTable(name = "member_roles", joinColumns = Array(new JoinColumn(name = "member", referencedColumnName = "id")), inverseJoinColumns = Array(new JoinColumn(name = "roles", referencedColumnName = "id")))
val roles: List[Role] = new ArrayList[Role]()
override def equals(that: Any): Boolean = that match {
View
3  spa/src/main/scala/TravelCompanionScala/model/Tour.scala
@@ -38,9 +38,6 @@ class Tour {
@OneToMany(mappedBy = "tour", cascade = Array(CascadeType.ALL), targetEntity = classOf[BlogEntry])
var blogEntries: List[BlogEntry] = new ArrayList[BlogEntry]()
- @OneToMany(mappedBy = "tour", cascade = Array(CascadeType.ALL), targetEntity = classOf[Picture])
- var pictures: List[Picture] = new ArrayList[Picture]()
-
}
View
51 spa/src/test/scala/TravelCompanionScala/model/TestJPAWeb.scala
@@ -45,21 +45,15 @@ class TestJPAWeb {
def save_stuff () = {
var em = emf.createEntityManager()
- em.getTransaction.begin
+ val tx = em.getTransaction()
- //Member
+ tx.begin()
val member = new Member
- member.name = "supertestuser111"
- member.password = "1234"
- member.email = "dhobi@hsr.ch"
- member.forename = "Hobi"
+ member.name = "Hobi"
em.persist(member)
-
- ///Tour
-
val tour = new Tour
tour.name = "My Travel"
tour.description = "description"
@@ -67,28 +61,31 @@ class TestJPAWeb {
em.persist(tour)
- em.getTransaction.commit
- em.close
+
+ tx.commit()
+
+ em.close()
/////assert
em = emf.createEntityManager()
- em.getTransaction.begin
val retrieved = em.createQuery("SELECT t from Tour t where t.name = :name").setParameter("name","My Travel").getResultList.asInstanceOf[java.util.List[Tour]]
assertEquals("My Travel", retrieved.get(0).name)
println("Found " + retrieved.get(0).name)
- assertEquals("Hobi",retrieved.get(0).owner.forename)
- println("Found member " + retrieved.get(0).owner.forename)
-
- //cleanup
+ assertEquals("Hobi",retrieved.get(0).owner.name)
+ println("Found member " + retrieved.get(0).owner.name)
+ ///clenaup
+ em.getTransaction().begin()
- em.remove(em.getReference(classOf[Member],member.id))
em.remove(em.getReference(classOf[Tour],tour.id))
- em.getTransaction.commit
- em.close
+ em.remove(em.getReference(classOf[Member],member.id))
+
+ em.getTransaction().commit()
+
+ em.close()
}
@Test
@@ -100,10 +97,7 @@ class TestJPAWeb {
tx.begin()
val member = new Member
- member.forename = "Hobi2"
- member.name = "supertestuser111"
- member.password = "1234"
- member.email = "dhobi@hsr.ch"
+ member.name = "Hobi2"
em.persist(member)
@@ -126,10 +120,14 @@ class TestJPAWeb {
///assert
em = emf.createEntityManager()
- val retrieved = em.createQuery("SELECT m from Member m where m.forename = :forename").setParameter("forename","Hobi2").getResultList.asInstanceOf[java.util.List[Member]]
- assertEquals("Hobi2", retrieved.get(0).forename)
- println("Found " + retrieved.get(0).forename)
+ val retrieved = em.createQuery("SELECT m from Member m where m.name = :name").setParameter("name","Hobi2").getResultList.asInstanceOf[java.util.List[Member]]
+
+ assertEquals("Hobi2", retrieved.get(0).name)
+ println("Found " + retrieved.get(0).name)
+
+// assertEquals("Hobi2",retrieved.get(1).name)
+// println("Found member " + retrieved.get(1).name)
///cleanup
em.getTransaction().begin()
@@ -142,6 +140,7 @@ class TestJPAWeb {
em.close()
}
+
}
}
View
123 web/pom.xml
@@ -15,40 +15,6 @@
<packaging>war</packaging>
<name>TravelCompanionScala Web</name>
- <repositories>
- <repository>
- <id>stax-releases</id>
- <url>http://mvn.stax.net/content/repositories/releases</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>stax-snapshots</id>
- <url>http://mvn.stax.net/content/repositories/snapshots</url>
- <releases>
- <enabled>false</enabled>
- </releases>
- </repository>
- </repositories>
-
- <pluginRepositories>
- <pluginRepository>
- <id>stax-plugins-releases</id>
- <url>http://mvn.stax.net/content/repositories/releases</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </pluginRepository>
- <pluginRepository>
- <id>stax-plugins-snapshots</id>
- <url>http://mvn.stax.net/content/repositories/snapshots</url>
- <releases>
- <enabled>true</enabled>
- </releases>
- </pluginRepository>
- </pluginRepositories>
-
<dependencies>
<dependency>
<groupId>TravelCompanionScala</groupId>
@@ -57,13 +23,13 @@
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
- <artifactId>lift-jpa</artifactId>
- <version>2.0-scala280-SNAPSHOT</version>
+ <artifactId>lift-jpa_2.8.1</artifactId>
+ <version>2.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
- <artifactId>lift-widgets</artifactId>
- <version>2.0-scala280-SNAPSHOT</version>
+ <artifactId>lift-widgets_2.8.1</artifactId>
+ <version>2.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
@@ -72,8 +38,8 @@
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
- <artifactId>lift-imaging</artifactId>
- <version>2.0-scala280-SNAPSHOT</version>
+ <artifactId>lift-imaging_2.8.1</artifactId>
+ <version>2.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
@@ -85,7 +51,7 @@
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
- <version>[6.1.6,7.0)</version>
+ <version>6.1.24</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -93,13 +59,9 @@
<build>
<plugins>
<plugin>
- <groupId>net.stax</groupId>
- <artifactId>stax-maven-plugin</artifactId>
- </plugin>
- <plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
- <version>6.1.22</version>
+ <version>6.1.24</version>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>5</scanIntervalSeconds>
@@ -129,73 +91,4 @@
</plugins>
</build>
- <profiles>
- <profile>
- <id>jetty-offline</id>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-assembly-plugin</artifactId>
- <version>2.2-beta-5</version>
- <configuration>
- <finalName>TravelCompanionScala-offline-${project.version}</finalName>
- <attach>false</attach>
- <descriptors>
- <descriptor>src/assembly/jetty-offline.xml</descriptor>
- </descriptors>
- </configuration>
- <executions>
- <execution>
- <id>make-assembly</id>
- <phase>package</phase>
- <goals>
- <goal>single</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- <dependencies>
- <!-- logging ...
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.1.1</version>
- </dependency>
- <dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.15</version>
- <scope>runtime</scope>
- </dependency>
- -->
- <!-- embedded jetty -->
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty</artifactId>
- <version>${jetty.version}</version>
- </dependency>
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>start</artifactId>
- <version>${jetty.version}</version>
- </dependency>
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.version}</version>
- </dependency>
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jsp-2.1-jetty</artifactId>
- <version>${jetty.version}</version>
- </dependency>
- </dependencies>
- <properties>
- <jetty.version>6.1.23</jetty.version>
- </properties>
- </profile>
- </profiles>
-
</project>
View
2  web/src/main/resources/Picture_de.properties
@@ -6,3 +6,5 @@ picture.others.desc=Nachfolgend erhalten Sie eine Anzeige der Bilder von anderen
picture.new=Neues Bild hinzufügen
picture.new.desc=Füllen Sie das folgende Formular aus, um ein neues Bild hochzuladen
picture.choose=Bild auswählen
+picture.creator=Ersteller
+picture.name=Bild
View
2  web/src/main/resources/Picture_en.properties
@@ -6,3 +6,5 @@ picture.others.desc=The following list shows all pictures uploaded by others
picture.new=Add new picture
picture.new.desc=Please fill in the following form to upload a new picture
picture.choose=choose picture
+picture.creator=creator
+picture.name=picture
View
4 web/src/main/resources/TravelCompanion_de.properties
@@ -1,4 +1,3 @@
-hsr=Hochschule für Technik Rapperswil
name=Name
description=Beschreibung
operations=Operationen
@@ -37,4 +36,5 @@ details=Details
userexists=Dieser Benutzer existiert bereits
useradderr=Beim Hinzufügen des Benutzers trat ein Fehler auf
noAccess=Kein Zugriff
-noAccess.desc=Der Zugriff auf die gewählte Seite oder Operation ist nicht gestattet
+noAccess.desc=Der Zugriff auf die gewählte Seite oder Operation ist nicht gestattet
+options=Optionen
View
4 web/src/main/resources/TravelCompanion_en.properties
@@ -1,4 +1,3 @@
-hsr=University of Applied Sciences Rapperswil
name=Name
description=Description
operations=Operations
@@ -37,4 +36,5 @@ details=details
userexists=This user exists already
useradderr=An error occured while adding the user
noAccess=No Access
-noAccess.desc=Access to the selected page or operation is permitted
+noAccess.desc=Access to the selected page or operation is permitted
+options=options
View
4 web/src/main/resources/default.props
@@ -1,3 +1,3 @@
version=1.0
-lastUpdate=17.06.2010
-authors=Ralf Muri, Daniel Hobi
+lastUpdate=06.01.2011
+versions=Scala 2.8.1, Lift 2.2
View
99 web/src/main/scala/TravelCompanionScala/api/GridAPI.scala
@@ -1,28 +1,29 @@
package TravelCompanionScala.api
+/**
+ * Created by IntelliJ IDEA.
+ * User: pmei
+ * Date: 04.06.2010
+ * Time: 11:26:26
+ * Package: TravelCompanionScala.api
+ * Class: GridAPI
+ */
+
import TravelCompanionScala.model.EntityConverter._
import net.liftweb.common._
-import TravelCompanionScala.model.{Model, Tour}
import net.liftweb.util.Helpers._
-import net.liftweb.http.{SessionVar, RequestVar, SHtml, S}
-import net.liftweb.http.rest.{XmlSelect, JsonSelect, RestHelper}
-import net.liftweb.json.Xml
-import xml.{Elem, Text, Node}
+import net.liftweb.http.rest.RestHelper
+import xml.{Text, Node}
import collection.mutable.Buffer
-import TravelCompanionScala.snippet.tourVarSession
-
-/**
- * GridAPI, serves the special jqGrid Table. This component was chosen because of the paging
- *
- * @author Philipp Meier
- */
+import net.liftweb.http._
+import TravelCompanionScala.model.{UserManagement, Model, Tour}
object tourVar extends RequestVar[Tour](new Tour())
object GridAPI extends RestHelper {
//This var is needed for the construction of the xml
- var tourData = Buffer[Node]()
+ var tourData = Buffer[Node] ()
//a more performant solution with direct sql query
val resultOption: Option[java.lang.Long] = Model.createNativeQuery("select count(*) from tours").findOne
@@ -34,7 +35,7 @@ object GridAPI extends RestHelper {
def getData = {
- for{
+ for {
Spage <- S.param("page") ?~ "page parameter missing" ~> 400
Srows <- S.param("rows") ?~ "row parameter missing" ~> 400
//the sort order
@@ -43,63 +44,63 @@ object GridAPI extends RestHelper {
Ssidx <- S.param("sidx") ?~ "row parameter missing" ~> 400
} yield {
- page = Spage.toInt - 1
+ page = Spage.toInt -1
rows = Srows.toInt
//Not posssible to create typesafe query - because only in Java with Java Entity-Types
//see http://www.ibm.com/developerworks/java/library/j-typesafejpa
- var queryString = "SELECT t from Tour t order by"
+ var queryString = "SELECT t from Tour t where not t.owner = :owner order by"
- Ssidx match {
- case "ID" => queryString = queryString.concat(" t.id")
- case "Name" => queryString = queryString.concat(" t.name")
- case "Description" => queryString = queryString.concat(" t.description")
- case _ => queryString = queryString.concat(" t.id")
- }
+ Ssidx match {
+ case "ID" => queryString = queryString.concat(" t.id")
+ case "Name" => queryString = queryString.concat(" t.name")
+ case "Description" => queryString = queryString.concat(" t.description")
+ case _ => queryString = queryString.concat(" t.id")
+ }
- Ssord match {
- case "asc" => queryString = queryString.concat(" ASC")
- case "desc" => queryString = queryString.concat(" DESC")
- case _ => queryString = queryString.concat(" ASC")
- }
+ Ssord match {
+ case "asc" => queryString = queryString.concat(" ASC")
+ case "desc" => queryString = queryString.concat(" DESC")
+ case _ => queryString = queryString.concat(" ASC")
+ }
val customQuery = Model.createQuery[Tour](queryString)
+
+ //exclude "tours by others" as in named query findTourByOthers
+ customQuery.setParameter("owner", UserManagement.currentUser)
+
+
//Not all DBs support FETCH, FIRST, JPA might behave strange when changing the DB
//see http://troels.arvin.dk/db/rdbms/#select-limit
- customQuery.setFirstResult(page * rows)
+ customQuery.setFirstResult(page*rows)
customQuery.setMaxResults(rows)
- var tourData2 = customQuery.getResultList()
- tourData = tourData2.flatMap(tour => bind("tour", tour.toGrid, "name" -> PCData(SHtml.link("view", () => tourVarSession(tour), Text(tour.name)))))
+ val tmpResultList = customQuery.getResultList()
+ tourData = tmpResultList.flatMap(tour => bind("tour", tour.toGrid, "name" -> PCData(SHtml.link("view", () => tourVarFromAPI(tour), Text(tour.name)))))
}
}
def listTours = {
getData
<rows>
- <page>
- {page + 1}
- </page>
- <total>
- {tourSize / rows}
- </total>
- <records>
- {rows}
- </records>{tourData}
+ <page>{ page + 1 }</page>
+ <total>{ tourSize/rows}</total>
+ <records>{ rows }</records>
+ { tourData }
</rows>
}
- serveJx {
- // GET /api/tour lists all entries
- case Get("gridapi" :: "tour" :: Nil, _) => Full(listTours)
- }
- implicit def cvt: JxCvtPF[Object] = {
- case (JsonSelect, n: Elem, _) => Xml.toJson(n)
- case (XmlSelect, n: Elem, _) => n
- case (JsonSelect, o: Object, _) => o.toJson
- case (XmlSelect, o: Object, _) => o.toXml
+ serve {
+ //uses the suffix of the request to determine the response type
+ // (GET) /gridapi/tour.xml
+ case Req("gridapi" :: "tour" :: _, "xml", GetRequest) => Full(listTours)
+ //Works as well: uses the Accept header fto determine the response type
+ //case XmlGet("gridapi" :: "tour" :: _, _) => Full(listTours)
}
-}
+
+}
+
+object tourVarFromAPI extends SessionVar[Tour](new Tour())
View
2  web/src/main/scala/TravelCompanionScala/api/PCData.scala
@@ -13,5 +13,5 @@ import xml.{Unparsed, Elem}
//http://scala-programming-language.1934581.n4.nabble.com/scala-Ampersands-are-escaped-inside-CDATA-literals-tp2072719p2073464.html
object PCData {
- def apply(anElem: Elem): Elem = <cell>{Unparsed("<![CDATA["+anElem+"]]>")}</cell>
+ def apply(anElem: Elem): Elem = <cell>{Unparsed("<![CDATA["+anElem+"]]>")}></cell>
}
View
115 web/src/main/scala/TravelCompanionScala/api/RestAPI.scala
@@ -2,145 +2,91 @@ package TravelCompanionScala.api
import net.liftweb.http.rest.RestHelper
import net.liftweb.util.Helpers.toLong
-import net.liftweb.http.rest._
import TravelCompanionScala.model.EntityConverter._
+import net.liftweb.http.rest._
import net.liftweb.common._
-import xml.{Node, Elem}
+import xml.{Node, Elem, NodeSeq, UnprefixedAttribute}
import TravelCompanionScala.controller._
import net.liftweb.json.Xml
import TravelCompanionScala.model._
/**
- * The RestAPI Object provides a API for the blog functionality accessible for REST Clients.
- * It extends from the RestHelper trait which provides support for implementing a REST API.
- *
- * Further Information on RestHelper and creating a REST API can be found on:
- * - http://www.assembla.com/wiki/show/liftweb/REST_Web_Services
- * - Technologiestudium (github link) Chapter 4.7 [German]
- *
- * Known Issues:
- * - Moderately complex pattern matching with extractors is causing problems in the current scala release.
- * See open ticket for this bug: https://lampsvn.epfl.ch/trac/scala/ticket/1133
- * This bug is causing problems in the serve method, not allowing to match all the necessary requests.
- *
- * @author Ralf Muri
- * @see RestHelper
- * @see EntityConverter
- *
+ * Created by IntelliJ IDEA.
+ * User: rmuri
+ * Date: 18.05.2010
+ * Time: 10:05:32
+ * To change this template use File | Settings | File Templates.
*/
-object RestAPI extends RestHelper {
- /**
- * Extractor: extracts a blog entry by id
- * @param in the id of a blog entry
- */
+
+object RestAPI extends RestHelper {
object AsBlogEntry {
def unapply(in: String): Option[BlogEntry] = {
Model.find(classOf[BlogEntry], toLong(in))
}
}
-
- /**
- * Extractor: extracts a comment by id
- * @param in the id of a comment
- */
object AsComment {
def unapply(in: String): Option[Comment] = {
+ // Model.createNamedQuery[Comment]("findCommentByEntry", "id" -> toLong(in), "entry" -> entry).findOne
Model.find(classOf[Comment], toLong(in))
}
}
- /**
- * Returns a list of all tours in xml format sourrounded by the <Tour/> tag as parent.
- * Uses the implicit conversion toXml implemented by the EntitiyConverter class.
- */
- def listTours = {
+ def listTours = {
<Tours>
{Model.findAll[Tour]("findAllTours").flatMap(e => e.toXml)}
</Tours>
}
- /**
- * Returns a list of all blog entries in xml format sourrounded by the <BlogEntries/> tag as parent.
- * Uses the implicit conversion toXml implemented by the EntitiyConverter class.
- */
def listEntries = {
<BlogEntries>
{Model.findAll[BlogEntry]("findAllEntries").flatMap(e => e.toXml)}
</BlogEntries>
}
- /**
- * Returns a list of all comments by a blog entry in xml format sourrounded by the <Comments/> tag as parent.
- * Uses the implicit conversion toXml implemented by the EntitiyConverter class.
- * @param entry Blog entry to get comments from
- */
def listComments(entry: BlogEntry) = {
<Comments>
{Model.findAll[Comment]("findCommentsByEntry", "entry" -> entry).flatMap(e => e.toXml)}
</Comments>
}
- /**
- * Saves a blog entry described by xml to the database.
- * Uses the implicit conversion entryFromXml implemented by the EntitiyConverter class
- * @param xml A blog entry in xml format
- */
def saveBlogEntry(xml: Node) = {
val e = xml.entryFromXml
- // proccess only if valid entry
- if (Validator.is_valid_entity_?(e)) {
+ if (validator.is_valid_entity_?(e)) {
+ val merged = Model.mergeAndFlush(e)
+ BlogCache.cache ! EditEntry(merged)
+ merged.toXml
+ } else <error/>
+ }
+
+ def createBlogEntry(xml: Node) = {
+ val e = xml.entryFromXml
+ if (validator.is_valid_entity_?(e)) {
val merged = Model.mergeAndFlush(e)
- // fire the EditEntry event to the LiftActor and pass on the saved entry
BlogCache.cache ! EditEntry(merged)
- // return the newly created entry as xml
merged.toXml
} else <error/>
}
- /**
- * Creates a new comment described by xml and saves it to the database.
- * Uses the implicit conversion commentFromXml implemented by the EntityConverter class.
- * @param xml A comment in xml format
- * @param entry The blog entry where the comment belongs to
- */
def createComment(xml: Node, entry: BlogEntry) = {
val c = xml.commentFromXml
c.blogEntry = entry
- // process only if valid entry
- if (Validator.is_valid_entity_?(c)) {
- // merge comment and entry to the database
+ if (validator.is_valid_entity_?(c)) {
val mergedComment = Model.mergeAndFlush(c)
val mergedEntry = Model.merge(entry)
- // entry needs to be refreshed so that the new comment will appear
Model.refresh(mergedEntry)
- // fire the AddComment event to the LiftActor and pass on the new comment
BlogCache.cache ! AddComment(mergedEntry)
- // return the newly created comment as xml
mergedComment.toXml
} else <error/>
}
- /**
- * Removes a comment from a blog entry and the database.
- * @param c The comment to remove
- * @param e The blog entry which the comment to remove from
- */
def removeComment(c: Comment, e: BlogEntry) = {
val mergedc = Model.merge(c)
- // remove the comment from the database
Model.removeAndFlush(mergedc)
- // fire the DeleteComment to the LiftActor and pass on the blog entry to update
BlogCache.cache ! DeleteComment(e)
- // return the removed comment as xml
mergedc.toXml
}
- /**
- * Implicit definitions for handling xml or json for request/responses handled by the serverJx method.
- * More information on: http://www.assembla.com/wiki/show/liftweb/REST_Web_Services
- * Uses the implicit conversion toXml from the EntityConverter class
- */
implicit def cvt: JxCvtPF[Object] = {
case (JsonSelect, n: Elem, _) => Xml.toJson(n)
case (XmlSelect, n: Elem, _) => n
@@ -148,13 +94,9 @@ object RestAPI extends RestHelper {
case (XmlSelect, o: Object, _) => o.toXml
}
- /**
- * Serve method for REST request dispatching. The scala pattern matching and extractors are used to do
- * the right dispatching.
- */
serve {
// POST /api/blog creates new entry with xml data from request body
- case "api" :: "blog" :: Nil XmlPost xml -> _ => saveBlogEntry(xml)
+ case "api" :: "blog" :: Nil XmlPost xml -> _ => createBlogEntry(xml)
// PUT /api/blog/<valid id> updates the respective entry with xml data from request body
case "api" :: "blog" :: AsBlogEntry(entry) :: Nil XmlPut xml -> _ => saveBlogEntry(xml)
@@ -162,21 +104,10 @@ object RestAPI extends RestHelper {
// POST /api/blog/<valid id>/comment creates a new comment on the respective entry
case "api" :: "blog" :: AsBlogEntry(entry) :: "comment" :: Nil XmlPost xml -> _ => createComment(xml, entry)
- /**
- * Due to a bug in scala this request can't be served and is commented out.
- * Moderately complex pattern matching with extractors is causing problems in the current scala release.
- * See open ticket for this bug:
- * https://lampsvn.epfl.ch/trac/scala/ticket/1133
- */
// DELETE /api/blog/<valid id>/comment/<valid id> removes the comment with the given id
// case "api" :: "blog" :: AsBlogEntry(entry) :: "comment" :: AsComment(comment) :: Nil XmlDelete _ if entry.comments.contains(comment) => removeComment(comment, entry)
}
- /**
- * Serve method for REST request dispatching. Request and responses coming in and resulting from
- * this method are automatically converted from and in json/xml. The scala pattern matching and extractors
- * are used to do the right dispatching.
- */
serveJx {
// GET /api/tour lists all tours and stages
case Get("api" :: "tour" :: Nil, _) => Full(listTours)
View
103 web/src/main/scala/TravelCompanionScala/comet/DynamicBlogViews.scala
@@ -1,53 +1,32 @@
package TravelCompanionScala.comet
+import _root_.net.liftweb.http._
import _root_.net.liftweb.common._
-import _root_.scala.xml.{NodeSeq, Text}
+import _root_.net.liftweb.util._
+import _root_.scala.xml._
+import js.jquery.JqJsCmds.JqSetHtml
+import js.{JsCmd, JsCmds}
+import S._
+
+
import java.text.SimpleDateFormat
+import TravelCompanionScala.snippet.tourVar
import TravelCompanionScala.model.BlogEntry
import TravelCompanionScala.model.Comment
import TravelCompanionScala.controller._
-import TravelCompanionScala.model._
-import net.liftweb.util.TimeHelpers
-import net.liftweb.http.js._
-import jquery.JqJsCmds._
-import net.liftweb.http.{SHtml, CometActor, S}
-import S._
-import TravelCompanionScala.snippet.tourVarSession
-
-/**
- * The DynamicBogViews class represents one CometActor.
- * It extends from the CometActor (SimpleActor) trait which provides the ability of getting and sending asynchronous messages.
- *
- * Further Information on Comet and creating a Comet Service can be found on:
- * - Technologiestudium (github link) Chapter 4.6 [German]
- *
- *
- * @author Daniel Hobi
- *
- */
-class DynamicBlogViews extends CometActor {
- /**
- * Overrides starting tag in view / templates
- */
+
+class DynamicBlogViews extends CometActor {
override def defaultPrefix = Full("blog")
var blog: List[BlogEntry] = Nil
var comments: List[Comment] = Nil
- val commentErrorDivId = "commentErrorComet"
- /**
- * Renders the CometActor view with defaultXml
- */
+
def render = {
- /**
- * Ajax call by clicking on blog.readOn link in view
- */
def bindEntryFull(e: BlogEntry): JsCmd = {
- /**
- * Renders a blog entry
- */
+
def getEntry(entry: BlogEntry, html: NodeSeq) = {
bind("b", html,
"title" -> Text(entry.title),
@@ -56,16 +35,13 @@ class DynamicBlogViews extends CometActor {
if (entry.tour == null) {
NodeSeq.Empty
} else {
- Text(?("blog.belongsTo") + " ") ++ SHtml.link("/tour/view", () => tourVarSession(entry.tour), Text(entry.tour.name))
+ Text(?("blog.belongsTo") + " ") ++ SHtml.link("/tour/view", () => tourVar(entry.tour), Text(entry.tour.name))
}
},
"date" -> Text(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(entry.lastUpdated)),
"owner" -> Text(entry.owner.name))
}
- /**
- * Unregister and register itself from watching comments by a blogentry and renders view afterwards
- */
BlogCache.cache ! RemoveCommentWatcher(this)
(BlogCache.cache !? AddCommentWatcher(this, e)) match {
@@ -73,31 +49,23 @@ class DynamicBlogViews extends CometActor {
}
JqSetHtml("blog_single", getEntry(e, chooseTemplate("blog", "entryfull", defaultXml))) &
- JqSetHtml("blog_comments", getComments(chooseTemplate("blog", "comments", defaultXml))) &
- JqSetHtml("blog_comments_form", renderNewCommentForm(chooseTemplate("blog", "commentForm", defaultXml), e))
+ JqSetHtml("blog_comments", getComments(chooseTemplate("blog", "comments", defaultXml)))
}
- /**
- * Unregister itself from watching comments by a blogentry and renders view afterwards
- */
BlogCache.cache ! RemoveCommentWatcher(this)
bind("entryfull" -> NodeSeq.Empty,
"comments" -> NodeSeq.Empty,
- "commentForm" -> NodeSeq.Empty,
"entry" ->
blog.flatMap(entry =>
bind("e", chooseTemplate("blog", "entry", defaultXml),
"title" -> Text(entry.title),
- "content" -> Text(entry.content.substring(0, math.min(entry.content.length, 50))),
+ "content" -> Text(entry.content.substring(0, scala.math.min(entry.content.length, 50))),
"readon" -> SHtml.a(() => bindEntryFull(entry), Text(?("blog.readOn"))),
"date" -> Text(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(entry.lastUpdated)),
"owner" -> Text(entry.owner.name)))): NodeSeq
}
- /**
- * Renders all comments by a blog entry
- */
def getComments(html: NodeSeq) = {
bind("comment", html, "list" ->
this.comments.flatMap(comment =>
@@ -107,52 +75,13 @@ class DynamicBlogViews extends CometActor {
"content" -> comment.content)))
}
- /**
- * Renders an ajax comment form and saves the comment if submitted
- */
- def renderNewCommentForm(html: NodeSeq, entry: BlogEntry): NodeSeq = {
- def doSaveComment(c: Comment): JsCmd = {
- if (Validator.is_valid_entity_?(c)) {
- val merged = Model.merge(entry)
- merged.comments.add(c)
- Model.mergeAndFlush(merged)
- BlogCache.cache ! AddComment(merged)
- Hide(commentErrorDivId) & JqSetHtml("blog_comments_form", renderNewCommentForm(html, entry))
- } else {
- Show(commentErrorDivId)
- }
- }
-
- val newComment = new Comment
- newComment.blogEntry = entry
- newComment.member = UserManagement.currentUser
- newComment.dateCreated = TimeHelpers.now
- bind("blog", SHtml.ajaxForm(html),
- "error" -> getErrorDiv(commentErrorDivId),
- "newComment" -> SHtml.textarea(newComment.content, newComment.content = _),
- "submit" -> SHtml.ajaxSubmit(?("save"), () => doSaveComment(newComment)),
- "cancel" -> SHtml.a(() => Hide(commentErrorDivId), Text(?("cancel")), "class" -> "button"))
- }
-
- def getErrorDiv(divIdPrefix: String) = <div id={divIdPrefix} style="display: none;">
- <lift:Msgs>
- <lift:error_msg/>
- </lift:Msgs>
- </div>
- /**
- * This method is called in the very first beginning
- * Register itself in wachting blog entries
- */
override def localSetup {
(BlogCache.cache !? AddBlogWatcher(this)) match {
case BlogUpdate(entries) => this.blog = entries
}
}
- /**
- * LiftActor is able to call this method by sending SimpleActor ! Case class update
- */
override def lowPriority: PartialFunction[Any, Unit] = {
case BlogUpdate(entries: List[BlogEntry]) => this.blog = entries; reRender(false);
case CommentUpdate(entries: List[Comment]) => {
View
69 web/src/main/scala/TravelCompanionScala/controller/BlogCache.scala
@@ -4,56 +4,32 @@ import _root_.net.liftweb.actor._
import _root_.net.liftweb.common._
import TravelCompanionScala.model.{Comment, UserManagement, Model, BlogEntry}
-/**
- * The BlogCache class holds all information about registered CometActors.
- * It extends from the LiftActor trait which provides the ability of getting and sending asynchronous messages.
- *
- * Further Information on Comet and creating a Comet Service can be found on:
- * - Technologiestudium (github link) Chapter 4.6 [German]
- *
- * Known Issues:
- * - findEntriesByOthers query does not work, because BlogCache cannot detect whether the user is logged in or not and always returns 0 as user id
- *
- * @author Daniel Hobi
- *
- */
-
class BlogCache extends LiftActor {
private var cache: List[BlogEntry] = List()
private var sessions: List[SimpleActor[Any]] = List()
- private var csessions: Map[Long, List[SimpleActor[Any]]] = Map()
- private var entry: BlogEntry = _
+ private var csessions: Map[Long,List[SimpleActor[Any]]] = Map()
+ private var entry : BlogEntry = _
- /**
- * gets all entries by other users (does not work properly: see known issues)
- */
def getEntries(): List[BlogEntry] = Model.createNamedQuery[BlogEntry]("findEntriesByOthers").setParams("owner" -> UserManagement.currentUser).findAll.toList
- /**
- * gets all comments by a blog entry
- * @param in a blog entry
- */
def getComments(blogEntry: BlogEntry): List[Comment] = Model.createNamedQuery[Comment]("findCommentsByEntry").setParams("entry" -> blogEntry).findAll.toList
- /**
- * This method is called by sending: BlogCache ! (case class)
- */
protected def messageHandler =
{
case AddBlogWatcher(me) =>
val blog = getEntries
reply(BlogUpdate(blog))
- cache = getEntries
+ cache = cache ::: blog
sessions = sessions ::: List(me)
case AddEntry(e) =>
- cache = getEntries
+ cache = cache ::: List(e)
sessions.foreach(_ ! BlogUpdate(cache))
case EditEntry(e) =>
cache = getEntries
sessions.foreach(_ ! BlogUpdate(cache))
case DeleteEntry(e) =>
- cache = getEntries
+ cache = cache.filter(p => p.id != e.id)
sessions.foreach(_ ! BlogUpdate(cache))
case AddCommentWatcher(me, entry) =>
@@ -63,41 +39,30 @@ class BlogCache extends LiftActor {
csessions = csessions.filter(p => me != p)
case AddComment(e) =>
println("Comment added")
- csessions.getOrElse(e.id, Nil).foreach(_ ! CommentUpdate(getComments(e)))
+ csessions.getOrElse(e.id,Nil).foreach(_ ! CommentUpdate(getComments(e)))
case DeleteComment(e) =>
- println("Comment deleted")
- csessions.getOrElse(e.id, Nil).foreach(_ ! CommentUpdate(getComments(e)))
+ println("Comment deleted")
+ csessions.getOrElse(e.id,Nil).foreach(_ ! CommentUpdate(getComments(e)))
case _ =>
}
}
-/**
- * Case classes to keep entry watchers informed
- */
-case class AddBlogWatcher(me: SimpleActor[Any])
-case class AddEntry(entry: BlogEntry)
-case class EditEntry(entry: BlogEntry)
-case class DeleteEntry(entry: BlogEntry)
+case class AddBlogWatcher(me: SimpleActor[Any]) // id is the blog id
+case class AddEntry(entry: BlogEntry) // id is the blog id
+case class EditEntry(entry: BlogEntry) // id is the blog id
+case class DeleteEntry(entry: BlogEntry) // id is the blog id
-/**
- * Case classes to keep comment watchers informed
- */
case class AddCommentWatcher(me: SimpleActor[Any], entry: BlogEntry)
-case class RemoveCommentWatcher(me: SimpleActor[Any])
-case class AddComment(entry: BlogEntry)
-case class EditComment(entry: BlogEntry)
-case class DeleteComment(entry: BlogEntry)
+case class RemoveCommentWatcher(me: SimpleActor[Any]) // id is the blog id
+case class AddComment(entry: BlogEntry) // id is the blog id
+case class EditComment(entry: BlogEntry) // id is the blog id
+case class DeleteComment(entry: BlogEntry) // id is the blog id
-/**
- * Case classes to update the listeners
- */
+// A response sent to the cache listeners with the top 20 blog entries.
case class BlogUpdate(xs: List[BlogEntry])
case class CommentUpdate(xs: List[Comment])
-/**
- * Companion object to access BlogCache class from everywhere in the application
- */
object BlogCache {
lazy val cache = new BlogCache // {val ret = new BlogCache; ret.start; ret}
}
View
74 web/src/main/scala/TravelCompanionScala/model/EntityConverter.scala
@@ -1,45 +1,26 @@
package TravelCompanionScala.model
+
import scala.collection.JavaConversions._
-import net.liftweb.json.Xml
+import net.liftweb.json.{DefaultFormats, Xml}
import java.util.Date
-import xml.{Utility, Elem, Node}
+import xml.{Utility, Elem, Node, NodeSeq}
import net.liftweb.util.Helpers._
+import net.liftweb.json.JsonAST.JValue
/**
- * The EntityConverter class implements the conversion between blog entries and comments to and from xml.
- * The class is intendend to being used by implicit conversions. For this purpose the EntityConveter companion
- * objects exists which makes the implicit definition available to classes having an import to it. So to
- * use these implicit conversions, the following import is necessary:
- *
- * import TravelCompanionScala.model.EntityConverter._
- *
- * Further information on implicit conversion can be found on:
- * - http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6
- *
- * Known Issues:
- * - The converter methods are made available on class Object. A better solution would be a common superclass for
- * all the Entity classes. Since such a super class does not exist, Object is used to attach the converter
- * methods to.
- * - The class is missing general exception handling (invalid xml, parsing, etc.)
- *
- * @author Ralf Muri
+ * Created by IntelliJ IDEA.
+ * User: rmuri
+ * Date: 18.05.2010
+ * Time: 09:35:51
+ * To change this template use File | Settings | File Templates.
*/
-object EntityConverter {
- // implicit definition to add methods of EntityConverter to instances of the class Object
- implicit def serializeEntity(o: Object) = new EntityConverter(o)
-}
-/**
- * Converter logic
- * @param o Is the Object on which the method is called
- */
+// implicit def serializeEntity (o: Object) = new EntityConverter (o)
+
+
class EntityConverter(o: Object) {
- /**
- * Creates a blog entry from a xml representation
- */
def entryFromXml: BlogEntry = {
- // If the method is called on a instance of class Elem (xml data) a entry can be created
o match {
case elem: Elem => {
val entry = new BlogEntry
@@ -51,17 +32,12 @@ class EntityConverter(o: Object) {
entry.owner = Model.find[Member](classOf[Member], toLong((elem \ "owner" text))).getOrElse(null)
entry.lastUpdated = new Date()
entry.comments.addAll(Model.createNamedQuery[Comment]("findCommentsByEntry").setParams("entry" -> entry).findAll.toList)
- // return the created blog entry
entry
}
}
}
- /**
- * Creates a comment from a xml representation
- */
def commentFromXml: Comment = {
- // If the method is called on a instance of class Elem (xml data) a comment can be created
o match {
case elem: Elem => {
val comment = new Comment
@@ -69,20 +45,16 @@ class EntityConverter(o: Object) {
comment.content = (elem \ "content" text)
comment.member = Model.find[Member](classOf[Member], toLong((elem \ "member" text))).getOrElse(null)
comment.blogEntry = Model.find[BlogEntry](classOf[BlogEntry], toLong((elem \ "blogEntry" text))).getOrElse(null)
+
comment.dateCreated = new Date()
- // return the created comment
+
comment
}
}
}
- /**
- * Converts a entity to xml
- */
def toXml: Node = {
- // If the method is called on a instance of domain entity, the instance can be converted
o match {
- // converting logic for BlogEntry
case e: BlogEntry => {
Utility.trim(
<BlogEntry>
@@ -112,7 +84,6 @@ class EntityConverter(o: Object) {
</comments>
</BlogEntry>)
}
- // converting logic for Comment
case c: Comment => {
Utility.trim(
<comment>
@@ -133,7 +104,6 @@ class EntityConverter(o: Object) {
</blogEntry>
</comment>)
}
- // converting logic for Tour
case e: Tour => {
Utility.trim(
<Tour>
@@ -154,7 +124,6 @@ class EntityConverter(o: Object) {
</stages>
</Tour>)
}
- // converting logic for Stage
case e: Stage => {
Utility.trim(
<Stage>
@@ -175,7 +144,6 @@ class EntityConverter(o: Object) {
</destination>
</Stage>)
}
- // converting logic for Location
case e: Location => {
Utility.trim(
<Location>
@@ -220,11 +188,11 @@ class EntityConverter(o: Object) {
o match {
case e: Tour => {
Utility.trim(
- <row id={e.id.toString}>
+ <row id={e.id.toString }>
<cell>
- {e.id}
+ { e.id }
</cell>
- <tour:name/>
+ <tour:name>{e.name}</tour:name>
<cell>
{e.description}
</cell>
@@ -233,14 +201,14 @@ class EntityConverter(o: Object) {
}
}
- /**
- * Converts a entity into json using the toJson method on the Xml class
- */
def toJson = {
- // converting logic is defined for BlogEntry and Comment
o match {
case e: BlogEntry => Xml.toJson(new EntityConverter(e).toXml)
case c: Comment => Xml.toJson(new EntityConverter(c).toXml)
}
}
+}
+
+object EntityConverter {
+ implicit def serializeEntity(o: Object) = new EntityConverter(o)
}
View
38 web/src/main/scala/TravelCompanionScala/model/GeoCoder.scala
@@ -1,38 +1,31 @@
package TravelCompanionScala.model
-import java.net.{URL, URLEncoder}
+import java.net.{URLConnection, URL, URLEncoder}
import collection.mutable.Queue
-import java.io.{IOException}
+import java.io.{IOException, BufferedInputStream, DataInputStream}
import net.liftweb.http.S
import xml.{Elem, XML}
/**
- * The GeoCoder Object provides a service for finding Locations by Name.
- *
- * @author Daniel Hobi
- *
+ * Created by IntelliJ IDEA.
+ * User: dhobi
+ * Date: 27.04.2010
+ * Time: 14:23:57
+ * To change this template use File | Settings | File Templates.
*/
object GeoCoder {
val wsAdress: String = "http://ws.geonames.org/search?"
- var locations: Seq[Location] = List()
+ var locations : Seq[Location] = List()
- /**
- * Returns a sequence of location objects
- */
def getCurrentLocations(): Seq[Location] = {
locations
}
- /**
- * Returns a sequence of location objects
- * @param in a string of a location name
- */
def findLocationsByName(locationName: String): Seq[Location] = {
var root: Elem = getElement(locationName)
- var results = new Queue[Location]()
- root \ "geoname" foreach {
- (geoname) =>
+ var results = new Queue[Location]()
+ root \ "geoname" foreach {(geoname) =>
{
val loc = new Location()
loc.admincode = (geoname \ "adminCode1" text)
@@ -51,18 +44,15 @@ object GeoCoder {
locations
}
- /**
- * Connects to the geonames.org service and returns the response in XML
- */
- private def getElement(locationName: String): Elem = {
- val query: String = "name_equals=" + URLEncoder.encode(locationName, "UTF-8") + "&fclass=P&style=FULL"
+ def getElement(locationName: String): Elem = {
+ val query: String = "name_equals=" + URLEncoder.encode(locationName,"UTF-8") + "&fclass=P&style=FULL"
val url = new URL(wsAdress + "" + query)
try {
val conn = url.openConnection
XML.load(conn.getInputStream)
} catch {
- case e: IOException => S.error("Verbindung zu geonames.org fehlgeschlagen.")
- XML.loadString("<geoname />")
+ case e: IOException => S.error("Verbindung zu geonames.org fehlgeschlagen.")
+ XML.loadString("<geoname />")
}
}
View
37 web/src/main/scala/TravelCompanionScala/model/ImageLogic.scala
@@ -6,49 +6,24 @@ import net.liftweb.util.Helpers._
import net.liftweb.util.Helpers
/**
- * ImageLogic Object implements functionality which is necessary provide pictures taken out of the database
- * to a requesting client. A client request on a specifc url containing the id of a picture will delivier the
- * respective image from the database.
- * URL Rewriting is used to dispatch to the correct url and LiftResponse to return a image to the client.
- *
- * Further Information on URL Rewriting can be found on:
- * - http://www.assembla.com/wiki/show/liftweb/URL_Rewriting
- * - Technologiestudium (github link) Chapter 5.4 [German]
- *
- * Further Information on generation of http Responses can be found on:
- * - "The Definitive Guide to Lift" Chapter "Exploring LiftResponse in Detail" on
- * http://books.google.com/books?id=5lPmFLC6sHAC&pg=PA122
- *
- * @author Ralf Muri
- *
+ * Created by IntelliJ IDEA.
+ * User: rmuri
+ * Date: 27.04.2010
+ * Time: 08:43:29
+ * To change this template use File | Settings | File Templates.
*/
+
object ImageLogic {
- /**
- * Extractor: extracts a picture from the database to a given id
- * @param in Id of the desired picture
- */
object TestImage {
def unapply(in: String): Option[Picture] =
Model.find(classOf[Picture], in.toLong)
}
- /**
- * Function that defines the dispatch logic for the following URLs:
- * /image/thumbnail/<id>
- * /image/full/<id>
- */
def matcher: LiftRules.DispatchPF = {
case req@Req("image" :: "thumbnail" :: TestImage(img) :: Nil, _, GetRequest) => () => serveImage(img, req, true)
case req@Req("image" :: "full" :: TestImage(img) :: Nil, _, GetRequest) => () => serveImage(img, req, false)
}
- /**
- * This method generates a LiftResponse which represents a http response. The created response contains the
- * byte data of the requested image.
- * @param img The requested picture
- * @param req The http request
- * @param thumbnail : Boolean indicating whether to show the thumbnail or the full image
- */
def serveImage(img: Picture, req: Req, thumbnail: Boolean): Box[LiftResponse] = {
val imageData: Array[Byte] = if (thumbnail) img.thumbnail else img.image
Full(InMemoryResponse(imageData, List("Last-Modified" -> toInternetDate(Helpers.millis),
View
47 web/src/main/scala/TravelCompanionScala/model/Model.scala
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2008 WorldWide Conferencing, LLC
+ *
+ * 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 TravelCompanionScala {
package model {
@@ -7,42 +22,14 @@ import javax.validation.{Validation, Validator}
import net.liftweb.http.S
import scala.collection.JavaConversions._
-/**
- * The Model Object is used for accessing the database. Basically it is a direct ScalaJPA EntitiyManager.
- * A RequestVar is used as a backing store. Like this, every requests gets is own EntityManager Instance to
- * work with.
- *
- * Further information on the Model Object can be found on:
- * - http://groups.google.com/group/liftweb/browse_thread/thread/792cc7e0b0b5cbed/dbb89e8b020ffd22
- *
- * @author Ralf Muri
- */
object Model extends LocalEMF("jpaweb") with RequestVarEM
-/**
- * The Validator Object can be used to validate domain entity instances. Hibernate Validator is used as
- * validation framework.
- *
- * Further information on Hibernate Validator can be found on:
- * - http://docs.jboss.org/hibernate/stable/validator/reference/en/html_single/
- * - Technologiestudium (github link) Chapter 4.4 [German]
- */
-object Validator {
- /**
- * The validator instance is obtained through the ValidatorFacotry
- */
+object validator {
def get: Validator = Validation.buildDefaultValidatorFactory.getValidator
- /**
- * This method takes a instance of a domain entity as parameter and runs validation on it.
- * @param toCheck The domain entity to validate
- */
def is_valid_entity_?(toCheck: Object): Boolean = {
- // validate domain entity
- val validationResult = Validator.get.validate(toCheck)
- // put the validation result (constraint vialoations...) in the S.error listing to provide user feedback
+ val validationResult = validator.get.validate(toCheck)
validationResult.foreach((e) => S.error(e.getPropertyPath + " " + e.getMessage))
- // return whether the validation was successful or not
validationResult.isEmpty
}
}
View
109 web/src/main/scala/TravelCompanionScala/model/UserManagement.scala
@@ -16,47 +16,33 @@ import scala.collection.JavaConversions._
import javax.persistence.{PersistenceException, EntityExistsException}
/**
- * The UserManagement object provides login and registering mechanism.
- *
- * Further information on user management can be found on:
- * - Technologiestudium (github link) Chapter 4.2 [German]
- *
- * Specials:
- * Certain parts of the user management class are copied from ProtoUser class
- * http://github.com/dpp/liftweb/blob/master/framework/lift-persistence/lift-mapper/src/main/scala/net/liftweb/mapper/ProtoUser.scala
- *
- * @author Daniel Hobi
- *
+ * Created by IntelliJ IDEA.
+ * User: dhobi
+ * Date: 25.03.2010
+ * Time: 15:08:12
+ * To change this template use File | Settings | File Templates.
*/
object UserManagement {
+ ///
+ val basePath: List[String] = "user" :: Nil
+
lazy val testLogginIn = If(loggedIn_? _, S.??("must.be.logged.in"))
- /**
- * this object holds the logged-in user or is empty. Access is only permitted within this class.
- */
+ // this object holds the logged-in user or is empty. Access is only permitted within this class.
private object curUsr extends SessionVar[Box[Member]](Empty)
- /**
- * This object holds a temporary user which is used for binding form fields (Signup, login...) to the user.
- * its not an entity from the datebase and should never come in touch with the EntityManager.
- */
+ // this object holds a temporary user which is used for binding form fields (Signup, login...) to the user.
+ // its not an entity from the datebase and should never come in touch with the EntityManager.
private object tempUserVar extends RequestVar[Member](new Member)
- /**
- * This method gives back a new or already defined Member object.
- */
def currentUser: Member = {
if (curUsr.is.isDefined)
curUsr.is.open_!
else
new Member
- }
- /**
- * every URL starting with "user" is handled by this object
- */
- val basePath: List[String] = "user" :: Nil
+ }
def loginSuffix = "login"
@@ -78,14 +64,12 @@ object UserManagement {
/**
- * Returns the URL of the "login" page
+ * Return the URL of the "login" page
*/
def loginPageURL = loginPath.mkString("/", "/", "")
- /**
- * Creating menues
- */
+ /// Menues
def loginMenuLoc: Box[Menu] =
Full(Menu(Loc("Login", loginPath, S.??("login"), loginMenuLocParams)))
@@ -138,23 +122,18 @@ object UserManagement {
Nil
- /**
- * Defines menu sitemap
- */
+
+ ///Menu sitemap
def menus: List[Menu] = sitemap
lazy val sitemap: List[Menu] = List(loginMenuLoc, logoutMenuLoc, createUserMenuLoc, profileMenuLoc).flatten(a => a)
- /**
- * Checks if user is logged in or not
- */
+ ///Login function
def notLoggedIn_? = !loggedIn_?
def loggedIn_? = !curUsr.get.isEmpty
- /**
- * Defines a wrapper for binding purposes
- */
+ ///wrap it
def screenWrap: Box[Node] = Full(<lift:surround with="default" at="content">
<lift:bind/>
</lift:surround>)
@@ -168,9 +147,7 @@ object UserManagement {
})) openOr in
- /**
- * Login form
- */
+ ///Login form
def loginXhtml = {
(<p>
{S.?("member.login")}
@@ -216,32 +193,23 @@ object UserManagement {
</form>)
}
- /**
- * Logs in user
- * @param in a member
- */
def logInUser(user: Member) = {
curUsr.set(Full(user))
tempUserVar(user)
S.redirectTo("/")
}
- /**
- * Logs out the current user
- */
+
+ ///Functions
+
def logout = {
curUsr.set(Empty)
tempUserVar(new Member)
S.redirectTo("/")
}
- /**
- * Authentification
- */
+
def login = {
- /**
- * Tries to authentificate the user and logs him in if he was found in the database
- */
def checkLogin() {
val tryUser = Model.createQuery[Member]("SELECT m from Member m where m.name = :name and m.password = :password").setParams("name" -> tempUserVar.is.name, "password" -> tempUserVar.is.password).findOne
if (tryUser.isDefined) {
@@ -255,9 +223,6 @@ object UserManagement {
val current = tempUserVar.is
- /**
- * Renders the login form
- */
bind("user", loginXhtml,
"username" -> SHtml.text(current.name, current.name = _),
"password" -> SHtml.password(current.password, current.password = _),
@@ -267,9 +232,6 @@ object UserManagement {
}))
}
- /**
- * Registration form
- */
def memberXhtml() = {
(<form method="post" action={S.uri}>
<h2>
@@ -373,16 +335,11 @@ object UserManagement {
</form>)
}
- /**
- * Registration
- */
+
def signup() =
{
- /**
- * Validates user input and register if there were no errors
- */
def testSignup() {
- val validationResult = Validator.get.validate(tempUserVar.is)
+ val validationResult = validator.get.validate(tempUserVar.is)
if (validationResult.isEmpty) {
try {
logInUser(Model.mergeAndFlush(tempUserVar.is))
@@ -399,10 +356,6 @@ object UserManagement {
val current = tempUserVar.is
- /**
- * Checks if a username already exists and returns a visual output
- * This method is called by SHtml.ajaxText()
- */
def checkUsername(username: String) = {
current.name = username
@@ -419,9 +372,6 @@ object UserManagement {
JsCmds.JsHideId("lift__noticesContainer__")
}
- /**
- * Render register form
- */
bind("user",
memberXhtml,
"title" -> S.??("sign.up"),
@@ -441,11 +391,8 @@ object UserManagement {
def editProfile =
{
- /**
- * Validates user input and edit the user object if there were no errors
- */
def testSave() {
- val validationResult = Validator.get.validate(tempUserVar.is)
+ val validationResult = validator.get.validate(tempUserVar.is)
if (validationResult.isEmpty) {
try {
tempUserVar(Model.mergeAndFlush(tempUserVar.is))
@@ -462,9 +409,7 @@ object UserManagement {
}
val current = currentUser
- /**
- * Render register form
- */
+
bind("user",
memberXhtml,
"title" -> S.?("member.editProfile"),
View
24 web/src/main/scala/TravelCompanionScala/model/Util.scala
@@ -0,0 +1,24 @@
+package TravelCompanionScala.model
+
+import java.util.Date
+import net.liftweb.common.{Empty, Full, Box}
+import net.liftweb.http.S
+import java.text.SimpleDateFormat
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: dhobi
+ * Date: 21.04.2010
+ * Time: 14:38:41
+ * To change this template use File | Settings | File Templates.
+ */
+
+object Util {
+
+ val noSlashDate = new SimpleDateFormat("ddMMyyyy")
+
+ val slashDate = new SimpleDateFormat("dd.MM.yyyy")
+
+
+
+}
View
468 web/src/main/scala/TravelCompanionScala/snippet/BlogSnippet.scala
@@ -1,59 +1,49 @@
package TravelCompanionScala.snippet
import _root_.scala.xml.{NodeSeq, Text}
+
import _root_.net.liftweb._
import common.{Full, Empty}
import http._
-import js.jquery.{JqJsCmds}
-import js.{JsCmd}
+import js.JE.ElemById
+import js.jquery.JqJE.{JqRemove, JqId}
+import js.jquery.{JqJE, JqJsCmds}
+import js.{JE, JsCmds, JsCmd}
import S._
import util._
import Helpers._
import JqJsCmds._
+
import TravelCompanionScala.model._
import java.text.SimpleDateFormat
import scala.collection.JavaConversions._
import TravelCompanionScala.controller._
/**
- * The BlogSnippet class is responsible for the dynamic content in the blog section of TravelCompanion.
- * It deals with CRUD Operations on BlogEntries and Comments. It is implemented as Single Page Web
- * Application using Ajax functionality. Additionally the List of Blog Entries by others is updated via
- * Comet.
- *
- * Further Information on Ajax and Coment can be found on:
- * - http://demo.liftweb.net/ajax$
- * - Technologiestudium (github link) Chapter 4.6 [German]
- *
- * Known Issues:
- * - List with blog entries by others shows also the entries by the current user
- * @see BlogCache for further details
- *
- * @author Ralf Muri
- * @see
- *
+ * Created by IntelliJ IDEA.
+ * User: Ralf Muri
+ * Date: 19.04.2010
+ * Time: 09:02:05
+ * To change this template use File | Settings | File Templates.
*/
-// Set up a requestVar to track blog entry and comment objects for edits and adds
+// Set up a requestVar to track the STAGE object for edits and adds
object blogEntryVar extends RequestVar[BlogEntry](new BlogEntry())
+
object commentVar extends RequestVar[Comment](new Comment())
-/**
- * Function object implementing the jQuery remove function. Respective function from the Lift
- * JavaScript Libary is not working properly.
- */
object myJqRemove {
def apply(uid: String): JsCmd = new Remove(uid)
}
+
class Remove(uid: String) extends JsCmd {
def toJsCmd = "try{jQuery(" + ("#" + uid).encJs + ").remove();} catch (e) {}"
}
class BlogSnippet {
- /**
- * Frequently used constants
- */
+ /* Blog as single webpage Application */
+
val entriesDivId = "entriesList"
val entryFormDivId = "addEntryForm"
val newEntryLink = "newEntryLink"
@@ -63,119 +53,68 @@ class BlogSnippet {
val commentErrorDivId = "commentError"
val commentFormDivId = "commentForm"
- /**
- * Return markup code for a div rendering the Lift error messages
- */
def getErrorDiv(divIdPrefix: String) = <div id={divIdPrefix} style="display: none;">
- <lift:Msgs>
- <lift:error_msg/>
- </lift:Msgs>
+ <div class="lift:msgs">
+ <span class="lift:error_msg"/>
+ </div>
</div>
- /**
- * Default render method for de BlogSnippet. This big method contains all the functionality
- * for the Single Page Blog Webapplication. The functionality is split within inner functions.
- */
- def render(html: NodeSeq): NodeSeq = {
- /**
- * Via the chooseTemplate Method are various often used XML fragments extracted
- */
- val entryTemplate = chooseTemplate("choose", "entry", html)
- val entryFormTemplate = chooseTemplate("choose", "form", html)
- val commentsTemplate = chooseTemplate("choose", "comments", html)
- val commentFormTemplate = chooseTemplate("choose", "commentForm", html)
-
- /**
- * Ajax-Link Callack function. This inner function saves a updated blog entry to the database
- * and returns the JavaScript Commands for updating the clients screen.
- * @param entry The blog entry to save
- */
+ def render = {
+
+ val entryTemplate = TemplateFinder.findAnyTemplate("blog" :: "_chooseEntry" :: Nil).getOrElse(NodeSeq.Empty)
+ val entryFormTemplate = TemplateFinder.findAnyTemplate("blog" :: "_chooseForm" :: Nil).getOrElse(NodeSeq.Empty)
+ val commentsTemplate = TemplateFinder.findAnyTemplate("blog" :: "_chooseComments" :: Nil).getOrElse(NodeSeq.Empty)
+ val commentFormTemplate = TemplateFinder.findAnyTemplate("blog" :: "_chooseCommentForm" :: Nil).getOrElse(NodeSeq.Empty)
+
+
def doEditBlogEntry(entry: BlogEntry): JsCmd = {
- // define save function for the blog entry
val save = () => {
- // only proceed when the entry is valid
- if (Validator.is_valid_entity_?(entry)) {
+ if (validator.is_valid_entity_?(entry)) {
val merged = Model.mergeAndFlush(entry)
- // fire the EditEntry event on the LiftActor and pass on the updated entity
BlogCache.cache ! EditEntry(merged)
- // Update the div displaying the entry on the client side
JqSetHtml(blogEntryDivId + entry.id, listEntries(entryTemplate, List(merged)))
} else {
- // in case of invalid entity display error messages
Show(entryErrorDivId)
}
}
- // define cancel function for the blog entry
val cancel = () => Hide(entryErrorDivId) & JqSetHtml(blogEntryDivId + entry.id, listEntries(entryTemplate, List(Model.getReference(classOf[BlogEntry], entry.id))))
- // show the edit entry form on the client side and attach the submit and cancel function previously defined
JqSetHtml(blogEntryDivId + entry.id, getEntryForm(entry, entryFormTemplate, save, cancel))
}
- /**
- * Ajax-Link Callback Function. This inner function implements rendering comments, adding new comments and
- * removing comments. The different inner functions run their operation on the entity and update the clients
- * screen accorind to the made modifications.
- * @param entry The blog entry to operate on
- */
def doComments(entry: BlogEntry): JsCmd = {
- /**
- * Removes a given comment and updates the clients screen via Ajax
- * @param c The comment to remove
- */
def removeComment(c: Comment): JsCmd = {
val mergedc = Model.merge(c)
Model.removeAndFlush(mergedc)
BlogCache.cache ! DeleteComment(entry)
- // return the JavaScript command to rerendere the comments list
JqSetHtml(commentDivId + entry.id, renderComments)
}
- /**
- * Displays a single comment
- * @param c The comment to display
- */
- def bindComment(c: Comment) = bind("comment", chooseTemplate("blog", "comment", html),
- "member" -> c.member.name,
- "dateCreated" -> new SimpleDateFormat("dd.MM.yyyy HH:mm").format(c.dateCreated),
- "content" -> c.content,
- "options" -> {
- // if the current member has sufficient rights a remove link is displayed on the comment
- if ((c.member == UserManagement.currentUser) || (entry.owner == UserManagement.currentUser))
- bind("link", chooseTemplate("option", "list", html), "remove" -> SHtml.a(() => removeComment(c), Text(?("remove"))))
- else
- NodeSeq.Empty
- })
-
- /**
- * Render a list of comments
- */
def renderComments() = {
val merged = Model.merge(entry)
Model.refresh(merged)
- bind("blog", commentsTemplate, "comment" -> merged.comments.flatMap(c => bindComment(c)))