Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
1 result

Target

Select target project
  • cosc360-2016/project-base
  • aalkinani/project-base
  • ygurjar/project-base
  • dmccormi/project-base
  • cayeke/project-base
  • Afrah/project-base
  • amarsh24/project-base
  • dbarron2/project-base
8 results
Select Git revision
  • master
1 result
Show changes

Commits on Source 7

Showing
with 883 additions and 248 deletions
Architecture.png

206 KiB

package actors;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
/**
* A rather trivial Actor that just plays FizzBuzz
*/
public class FizzBuzzActor extends UntypedActor {
int nextNum = 0;
/**
* The marshall. We'll also send them the messages
*/
ActorRef marshallActor;
/**
*
*/
public static Props props = Props.create(FizzBuzzActor.class);
@Override
public void onReceive(Object message) {
if (message.equals("Marshall")) {
/*
* We're given the marshall's ActorRef by a message saying "Marshall". The Marshall will appear
* to be the sender (even though it's actually been sent by Setup)
*/
marshallActor = getSender();
} else if (message instanceof ActorRef) {
// If we're sent an ActorRef, that's the marshall telling us who our opponent is and to get started
((ActorRef) message).tell(1, getSelf());
marshallActor.tell(1, getSelf());
} else if (message instanceof Integer) {
nextNum = (Integer) message + 1;
reply();
} else {
nextNum = nextNum + 2;
// Stop at 1000 so we don't blow the heap by writing forever into a StringBuilder
if (nextNum < 1000) {
reply();
}
}
}
void reply() {
if (nextNum % 15 == 0) {
getSender().tell("fizzbuzz", getSelf());
marshallActor.tell("fizzbuzz", getSelf());
} else if (nextNum % 5 == 0) {
getSender().tell("buzz", getSelf());
marshallActor.tell("buzz", getSelf());
} else if (nextNum % 3 == 0) {
getSender().tell("fizz", getSelf());
marshallActor.tell("fizz", getSelf());
} else {
getSender().tell(nextNum, getSelf());
marshallActor.tell(nextNum, getSelf());
}
// Put this Actor to sleep for 250ms before it checks its next message
try {
Thread.sleep(250);
} catch (InterruptedException ex) {
// do nothing
}
}
}
package actors;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
import javax.inject.Inject;
/**
* You might or might not want this actor in the end. In this example, its role is to be the central point of
* contact between your web app and the actor system that might be receiving outside events.
*/
public class MarshallActor extends UntypedActor {
StringBuilder messages = new StringBuilder();
public static Props props = Props.create(MarshallActor.class);
public MarshallActor() {
}
@Override
public void onReceive(Object message) {
if (message.equals("Report!")) {
getSender().tell(messages.toString(), getSelf());
} else {
messages.append(getSender().toString());
messages.append(" said ");
messages.append(message);
messages.append("\n");
}
}
}
......@@ -15,22 +15,9 @@ import javax.inject.Singleton;
@Singleton
public class Setup {
/**
* This is a reference to an Actor
*/
public final ActorRef marshallActor;
@Inject
public Setup(ActorSystem system) {
marshallActor = system.actorOf(MarshallActor.props);
ActorRef fb1 = system.actorOf(FizzBuzzActor.props, "Player1");
ActorRef fb2 = system.actorOf(FizzBuzzActor.props, "Player2");
fb1.tell("Marshall", marshallActor);
fb2.tell("Marshall", marshallActor);
fb1.tell(fb2, fb2);
ActorRef fb1 = system.actorOf(TwitterStreamActor.props, "TwitterStream");
fb1.tell("ConnectToStream", ActorRef.noSender());
}
}
\ No newline at end of file
package actors;
import com.fasterxml.jackson.databind.node.ObjectNode;
import model.TweetHub;
import play.libs.Json;
import twitter4j.*;
import akka.actor.Props;
import akka.actor.UntypedActor;
import twitter4j.conf.ConfigurationBuilder;
/*
Consumer Key (API Key) MkpBoVPy3REAIdsPNSZpC7u0I
Consumer Secret (API Secret) 3cNybn6qSJPpw2AnizXGitOclFV3ZolFXidWBWLpOnafRw7uL8
Access Token 315679785-SVT1kCKoow1eCTgTWLzPrh6BPaKA5OiPOfIlmAxt
Access Token Secret TYTg0PLDbU7isjXxm3uPIeu2xdYg3rXGaEGUdJuBkvCg2
*/
/**
* A rather trivial Actor that just plays FizzBuzz
*/
public class TwitterStreamActor extends UntypedActor {
int nextNum = 0;
/**
*
*/
public static Props props = Props.create(TwitterStreamActor.class);
TwitterStream twitterStream;
StatusListener listener;
public TwitterStreamActor() {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey("MkpBoVPy3REAIdsPNSZpC7u0I")
.setOAuthConsumerSecret("3cNybn6qSJPpw2AnizXGitOclFV3ZolFXidWBWLpOnafRw7uL8")
.setOAuthAccessToken("315679785-SVT1kCKoow1eCTgTWLzPrh6BPaKA5OiPOfIlmAxt")
.setOAuthAccessTokenSecret("TYTg0PLDbU7isjXxm3uPIeu2xdYg3rXGaEGUdJuBkvCg2");
twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
listener = new StatusListener() {
@Override
public void onStatus(Status status) {
// Work around for bug in filter.
GeoLocation loc = status.getGeoLocation();
if (loc == null){
return;
}
// Build a json object to send over the wire to the client.
ObjectNode n = Json.newObject();
//n.put("msg", "@" + status.getUser().getScreenName() + "said " + status.getText() + " - " + loc);
n.put("lat", loc.getLatitude());
n.put("lng", loc.getLongitude());
TweetHub.getInstance().send(n);
}
@Override
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
}
@Override
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
System.out.println("Got track limitation notice:" + numberOfLimitedStatuses);
}
@Override
public void onScrubGeo(long userId, long upToStatusId) {
System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
}
@Override
public void onStallWarning(StallWarning warning) {
System.out.println("Got stall warning:" + warning);
}
@Override
public void onException(Exception ex) {
ex.printStackTrace();
}
};
}
@Override
public void onReceive(Object message) throws TwitterException {
if (message.equals("ConnectToStream")){
// Effectively the wake up call that starts the stream up.
ConnectToTwitterStreamWithFilter();
}
}
void ConnectToTwitterStreamWithFilter(){
FilterQuery fq = new FilterQuery();
double[][] boundingBox= {{-180, -90}, {180, 90}}; // whole world;
fq.locations(boundingBox);
twitterStream.addListener(listener);
twitterStream.filter(fq);
}
}
......@@ -3,13 +3,18 @@ package controllers;
import actors.Setup;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import model.UserService;
import play.mvc.Controller;
import play.mvc.LegacyWebSocket;
import play.mvc.Result;
import play.mvc.WebSocket;
import scala.compat.java8.FutureConverters;
import play.libs.ws.*;
import java.util.concurrent.CompletionStage;
import static akka.pattern.Patterns.ask;
import static controllers.UserController.getSessionId;
import static controllers.UserController.getUserService;
@Singleton
public class Application extends Controller {
......@@ -18,33 +23,48 @@ public class Application extends Controller {
WSClient wsClient;
protected static UserService getUserService() {
return UserService.instance;
}
protected static final String SESSIONVAR = "mySession";
/**
* Returns our generated session ID for this user, creating one if necessary
*/
protected static String getSessionId() {
String id = session(SESSIONVAR);
if (!getUserService().isValidId(id)) {
id = getUserService().allocateId();
session(SESSIONVAR, id);
}
return id;
}
@Inject
public Application(Setup actorSetup, WSClient wsClient) {
this.actorSetup = actorSetup;
this.wsClient = wsClient;
}
public Result index() {
return ok(views.html.application.index.render(getUserService().getUserFromSession(getSessionId())));
}
/**
* Play framework suppors asynchronous controller methods -- that is, methods that instead of returning a Result
* return a CompletionStage, which will produce a Result some time in the future
* Our WebSockets endpoint. We'll assume we're sending String messages for now
*/
public CompletionStage<Result> index() {
public LegacyWebSocket<String> socket(String topic) {
/*
* This code might look a little complex.
*
* ask sends a message to an ActorRef, and then returns a Future that will eventually contain the response.
* But Future is a Scala class, so FutureConverters.toJava converts it into a CompletionStage, which is Java's equivalent.
* thenApply is a method on CompletionStage that means "when you get the result, do this with it, and return a new CompletionStage"
*/
return FutureConverters.toJava(ask(actorSetup.marshallActor, "Report!", 1000))
.thenApply(response -> ok(response.toString()));
}
Play framework provides an Actor for client automatically. That's the <code>out</code> argument below.
/**
* This controller method uses Play's Web Service client to make another HTTP call, and do something with the
* response
We need to tell Play what kind of actor we want on the server side. We do that with a "Props" object, because
Play will ask Akka to create the actor for us.
We want a TweetWebSocketActor, and we want to pass the client actor and the topic as constructor arguments.
TweetWebSocketActor.props(topic, out) will produce a Props object saying that.
*/
public CompletionStage<Result> whatDidGitLabSay() {
return wsClient.url("http://www.une.edu.au/foo").get().thenApply(r -> ok(r.getStatusText()));
return WebSocket.withActor((out) -> TweetWebSocketActor.props(topic, out));
}
}
package controllers;
/**
* Created by davidmccormick on 21/08/16.
*/
import akka.actor.*;
import model.TweetHub;
import model.TweetListener;
public class TweetWebSocketActor extends UntypedActor {
/**
* We don't create the actor ourselves. Instead, Play will ask Akka to make it for us. We have to give Akka a
* "Props" object that tells Akka what kind of actor to create, and what constructor arguments to pass to it.
* This method produces that Props object.
*/
public static Props props(String topic, ActorRef out) {
// Create a Props object that says:
// - I want a TweetWebSocketActor,
// - and pass (topic, out) as the arguments to its constructor
return Props.create(TweetWebSocketActor.class, topic, out);
}
/** The Actor for the client (browser) */
private final ActorRef out;
/** The topic string we have subscribed to */
private final String topic;
/** A listener that we will register with our TweetHub */
private final TweetListener listener;
/**
* This constructor is called by Akka to create our actor (we don't call it ourselves).
*/
public TweetWebSocketActor(String topic, ActorRef out) {
this.topic = topic;
this.out = out;
/*
Our TweetListener, written as a Java 8 Lambda.
Whenever we receive a tweet, if it matches our topic, convert it to a JSON string, and send it to the client.
*/
this.listener = (tweetJson) -> {
String message = tweetJson.toString();
out.tell(message, self());
};
// Register this actor to hear tweets
TweetHub.getInstance().addListener(listener);
}
/**
* This is called whenever the browser sends a message to the serverover the websocket
*/
public void onReceive(Object message) throws Exception {
// The client isn't going to send us messages down the websocket in this example, so this doesn't matter
if (message instanceof String) {
out.tell("I received your message: " + message, self());
}
}
/**
* This is called by Play after the WebSocket has closed
*/
public void postStop() throws Exception {
// De-register our listener
TweetHub.getInstance().removeListener(this.listener);
}
}
package controllers;
import model.Session;
import model.User;
import model.UserService;
import org.mindrot.jbcrypt.BCrypt;
import play.mvc.Result;
import play.mvc.Controller;
public class UserController extends Controller {
protected static UserService getUserService() {
return UserService.instance;
}
protected static final String SESSIONVAR = "mySession";
/**
* Returns our generated session ID for this user, creating one if necessary
*/
protected static String getSessionId() {
String id = session(SESSIONVAR);
if (!getUserService().isValidId(id)) {
id = getUserService().allocateId();
session(SESSIONVAR, id);
}
return id;
}
public Result loginView() {
return ok(views.html.application.login.render(null));
}
public Result registerView() {
return ok(views.html.application.register.render(null));
}
public Result sessionsView() {
return ok(views.html.application.sessions.render(getUserService().getUserFromSession(getSessionId())));
}
public Result doLogin() {
String sessionId = getSessionId();
String email;
String password;
// We're doing this very basically, as Play forms are not in scope for the course
// (The unit prefers to teach things that are a little closer to the HTTP, rather than convenience wrappers)
try {
email = request().body().asFormUrlEncoded().get("email")[0];
password = request().body().asFormUrlEncoded().get("password")[0];
} catch (Exception e) {
return badRequest(views.html.application.login.render("Email and password could not be found in the request"));
}
if (getUserService().getUserFromSession(sessionId)!= null) {
return badRequest(views.html.application.login.render("You're already logged in!"));
}
User u = getUserService().getUser(email, password);
if (u != null) {
u.pushSession(new Session(sessionId, request().remoteAddress(), System.currentTimeMillis()));
getUserService().update(u);
return redirect("/");
} else {
return forbidden(views.html.application.login.render("Wrong email address or password"));
}
}
public Result doRegister() {
String sessionId = getSessionId();
String email;
String password;
// We're doing this very basically, as Play forms are not in scope for the course
// (The unit prefers to teach things that are a little closer to the HTTP, rather than convenience wrappers)
try {
email = request().body().asFormUrlEncoded().get("email")[0];
password = request().body().asFormUrlEncoded().get("password")[0];
} catch (Exception e) {
return badRequest(views.html.application.login.render("Email and password could not be found in the request"));
}
if (getUserService().getUserFromSession(sessionId)!= null) {
return badRequest(views.html.application.login.render("You're already logged in!"));
}
// Create a new user object
User u = new User(getUserService().allocateId(), email, BCrypt.hashpw(password, BCrypt.gensalt()));
// Try to register them
try {
getUserService().registerUser(u);
} catch (Exception ex) {
return badRequest(views.html.application.register.render(ex.getMessage()));
}
// Log them in
u.pushSession(new Session(sessionId, request().remoteAddress(), System.currentTimeMillis()));
return redirect("/");
}
public Result doLogout() {
String sessionId = getSessionId();
User u = getUserService().getUserFromSession(sessionId);
if (u != null) {
u.removeSession(sessionId);
}
return ok(views.html.application.login.render(null));
}
public Result doRemoteLogout() {
String sessionId = getSessionId();
String toRemove;
try {
toRemove = request().body().asFormUrlEncoded().get("remove")[0];
} catch (Exception e) {
return badRequest(views.html.application.login.render("Session to remove could not be found in the request"));
}
User u = getUserService().getUserFromSession(sessionId);
if (u != null) {
u.removeSession(toRemove);
}
getUserService().update(u);
return redirect("/");
}
}
\ No newline at end of file
package model;
import org.mindrot.jbcrypt.BCrypt;
/**
* A little demo code to show how BCrypt is used to hash passwords
*/
public class BCryptExample {
// Eek, not threadsafe.
static String last;
public static String encrypt(String s) {
last = BCrypt.hashpw(s, BCrypt.gensalt());
return last;
}
public static boolean matchesLastEncrypted(String pw) {
return BCrypt.checkpw(pw, last);
}
}
package model;
public class Captcha {
/**
* URLs of photos the user should select
*/
public static String[] yesPhotos = {
"http://assets.dogtime.com/breed/profile/image/4d3771f10106195669001f8c/beagle.jpg",
"https://gdkennel.files.wordpress.com/2013/09/010.jpg",
"http://www.perros2.com/wp-content/uploads/2012/06/ddd.jpg",
"http://dogobedienceadvice.com/images/beagle_training.jpg",
"http://www.swish-swash.co.uk/images/beagle/beagles.jpg"
};
/**
* URLs of photos the user should not select
*/
public static String[] noPhotos = {
"http://www.pets4homes.co.uk/images/articles/1265/large/ten-reasons-why-dogs-make-better-pets-than-cats-52bc3172b4816.jpg",
"http://www.adweek.com/files/imagecache/node-blog/sa_article/dog_image_0.jpg",
"http://7-themes.com/data_images/out/36/6891162-dogs.jpg",
"http://www.boxer-dog.org/sites/default/files/imagecache/500_width/ruby2.jpg",
"http://www.imagepuppy.com/resized/762ce6cb8241c08d73ec9304b42f6d5f.jpg"
};
/**
* How many photos there are in total
*/
public static int numPhotos() {
return yesPhotos.length + noPhotos.length;
}
/**
* The index of a random photo
*/
public static int randomPhotoIdx() {
return (int)(Math.random() * numPhotos());
}
/**
* Get a photo with the specified index
*/
public static String getPhoto(int idx) {
if (idx < 0 || idx >= numPhotos()) {
throw new IllegalArgumentException("index out of range");
}
if (idx < yesPhotos.length) {
return yesPhotos[idx];
} else {
return noPhotos[idx - yesPhotos.length];
}
}
/**
* Whether a photo selected by the user was in the "yes" set
*/
public static boolean isCorrect(int idx) {
return idx >= 0 && idx < yesPhotos.length;
}
}
package model;
public class Session {
String id;
String ipAddress;
long since;
public Session(String id, String ipAddress, long since) {
this.id = id;
this.ipAddress = ipAddress;
this.since = since;
}
public String getId() {
return id;
}
public String getIpAddress() {
return ipAddress;
}
public long getSince() {
return since;
}
}
package model;
/**
* Created by davidmccormick on 21/08/16.
*/
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This is a simple publish-subscribe list. It maintains a list of listeners, and whenever it receives a call to
* <code>send</code>, it calls <code>receiveTweet</code> on every registered listener.
*/
public class TweetHub {
List<TweetListener> listeners;
static final TweetHub instance = new TweetHub();
public static TweetHub getInstance() {
return instance;
}
protected TweetHub() {
this.listeners = Collections.synchronizedList(new ArrayList<>());
}
public void send(ObjectNode g) {
for (TweetListener listener : listeners) {
listener.receiveTweet(g);
}
}
public void addListener(TweetListener l) {
this.listeners.add(l);
}
public void removeListener(TweetListener l) {
this.listeners.remove(l);
}
}
\ No newline at end of file
package model;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Created by davidmccormick on 21/08/16.
*/
public interface TweetListener {
void receiveTweet(ObjectNode g);
}
\ No newline at end of file
package model;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
public class User {
String id;
String email;
String hash;
/**
* We need to hold the user's active sessions; this stands in for our database table
*/
ConcurrentHashMap<String, Session> activeSessions = new ConcurrentHashMap<>();
public User(String id, String email, String hash) {
this.id = id;
this.email = email;
this.hash = hash;
}
public String getId() {
return id;
}
public String getEmail() {
return email;
}
public String getHash() {
return hash;
}
/**
* Is a particular session active on this user?
*/
public boolean hasSession(String sessionId) {
return activeSessions.containsKey(sessionId);
}
public Session[] getSessions() {
Collection<Session> values = activeSessions.values();
return values.toArray(new Session[values.size()]);
}
/**
* Record that a particular session is logged in as this user
*/
public void pushSession(Session s) {
activeSessions.put(s.id, s);
}
/**
* Remove an active session from this user
*/
public void removeSession(String sessionId) {
activeSessions.remove(sessionId);
}
}
\ No newline at end of file
package model;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.BsonDocument;
import org.bson.BsonWriter;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.mindrot.jbcrypt.BCrypt;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class UserService {
public static final UserService instance = new UserService();
protected MongoClient mongoClient;
protected UserService() {
mongoClient = new MongoClient("127.0.0.1", 27017);
}
protected MongoDatabase getDB() {
// TODO: Change your database name, to avoid clashing with others on turing
return mongoClient.getDatabase("comp560_dmccormi");
}
protected MongoCollection<Document> getChitterCollection() {
return getDB().getCollection("chitterUser");
}
/**
* Allocates an ObjectID and returns it as a hex string; I've exposed this so we can use it also for session IDs.
*/
public String allocateId() {
return new ObjectId().toHexString();
}
/**
* Checks if this is a valid ObjectID, as some browsers might have old UUIDs cached
*/
public boolean isValidId(String id) {
try {
ObjectId i = new ObjectId(id);
return i.toHexString().equals(id);
} catch (Exception ex) {
return false;
}
}
public User registerUser(User u) {
// Let's first check the user isn't already registered
if (getChitterCollection().find(new Document("email", u.getEmail())).first() != null) {
throw new IllegalArgumentException("That email address has already been registered");
}
insert(u);
return u;
}
public User getUser(String id) {
Document d = getChitterCollection().find(new Document("_id", new ObjectId(id))).first();
if (d != null) {
return userFromBson(d);
} else {
return null;
}
}
/**
* Get the user by email and password, returning null if they don't exist (or the password is wrong)
*/
public User getUser(String email, String password) {
Document d = getChitterCollection().find(new Document("email", email)).first();
// I wrote userFromBson to accept nulls
User u = userFromBson(d);
if (u != null && BCrypt.checkpw(password, u.getHash())) {
return u;
} else {
return null;
}
}
/**
* Get the user who is logged in with this session, if there is one
*/
public User getUserFromSession(String sessionId) {
Document d = getChitterCollection().find(new Document("sessions._id", new ObjectId(sessionId))).first();
return userFromBson(d);
}
protected static Document userToBson(User u) {
List<Document> sessions = new ArrayList<>();
for (Session s : u.getSessions()) {
sessions.add(sessionToBson(s));
}
return new Document("_id", new ObjectId(u.getId()))
.append("email", u.email)
.append("hash", u.getHash())
.append("sessions", sessions);
}
protected static User userFromBson(Document d) {
// This lets us call this method even if d is null
if (d == null) {
return null;
}
String id = d.getObjectId("_id").toHexString();
String email = d.getString("email");
String hash = d.getString("hash");
User u = new User(id, email, hash);
// This gives an unchecked warning; we'd need to use the safer means of doing this (which we don't cover)
// to avoid the warning
List<Document> sessions = d.get("sessions", List.class);
for (Document sd : sessions) {
Session s = sessionFromBson(sd);
u.pushSession(s);
}
return u;
}
protected static Session sessionFromBson(Document d) {
// This lets us call this method even if d is null
if (d == null) {
return null;
}
String id = d.getObjectId("_id").toHexString();
String ip = d.getString("ipAddress");
long since = d.getLong("since");
return new Session(id, ip, since);
}
protected static Document sessionToBson(Session s) {
return new Document("_id", new ObjectId(s.getId()))
.append("ipAddress", s.getIpAddress())
.append("since", s.getSince());
}
protected void insert(User u) {
getChitterCollection().insertOne(userToBson(u));
}
public void update(User u) {
getChitterCollection().replaceOne(new Document("_id", new ObjectId(u.getId())), userToBson(u));
}
}
\ No newline at end of file
@(indexes:Array[Int])
@(u:model.User)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/assets/html/favicon.ico">
<html>
<head lang="en">
<meta name="referrer" content="no-referrer">
<meta charset="UTF-8">
<title></title>
<title>Hawkeye</title>
<!-- Bootstrap core CSS -->
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/assets/bootstrap/css/cover.css" rel="stylesheet">
<!--<meta name="viewport" content="initial-scale=1.0, user-scalable=yes" />-->
<script src="//maps.googleapis.com/maps/api/js?sensor=false&libraries=placeses,visualization,drawing,geometry,places"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="//rawgit.com/angular-ui/angular-google-maps/2.1.5/dist/angular-google-maps.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
<script src="/assets/javascript/cbuffer.js"></script>
<script src="/assets/javascript/dynamicHeatMap.js"></script>
</head>
<body>
<h1>Tick all the beagles:</h1>
<style>
.angular-google-map-container { height: 700px; }
</style>
@if(u != null) {
<div ng-app="myApp" ng-controller="Example">
<ui-gmap-google-map center='map.center' zoom='map.zoom' options="map.options">
<ui-gmap-layer namespace="visualization" type="HeatmapLayer" show="map.showHeat" onCreated="map.heatLayerCallback"></ui-gmap-layer>
</ui-gmap-google-map>
@for(session <- u.getSessions()) {
<form action="remoteLogout" method="POST">
<input type="hidden" value="@{session.getId()}" name="remove" />
<button type="submit">Log out remotely</button>
</form>
}
</div>
} else {
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<form action="matches" method="POST">
@for(idx <- indexes) {
<div>
<input name="sent" value="@idx" type="hidden" />
<input name="beagle" value="@idx" type="checkbox" />
<img src="@model.Captcha.getPhoto(idx)" style="max-width: 400px; max-height: 200px;" />
<div class="masthead clearfix">
<div class="inner">
<h3 class="masthead-brand">Hawkeye</h3>
<nav>
<ul class="nav masthead-nav">
<li class="active"><a href="/">Home</a></li>
<li><a href="/login">Login</a></li>
<li><a href="/register">Register</a></li>
</ul>
</nav>
</div>
</div>
<div class="inner cover">
<h1 class="cover-heading">See whats going on around you.</h1>
<p class="lead">Hawkeye is a single-page app that displays all tweets globally as a realtime heatmap.</p>
</div>
</div>
</div>
</div>
}
<!--
TODO: For each index in the array, show a checkbox with the value of the index, and the relevant img
-->
<button type="submit">Submit</button>
</form>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
\ No newline at end of file
@(error:String)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/assets/html/favicon.ico">
<!-- Bootstrap core CSS -->
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/assets/bootstrap/css/cover.css" rel="stylesheet">
</head>
<body>
<h2>Log in</h2>
<form method="post" action="login" >
<input type="text" placeholder="Email address" name="email" /> <br/>
<input type="password" placeholder="Password" name="password" /> <br/>
<button type="submit" >Log in</button>
@if(error != null) {
<p>@error</p>
}
</form>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="../../dist/js/bootstrap.min.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>
</body>
</html>
\ No newline at end of file
package views
/**
* Created by davidmccormick on 16/08/16.
*/
package object application {
}
@(error:String)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Register</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/assets/html/favicon.ico">
<!-- Bootstrap core CSS -->
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/assets/bootstrap/css/cover.css" rel="stylesheet">
</head>
<body>
<h2>Register</h2>
<form method="post" action="register" >
<input type="text" placeholder="Email address" name="email" /> <br/>
<input type="password" placeholder="Password" name="password" /> <br/>
<button type="submit" >Register</button>
@if(error != null) {
<p>@error</p>
}
</form>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="../../dist/js/bootstrap.min.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>
</body>
</html>
\ No newline at end of file
@(u:model.User)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<h2>Sessions</h2>
@if(u != null) {
<p>
You are logged in as @{u.getEmail()}
</p>
<p>Your sessions:</p>
<ul>
@for(session <- u.getSessions()) {
<li>
<label>IP address: </label> @{session.getIpAddress()}
<label>Since: </label> @{session.getSince()}
<form action="remoteLogout" method="POST">
<input type="hidden" value="@{session.getId()}" name="remove" />
<button type="submit">Log out remotely</button>
</form>
</li>
}
</ul>
} else {
<p>You are not logged in</p>
}
</body>
</html>
\ No newline at end of file