← Back to team overview

mugle-dev team mailing list archive

[Merge] lp:~mugle-dev/mugle/ui into lp:mugle

 

Matt Giuca has proposed merging lp:~mugle-dev/mugle/ui into lp:mugle with lp:~mugle-dev/mugle/dev-api as a prerequisite.

Requested reviews:
  Matt Giuca (mgiuca)

For more details, see:
https://code.launchpad.net/~mugle-dev/mugle/ui/+merge/59455

Adds some new UI capabilities. Merge because it's too hard to separate changes between branches and trunk isn't being worked on anyway.
-- 
https://code.launchpad.net/~mugle-dev/mugle/ui/+merge/59455
Your team MUGLE Developers is subscribed to branch lp:~mugle-dev/mugle/dev-api.
=== renamed file 'src/au/edu/unimelb/csse/mugle/client/LoginService.java' => 'src/au/edu/unimelb/csse/mugle/client/ClientService.java'
--- src/au/edu/unimelb/csse/mugle/client/LoginService.java	2011-03-06 10:21:39 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ClientService.java	2011-04-29 07:02:27 +0000
@@ -16,10 +16,12 @@
  */
 package au.edu.unimelb.csse.mugle.client;
 
+import au.edu.unimelb.csse.mugle.client.LoginInfo;
+
 import com.google.gwt.user.client.rpc.RemoteService;
 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
-@RemoteServiceRelativePath("login")
-public interface LoginService extends RemoteService {
+@RemoteServiceRelativePath("client")
+public interface ClientService extends RemoteService {
 	LoginInfo login(String requestUri);
 }

=== renamed file 'src/au/edu/unimelb/csse/mugle/client/LoginServiceAsync.java' => 'src/au/edu/unimelb/csse/mugle/client/ClientServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/LoginServiceAsync.java	2011-03-06 10:21:39 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ClientServiceAsync.java	2011-04-29 07:02:27 +0000
@@ -18,6 +18,6 @@
 
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
-public interface LoginServiceAsync {
+public interface ClientServiceAsync {
 	void login(String requestUri, AsyncCallback<LoginInfo> async);
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/client/Mugle.java'
--- src/au/edu/unimelb/csse/mugle/client/Mugle.java	2011-04-08 11:51:46 +0000
+++ src/au/edu/unimelb/csse/mugle/client/Mugle.java	2011-04-29 07:02:27 +0000
@@ -1,20 +1,17 @@
 package au.edu.unimelb.csse.mugle.client;
 
+import au.edu.unimelb.csse.mugle.client.ui.MugleUiBuilder;
+
 import com.google.gwt.core.client.EntryPoint;
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.StackPanel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
 
 /**
  * Entry point classes define <code>onModuleLoad()</code>.
@@ -27,13 +24,11 @@
   private static final String SERVER_ERROR = "An error occurred while "
       + "attempting to contact the server. Please check your network "
       + "connection and try again.";
-
-  private final DataTestServiceAsync dataTestService = GWT.create(DataTestService.class);
-  
+  	
   private RootPanel mainPanel = null;
     
   private AuthenticatedUser authUser = new AuthenticatedUser();
-
+  
   /**
    * This is the entry point method.
    * Checks if logged in, if you are, runs the standard code for page,
@@ -42,11 +37,13 @@
   @Override
   public void onModuleLoad() {
 	  	  
-	  this.getStaticPanels();
+	  this.mainPanel = RootPanel.get("main-panel");	  
+	  Element element = RootPanel.get("error-display").getElement();
+	  DOM.removeChild(RootPanel.get("container").getElement(), element);
 	  
 	  //Check login status using login service.
-	  LoginServiceAsync loginService = GWT.create(LoginService.class);
-	  loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
+	  ClientServiceAsync clientService = GWT.create(ClientService.class);
+	  clientService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
 	   
 		  public void onFailure(Throwable error) {
 			  System.err.print(error.getMessage());
@@ -61,80 +58,75 @@
 	  });	  
 
   }
-  
-  private void getStaticPanels() {
-	  	  	  
-	  mainPanel = RootPanel.get("main-panel");
-	  	
-	  /*
-	  final RootPanel topPanel = RootPanel.get("user-panel");
-	  final RootPanel footer = RootPanel.get("footer");
-	  final RootPanel errPanel = RootPanel.get("error-display");
-	  
-	  Window.addResizeHandler(new ResizeHandler() {
-		  public void onResize(ResizeEvent event) {			  
-			  int h = topPanel.getOffsetHeight() + footer.getOffsetHeight() + errPanel.getOffsetHeight();
-		       int height = event.getHeight() - h - 10;
-		       if (height < 200) { height = 200; }
-		       mainPanel.setHeight(height + "px");		       
-		  }		     
-	  });  
-	  
-	  */
-	  
-  }
-    
+      
   private void arrangeUI() {
 	    
 	  if (this.authUser.isLoggedIn()) {	
 		  
 		  Grid userPanel = new Grid(1,3);
-		  RootPanel.get("user-panel-holder").add(userPanel);
-		  
-		  RootPanel mainPanel = RootPanel.get("main-panel");
-		  
-		  this.arrangeUserPanel(userPanel);
-		  this.arrangeMainPanel(mainPanel);
+		  DOM.getElementById("user-panel-holder").appendChild(userPanel.getElement());
+		  
+		  //this.mainPanel = RootPanel.get("main-panel");
+		  
+		  MugleUiBuilder.arrangeUserPanel(userPanel, this.authUser);
+		  MugleUiBuilder.arrangeMainPanel(this.mainPanel);
 		  
 	  } else {
 		  
 		  this.mainPanel.add(this.authUser.resetLoginPanel());
 		  
+
+		  // debugging
+		  
+		  //final MugleDialogBox dialog = new MugleDialogBox("Data Population");			
+		  final Button b = new Button("Populate Data");
+		  
+		  //dialog.setReturnFocus(b);
+		  
+		  b.addStyleName("sendButton");
+		  b.addClickHandler(new ClickHandler() {
+					
+			  DataTestServiceAsync dataTestService = GWT.create(DataTestService.class);
+			  
+			  @Override
+			  public void onClick(ClickEvent event) {
+								  
+					dataTestService.populateDatastore(new AsyncCallback<String>() {
+
+						@Override
+						public void onFailure(Throwable caught) {
+							System.err.println("failed");
+							b.setEnabled(true);
+							b.setText("Try again! Populate Database");
+							//dialog.setBody(new HTML(SERVER_ERROR));	
+							//dialog.center();
+							//dialog.show();
+						}
+	
+						@Override
+						public void onSuccess(String result) {	
+							System.err.println("success");
+							b.setText("Database successfully populated");
+							//dialog.setBody(new HTML(result));						
+							//dialog.show();
+							//dialog.center();
+						}
+					
+					});
+					
+					b.setEnabled(false);
+					b.setText("Processing");
+					
+			  }
+			
+		  });
+		  
+		  this.mainPanel.add(b);
+		  
 	  }
 	  
   }
   
-  private void arrangeUserPanel(Grid userPanel) {
-	  
-	  RootPanel.get("user-panel-title").add(new HTML("<b>MUGLE</b>"));
-	  userPanel.setWidget(0, 0, new HTML("<b>" + this.authUser.getLoginInfo().getEmailAddress() + "</b>"));
-	  userPanel.setWidget(0, 1, new HTML("|"));
-	  userPanel.setWidget(0, 2, this.authUser.getSignOutLink());
-	  
-  }
-  
-  private void arrangeMainPanel(RootPanel mainPanel) {
-	  
-	  HorizontalPanel hp = new HorizontalPanel();
-	  
-	  // left panel
-	  StackPanel sp = new StackPanel();
-	  //sp.setWidth("200px");
-	  DOM.setElementAttribute(sp.getElement(), "id", "tools-panel");
-	  
-	  
-	  sp.setStackText(0, "Topic", false);
-	  
-	  sp.insert(new HTML("test"), 0);
-	  sp.insert(new HTML("test 2"), 1);
-	  sp.insert(new HTML("test 3"), 2);
-	  
-	  hp.insert(sp, 0);
-	  hp.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
-	  mainPanel.add(hp);
-	  
-  }
-  
 /*
   private void loadDataTest() {
 	  //Set up sign out hyperlink.

=== modified file 'src/au/edu/unimelb/csse/mugle/client/platform/AdminService.java'
--- src/au/edu/unimelb/csse/mugle/client/platform/AdminService.java	2011-03-20 11:06:48 +0000
+++ src/au/edu/unimelb/csse/mugle/client/platform/AdminService.java	2011-04-29 07:02:27 +0000
@@ -41,6 +41,8 @@
 	
 	boolean banUser(Long id) throws UserNotExists, UserPrivilegeException;
 
+	User getCurrentUserDetails() throws ServerException;
+	
 	// DevTeams
 
 	DevTeam addNewDevTeam(DevTeam devTeam) throws DevTeamExists, UserPrivilegeException;

=== modified file 'src/au/edu/unimelb/csse/mugle/client/platform/AdminServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/platform/AdminServiceAsync.java	2011-03-20 11:06:48 +0000
+++ src/au/edu/unimelb/csse/mugle/client/platform/AdminServiceAsync.java	2011-04-29 07:02:27 +0000
@@ -100,5 +100,7 @@
 
     void demoteGame(String promotedGameKey,
             AsyncCallback<GameVersion> callback);
+
+	void getCurrentUserDetails(AsyncCallback<User> callback);
 	
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/client/platform/DeveloperService.java'
--- src/au/edu/unimelb/csse/mugle/client/platform/DeveloperService.java	2011-03-20 11:06:48 +0000
+++ src/au/edu/unimelb/csse/mugle/client/platform/DeveloperService.java	2011-04-29 07:02:27 +0000
@@ -21,12 +21,13 @@
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionExists;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.ServerException;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserPrivilegeException;
 
 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
 @RemoteServiceRelativePath("dev")
-public interface DeveloperService extends UserService {
+public interface DeveloperService extends GuestService {
 
     GameVersion addGameVersion(GameVersion gameVersion) throws GameVersionExists, GameNotExists, UserPrivilegeException;
 
@@ -38,4 +39,5 @@
     
     boolean removeGameVersion(Long pKey) throws GameVersionNotExists, UserPrivilegeException;
     
+    User getCurrentUserDetails() throws ServerException;
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/client/platform/DeveloperServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/platform/DeveloperServiceAsync.java	2011-03-20 11:06:48 +0000
+++ src/au/edu/unimelb/csse/mugle/client/platform/DeveloperServiceAsync.java	2011-04-29 07:02:27 +0000
@@ -42,4 +42,6 @@
 
     void removeGameVersion(Long pKey, AsyncCallback<Boolean> callback);
 
+	void getCurrentUserDetails(AsyncCallback<User> callback);
+
 }

=== renamed file 'src/au/edu/unimelb/csse/mugle/client/platform/UserService.java' => 'src/au/edu/unimelb/csse/mugle/client/platform/GuestService.java'
--- src/au/edu/unimelb/csse/mugle/client/platform/UserService.java	2011-03-09 08:55:08 +0000
+++ src/au/edu/unimelb/csse/mugle/client/platform/GuestService.java	2011-04-29 07:02:27 +0000
@@ -17,10 +17,15 @@
 
 package au.edu.unimelb.csse.mugle.client.platform;
 
+import au.edu.unimelb.csse.mugle.shared.model.User;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.ServerException;
+
 import com.google.gwt.user.client.rpc.RemoteService;
 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
-@RemoteServiceRelativePath("users")
-public interface UserService extends RemoteService {
+@RemoteServiceRelativePath("guests")
+public interface GuestService extends RemoteService {
 
+	User getCurrentUserDetails() throws ServerException;
+	
 }

=== renamed file 'src/au/edu/unimelb/csse/mugle/client/platform/UserServiceAsync.java' => 'src/au/edu/unimelb/csse/mugle/client/platform/GuestServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/platform/UserServiceAsync.java	2011-03-10 04:50:07 +0000
+++ src/au/edu/unimelb/csse/mugle/client/platform/GuestServiceAsync.java	2011-04-29 07:02:27 +0000
@@ -19,10 +19,13 @@
 
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
+import au.edu.unimelb.csse.mugle.shared.model.User;
+
 /**
- * The async counterpart of <code>UserService</code>.
+ * The async counterpart of <code>GuestService</code>.
  */
-public interface UserServiceAsync {
+public interface GuestServiceAsync {
 
+	void getCurrentUserDetails(AsyncCallback<User> callback);
 
 }

=== added file 'src/au/edu/unimelb/csse/mugle/client/ui/MugleDialogBox.java'
--- src/au/edu/unimelb/csse/mugle/client/ui/MugleDialogBox.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ui/MugleDialogBox.java	2011-04-29 07:02:27 +0000
@@ -0,0 +1,104 @@
+package au.edu.unimelb.csse.mugle.client.ui;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.FocusWidget;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+
+public class MugleDialogBox extends DialogBox {
+	
+	private final Button closeButton = new Button("Close");
+	private final HorizontalPanel bodyHolder = new HorizontalPanel();
+	
+	private FocusWidget returnFocus = null;
+	private HTML body = new HTML();
+		  
+	public MugleDialogBox(String text, HTML body, FocusWidget focus) {	
+		super(false);
+		this.returnFocus = focus;
+		this.setText(text);
+		this.setup();	
+		this.setBody(body);
+	}
+	
+	public MugleDialogBox(String text) {
+		super(false);
+		this.setText(text);
+		this.setup();		
+	}
+	
+	public MugleDialogBox() {
+		super(false);
+		this.setup();		
+	}
+	
+	private void setup() {
+		
+		this.setAnimationEnabled(true);
+		
+		this.closeButton.getElement().setId("closeButton");	
+		VerticalPanel dialogVPanel = new VerticalPanel();
+		dialogVPanel.addStyleName("dialogVPanel");					
+		dialogVPanel.add(bodyHolder);		
+		dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
+		dialogVPanel.add(closeButton);
+		this.setWidget(dialogVPanel);
+		
+		// Add a handler to close the DialogBox
+		closeButton.addClickHandler(new DialogClickHandler(this));
+	    		
+	}
+	
+	public void show() {
+		this.center();
+		this.closeButton.setFocus(true);
+	}
+		
+	protected Button getCloseButton() {
+		return this.closeButton;
+	}
+
+	protected FocusWidget getReturnFocus() {
+		return this.returnFocus;
+	}
+
+	public HTML getBody() {
+		return this.body;
+	}
+
+	public void setBody(HTML body) {
+		this.body = body;
+		if (body != null) {
+			this.bodyHolder.clear();
+			this.bodyHolder.add(body);
+		}
+	}
+
+	public void setReturnFocus(FocusWidget returnFocus) {
+		this.returnFocus = returnFocus;
+	}
+
+	private class DialogClickHandler implements ClickHandler {
+
+		MugleDialogBox mdb = null;
+		
+		public DialogClickHandler(MugleDialogBox mdb) {
+			this.mdb = mdb;
+		}
+		
+		@Override
+		public void onClick(ClickEvent event) {
+			this.mdb.hide();
+			if (this.mdb.getReturnFocus() != null) {
+				this.mdb.getReturnFocus().setEnabled(true);
+				this.mdb.getReturnFocus().setFocus(true);
+			}
+		}
+				
+	}
+    
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/client/ui/MugleUiBuilder.java'
--- src/au/edu/unimelb/csse/mugle/client/ui/MugleUiBuilder.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ui/MugleUiBuilder.java	2011-04-29 07:02:27 +0000
@@ -3,17 +3,38 @@
 import java.util.Collection;
 import java.util.Iterator;
 
+import au.edu.unimelb.csse.mugle.client.AuthenticatedUser;
+import au.edu.unimelb.csse.mugle.client.platform.GuestService;
+import au.edu.unimelb.csse.mugle.client.platform.GuestServiceAsync;
+import au.edu.unimelb.csse.mugle.shared.model.Role;
+import au.edu.unimelb.csse.mugle.shared.model.User;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Overflow;
+import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.AbsolutePanel;
+import com.google.gwt.user.client.ui.Anchor;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.CellPanel;
+import com.google.gwt.user.client.ui.DockLayoutPanel;
 import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.StackPanel;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
 
 public class MugleUiBuilder {
 	
+	private static GuestServiceAsync guestService = GWT.create(GuestService.class);
+	
 	private MugleUiBuilder() { }
 	
 	public static CellPanel buildStringTable(Collection<Collection<String>> list, Collection<String> headers) {
@@ -140,5 +161,176 @@
 		if (handler != null) { b.addClickHandler(handler); }
 		panel.add(b);		
 	}
+	
+	public static <T extends Widget> T setId(T w, String id) {
+		w.getElement().setId(id);
+		return w;
+	}
+	
+	public static void arrangeUserPanel(Grid userPanel, AuthenticatedUser authuser) {
+	  
+	  DOM.getElementById("user-panel-title").appendChild((new HTML("<b>MUGLE</b>")).getElement());
+	  userPanel.setWidget(0, 0, new HTML("<b>" + authuser.getLoginInfo().getEmailAddress() + "</b>"));
+	  userPanel.setWidget(0, 1, new HTML("|"));
+	  userPanel.setWidget(0, 2, authuser.getSignOutLink());	  
+		  
+	}
+	  
+	public static void arrangeMainPanel(RootPanel mainPanel) {
+	  		
+	  AbsolutePanel container = setId(new AbsolutePanel(), "main-panel-container");
+	  mainPanel.add(container);
+	  
+	  AbsolutePanel inner = setId(new AbsolutePanel(), "main-panel-container-inner");
+	  inner.getElement().getStyle().setOverflow(Overflow.VISIBLE);
+	  container.add(inner);
+	  
+	  AbsolutePanel tp = setId(new AbsolutePanel(), "tools-panel");
+	  AbsolutePanel rp = setId(new AbsolutePanel(), "right-panel");
+	  AbsolutePanel rpi = setId(new AbsolutePanel(), "right-panel-inner");
+	  
+	  tp.add(buildToolsPanel());
+	  rpi.add(buildRightPanel());
+	  rp.add(rpi);
+	  
+	  inner.add(tp);
+	  inner.add(rp);	  
+	  
+	}
+		
+	private static Widget buildToolsPanel() {
+	
+	  // left panel
+	  final StackPanel sp = new StackPanel();
+	  sp.setWidth("100%");
+	  sp.getElement().getStyle().setFontSize(1, Unit.EM);
+	  
+	  System.out.println("test1");
+	  
+	  guestService.getCurrentUserDetails(new AsyncCallback<User>() {
+			
+			@Override
+			public void onSuccess(User result) {
+				
+				System.out.println("success");
+				  
+				Widget tmp = null;
+				if ((tmp = buildMyMugle(result)) != null) {
+					sp.add(tmp, "My Mugle", false);	 
+				}
+				if ((tmp = buildMuglePublic(result)) != null) {
+					sp.add(tmp, "Mugle Public", false);	 
+				}
+				if ((tmp = buildSettings(result)) != null) {
+					sp.add(tmp, "Settings", false);	 
+				}
+				if ((tmp = buildAdmin(result)) != null) {
+					sp.add(tmp, "Admin", false);	 
+				}	  
+				
+			}
+			
+			@Override
+			public void onFailure(Throwable caught) {
+				
+				// TODO Auto-generated method stub
+				System.out.println("fail");
+				  
+			}
+		
+	  });
+	  	  	  
+	  return sp;
+		
+	}
+	
+	private static Widget buildMyMugle(User user) {
+				
+		Role role = user.getRole();
+		
+		switch (role) {
+		
+			case ADMIN:
+			case DEVELOPER:
+				
+				VerticalPanel vp = new VerticalPanel();
+				vp.add(new Anchor("Promoted Games"));
+				vp.add(new Anchor("Games Gallery"));
+				vp.add(new Anchor("Promoted Games"));
+				return vp;
+				
+		}	
+	
+		return null;
+		
+	}
 
+	private static Widget buildMuglePublic(User user) {
+		
+		VerticalPanel vp = new VerticalPanel();
+		
+		vp.add(new Anchor("Promoted Games"));
+		vp.add(new Anchor("Games Gallery"));
+		
+		return vp;
+		
+	}
+	
+	private static Widget buildSettings(User user) {
+		
+		VerticalPanel vp = new VerticalPanel();
+		
+		vp.add(new Anchor("Settings 1"));
+		vp.add(new Anchor("Settings 2"));
+		
+		return vp;
+		
+	}
+	
+	private static Widget buildAdmin(User user) {
+		
+		Role role = user.getRole();
+		
+		switch (role) {
+		
+			case ADMIN:
+				
+				VerticalPanel vp = new VerticalPanel();
+				vp.add(new Anchor("Promoted Games"));
+				vp.add(new Anchor("Games Gallery"));
+				vp.add(new Anchor("Promoted Games"));
+				return vp;
+				
+		}	
+	
+		return null;
+		
+	}
+	
+	private static Widget buildRightPanel() {
+		
+		final HorizontalPanel panel = new HorizontalPanel();
+		
+		panel.add(new HTML("Under Construction!"));
+		
+		// debug
+		  GuestServiceAsync us = GWT.create(GuestService.class);
+		  us.getCurrentUserDetails(new AsyncCallback<User>() {
+			  
+				@Override
+				public void onSuccess(User result) {
+					panel.add(new HTML("NAME: "+result.getFullName()));
+				}
+				
+				@Override
+				public void onFailure(Throwable caught) {
+					panel.add(new HTML("FAILED!"));
+				}
+			
+		  });
+		
+		return panel;
+		
+	}
+	
 }

=== renamed file 'src/au/edu/unimelb/csse/mugle/server/LoginServiceImpl.java' => 'src/au/edu/unimelb/csse/mugle/server/ClientServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/LoginServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/ClientServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -20,12 +20,11 @@
 import com.google.appengine.api.users.UserService;
 import com.google.appengine.api.users.UserServiceFactory;
 import au.edu.unimelb.csse.mugle.client.LoginInfo;
-import au.edu.unimelb.csse.mugle.client.LoginService;
+import au.edu.unimelb.csse.mugle.client.ClientService;
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
 @SuppressWarnings("serial")
-public class LoginServiceImpl extends RemoteServiceServlet implements
-    LoginService {
+public class ClientServiceImpl extends RemoteServiceServlet implements ClientService {
 
   public LoginInfo login(String requestUri) {
     UserService userService = UserServiceFactory.getUserService();

=== modified file 'src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -31,26 +31,35 @@
 	  
 	  PersistenceManager pm = PMF.getManager();
 
+	  System.err.println("test1");
+	  
 	  if (pm == null) {
 		  return "Datastore populating failed!";
 	  }
 
+	  System.err.println("test2");
+	  
 	  UserData[] users = new UserData[] {	
 										new UserData("~matt", "Matt Giuca", "matt@xxxxxxxx", Role.ADMIN),
 										new UserData("~scott", "Scott Ritchie", "scott@xxxxxxxx", Role.ADMIN),
 										new UserData("~prageeth", "Prageeth Silva", "prageeth@xxxxxxxx", Role.ADMIN),
 										new UserData("~bob", "Bob", "bob@xxxxxxxx", Role.DEVELOPER),
 										new UserData("~jim", "Jim", "jim@xxxxxxxx", Role.DEVELOPER),
-										new UserData("~john", "John", "john@xxxxxxxx", Role.USER),
+										new UserData("~john", "John", "john@xxxxxxxx", Role.GUEST),
 										new UserData("~admin", "Admin", "admin@xxxxxxxx", Role.ADMIN),
-										new UserData("~dev", "Dev", "dev@xxxxxxxx", Role.DEVELOPER)
+										new UserData("~dev", "Dev", "dev@xxxxxxxx", Role.DEVELOPER),
+										new UserData("~test", "Test", "test@xxxxxxxxxxx", Role.DEVELOPER)
 									  };
 	  		
+	  System.err.println("test3");
+	  
 	  pm.makePersistentAll(users);
 	  pm.makePersistent(users[0]);
 	  pm.flush();
 	  pm.close();
 	
+	  System.err.println("test4");
+	  
 	  return "Data addition succeful!";
 
   }

=== added file 'src/au/edu/unimelb/csse/mugle/server/GameFileServer.java'
--- src/au/edu/unimelb/csse/mugle/server/GameFileServer.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/GameFileServer.java	2011-04-29 07:02:27 +0000
@@ -0,0 +1,94 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package au.edu.unimelb.csse.mugle.server;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import au.edu.unimelb.csse.mugle.server.model.GameFileData;
+import au.edu.unimelb.csse.mugle.server.model.GameFileGetter;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;
+
+import com.google.appengine.api.blobstore.BlobKey;
+import com.google.appengine.api.blobstore.BlobstoreService;
+import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
+
+@SuppressWarnings("serial")
+public class GameFileServer extends HttpServlet {
+	
+	public void doGet(HttpServletRequest req, HttpServletResponse res) 
+		throws IOException {
+		BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
+		
+		try {
+			BlobKey blobKey = getBlobKey(req);
+			blobstoreService.serve(blobKey, res);
+		} catch (GameFileNotExists e) {
+			//TODO: Construct a page with the error
+		}
+	}
+
+	private BlobKey getBlobKey(HttpServletRequest req) 
+		throws GameFileNotExists {
+		GameFileGetter gfg = new GameFileGetter();
+		String gameName = null;
+		String gameVersion = null;
+		String path = null;
+		
+		/* We need to parse the request to get the gameName, gameVersion
+		 * and path,
+		 * URLs are in the form /game/gameName/gameVersion/path
+		 */
+		String toParse = req.getRequestURI().split("/game/")[1];  // gets everything after /game/ in the URI
+		String[] splitted = toParse.split("/");
+		gameName = splitted[0];
+		gameVersion = splitted[1];
+		path = toParse.split(gameVersion)[1];
+		
+		try {
+			GameFileData file = gfg.getGameFile(gameName, gameVersion, path);
+			return file.getBlobKey();
+		} catch (GameNotExists e) {
+			throw new GameFileNotExists(gameName, gameVersion, path);
+		} catch (GameVersionNotExists e) {
+			throw new GameFileNotExists(gameName, gameVersion, path);
+		}
+	}
+
+	/*
+	public void doPost(HttpServletRequest req, HttpServletResponse res) 
+		throws ServletException, IOException{
+		BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
+		
+		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);
+		/* We want to iterate over each of the blobs, and create the 
+		 * appropriate datastore classes
+		 *
+		for(Entry<String, BlobKey> blobEntry : blobs.entrySet()){
+			
+		}
+		
+	} */
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/api/BadgeServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/BadgeServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/BadgeServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -58,8 +58,6 @@
 		UserAchievementGetter u = new UserAchievementGetter();
 		try {
 			return u.getUserAchievement(pm, name, gameToken);
-		} catch (UserNotExists e) {
-			throw new Error(e);
 		} catch (AchievementNotExists e) {
 			throw new BadgeError(e.getMessage());
 		}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/api/HighscoreServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/HighscoreServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/HighscoreServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -22,8 +22,6 @@
 import au.edu.unimelb.csse.mugle.server.model.UserGameProfileGetter;
 import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
 import au.edu.unimelb.csse.mugle.server.model.UserGameProfileData;
-import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
-
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
 import javax.jdo.PersistenceManager;
@@ -40,11 +38,7 @@
 	public int getHighScore(String gameToken) throws GameTokenError {
 		UserGameProfileGetter u = new UserGameProfileGetter();
 		UserGameProfileData ugp;
-		try {
-			ugp = u.getCurrentUserGameProfile(gameToken);
-		} catch (UserNotExists e) {
-			throw new Error(e);
-		}
+		ugp = u.getCurrentUserGameProfile(gameToken);
 		return ugp.getHighscore();
 	}
 
@@ -56,8 +50,6 @@
     		ugp = u.getCurrentUserGameProfile(pm, gameToken);
     		if (score > ugp.getHighscore())
     			ugp.setHighscore(score);
-    	} catch (UserNotExists e) {
-    		throw new Error(e);
     	} finally {
     		pm.close();
     	}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/api/KeyValueServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/KeyValueServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/KeyValueServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -25,8 +25,6 @@
 import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
 import au.edu.unimelb.csse.mugle.shared.api.KeyError;
 import au.edu.unimelb.csse.mugle.server.model.KeyValuePairData;
-import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;
-
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
 import javax.jdo.PersistenceManager;
@@ -48,8 +46,6 @@
     	try {
     		kvp = k.getKeyValuePair(pm, key, gameToken, true);
     		kvp.setValue(value);
-    	} catch (UserNotExists e) {
-    		throw new Error(e);
     	} catch (KeyError e) {
     		// XXX This should never happen (since we are using createIfNotFound=true)
     		throw new Error(e);
@@ -63,11 +59,7 @@
     {
     	KeyValuePairGetter k = new KeyValuePairGetter();
     	KeyValuePairData kvp;
-    	try {
-    		kvp = k.getKeyValuePair(key, gameToken);
-    	} catch (UserNotExists e) {
-    		throw new Error(e);
-    	}
+    	kvp = k.getKeyValuePair(key, gameToken);
         return (Serializable) kvp.getValue();
     }
 
@@ -77,8 +69,6 @@
     	KeyValuePairGetter k = new KeyValuePairGetter();
     	try {
     		k.getKeyValuePair(key, gameToken);
-    	} catch (UserNotExists e) {
-    		throw new Error(e);
     	} catch (KeyError e) {
     		return false;
     	}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/api/UserServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/UserServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/UserServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -19,8 +19,6 @@
 
 import au.edu.unimelb.csse.mugle.client.api.UserService;
 import au.edu.unimelb.csse.mugle.server.model.UserGetter;
-import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
-
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
 
@@ -34,25 +32,11 @@
 
 	public String getUserNickName() {
 		UserGetter u = new UserGetter();
-		try
-		{
-			return u.getCurrentUser().getUrlName();
-		}
-		catch (UserNotExists e)
-		{
-			throw new Error(e);
-		}
+		return u.getCurrentUser().getUrlName();
 	}
 	
 	public String getUserID() {
 		UserGetter u = new UserGetter();
-		try
-		{
-			return u.getCurrentUser().getGoogleID();
-		}
-		catch (UserNotExists e)
-		{
-			throw new Error(e);
-		}
+		return u.getCurrentUser().getGoogleID();
 	}
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/AchievementGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/AchievementGetter.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/AchievementGetter.java	2011-04-29 07:02:27 +0000
@@ -17,6 +17,8 @@
 
 package au.edu.unimelb.csse.mugle.server.model;
 
+import java.util.List;
+
 import javax.jdo.PersistenceManager;
 import javax.jdo.Query;
 
@@ -54,6 +56,7 @@
 	 * @throws GameTokenError
 	 * @throws AchievementNotExists
 	 */
+	@SuppressWarnings("unchecked")
 	public AchievementData getAchievement(PersistenceManager pm, String name, String gameToken) 
 			throws GameTokenError, AchievementNotExists {
 		GameGetter g = new GameGetter();
@@ -61,13 +64,13 @@
 		
 		Query q = pm.newQuery(AchievementData.class, "game == g && name == n");
 		q.declareParameters("GameData g, String n");
-		AchievementData achievement = (AchievementData) q.execute(curGame, name);
+		List<AchievementData> results = (List<AchievementData>) q.execute(curGame, name);
 
-		if (achievement == null) {
+		if (results.isEmpty()) {
 			throw new AchievementNotExists(curGame.getUrlName(), name);
 		}
 		
-		return achievement;
+		return results.get(0);
 	}
 
 	/**

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/DevTeamGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/DevTeamGetter.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/DevTeamGetter.java	2011-04-29 07:02:27 +0000
@@ -1,5 +1,7 @@
 package au.edu.unimelb.csse.mugle.server.model;
 
+import java.util.List;
+
 import javax.jdo.PersistenceManager;
 import javax.jdo.Query;
 
@@ -31,17 +33,17 @@
 	 * @return The DevTeamData
 	 * @throws DevTeamDataNotExists
 	 */
+	@SuppressWarnings("unchecked")
 	public DevTeamData getDevTeam(PersistenceManager pm, String name) throws DevTeamNotExists {
-		DevTeamData devTeam= null;
 		Query q = pm.newQuery(DevTeamData.class, "urlName == u");
 		q.declareParameters("String u");
-		devTeam = (DevTeamData) q.execute(name);
+		List<DevTeamData> results = (List<DevTeamData>) q.execute(name);
 		
-		if (devTeam == null) {
+		if (results.isEmpty()) {
 			throw new DevTeamNotExists(name);
 		}
 		
-		return devTeam;
+		return results.get(0);
 	}
 	/**
 	 * Gets the DevTeamDataData by its primary key - READ ONLY

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java	2011-04-29 07:02:27 +0000
@@ -17,9 +17,6 @@
 
 package au.edu.unimelb.csse.mugle.server.model;
 
-import java.util.HashSet;
-import java.util.Set;
-
 import javax.jdo.annotations.IdGeneratorStrategy;
 import javax.jdo.annotations.PersistenceCapable;
 import javax.jdo.annotations.Persistent;
@@ -54,17 +51,16 @@
 
 	@UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, useMethod=true, mappedBy="getBlobKeyString")
     @Persistent
-	private BlobKey blobKey; //key to the actual blob
+	private BlobKey blobKey; //key to the actual blob -- may be shared between GameFiles across versions
 	
-    // Many-to-many
-    @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, useMethod=true, mappedBy="keysToGameVersions")
+    @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="version")
     @Persistent
-	private Set<Key> versions; //Game versions this file belongs to
+	private GameVersionData version; //Game version this Gamefile belongs to
 
 
     // Constructors
     public GameFileData() {
-	    this.versions = new HashSet<Key>();
+	    this.version = null;
 	    this.setMimeType(null);
 	    this.setBlobKey(null);
 	    this.path = null;
@@ -101,8 +97,8 @@
 		return this.path;
 	}
 
-	public Set<Key> getGameVersions() {
-		return this.versions;
+	public GameVersionData getGameVersions() {
+		return this.version;
 	}
 
 
@@ -139,23 +135,6 @@
 	}
 */
 
-    public boolean addGameVersion(GameVersionData version) {
-        // many-to-many relationship
-        if (this.versions.add(version.getServerKey()))
-        {
-            return version.getGameFiles().add(this.getServerKey());
-        }
-        return false;
-	}
-
-    public boolean removeGameVersion(GameVersionData version) {
-        // many-to-many relationship
-        if (this.versions.remove(version.getServerKey()))
-        {
-            return version.getGameFiles().remove(this.getServerKey());
-        }
-        return false;
-	}
     
 /**  TODO: Pending Blob implementation for files
     public GameFileData(String path, GameVersion version, InputStream contents) {
@@ -183,10 +162,6 @@
 	public Class<GameFile> getClientClassType() {
 		return GameFile.class;
 	}
-	
-	public Set<Long> keysToGameVersions() {
-		return this.fetchClientObjects(GameVersionData.class, this.versions);
-	}
 
 	public String getBlobKeyString() {
 		return this.blobKey.getKeyString();

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java	2011-04-29 07:02:27 +0000
@@ -0,0 +1,116 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import java.util.List;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;
+
+public class GameFileGetter {
+	/**
+	 * Gets the GameFile by its Primary Key - READ ONLY
+	 * @param primaryKey
+	 * @return the GameFile - read only
+	 * @throws GameFileNotExists 
+	 */
+	public GameFileData getGameFile(Long primaryKey) throws GameFileNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getGameFile(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the GameFile by its Primary Key, for editing object in datastore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm the PersistenceManager
+	 * @param primaryKey
+	 * @return the GameFile
+	 * @throws GameFileNotExists 
+	 */
+	public GameFileData getGameFile(PersistenceManager pm, Long primaryKey) throws GameFileNotExists{
+		GameFileData gameFile = pm.getObjectById(GameFileData.class, primaryKey);
+		
+		if(gameFile == null) {
+			throw new GameFileNotExists(primaryKey);
+		}
+		
+		return gameFile;
+	}
+	
+	/**
+	 * Gets the GameFile by its path, version and game - READ ONLY
+	 * @param gameName the name of the game
+	 * @param gameVersion the version of the game
+	 * @param path the path of the file
+	 * @return the GameFile - read only
+	 * @throws GameNotExists
+	 * @throws GameVersionNotExists
+	 * @throws GameFileNotExists 
+	 */
+	public GameFileData getGameFile(String gameName, String gameVersion, String path) 
+		throws GameNotExists, GameVersionNotExists, GameFileNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getGameFile(pm, gameName, gameVersion, path);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the GameFile by its path, version and game, for editing object in datastore
+	 * PersistenceManager must be handled by the caller
+	 * @para pm The Persistence Manager
+	 * @param gameName the name of the game
+	 * @param gameVersion the version of the game
+	 * @param path the path of the file
+	 * @return the GameFile - read only
+	 * @throws GameNotExists
+	 * @throws GameVersionNotExists
+	 * @throws GameFileNotExists 
+	 */
+	
+
+	@SuppressWarnings("unchecked")
+	public GameFileData getGameFile(PersistenceManager pm, String gameName, String gameVersion, String path) 
+	 	throws GameFileNotExists, GameNotExists, GameVersionNotExists {
+		GameVersionGetter gvg = new GameVersionGetter();
+		GameVersionData gv = gvg.getGameVersion(pm, gameName, gameVersion);
+		
+		
+		Query q = pm.newQuery(GameFileData.class, "path == p && version == v");
+		q.declareParameters("String p, GameVersionData v");
+		List<GameFileData> results = (List<GameFileData>) q.execute(path, gv);
+
+		if (results.isEmpty()) {
+			throw new GameFileNotExists(gameName, gameVersion, path);
+		}
+		
+		return results.get(0);
+	}
+	
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameVersionData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameVersionData.java	2011-04-08 11:00:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameVersionData.java	2011-04-29 07:02:27 +0000
@@ -73,16 +73,15 @@
     @Element(dependent="true")
     private PromotedGameData promotedGame;
 
-    // Many-to-many
 	@UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, useMethod=true, mappedBy="keysToGameFiles")
-    @Persistent
-    private Set<Key> gameFiles;
+    @Persistent(mappedBy="version")
+    private Set<GameFileData> gameFiles;
 
     // Constructors
 	
 	public GameVersionData() {
 
-        this.gameFiles = new HashSet<Key>();
+        this.gameFiles = new HashSet<GameFileData>();
         this.promotedGame = null;
         this.game = null;
         //this.version = null;
@@ -98,7 +97,7 @@
 	 */
 	public GameVersionData(GameData game, String displayVersion, String fullName, String description) {
 
-        this.gameFiles = new HashSet<Key>();
+        this.gameFiles = new HashSet<GameFileData>();
 		this.promotedGame = null;
 		
 		/* To be unique, the Primary key must be made up of the ID of the UGP and 
@@ -142,7 +141,7 @@
 	 *  }
 	 */
 	
-    public Set<Key> getGameFiles() {
+    public Set<GameFileData> getGameFiles() {
         return this.gameFiles;
     }
 
@@ -175,31 +174,6 @@
 
     // TODO: setImage(Image image);
 
-
-    // Misc methods
-    
-    public boolean addGameFile(GameFileData file) {
-        // many-to-many relationship
-        if (this.gameFiles.add(file.getServerKey())) {
-            return file.getGameVersions().add(this.getServerKey());
-        }
-        return false;
-    }
-
-    public boolean removeGameFile(GameFileData file) {
-        // many-to-many relationship
-        if (this.gameFiles.remove(file.getServerKey())) {
-            if  (file.getGameVersions().remove(this.getServerKey()))
-            {
-                // delete file if this is the only reference
-                if (file.getGameVersions().isEmpty()) {
-                    // TODO: delete the file off the database
-                    //       is this automatically done by GAE?
-                }
-            }
-        }
-        return false;
-    }
         
     @Override
     public Key getServerKey() {
@@ -225,9 +199,9 @@
 	public Class<GameVersion> getClientClassType() {
 		return GameVersion.class;
 	}
-	
+		
 	public Set<String> keysToGameFiles() {
-		return this.fetchClientObjects(GameFileData.class, this.gameFiles);
+		return this.fetchClientObjects(this.gameFiles);
 	}
 
 }

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java	2011-04-29 07:02:27 +0000
@@ -0,0 +1,107 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import java.util.List;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;
+
+public class GameVersionGetter {
+	/**
+	 * Gets the GameVersion by its Primary Key - READ ONLY
+	 * @param primaryKey
+	 * @return the GameVersion - read only
+	 * @throws GameVersionNotExists 
+	 */
+	public GameVersionData getGameVersion(Long primaryKey) throws GameVersionNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getGameVersion(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the GameVersion by its Primary Key, for editing object in datstore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm the PersistenceManager
+	 * @param primaryKey
+	 * @return the GameVersion
+	 * @throws GameVersionNotExists 
+	 */
+	public GameVersionData getGameVersion(PersistenceManager pm, Long primaryKey) throws GameVersionNotExists{
+		GameVersionData gameversion = pm.getObjectById(GameVersionData.class, primaryKey);
+		
+		if(gameversion == null) {
+			throw new GameVersionNotExists(primaryKey);
+		}
+		
+		return gameversion;
+	}
+	
+	/**
+	 * Gets the GameVersion by its display Version - READ ONLY
+	 * @param gameName the name of the Game that it belongs to
+	 * @param dispVersion the displayed version number
+	 * @return the GameVersion - read only
+	 * @throws GameVersionNotExists 
+	 */
+	public GameVersionData getGameVersion(String gameName, String dispVersion) 
+		throws GameVersionNotExists, GameNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getGameVersion(pm, gameName, dispVersion);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the GameVersion by its display name, for editing object in datastore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm the PersistenceManager
+	 * @param gameName the name of the Game that it belongs to
+	 * @param dispVersion the displayed version number
+	 * @return the GameVersion
+	 * @throws GameVersionNotExists
+	 * @throws GameNotExists 
+	 */
+	@SuppressWarnings("unchecked")
+	public GameVersionData getGameVersion(PersistenceManager pm, String gameName, String dispVersion) 
+		throws GameVersionNotExists, GameNotExists{
+		GameGetter g = new GameGetter();
+		GameData game = g.getGame(pm, gameName);
+		
+		Query q = pm.newQuery(GameData.class, "dispVersion == d && game == g");
+		q.declareParameters("String d, GameData g");
+		List<GameVersionData> results = (List<GameVersionData>) q.execute(dispVersion, game);
+		
+		if(results.isEmpty()) {
+			throw new GameNotExists(dispVersion, game.getUrlName());
+		}
+		
+		return results.get(0);
+	}
+}
+

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairGetter.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairGetter.java	2011-04-29 07:02:27 +0000
@@ -16,6 +16,8 @@
  */
 package au.edu.unimelb.csse.mugle.server.model;
 
+import java.util.List;
+
 import javax.jdo.PersistenceManager;
 import javax.jdo.Query;
 
@@ -37,7 +39,7 @@
 	 * @throws KeyError
 	 */
 	public KeyValuePairData getKeyValuePair(String key, String gameToken) 
-		throws UserNotExists, GameTokenError, KeyError {
+		throws GameTokenError, KeyError {
 		PersistenceManager pm = PMF.getManager();
 		try {
 			return getKeyValuePair(pm, key, gameToken);
@@ -60,7 +62,7 @@
 	 * @throws KeyError
 	 */
 	public KeyValuePairData getKeyValuePair(PersistenceManager pm, String key, String gameToken) 
-			throws UserNotExists, GameTokenError, KeyError {
+			throws GameTokenError, KeyError {
 		return getKeyValuePair(pm, key, gameToken, false);
 	}
 
@@ -79,30 +81,32 @@
 	 * @throws GameTokenError
 	 * @throws KeyError
 	 */
+	@SuppressWarnings("unchecked")
 	public KeyValuePairData getKeyValuePair(PersistenceManager pm, String key, String gameToken, boolean createIfNotFound) 
-			throws UserNotExists, GameTokenError, KeyError {
+			throws GameTokenError, KeyError {
 		UserGameProfileGetter u = new UserGameProfileGetter();
 		UserGameProfileData ugp = u.getCurrentUserGameProfile(pm, gameToken);
 		
-		KeyValuePairData kvp = null;
-		
 		Query q = pm.newQuery(KeyValuePairData.class, "key == k && ugp == u");
 		q.declareParameters("String k, UserGameProfileData u");
-		kvp = (KeyValuePairData) q.execute(key, ugp);
+		List<KeyValuePairData> results = (List<KeyValuePairData>) q.execute(key, ugp);
 		
 		/* If the key value pair isn't found - we want to create one if 
 		 * createIfNotFound is set to true
 		 */
-		if (kvp == null) {
+		if (results.isEmpty()) {
 			if (createIfNotFound) {
-				kvp = new KeyValuePairData();
+				KeyValuePairData kvp = new KeyValuePairData();
 				kvp.setKey(key);
 				kvp.setUserGameProfile(ugp);
+				pm.makePersistent(kvp);
+				
+				results.add(kvp);
 			} else {
 				throw new KeyError(key);
 			}
 		}
 		
-		return kvp;
+		return results.get(0);
 	}
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/ModelDataClass.java'
--- src/au/edu/unimelb/csse/mugle/server/model/ModelDataClass.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/ModelDataClass.java	2011-04-29 07:02:27 +0000
@@ -98,5 +98,17 @@
 		return list;
 		
 	}
+	
+	protected <A, C extends ModelDataClass<A,?,C>> Set<A> fetchClientObjects(Set<C> dataList) {
+
+		HashSet<A> list = new HashSet<A>(dataList.size());
+		
+		for (C cls : dataList) {
+			list.add(cls.getPrimaryKey());
+		}		
+		
+		return list;
+		
+	}
 	    
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/ModelWrapper.java'
--- src/au/edu/unimelb/csse/mugle/server/model/ModelWrapper.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/ModelWrapper.java	2011-04-29 07:02:27 +0000
@@ -20,11 +20,17 @@
 import java.lang.reflect.*;
 
 import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import com.google.appengine.api.users.User;
+import com.google.appengine.api.users.UserService;
+import com.google.appengine.api.users.UserServiceFactory;
 
 import au.edu.unimelb.csse.mugle.server.model.annotations.*;
 import au.edu.unimelb.csse.mugle.server.PMF;
 import au.edu.unimelb.csse.mugle.shared.model.*;
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.ServerException;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserPrivilegeException;
 
 public final class ModelWrapper {
@@ -58,7 +64,7 @@
             case DEVELOPER:
                 return (role == Role.ADMIN || role == Role.DEVELOPER);
                 
-            case USER:
+            case GUEST:
                 return true;
                 
             default:
@@ -168,8 +174,8 @@
      * @throws IllegalArgumentException 
      * @throws NoSuchMethodException 
      */
-    public static <T extends ModelClass<?,T>, U extends ModelDataClass<?,T,U>, B extends ModelClass<?,B>, C extends ModelDataClass<?,B,C>>
-        T getClientClone(U obj, Role role, ClientView cview)
+    public static <T extends ModelClass<?,T>, U extends ModelDataClass<?,T,U>>
+    	T getClientClone(U obj, Role role, ClientView cview)
     		throws 	InstantiationException, IllegalAccessException, SecurityException,
     				NoSuchFieldException, NoSuchMethodException, IllegalArgumentException,
     				InvocationTargetException {
@@ -230,6 +236,37 @@
         return stripped;
         
     }
+    
+    public static <T extends ModelClass<?,T>, U extends ModelDataClass<?,T,U>>
+		T getClientCloneSafe(U obj, Role role, ClientView cview) 
+    		throws ServerException {
+    	
+    	try {
+			return getClientClone(obj, role, cview);
+		} catch (SecurityException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		} catch (IllegalArgumentException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		} catch (InstantiationException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		} catch (IllegalAccessException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		} catch (NoSuchFieldException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		} catch (NoSuchMethodException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		} catch (InvocationTargetException e) {
+			e.printStackTrace();
+			throw new ServerException(e.getMessage());
+		}
+    	
+    }
         
     /**
      * Converts a ModelClass instance to its counter part ModelDataClass,
@@ -655,10 +692,22 @@
 
 
     // Current login privileges
-    
+      
     public static Role getCurrentRole() {
-        // TODO needs to actually check login details and return the actual role.
-        return Role.USER;
+    	
+    	UserService userService = UserServiceFactory.getUserService();
+        User user = userService.getCurrentUser();
+        
+        PersistenceManager pm = PMF.getManager();
+        if (pm == null) { return Role.NONE; }
+        
+        Query q = pm.newQuery(UserData.class, "googleID == i || email == e");
+		q.declareParameters("String i, String e");
+		UserData ud = (UserData) q.execute(user.getUserId(), user.getEmail());
+		if (ud == null) { return Role.NONE; }
+        
+		return ud.getRole();
+		
     }
     
     public static ClientView getCurrentClientView() {

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/UserAchievementGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserAchievementGetter.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserAchievementGetter.java	2011-04-29 07:02:27 +0000
@@ -17,6 +17,8 @@
 
 package au.edu.unimelb.csse.mugle.server.model;
 
+import java.util.List;
+
 import javax.jdo.PersistenceManager;
 import javax.jdo.Query;
 
@@ -37,7 +39,7 @@
 	 * @throws AchievementNotExists
 	 */
 	public UserAchievementData getUserAchievement(String achievementName, String gameToken) 
-			throws UserNotExists, GameTokenError, AchievementNotExists {
+			throws GameTokenError, AchievementNotExists {
 		PersistenceManager pm = PMF.getManager();
 		try {
 			return getUserAchievement(pm, achievementName, gameToken);
@@ -58,8 +60,9 @@
 	 * @throws GameTokenError
 	 * @throws AchievementNotExists
 	 */
+	@SuppressWarnings("unchecked")
 	public UserAchievementData getUserAchievement(PersistenceManager pm, String achievementName, String gameToken) 
-			throws UserNotExists, GameTokenError, AchievementNotExists {
+			throws GameTokenError, AchievementNotExists {
 		UserGameProfileGetter u = new UserGameProfileGetter();
 		AchievementGetter a = new AchievementGetter();
 		
@@ -68,17 +71,20 @@
 		
 		Query q = pm.newQuery(UserAchievementData.class, "ugp == u && achievement == a");
 		q.declareParameters("UserGameProfileData ugp, AchievementData a");
-		UserAchievementData ua = (UserAchievementData) q.execute(ugp, achievement);
+		List<UserAchievementData> results = (List<UserAchievementData>) q.execute(ugp, achievement);
 		
 		/* If we can't find the UserAchievement it makes sense to create one
 		 * if the UserGameProfile and the Achievement exist.
 		 */
-		if (ua == null) {
-			ua = new UserAchievementData();
+		if (results.isEmpty()) {
+			UserAchievementData ua = new UserAchievementData();
 			ua.setAchievement(achievement);
 			ua.setUserGameProfile(ugp);
+			pm.makePersistent(ua);
+			
+			results.add(ua);
 		}
 		
-		return ua;
+		return results.get(0);
 	}
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/UserGameProfileGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserGameProfileGetter.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserGameProfileGetter.java	2011-04-29 07:02:27 +0000
@@ -17,6 +17,8 @@
 
 package au.edu.unimelb.csse.mugle.server.model;
 
+import java.util.List;
+
 import javax.jdo.PersistenceManager;
 import javax.jdo.Query;
 
@@ -68,7 +70,7 @@
 	 * @throws GameTokenError
 	 */
 	public UserGameProfileData getCurrentUserGameProfile(String gameToken) 
-				throws UserNotExists, GameTokenError {
+				throws GameTokenError {
 		PersistenceManager pm = PMF.getManager();
 		try {
 			return getCurrentUserGameProfile(pm, gameToken);
@@ -87,27 +89,31 @@
 	 * @throws UserNotExists
 	 * @throws GameTokenError
 	 */
+	@SuppressWarnings("unchecked")
 	public UserGameProfileData getCurrentUserGameProfile(PersistenceManager pm, String gameToken) 
-				throws UserNotExists, GameTokenError {
+				throws GameTokenError {
 		UserGetter u = new UserGetter();
 		GameGetter g = new GameGetter();
 		UserData curUser = u.getCurrentUser(pm);
 		GameData curGame = g.getCurrentGame(pm, gameToken);
-		UserGameProfileData ugp = null;
+
 		
 		Query q = pm.newQuery(UserGameProfileData.class, "user == u && game == g");
 		q.declareParameters("UserData u, GameData g");
-		ugp = (UserGameProfileData) q.execute(curUser, curGame);
+		List<UserGameProfileData> results = (List<UserGameProfileData>) q.execute(curUser, curGame);
 		
 		/* If we can't find the UserGameProfile it makes sense to create one
 		 * if the user is playing the game
 		 */
-		if (ugp == null) {
-			ugp = new UserGameProfileData();
+		if (results.isEmpty()) {
+			UserGameProfileData ugp = new UserGameProfileData();
 			ugp.setGame(curGame);
 			ugp.setUser(curUser);
+			pm.makePersistent(ugp);
+			
+			results.add(ugp);
 		}
-		return ugp;
+		return results.get(0);
 	}
 
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/UserGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserGetter.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserGetter.java	2011-04-29 07:02:27 +0000
@@ -20,9 +20,12 @@
 import javax.jdo.PersistenceManager;
 import javax.jdo.Query;
 
+import java.util.List;
+
 import au.edu.unimelb.csse.mugle.client.LoginInfo;
-import au.edu.unimelb.csse.mugle.server.LoginServiceImpl;
+import au.edu.unimelb.csse.mugle.server.ClientServiceImpl;
 import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.model.Role;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
 
 public class UserGetter {
@@ -82,16 +85,17 @@
 	 * @return The User class
 	 * @throws UserNotExists
 	 */
+	@SuppressWarnings("unchecked")
 	public UserData getUserByName(PersistenceManager pm, String name) throws UserNotExists {
 		Query q = pm.newQuery(UserData.class, "urlName == u");
 		q.declareParameters("String u");
-		UserData u = (UserData) q.execute(name);
+		List<UserData> results = (List<UserData>) q.execute(name);
 		
-		if (u == null) {
+		if (results.isEmpty()) {
 			throw new UserNotExists(name);
 		}
 		
-		return u;
+		return results.get(0);
 	}
 	
 	/**
@@ -117,24 +121,24 @@
 	 * @return The User class
 	 * @throws UserNotExists
 	 */
+	@SuppressWarnings("unchecked")
 	public UserData getUser(PersistenceManager pm, String googleID) throws UserNotExists {
 		Query q = pm.newQuery(UserData.class, "googleID == u");
 		q.declareParameters("String u");
-		UserData u = (UserData) q.execute(googleID);
+		List<UserData> results = (List<UserData>) q.execute(googleID);
 		
-		if (u == null) {
+		if (results.isEmpty()) {
 			throw new UserNotExists(googleID);
 		}
 		
-		return u;
+		return results.get(0);
 	}
 	
 	/**
 	 * Gets the currently logged in User - READ ONLY
 	 * @return the User - read only
-	 * @throws UserNotExists
 	 */
-	public UserData getCurrentUser() throws UserNotExists {
+	public UserData getCurrentUser() {
 		PersistenceManager pm = PMF.getManager();
 		try {
 			return getCurrentUser(pm);
@@ -144,17 +148,35 @@
 	}
 	
 	/**
-	 * Gets the currently logged in User, for editing object in datstore
+	 * Gets the currently logged in User, for editing object in datastore
+	 * Creates a new user if the currently logged in user is not found
 	 * Persistence Manager must be handled by the caller
 	 * @param pm The Persistence Manager
 	 * @return the current User
-	 * @throws UserNotExists
 	 */
-	public UserData getCurrentUser(PersistenceManager pm) throws UserNotExists {
-    	LoginServiceImpl loginService = new LoginServiceImpl();
+	public UserData getCurrentUser(PersistenceManager pm) {
+		ClientServiceImpl loginService = new ClientServiceImpl();
     	LoginInfo loginInfo = loginService.getCurrentUser();
     	
-    	return getUser(pm, loginInfo.getUserID());
+    	try {
+    		return getUser(pm, loginInfo.getUserID());
+    	} catch (UserNotExists e) {
+    		UserData newUser = new UserData();
+    		
+    		//Creates the new user using the info provided by google
+    		newUser.setGoogleID(loginInfo.getUserID());
+    		newUser.setUrlName(loginInfo.getNickname());
+    		newUser.setFullName(loginInfo.getNickname());
+    		newUser.setEmail(loginInfo.getEmailAddress());
+    		newUser.setRole(Role.DEVELOPER);
+    		newUser.setActive(true);
+    		
+    		//Stores the new user in the datastore
+    		pm.makePersistent(newUser);
+    		
+    		return newUser;
+    	}
+    	
 	}
 
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/annotations/UserLevel.java'
--- src/au/edu/unimelb/csse/mugle/server/model/annotations/UserLevel.java	2011-03-31 09:56:28 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/annotations/UserLevel.java	2011-04-29 07:02:27 +0000
@@ -27,8 +27,8 @@
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface UserLevel {
-    Role privateView() default Role.USER;
-    Role publicView() default Role.USER;
+    Role privateView() default Role.GUEST;
+    Role publicView() default Role.GUEST;
     boolean useMethod() default false;
     boolean mapField() default true;
     String mappedBy() default "";

=== modified file 'src/au/edu/unimelb/csse/mugle/server/platform/DeveloperServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/platform/DeveloperServiceImpl.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/server/platform/DeveloperServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -23,7 +23,7 @@
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;
 
 @SuppressWarnings("serial")
-public class DeveloperServiceImpl extends UserServiceImpl implements DeveloperService {
+public class DeveloperServiceImpl extends GuestServiceImpl implements DeveloperService {
 
     @Override
     public GameVersion addGameVersion(GameVersion gameVersion)

=== renamed file 'src/au/edu/unimelb/csse/mugle/server/platform/UserServiceImpl.java' => 'src/au/edu/unimelb/csse/mugle/server/platform/GuestServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/platform/UserServiceImpl.java	2011-03-26 14:01:07 +0000
+++ src/au/edu/unimelb/csse/mugle/server/platform/GuestServiceImpl.java	2011-04-29 07:02:27 +0000
@@ -19,9 +19,21 @@
 
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
-import au.edu.unimelb.csse.mugle.client.platform.UserService;
+import au.edu.unimelb.csse.mugle.client.platform.GuestService;
+import au.edu.unimelb.csse.mugle.server.model.*;
+import au.edu.unimelb.csse.mugle.shared.model.*;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.ServerException;
 
 @SuppressWarnings("serial")
-public class UserServiceImpl extends RemoteServiceServlet implements UserService {
+public class GuestServiceImpl extends RemoteServiceServlet implements GuestService {
+
+	@Override
+	public User getCurrentUserDetails() throws ServerException {
+		
+		UserGetter u = new UserGetter();
+		UserData ud = u.getCurrentUser();
+		
+		return ModelWrapper.getClientCloneSafe(ud, ud.getRole(), ClientView.PRIVATE);
+	}
 
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/DataServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/DataServiceAsync.java	2011-04-08 09:52:55 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/DataServiceAsync.java	2011-04-29 07:02:27 +0000
@@ -26,7 +26,7 @@
 
 	void fetchDevTeams(Set<Long> keys, AsyncCallback<Set<DevTeam>> callback);
 
-	void fetchGameFiles(Set<String> keys, AsyncCallback<Set<GameFile>> callback);
+	void fetchGameFiles(Set<Long> keys, AsyncCallback<Set<GameFile>> callback);
 
 	void fetchGames(Set<Long> keys, AsyncCallback<Set<Game>> callback);
 

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/GameFile.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/GameFile.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/GameFile.java	2011-04-29 07:02:27 +0000
@@ -17,10 +17,6 @@
 
 package au.edu.unimelb.csse.mugle.shared.model;
 
-import java.util.Set;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
 @MugleDataWrapper
@@ -36,11 +32,11 @@
 
 	private String blobKey; //key to the actual blob
 	
-	private Set<Long> gameVersionKeys; //Game versions this file belongs to
+	private GameVersion version; //Game versions this file belongs to
 
     // Constructors
 	public GameFile() {
-	    this.gameVersionKeys = null;
+	    this.version = null;
 	    this.mimeType = null;
 	    this.blobKey = null;
 	    this.path = null;
@@ -52,12 +48,8 @@
 		return this.path;
 	}
 
-	public Set<Long> getGameVersionKeys() {
-		return this.gameVersionKeys;
-	}
-	
-	public void fetchGameVersions(AsyncCallback<Set<GameVersion>> callback) {
-		DataProvider.getService().fetchGameVersions(this.gameVersionKeys, callback);
+	public GameVersion getGameVersionKeys() {
+		return this.version;
 	}
 	
 	@Override
@@ -71,8 +63,8 @@
         this.path = path;
     }
 
-	public void setGameVersionKeys(Set<Long> versions) {
-		this.gameVersionKeys = versions;
+	public void setGameVersionKeys(GameVersion version) {
+		this.version = version;
 	}
 
 	public void setMimeType(String mimeType) {

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/GameVersion.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/GameVersion.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/GameVersion.java	2011-04-29 07:02:27 +0000
@@ -49,7 +49,7 @@
 
     private PromotedGame promotedGame;
 
-    private Set<String> gameFileKeys;
+    private Set<Long> gameFileKeys;
 
     // Constructors
 	
@@ -95,7 +95,7 @@
         return this.promotedGame;
     }
 	
-    public Set<String> getGameFileKeys() {
+    public Set<Long> getGameFileKeys() {
         return this.gameFileKeys;
     }
     
@@ -130,7 +130,7 @@
         this.promotedGame = promotedGame;
     }
 
-	public void setGameFileKeys(Set<String> gameFiles) {
+	public void setGameFileKeys(Set<Long> gameFiles) {
 		this.gameFileKeys = gameFiles;
 	}       
     

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/ModelClass.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/ModelClass.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/ModelClass.java	2011-04-29 07:02:27 +0000
@@ -19,17 +19,20 @@
 
 import java.io.Serializable;
 
+import com.google.gwt.user.client.rpc.IsSerializable;
+
 /**
  * Base model class that should be extended by all shared model class entities.
  * @param <S> The primary key type
  * @param <T> The client class type (usually self type)
  * @param <U> The server class type (usually the data counter part type)
  */
-@SuppressWarnings("serial")
-public abstract class ModelClass<S,T extends ModelClass<S,?>> implements Serializable {
-	
 
-    private S id = null;
+public abstract class ModelClass<S,T extends ModelClass<S,?>> implements Serializable, IsSerializable {
+	
+	private static final long serialVersionUID = -2271296896173152105L;
+	
+	private S id = null;
     
     /**
      * The getter for the primary key

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/Role.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/Role.java	2011-03-10 04:56:07 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/Role.java	2011-04-29 07:02:27 +0000
@@ -18,5 +18,5 @@
 package au.edu.unimelb.csse.mugle.shared.model;
 
 public enum Role {	
-	USER, DEVELOPER, ADMIN;	
+	NONE, GUEST, DEVELOPER, ADMIN;	
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/annotations/MugleDataWrapper.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/annotations/MugleDataWrapper.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/annotations/MugleDataWrapper.java	2011-04-29 07:02:27 +0000
@@ -31,10 +31,10 @@
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface MugleDataWrapper {
-	Role privateAdd() default Role.USER;
-	Role privateUpdate() default Role.USER;
-	Role privateRemove() default Role.USER;
-	Role publicAdd() default Role.USER;
-	Role publicUpdate() default Role.USER;
-	Role publicRemove() default Role.USER;
+	Role privateAdd() default Role.GUEST;
+	Role privateUpdate() default Role.GUEST;
+	Role privateRemove() default Role.GUEST;
+	Role publicAdd() default Role.GUEST;
+	Role publicUpdate() default Role.GUEST;
+	Role publicRemove() default Role.GUEST;
 }

=== added file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java	2011-04-29 07:02:27 +0000
@@ -0,0 +1,22 @@
+package au.edu.unimelb.csse.mugle.shared.platform.exceptions;
+
+public class GameFileNotExists extends Exception{
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 728229816363066583L;
+
+	public GameFileNotExists() {
+		super();
+	}
+	
+	public GameFileNotExists(Long primaryKey) {
+		super("The gameFile with primary key " + primaryKey.toString() + " does not exist");
+	}
+	
+	public GameFileNotExists(String gameName, String gameVersion, String path) {
+		super("The file " + path + " for the version " + gameVersion + 
+						" of " + gameName + " does not Exist.");
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java	2011-04-29 07:02:27 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java	2011-04-29 07:02:27 +0000
@@ -46,5 +46,9 @@
     public GameNotExists(Long id) {
         super("Game with ID '" + id + "' does not exist.");
     }
+
+	public GameNotExists(String dispVersion, String gameName) {
+		super("Game " + gameName + " does not have version " + dispVersion + ".");
+	}
     
 }
\ No newline at end of file

=== added file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/ServerException.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/ServerException.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/ServerException.java	2011-04-29 07:02:27 +0000
@@ -0,0 +1,34 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.platform.exceptions;
+
+import java.io.Serializable;
+
+public class ServerException extends Exception implements Serializable {
+
+	private static final long serialVersionUID = -5641489622969459865L;
+
+	public ServerException() {
+		super();
+	}
+	
+	public ServerException(final String msg) {
+		super(msg);
+	}
+
+}

=== modified file 'war/Mugle.css'
--- war/Mugle.css	2011-04-08 11:51:46 +0000
+++ war/Mugle.css	2011-04-29 07:02:27 +0000
@@ -1,7 +1,8 @@
 /** Add css rules here for your application. */
 
 body {
-  min-width: 600px;
+  min-width: 1000px;
+  background: white;
 }
 
 h1 {
@@ -12,54 +13,103 @@
   text-align: center;
 }
 
+#container {
+   min-height: 100%;
+   width: 100%;
+   position: relative;
+   float: left;
+}
+
+/* HEADER */
+
 #user-panel-container {
   height: 25px;
   border-bottom: #b6bac0 1px solid;
   margin: 0;
-  padding: 0 5px;
-  background-color: #ffffff;
+  padding: 0;
+  background: white;
+  position: relative;
+  float: left;
+  width: 100%;
 }
 
 #user-panel-title {
   height: 25px;
   width:50px;
-  margin: 0;
+  margin: 5px 5px 0 5px;
   padding: 0;
   float: left;
-  padding-top: 5px;
 }
 
 #user-panel-holder {
   height: 25px;
   margin: 0;
-  padding: 0;
+  padding: 0 5px;
   float: right;
   text-align: : right;
 }
 
+/* BODY */
+
 #main-panel {
-  text-align: center;
-  min-height:500px;
-  height:100%;
-  background-color: #ffffff;
-}
-
-table#tools-panel {
-  position: absolute;
-  left: 0;
-  top: 25px;
-  width: 250px;
-}
-
-#error-display {
-  background-color: #ffffff;
+  padding:0;
+  padding-bottom:100px;   /* Height of the footer */
+  background:white;
+  position: relative;
+  float: left;
   width: 100%;
 }
 
+#main-panel-container {
+  clear:left;
+  float:left;
+  width:100%;
+  overflow:hidden;
+  background:white; /* column 2 background colour */  
+}
+
+#main-panel-container-inner {	
+  float:left;
+  width:100%;
+  position:relative;
+  right:80%;
+  background:white; /* column 1 background colour */
+  border-right: #b6bac0 1px solid;
+  min-height: 500px;
+}
+
+#tools-panel {
+  float:left;
+  width:20%;
+  position:relative;
+  left:80%;
+  overflow:hidden;
+}
+
+#right-panel {
+  float:left;
+  width:80%;
+  position:relative;
+  left:80%;
+  overflow:hidden;
+}
+
+#right-panel-inner {
+  float:left;
+  width:100%;
+  margin:5px;
+}
+
+/* FOOTER */
+
 #footer {
+  position:absolute;
+  /*position: relative;*/
+  width: 100%;
+  bottom: 0;
   text-align: center;
   background: #01467d url('img/footer-top.png') top center repeat-x;
-  height: 100px;
+  height: 100px;  /* Height of the footer */
 }
 
 #footer-banner {
@@ -80,6 +130,11 @@
   float: right;	
 }
 
+#error-display {
+  background-color: #ffffff;
+  width: 100%;
+}
+
 .sendButton {
   display: block;
   font-size: 16pt;

=== modified file 'war/Mugle.html'
--- war/Mugle.html	2011-04-02 04:18:20 +0000
+++ war/Mugle.html	2011-04-29 07:02:27 +0000
@@ -30,7 +30,10 @@
     <!-- Consider inlining CSS to reduce the number of requested files -->
     <!--                                                               -->
     <link type="text/css" rel="stylesheet" href="Mugle.css">
-
+	<!--[if lte IE 6]>
+	<link type="text/css" rel="stylesheet" href="MugleIE6.css">	
+	<![endif]-->
+	
     <!--                                           -->
     <!-- Any title is fine                         -->
     <!--                                           -->
@@ -62,28 +65,34 @@
       </div>
     </noscript>
     
-    <div id="user-panel-container">
-    	<div id="user-panel-title"></div>
-    	<div id="user-panel-holder"></div>
-    </div>
-    
-    <div id="main-panel"></div>
-    
-    <table id="error-display" align="center">	  
-	  <tr>
-	    <td colspan="2" style="color:red;" id="errorLabelContainer"></td>
-	  </tr>
-	</table>
-    
-    <div id="footer">
-	  <div id="footer-banner">
-	  	<a id="unimelb-logo" href="http://www.unimelb.edu.au"; title="University of Melbourne" target="_blank">
-	  		<img src="img/unimelb.png"></img>
-	  	</a>
-	  	<div id="footer-content">
-	    	<!-- Add copyright details, etc -->
-	    </div>
-	  </div>
+    <div id="container">
+    
+	    <div id="user-panel-container">
+	    	<div id="user-panel-title"></div>
+	    	<div id="user-panel-holder"></div>
+	    </div>
+	    
+	    <div id="main-panel">
+			<!-- All GWT elements gets added/removed in here. -->    
+	    </div>
+	    
+	    <table id="error-display" align="center">	  
+			  <tr>
+			    <td colspan="2" style="color:red;" id="errorLabelContainer"></td>
+			  </tr>
+		</table>
+	    	    
+	    <div id="footer">
+		  <div id="footer-banner">
+		  	<a id="unimelb-logo" href="http://www.unimelb.edu.au"; title="University of Melbourne" target="_blank">
+		  		<img src="img/unimelb.png"></img>
+		  	</a>
+		  	<div id="footer-content">
+		    	<!-- Add copyright details, etc -->
+		    </div>
+		  </div>
+		</div>
+		
 	</div>
     
   </body>

=== added file 'war/MugleIE6.css'
--- war/MugleIE6.css	1970-01-01 00:00:00 +0000
+++ war/MugleIE6.css	2011-04-29 07:02:27 +0000
@@ -0,0 +1,3 @@
+#container {
+   height:100%;
+}
\ No newline at end of file

=== modified file 'war/WEB-INF/web.xml'
--- war/WEB-INF/web.xml	2011-04-29 07:02:27 +0000
+++ war/WEB-INF/web.xml	2011-04-29 07:02:27 +0000
@@ -12,13 +12,13 @@
   <!-- Servlets -->
   
   <servlet>
-    <servlet-name>LoginService</servlet-name>
-    <servlet-class>au.edu.unimelb.csse.mugle.server.LoginServiceImpl</servlet-class>
+    <servlet-name>ClientService</servlet-name>
+    <servlet-class>au.edu.unimelb.csse.mugle.server.ClientServiceImpl</servlet-class>
   </servlet>
 
   <servlet-mapping>
-    <servlet-name>LoginService</servlet-name>
-    <url-pattern>/mugle/login</url-pattern>
+    <servlet-name>ClientService</servlet-name>
+    <url-pattern>/mugle/client</url-pattern>
   </servlet-mapping>
       
   <servlet>
@@ -52,13 +52,13 @@
   </servlet-mapping>
   
   <servlet>
-    <servlet-name>UserService</servlet-name>
-    <servlet-class>au.edu.unimelb.csse.mugle.server.platform.UserServiceImpl</servlet-class>
+    <servlet-name>GuestService</servlet-name>
+    <servlet-class>au.edu.unimelb.csse.mugle.server.platform.GuestServiceImpl</servlet-class>
   </servlet>
 
   <servlet-mapping>
-    <servlet-name>UserService</servlet-name>
-    <url-pattern>/mugle/users</url-pattern>
+    <servlet-name>GuestService</servlet-name>
+    <url-pattern>/mugle/guests</url-pattern>
   </servlet-mapping>
   
     <servlet>


Follow ups