Building on the baseline spacebook from the previous lab, implement stories 1 to 5 of the assignment specification from last semester.
This is a completed project from last week:
Now download an unarchive the source project. In order to import this, you will need to do the following commands:
cd 'the directory into which you unarchived the project'
play deps
play eclipsify
In eclipse, import the project as an 'existing project'. Run the project in the usual way, and verify that it behaves as expected.
We already have a 'data.yml' file in th conf directory (open this). This contained initial user data loaded at startup:
User(homer):
firstName: Homer
lastName: Simpson
email: homer@simpson.com
password: secret
User(marge):
firstName: marge
lastName: Simpson
email: marge@simpson.com
password: secret
Verify that these are in your app.
Extend this data to include the full homer clan. Note, the number of spaces (4) before each field is significant.
Introduce the following entries into the data.yml file:
User(lisa):
firstName: Lisa
lastName: Simpson
email: lisa@simpson.com
password: secret
User(bart):
firstName: Bart
lastName: Simpson
email: bart@simpson.com
password: secret
User(maggie):
firstName: Maggie
lastName: Simpson
email: maggie@simpson.com
password: secret
Extend the User Model to include the following new fields:
These fields must be filled in when a user registers.
Bring these fields into User.java:
public int age;
public String nationality;
And change the constructor to inialize these fields:
public User(String firstName, String lastName, String email, String password, int age, String nationality)
{
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.password = password;
this.age = age;
this.nationality = nationality;
}
Now rework Accounts.register method:
public static void register(String firstName, String lastName, int age, String nationality,
String email, String password, String password2)
{
Logger.info(firstName + " " + lastName + " " + email + " " + password);
User user = new User(firstName, lastName, age, nationality, email, password);
user.save();
index();
}
Extend Accounts/signup.html to allow the user provide these new fields
For the new fields accepted in Story 2, display them on the users Home Profile page.
In addition, on the users 'Public' profile (then one a friend can see), display just the 'Nationality' field
In views/Profile/index.html
we can introduce a table to render the user details:
<table class="ui table segment">
<thead>
<tr>
<th>Email</th>
<th>Nationality</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>${user.email}</td>
<td>${user.nationality}</td>
<td>${user.age}</td>
</tr>
</tbody>
</table>
Place this in the left hand column, above the profile image
In views/PublicProfile/visit.html, bring in a reduced version somewhere suitable:
<table class="ui table segment">
<thead>
<tr>
<th>Email</th>
<th>Nationality</th>
</tr>
</thead>
<tbody>
<tr>
<td>${user.email}</td>
<td>${user.nationality}</td>
</tr>
</tbody>
</table>
Even if a user seems to have logged out, we can still access the spacebook pages by entering the url of some of the controllers. For instance, log in, then log our immediately and try these links:
How could you prevent this?
HINT: The session object has a method called 'clear()'. If we call this (say in logout action), then it will remove any things we have put in there. For every action, we could first check to make sure a valid ID is in the session, otherwise redirect to the start page.
Introduce a new method into Accounts controller:
public static User getLoggedInUser()
{
User user = null;
if (session.contains("logged_in_userid"))
{
String userId = session.get("logged_in_userid");
user = User.findById(Long.parseLong(userId));
}
else
{
login();
}
return user;
}
Also, in Accounts.logout method - we clear the session:
public static void logout()
{
session.clear();
index();
}
For every method in every controller, compare them with the following, and make the appropriate adjustments:
public class Home extends Controller
{
public static void index()
{
User user = Accounts.getLoggedInUser();
render(user);
}
public static void drop(Long id)
{
User user = Accounts.getLoggedInUser();
User friend = User.findById(id);
user.unfriend(friend);
Logger.info("Dropping " + friend.email);
index();
}
}
public class Profile extends Controller
{
public static void index()
{
User user = Accounts.getLoggedInUser();
render(user);
}
public static void changeStatus(String statusText)
{
User user = Accounts.getLoggedInUser();
user.statusText = statusText;
user.save();
Logger.info("Status changed to " + statusText);
index();
}
public static void getPicture(Long id)
{
User user = User.findById(id);
Blob picture = user.profilePicture;
if (picture.exists())
{
response.setContentTypeIfNotSet(picture.type());
renderBinary(picture.get());
}
}
public static void uploadPicture(Long id, Blob picture)
{
User user = User.findById(id);
user.profilePicture = picture;
user.save();
Logger.info("saving picture");
index();
}
}
public class Members extends Controller
{
public static void index()
{
User user = Accounts.getLoggedInUser();
List<User> users = User.findAll();
render(users);
}
public static void follow(Long id)
{
User user = Accounts.getLoggedInUser();
User friend = User.findById(id);
user.befriend(friend);
Home.index();
}
}
public class PublicProfile extends Controller
{
public static void visit(Long id)
{
User currentUser = Accounts.getLoggedInUser();
User user = User.findById(id);
Logger.info("Just visiting the page for " + user.firstName + ' ' + user.lastName);
render(user);
}
public static void sendMessage(Long id, String messageText)
{
User fromUser = Accounts.getLoggedInUser();;
User toUser = User.findById(id);
Logger.info("Message from user " +
fromUser.firstName + ' ' + fromUser.lastName +" to " +
toUser.firstName + ' ' + toUser.lastName +": " +
messageText);
fromUser.sendMessage(toUser, messageText);
visit(id);
}
When all these changes have been made, run the app and, without logging in, try to visit the urls of the home, profile and members pages.
Then, log in and do the same. Finally, log out and do the same. Observe and verify the behaviour.
Provide a way for a user, once logged in, to change some of their profile information. You could take two approaches to this:
Introduce a new controller called EditProfile:
package controllers;
import play.*;
import play.mvc.*;
import java.util.*;
import models.*;
public class EditProfile extends Controller
{
public static void change (String firstName, String lastName, int age,
String nationality, String email, String password, String password2)
{
User user = Accounts.getLoggedInUser();
user.firstName = firstName;
user.lastName = lastName;
user.email = email;
user.nationality = nationality;
user.age = age;
user.password = password;
user.save();
Profile.index();
}
public static void index()
{
User user = Accounts.getLoggedInUser();
render(user);
}
}
Match this with an accompanying view in views/EditProfile/index.html:
#{extends 'main.html' /}
#{set title:'Edit Details' /}
<nav class="ui inverted menu">
<header class="header item"> Spacebook </header>
<div class="right menu">
<a class="active item" href="/profile"> Profile </a>
</div>
</nav>
<div class="ui raised form segment">
<form action="/editprofile/change" method="POST">
<div class="two fields">
<div class="field">
<label>First Name</label>
<input placeholder="First Name" type="text" name="firstName">
</div>
<div class="field">
<label>Last Name</label>
<input placeholder="Last Name" type="text" name="lastName">
</div>
</div>
<div class="two fields">
<div class="field">
<label>Nationality</label>
<input placeholder="Nationality" type="text" name="nationality">
</div>
<div class="field">
<label>Age</label>
<input placeholder="Age" type="text" name="age">
</div>
</div> <div class="field">
<label>Email</label>
<input placeholder="Email" type="text" name="email">
</div>
<div class="field">
<label>Password</label>
<input type="password" name="password">
</div>
<button class="ui blue submit button">Submit</button>
</form>
</div>
In the views/HomeProfile/index.html, insert a link to this new controller:
<a href="/editprofile" class="ui button"> Edit </a>
Finally, make introduce the appropriate routes:
# Edit Profiles
GET /editprofile EditProfile.index
POST /editprofile/change EditProfile.change
Test that this works as expected.
You will need to run 'play deps' and 'play eclipsify' before you import this.
The EditProfile view is problematic. It presents the save fields as the signup form, however they are all blank. We really should have these fields 'filled in' - so that the user can see and make changes to the entries.
The controller already passes the user details to the view:
public static void index()
{
User user = Accounts.getLoggedInUser();
render(user);
}
We need to change each entry in the form views/EditProgfile/index.html to something like this:
<input placeholder="First Name" type="text" name="firstName" value="${user.firstName}">
(Note the last 'value' entry)
Do this for all the fields in views/EditProgfile/index.html - except perhaps the password field
Some of the form views are not correctly sized, with input boxes elongated. See if you can redesign the signup, login and editprofile forms to look like this:
Edit data.yaml to make sure that all users have nationality and age initial values.
Additionally, it is possible to set up some initial messages:
Message(message1):
from : marge
to : homer
messageText: 'how are things going?'
Message(message2):
from : lisa
to : homer
messageText: 'Get me outta here!'
and even some friend relationships:
User(homer):
firstName: Homer
lastName: Simpson
email: homer@simpson.com
password: secret
following:
- marge
- lisa
Experiment with these to have a range of friends and messages for each user in the yaml file.