azkaban-aplcache

File uploading finished.

6/4/2012 9:10:27 PM

Details

diff --git a/src/java/azkaban/project/FileProjectManager.java b/src/java/azkaban/project/FileProjectManager.java
index b26b3a1..84dea0d 100644
--- a/src/java/azkaban/project/FileProjectManager.java
+++ b/src/java/azkaban/project/FileProjectManager.java
@@ -3,6 +3,7 @@ package azkaban.project;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -87,15 +88,26 @@ public class FileProjectManager implements ProjectManager {
     public List<Project> getProjects(User user) {
     	ArrayList<Project> array = new ArrayList<Project>();
     	for(Project project : projects.values()) {
-    		if (project.hasPermission(user, Type.READ)) {
+    		Permission perm = project.getUserPermission(user);
+    		if (perm.isPermissionSet(Type.ADMIN) || perm.isPermissionSet(Type.READ)) {
     			array.add(project);
     		}
     	}
     	return array;
     }
     
-    public Project getProject(String name, User user) {
-    	return projects.get(name);
+    public Project getProject(String name, User user) throws AccessControlException {
+    	Project project = projects.get(name);
+    	if (project != null) {
+    		Permission perm = project.getUserPermission(user);
+    		if (perm.isPermissionSet(Type.ADMIN) || perm.isPermissionSet(Type.READ)) {
+    			return project;
+    		}
+    		else {
+    			throw new AccessControlException("Permission denied. Do not have read access.");
+    		}
+    	}
+    	return null;
     }
     
     @Override
diff --git a/src/java/azkaban/project/Project.java b/src/java/azkaban/project/Project.java
index 2662fbd..12a4934 100644
--- a/src/java/azkaban/project/Project.java
+++ b/src/java/azkaban/project/Project.java
@@ -10,49 +10,60 @@ import azkaban.user.Permission.Type;
 import azkaban.user.User;
 
 public class Project {
-    private final String name;
-    private String description;
-    private long createTimestamp;
-    private long lastModifiedTimestamp;
-    private String lastModifiedUser;
-    private HashMap<String, Permission> userToPermission = new HashMap<String, Permission>();
-    
-    public Project(String name) {
-        this.name = name;
-    }
+	private final String name;
+	private String description;
+	private long createTimestamp;
+	private long lastModifiedTimestamp;
+	private String lastModifiedUser;
+	private HashMap<String, Permission> userToPermission = new HashMap<String, Permission>();
+
+	public Project(String name) {
+		this.name = name;
+	}
 
 	public String getName() {
-        return name;
-    }
-    
+		return name;
+	}
+
 	public boolean hasPermission(User user, Type type) {
 		Permission perm = userToPermission.get(user.getUserId());
 		if (perm == null) {
 			return false;
 		}
-		
+
 		if (perm.isPermissionSet(Type.ADMIN) || perm.isPermissionSet(type)) {
 			return true;
 		}
 
 		return false;
 	}
-	
-    public void setDescription(String description) {
-    	this.description = description;
-    }
-    
-    public String getDescription() {
-    	return description;
-    }
-    
-    public void setUserPermission(String userid, Permission perm) {
-    	userToPermission.put(userid, perm);
-    }
-    
-    public Permission getUserPermission(User user) {
-    	return userToPermission.get(user.getUserId());
-    }
+
+	public List<String> getUsersWithPermission(Type type) {
+		ArrayList<String> users = new ArrayList<String>();
+		for (Map.Entry<String, Permission> entry : userToPermission.entrySet()) {
+			Permission perm = entry.getValue();
+			if (perm.isPermissionSet(type)) {
+				users.add(entry.getKey());
+			}
+		}
+		return users;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setUserPermission(String userid, Permission perm) {
+		userToPermission.put(userid, perm);
+	}
+
+	public Permission getUserPermission(User user) {
+		return userToPermission.get(user.getUserId());
+	}
 
 	public long getCreateTimestamp() {
 		return createTimestamp;
@@ -69,67 +80,70 @@ public class Project {
 	public void setLastModifiedTimestamp(long lastModifiedTimestamp) {
 		this.lastModifiedTimestamp = lastModifiedTimestamp;
 	}
-	
-    public Object toObject() {
-    	HashMap<String,Object> projectObject = new HashMap<String, Object>();
-    	projectObject.put("name", name);
-    	projectObject.put("description", description);
-    	projectObject.put("createTimestamp", createTimestamp);
-    	projectObject.put("lastModifiedTimestamp",lastModifiedTimestamp);
-    	projectObject.put("lastModifiedUser", lastModifiedUser);
-    	
-    	ArrayList<Map<String,Object>> users = new ArrayList<Map<String,Object>>();
-    	for (Map.Entry<String, Permission> entry: userToPermission.entrySet()) {
-    		HashMap<String,Object> userMap = new HashMap<String,Object>();
-    		userMap.put("userid", entry.getKey());
-    		userMap.put("permissions", entry.getValue().toStringArray());
-    		users.add(userMap);
-    	}
- 
-    	projectObject.put("users", users);
-    	return projectObject;
-    }
-    
-    @SuppressWarnings("unchecked")
+
+	public Object toObject() {
+		HashMap<String, Object> projectObject = new HashMap<String, Object>();
+		projectObject.put("name", name);
+		projectObject.put("description", description);
+		projectObject.put("createTimestamp", createTimestamp);
+		projectObject.put("lastModifiedTimestamp", lastModifiedTimestamp);
+		projectObject.put("lastModifiedUser", lastModifiedUser);
+
+		ArrayList<Map<String, Object>> users = new ArrayList<Map<String, Object>>();
+		for (Map.Entry<String, Permission> entry : userToPermission.entrySet()) {
+			HashMap<String, Object> userMap = new HashMap<String, Object>();
+			userMap.put("userid", entry.getKey());
+			userMap.put("permissions", entry.getValue().toStringArray());
+			users.add(userMap);
+		}
+
+		projectObject.put("users", users);
+		return projectObject;
+	}
+
+	@SuppressWarnings("unchecked")
 	public static Project projectFromObject(Object object) {
-    	Map<String,Object> projectObject = (Map<String,Object>)object;
-    	String name = (String)projectObject.get("name");
-    	String description = (String)projectObject.get("description");
-    	String lastModifiedUser = (String)projectObject.get("lastModifiedUser");
-    	long createTimestamp = coerceToLong(projectObject.get("createTimestamp"));
-    	long lastModifiedTimestamp = coerceToLong(projectObject.get("lastModifiedTimestamp"));
-    	
-    	Project project = new Project(name);
-    	project.setDescription(description);
-    	project.setCreateTimestamp(createTimestamp);
-    	project.setLastModifiedTimestamp(lastModifiedTimestamp);
-    	project.setLastModifiedUser(lastModifiedUser);
-    	
-		List<Map<String,Object>> users = (List<Map<String,Object>>)projectObject.get("users");
-    	
-    	for (Map<String, Object> user: users) {
-    		String userid = (String)user.get("userid");
-    		Permission perm = new Permission();
-    		List<String> list = (List<String>)user.get("permissions");
-    		perm.setPermissionsByName(list);
-    		
-    		project.setUserPermission(userid, perm);
-    	}
-    	
-    	return project;
-    }
-    
-    private static long coerceToLong(Object obj) {
-    	if (obj == null) {
-    		return 0;
-    	}
-    	else if (obj instanceof Integer) {
-    		return (Integer)obj;
-    	}
-    	
-    	return (Long)obj;
-    }
-    
+		Map<String, Object> projectObject = (Map<String, Object>) object;
+		String name = (String) projectObject.get("name");
+		String description = (String) projectObject.get("description");
+		String lastModifiedUser = (String) projectObject
+				.get("lastModifiedUser");
+		long createTimestamp = coerceToLong(projectObject
+				.get("createTimestamp"));
+		long lastModifiedTimestamp = coerceToLong(projectObject
+				.get("lastModifiedTimestamp"));
+
+		Project project = new Project(name);
+		project.setDescription(description);
+		project.setCreateTimestamp(createTimestamp);
+		project.setLastModifiedTimestamp(lastModifiedTimestamp);
+		project.setLastModifiedUser(lastModifiedUser);
+
+		List<Map<String, Object>> users = (List<Map<String, Object>>) projectObject
+				.get("users");
+
+		for (Map<String, Object> user : users) {
+			String userid = (String) user.get("userid");
+			Permission perm = new Permission();
+			List<String> list = (List<String>) user.get("permissions");
+			perm.setPermissionsByName(list);
+
+			project.setUserPermission(userid, perm);
+		}
+
+		return project;
+	}
+
+	private static long coerceToLong(Object obj) {
+		if (obj == null) {
+			return 0;
+		} else if (obj instanceof Integer) {
+			return (Integer) obj;
+		}
+
+		return (Long) obj;
+	}
+
 	public String getLastModifiedUser() {
 		return lastModifiedUser;
 	}
@@ -137,8 +151,8 @@ public class Project {
 	public void setLastModifiedUser(String lastModifiedUser) {
 		this.lastModifiedUser = lastModifiedUser;
 	}
-    
-    @Override
+
+	@Override
 	public int hashCode() {
 		final int prime = 31;
 		int result = 1;
diff --git a/src/java/azkaban/project/ProjectManager.java b/src/java/azkaban/project/ProjectManager.java
index 73fda78..890363c 100644
--- a/src/java/azkaban/project/ProjectManager.java
+++ b/src/java/azkaban/project/ProjectManager.java
@@ -1,5 +1,6 @@
 package azkaban.project;
 
+import java.security.AccessControlException;
 import java.util.List;
 
 import azkaban.user.User;
@@ -10,7 +11,7 @@ public interface ProjectManager {
     
     public List<Project> getProjects(User user);
     
-    public Project getProject(String name, User user);
+    public Project getProject(String name, User user) throws AccessControlException;
     
     public Project createProjects(String projectName, String description, User creator) throws ProjectManagerException;
     
diff --git a/src/java/azkaban/project/ProjectManagerException.java b/src/java/azkaban/project/ProjectManagerException.java
index fe16a8c..a7b3575 100644
--- a/src/java/azkaban/project/ProjectManagerException.java
+++ b/src/java/azkaban/project/ProjectManagerException.java
@@ -2,6 +2,9 @@ package azkaban.project;
 
 public class ProjectManagerException extends Exception{
 	private static final long serialVersionUID = 1L;
+	public enum Type {
+		
+	}
 
 	public ProjectManagerException(String message) {
 		super(message);
diff --git a/src/java/azkaban/user/Permission.java b/src/java/azkaban/user/Permission.java
index b816b65..df4adac 100644
--- a/src/java/azkaban/user/Permission.java
+++ b/src/java/azkaban/user/Permission.java
@@ -2,9 +2,12 @@ package azkaban.user;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
+import azkaban.utils.Utils;
+
 public class Permission {
 	public enum Type {
 		READ,
@@ -83,17 +86,12 @@ public class Permission {
 			list.add(type.toString());
 			count++;
 		}
+
 		return list.toArray(new String[count]);
 	}
 	
 	public String toString() {
-		StringBuffer buffer = new StringBuffer();
-		for (Type perm: permissions) {
-			buffer.append(perm.toString());
-			buffer.append(",");
-		}
-		
-		return buffer.toString();
+		return Utils.flattenToString(permissions, ",");
 	}
 	
 	@Override
diff --git a/src/java/azkaban/utils/Utils.java b/src/java/azkaban/utils/Utils.java
index bfb0432..1d22bb7 100644
--- a/src/java/azkaban/utils/Utils.java
+++ b/src/java/azkaban/utils/Utils.java
@@ -24,6 +24,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Collection;
 import java.util.Enumeration;
 import java.util.Random;
 import java.util.zip.ZipEntry;
@@ -147,4 +148,16 @@ public class Utils {
         }
     }
 
+    public static String flattenToString(Collection<?> collection, String delimiter) {
+    	StringBuffer buffer = new StringBuffer();
+    	for (Object obj: collection) {
+    		buffer.append(obj.toString());
+    		buffer.append(',');
+    	}
+    	
+    	if (buffer.length() > 0) {
+    		buffer.setLength(buffer.length() - 1);
+    	}
+    	return buffer.toString();
+    }
 }
\ No newline at end of file
diff --git a/src/java/azkaban/webapp/AzkabanWebServer.java b/src/java/azkaban/webapp/AzkabanWebServer.java
index 599c376..89ead27 100644
--- a/src/java/azkaban/webapp/AzkabanWebServer.java
+++ b/src/java/azkaban/webapp/AzkabanWebServer.java
@@ -318,8 +318,16 @@ public class AzkabanWebServer {
         Context root = new Context(server, "/", Context.SESSIONS);
 
         root.setResourceBase(staticDir);
-        root.addServlet(new ServletHolder(new DefaultServlet()), "/*");
-        root.addServlet(new ServletHolder(new IndexServlet()), "/index");
+        ServletHolder index = new ServletHolder(new IndexServlet());
+        root.addServlet(index, "/index");
+        root.addServlet(index, "/");
+        
+        ServletHolder staticServlet = new ServletHolder(new DefaultServlet());
+        root.addServlet(staticServlet, "/css/*");
+        root.addServlet(staticServlet, "/js/*");
+        root.addServlet(staticServlet, "/images/*");
+        root.addServlet(staticServlet, "/favicon.ico");
+        
         root.addServlet(new ServletHolder(new ProjectManagerServlet()), "/manager");
         root.setAttribute(AzkabanServletContextListener.AZKABAN_SERVLET_CONTEXT_KEY, app);
 
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index 028e519..960fa83 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -1,8 +1,15 @@
 package azkaban.webapp.servlet;
 
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.Writer;
+import java.security.AccessControlException;
 import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipFile;
 
 
 import javax.servlet.ServletConfig;
@@ -10,41 +17,87 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.io.IOUtils;
 import org.apache.log4j.Logger;
 
+import azkaban.project.Project;
 import azkaban.project.ProjectManager;
 import azkaban.project.ProjectManagerException;
+import azkaban.user.Permission.Type;
 import azkaban.user.User;
+import azkaban.utils.Utils;
 import azkaban.webapp.session.Session;
+import azkaban.webapp.servlet.MultipartParser;
 
 public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
     private static final long serialVersionUID = 1;
     private static final Logger logger = Logger.getLogger(ProjectManagerServlet.class);
-
+    private static final int DEFAULT_UPLOAD_DISK_SPOOL_SIZE = 20 * 1024 * 1024;
+    
     private ProjectManager manager;
+    private MultipartParser multipartParser;
+    private File tempDir;
 
     @Override
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
         manager = this.getApplication().getProjectManager();
+        tempDir = this.getApplication().getTempDirectory();
+        multipartParser = new MultipartParser(DEFAULT_UPLOAD_DISK_SPOOL_SIZE);
     }
 
     @Override
     protected void handleGet(HttpServletRequest req, HttpServletResponse resp,
             Session session) throws ServletException, IOException {
         Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/projectmanager.vm");
+        User user = session.getUser();
         
+        if ( hasParam(req, "project") ) {
+        	String projectName = getParam(req, "project");
+        	Project project = null;
+        	try {
+        		project = manager.getProject(projectName, user);
+        		if (project == null) {
+            		page.add("errorMsg", "Project " + projectName + " not found.");
+        		}
+        		else {
+        			page.add("project", project);
+        			page.add("admins", Utils.flattenToString(project.getUsersWithPermission(Type.ADMIN), ","));
+        			page.add("permissions", project.getUserPermission(user));
+        		}
+        		
+        	}
+        	catch (AccessControlException e) {
+        		page.add("errorMsg", e.getMessage());
+        	}
+
+        }
+        else {
+    		page.add("errorMsg", "No project set.");
+        }
         page.render();
     }
 
     @Override
     protected void handlePost(HttpServletRequest req, HttpServletResponse resp,
             Session session) throws ServletException, IOException {
-    	if (hasParam(req, "action")) {
+    	if (ServletFileUpload.isMultipartContent(req)) {
+    		logger.info("Post is multipart");
+    		Map<String, Object> params = multipartParser.parseMultipart(req);
+    		if (params.containsKey("action")) {
+    			String action = (String)params.get("action");
+    			if (action.equals("upload")) {
+    				handleUpload(req, resp, params, session);
+    			}
+    		}
+    	}
+    	else if (hasParam(req, "action")) {
     		String action = getParam(req, "action");
             if (action.equals("create")) {
             	handleCreate(req, resp, session);
-            }    		
+            } 
     	}
 
     }
@@ -83,5 +136,51 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 			e.printStackTrace();
 		}
     }
+    
+    private void handleUpload(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> multipart,
+            Session session) throws ServletException, IOException {
+    	
+    	User user = session.getUser();
+    	String projectName = (String) multipart.get("project");
+        FileItem item = (FileItem) multipart.get("file");
+        if (projectName == null || projectName.isEmpty()) {
+        	setErrorMessageInCookie(resp, "No project name found.");
+        }
+        else if (item == null) {
+        	setErrorMessageInCookie(resp, "No file found.");
+        }
+        else {
+	    	try {
+	            File jobDir = extractFile(item);
+	        } catch (Exception e) {
+	            logger.info("Installation Failed.", e);
+	            setErrorMessageInCookie(resp, "Installation Failed.");
+	        }
+
+	    	resp.sendRedirect(req.getRequestURI() + "?project=" + projectName);
+        }
+    }
+
+    private File extractFile(FileItem item) throws IOException, ServletException {
+        final String contentType = item.getContentType();
+        if (contentType.startsWith("application/zip")) {
+            return unzipFile(item);
+        }
+
+        throw new ServletException(String.format("Unsupported file type[%s].", contentType));
+    }
+    
+    private File unzipFile(FileItem item) throws ServletException, IOException {
+        File temp = File.createTempFile("job-temp", ".zip");
+        temp.deleteOnExit();
+        OutputStream out = new BufferedOutputStream(new FileOutputStream(temp));
+        IOUtils.copy(item.getInputStream(), out);
+        out.close();
+        ZipFile zipfile = new ZipFile(temp);
+        File unzipped = Utils.createTempDir(tempDir);
+        Utils.unzip(zipfile, unzipped);
+        temp.delete();
+        return unzipped;
+    }
 
 }
diff --git a/src/java/azkaban/webapp/servlet/velocity/projectmanager.vm b/src/java/azkaban/webapp/servlet/velocity/projectmanager.vm
index ee315b1..50bfc2f 100644
--- a/src/java/azkaban/webapp/servlet/velocity/projectmanager.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/projectmanager.vm
@@ -8,7 +8,7 @@
     <script type="text/javascript" src="${context}/js/backbone-0.5.3-min.js"></script>
     <script type="text/javascript" src="${context}/js/jquery.simplemodal.js"></script>
     <script type="text/javascript" src="${context}/js/azkaban.nav.js"></script>
-    <script type="text/javascript" src="${context}/js/azkaban.job.view.js"></script>
+    <script type="text/javascript" src="${context}/js/azkaban.project.view.js"></script>
     <script type="text/javascript">
       var contextURL = "${context}";
       var currentTime = ${currentTime};
@@ -23,12 +23,64 @@
     <div class="messaging"><p id="messageClose">X</p><p id="message"></p></div>  
     
     <div class="content">
-      <div id="all-jobs-content">
-        <div class="section-hd">
-          <h2>Project</h2>
-        </div><!-- end .section-hd -->
+	  #if($errorMsg)
+    	  <div class="box-error-message">$errorMsg</div>
+      #else
+	      <div id="all-jobs-content">
+	        <div class="section-hd">
+	          <h2>Project <span>$project.name</span></h2>
+	          
+	          <a id="project-upload-btn" class="btn1 projectupload" href="#">Upload</a>
+	          <a id="project-permission-btn" class="btn5 projectpermission" href="#">Permissions</a>
+	        </div><!-- end .section-hd -->
+	      </div>
+
+         <div id="project-users">
+         	<table class="user-table">
+         		<tr><td class="first">Project Admins:</td><td>$admins</td></tr>
+         		<tr><td class="first">User ${user_id} Permissions:</td><td>$permissions.toString()</td></tr>
+         	</table>
+         </div>
+         <div id="project-summary">
+         	<table class="summary-table">
+         		<tr><td class="first">Name:</td><td>$project.name</td></tr>
+         		<tr><td class="first">Created Date:</td><td>$utils.formatDate($project.lastModifiedTimestamp)</td></tr>
+         		<tr><td class="first">Modified Date:</td><td>$utils.formatDate($project.createTimestamp)</td></tr>
+         		<tr><td class="first">Last Modified by:</td><td>$project.lastModifiedUser</td></tr>
+         		<tr><td class="first">Description:</td><td>$project.description</td></tr>
+         	</table>
+         </div>
+      #end
       </div>
-    <div class="content">
+    
+    <div id="upload-project" class="modal">
+    <h3>Upload Project Files</h3>
+      <div id="errorMsg" class="box-error-message">$errorMsg</div>
+      <div class="message">
+        <form id="upload-form" enctype="multipart/form-data" method="post" action="$!context/manager">
+          <fieldset>
+            <dl>
+	          <dt>Job Archive</dt>
+	          <dd><input id="file" name="file" class="file" type="file" onChange="changeFile()" /></dd>
+	          <input type="hidden" name="project" value="$project.name" />
+	          <input type="hidden" name="action" value="upload" />
+	        </dl>
+	      </fieldset>
+	    </form>
+	    
+      </div>
+      <div class="actions">
+        <a class="yes btn2" id="upload-btn" href="#">Upload</a>
+        <a class="no simplemodal-close btn3" href="#">Cancel</a>
+      </div>
+    </div>
+    <div id="invalid-session" class="modal">
+      <h3>Invalid Session</h3>
+      <p>Session has expired. Please re-login.</p>
+      <div class="actions">
+          <a class="yes btn2" id="login-btn" href="#">Re-login</a>
+      </div>
+    </div>
   </body>
 </html>
 
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index 9e44903..11ba190 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -132,6 +132,12 @@ textarea {
   font-weight: bold;
 }
 
+.section-hd h2 span{
+  padding-left: 5px;
+  font-weight: normal;
+  font-size: 90%;
+}
+
 table {
   background-color: #fff;
   font-family: Arial;
@@ -229,7 +235,8 @@ tr:hover td {
 .btn1,
 .btn2,
 .btn3,
-.btn4 {
+.btn4,
+.btn5 {
   -moz-border-radius: 3px;
   -webkit-border-radius: 3px;
   border-radius: 3px;
@@ -256,10 +263,16 @@ tr:hover td {
   background: -webkit-gradient(linear, center top, center bottom, color-stop(0,#AFE47B), color-stop(5%,#8BD03F),color-stop(100%,#69B219));
 }
 
-.section-hd .btn1,
 .section-hd .btn4,
-.section-ft .btn1,
-.section-ft .btn4 {
+.section-hd .btn5,
+.section-ft .btn4, 
+.section-ft .btn5{
+  float: right;
+  margin-right: 10px;
+}
+
+.section-hd .btn1,
+.section-ft .btn1 {
   float: right;
   margin-right: 25px;
 }
@@ -296,6 +309,23 @@ tr:hover td {
   display: inline-block;
   font-weight: normal;
 }
+
+/* grey right */
+.btn5 {
+  background: #CECECE;
+  background: -moz-linear-gradient(top, #F2F2F2 0, #F2F2F2 1px, #E4E4E4 1px, #CECECE 100%);
+  background: -o-linear-gradient(top, #F2F2F2 0, #F2F2F2 1px, #E4E4E4 1px, #CECECE 100%);
+  background: -webkit-gradient(linear, left top, left bottom, color-stop(0,#F2F2F2), color-stop(5%,#F2F2F2), color-stop(5%,#E4E4E4), color-stop(100%,#CECECE));
+  background: linear-gradient(top, #F2F2F2 0, #F2F2F2 1px, #E4E4E4 1px, #CECECE 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#E4E4E4', endColorstr='#CECECE',GradientType=0 );
+  border-color: #999;
+  color: #666;
+}
+.btn5:hover {
+  background: -moz-linear-gradient(center top , white 0pt, #ECECEC 100%) repeat scroll 0 0 transparent;
+  background: -webkit-gradient(linear, center top, center bottom, from(#FFF), to(#ECECEC));
+}
+
 /* confirm modal */
 .modal {
   display: none;
@@ -402,6 +432,8 @@ tr:hover td {
   background-color: #fff;
   border: 1px solid #9c9c9c;
   border-radius: 10px;
+  -moz-border-radius: 10px;
+
   overflow: hidden;
 }
 
@@ -444,7 +476,7 @@ tr:hover td {
   float: left;
   padding: 0 10px 6px 0;
   text-align: right;
-  width: 80px;
+  width: 100px;
 }
 
 .modal dd {
@@ -452,11 +484,6 @@ tr:hover td {
   padding-bottom: 6px;
 }
 
-.modal dd input {
-  float: left;
-  padding-bottom: 6px;
-}
-
 #create-project #overwrite {
   width: 12px;
 }
@@ -700,6 +727,50 @@ tr:hover td {
 	top: 20px;
 }
 
+/*project manager page*/
+#project-summary {
+	margin-left: 30px;
+	margin-bottom: 10px;
+	width: 60%;
+}
+
+#project-users {
+	float: right;
+	margin-right: 30px;
+	margin-bottom: 10px;
+	width: 30%;
+}
+
+.summary-table {
+	font-size: 12px;
+	background: none;
+	border-style: none;
+	border-width: 0px;
+}
+.summary-table .first {
+	width: 100px;
+	font-weight: bold;
+}
+
+.summary-table td {
+	border-style: none;
+}
+
+.user-table {
+	font-size: 12px;
+	background: none;
+	border-style: none;
+	border-width: 0px;
+}
+.user-table .first {
+	width: 150px;
+	font-weight: bold;
+}
+
+.user-table td {
+	border-style: none;
+}
+
 /* old styles */
 
 .azkaban-charts .hitarea {
diff --git a/src/web/js/azkaban.project.view.js b/src/web/js/azkaban.project.view.js
new file mode 100644
index 0000000..3e51e2a
--- /dev/null
+++ b/src/web/js/azkaban.project.view.js
@@ -0,0 +1,49 @@
+$.namespace('azkaban');
+
+var projectView;
+azkaban.ProjectView= Backbone.View.extend({
+  events : {
+      "click #project-upload-btn":"handleUploadProjectJob"
+  },
+  initialize : function(settings) {
+
+  },
+  handleUploadProjectJob : function(evt) {
+      console.log("click upload project");
+      $('#upload-project').modal({
+          closeHTML: "<a href='#' title='Close' class='modal-close'>x</a>",
+          position: ["20%",],
+          containerId: 'confirm-container',
+          containerCss: {
+            'height': '220px',
+            'width': '565px'
+          },
+          onShow: function (dialog) {
+            var modal = this;
+            $("#errorMsg").hide();
+          }
+        });
+  },
+  render: function() {
+  }
+});
+
+var uploadProjectView;
+azkaban.UploadProjectView= Backbone.View.extend({
+  events : {
+    "click #upload-btn": "handleCreateProject"
+  },
+  initialize : function(settings) {
+    $("#errorMsg").hide();
+  },
+  handleCreateProject : function(evt) {
+	$("#upload-form").submit();
+  },
+  render: function() {
+  }
+});
+
+$(function() {
+	projectView = new azkaban.ProjectView({el:$( '#all-jobs-content'), successMsg: successMessage, errorMsg: errorMessage });
+	uploadView = new azkaban.UploadProjectView({el:$('#upload-project')});
+});