-
Notifications
You must be signed in to change notification settings - Fork 0
JavaGuide5
Now that we can log in, let's start writing functionality for our application. We'll start simple, by adding dynamic functionality to the drawer, that is, the sidebar with the list of projects.
To implement the client side logic, we are going to use CoffeeScript, a language that makes Javascript very simple and easy to work with. We could just as easily use JavasSript, but Play comes with a build in CoffeeScript compiler, so we'll see how we can utilise that.
Additionally, we'll use Backbone.js to manage our views. In contrast to a typical Backbone app, where your models live on the client side, we'll keep our models all on the server side, and just use Backbone views, binding them to the views rendered by Plays scala templates. This allows us to use Plays templating system, and also means that our application will be much easier to make work in browsers that don't support Javascript, be crawled by search engines, etc.
Let's start with the backend. We're going to add a few new backend actions, specifically to:
- Add a project
- Rename a project
- Delete a project
- Add a group
To start, create a controller class called app/controllers/Projects.java:
package controllers;
import play.*;
import play.mvc.*;
import play.data.*;
import java.util.*;
import models.*;
import views.html.*;
import views.html.projects.*;
@Security.Authenticated(Secured.class)
public class Projects extends Controller {
}An important thing to notice here is that we have annotated the entire class with our security authenticator. With the dashboard, we just annotated the method, but these annotations can also be placed at the class level to say that every method in this class must use this action. This can save us a lot of boiler plate code, and also saves us from accidentally forgetting to annotate a method.
Now let's add a method to create a new project:
public static Result add() {
Project newProject = Project.create(
"New project",
form().bindFromRequest().get("group"),
request().username()
);
return ok(item.render(newProject));
}We've used our existing create method on our Project model to create the new project, owned by the currently logged
in user, which is returned by request().username().
Also notice that we are reusing that item template that we created earlier to render the new project. Now you'll be
begin to see why we created our templates in the structure that we did earlier.
Let's now add a method to rename a project, but before we do, let's consider the security requirements of this function.
A user should only be allowed to rename a project if they are a member of that project. Let's write a utility method in
our app/controllers/Secured.java class that checks this:
public static boolean isMemberOf(Long project) {
return Project.isMember(
project,
Context.current().request().username()
);
}You may notice here that we've used Context.current() to get the request(). This is a convenient way to get access
to a request if you aren't in an action. Underneath, it uses thread locals to find the current request, response,
session and so on.
Our isMemberOf method has used a new method that we haven't written on Project yet. In fact we are going to need a
few new methods on the Project object, so let's open app/models/Project.java now to add them:
public static boolean isMember(Long project, String user) {
return find.where()
.eq("members.email", user)
.eq("id", project)
.findRowCount() > 0;
}
public static String rename(Long projectId, String newName) {
Project project = find.ref(projectId);
project.name = newName;
project.update();
return newName;
}Having added a rename method to our Project model, we are now ready to implement our action in
app/controllers/Projects.java:
public static Result rename(Long project) {
if(Secured.isMemberOf(project)) {
return ok(
Project.rename(
project,
form().bindFromRequest().get("name")
)
);
} else {
return forbidden();
}
}Notice that first we check that the current user is a member of the project, and if they aren't, we return them a
forbidden response. Also note our use of the form() method. We've seen this before, when we were populating and
validating our login form. However this time, we haven't passed in a bean to decode the form into and to validate it
with. Rather, we've used what's called a dynamic form. A dynamic just parses a form submission into a map of string
keys to string values, and is very convenient for simple form submissions with only one or two values where you don't
want to do any validation.
Let's move on to our method to delete a project:
public static Result delete(Long project) {
if(Secured.isMemberOf(project)) {
Project.find.ref(project).delete();
return ok();
} else {
return forbidden();
}
}And finally, let's add a method to create a group:
public static Result addGroup() {
return ok(
group.render("New group", new ArrayList<Project>())
);
}Now that we have our controller methods implemented, let's add routes to these controllers in conf/routes:
POST /projects controllers.Projects.add() POST /projects/groups controllers.Projects.addGroup() DELETE /projects/:project controllers.Projects.delete(project: Long) PUT /projects/:project controllers.Projects.rename(project: Long)
Now do a quick refresh of the application in the browser, to make sure there are no compile errors.
- Starting up the project
- A first iteration for the data model
- Building the first screen
- Adding authentication
- Adding some AJAX actions
- Invoking actions from Javascript
- HTTP programming
- Asynchronous HTTP programming
- The template engine
- HTTP form submission and validation
- Working with JSON
- Working with XML
- Handling file upload
- Accessing an SQL database
- Using the Cache
- Calling web services
- Integrating with Akka
- Internationalization
- The application Global object
- Testing your application