azkaban-developers

Merge branch 'release-3.0' of github.com:azkaban/azkaban2

1/9/2014 12:39:46 AM

Details

diff --git a/src/java/azkaban/webapp/servlet/ExecutorServlet.java b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
index ad572ed..6cc091b 100644
--- a/src/java/azkaban/webapp/servlet/ExecutorServlet.java
+++ b/src/java/azkaban/webapp/servlet/ExecutorServlet.java
@@ -66,7 +66,8 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 	}
 
 	@Override
-	protected void handleGet(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
+	protected void handleGet(HttpServletRequest req, HttpServletResponse resp, 
+			Session session) throws ServletException, IOException {
 		if (hasParam(req, "ajax")) {
 			handleAJAXAction(req, resp, session);
 		}
@@ -83,6 +84,98 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 		}
 	}
 	
+	private void handleAJAXAction(HttpServletRequest req, 
+			HttpServletResponse resp, Session session) 
+			throws ServletException, IOException {
+		HashMap<String, Object> ret = new HashMap<String, Object>();
+		String ajaxName = getParam(req, "ajax");
+		
+		if (hasParam(req, "execid")) {
+			int execid = getIntParam(req, "execid");
+			ExecutableFlow exFlow = null;
+
+			try {
+				exFlow = executorManager.getExecutableFlow(execid);
+			} catch (ExecutorManagerException e) {
+				ret.put("error", "Error fetching execution '" + execid + "': " + e.getMessage());
+			}
+
+			if (exFlow == null) {
+				ret.put("error", "Cannot find execution '" + execid + "'");
+			}
+			else {
+				if (ajaxName.equals("fetchexecflow")) {
+					ajaxFetchExecutableFlow(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("fetchexecflowupdate")) {
+					ajaxFetchExecutableFlowUpdate(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("cancelFlow")) {
+					ajaxCancelFlow(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("restartFlow")) {
+					ajaxRestartFlow(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("pauseFlow")) {
+					ajaxPauseFlow(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("resumeFlow")) {
+					ajaxResumeFlow(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("fetchExecFlowLogs")) {
+					ajaxFetchExecFlowLogs(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("fetchExecJobLogs")) {
+					ajaxFetchJobLogs(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("fetchExecJobSummary")) {
+					ajaxFetchJobSummary(req, resp, ret, session.getUser(), exFlow);
+				}
+				else if (ajaxName.equals("retryFailedJobs")) {
+					ajaxRestartFailed(req, resp, ret, session.getUser(), exFlow);
+				}
+//				else if (ajaxName.equals("fetchLatestJobStatus")) {
+//					ajaxFetchLatestJobStatus(req, resp, ret, session.getUser(), exFlow);
+//				}
+				else if (ajaxName.equals("flowInfo")) {
+					//String projectName = getParam(req, "project");
+					//Project project = projectManager.getProject(projectName);
+					//String flowName = getParam(req, "flow");
+					ajaxFetchExecutableFlowInfo(req, resp, ret, session.getUser(), exFlow);
+				}
+			}
+		}
+		else if (ajaxName.equals("getRunning")) {
+			String projectName = getParam(req, "project");
+			String flowName = getParam(req, "flow");
+			ajaxGetFlowRunning(req, resp, ret, session.getUser(), projectName, flowName);
+		}
+		else if (ajaxName.equals("flowInfo")) {
+			String projectName = getParam(req, "project");
+			String flowName = getParam(req, "flow");
+			ajaxFetchFlowInfo(req, resp, ret, session.getUser(), projectName, flowName);
+		}
+		else {
+			String projectName = getParam(req, "project");
+			
+			ret.put("project", projectName);
+			if (ajaxName.equals("executeFlow")) {
+				ajaxAttemptExecuteFlow(req, resp, ret, session.getUser());
+			}
+		}
+		if (ret != null) {
+			this.writeJSON(resp, ret);
+		}
+	}
+
+	@Override
+	protected void handlePost(HttpServletRequest req, HttpServletResponse resp, 
+			Session session) throws ServletException, IOException {
+		if (hasParam(req, "ajax")) {
+			handleAJAXAction(req, resp, session);
+		}
+	}
+	
 	private void handleExecutionJobDetailsPage(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
 		Page page = newPage(req, resp, session, "azkaban/webapp/servlet/velocity/jobdetailspage.vm");
 		User user = session.getUser();
@@ -212,96 +305,7 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 		}
 		
 		return null;
-	}
-	
-	@Override
-	protected void handlePost(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
-		if (hasParam(req, "ajax")) {
-			handleAJAXAction(req, resp, session);
-		}
-	}
-
-	private void handleAJAXAction(HttpServletRequest req, HttpServletResponse resp, Session session) throws ServletException, IOException {
-		HashMap<String, Object> ret = new HashMap<String, Object>();
-		String ajaxName = getParam(req, "ajax");
-		
-		if (hasParam(req, "execid")) {
-			int execid = getIntParam(req, "execid");
-			ExecutableFlow exFlow = null;
-
-			try {
-				exFlow = executorManager.getExecutableFlow(execid);
-			} catch (ExecutorManagerException e) {
-				ret.put("error", "Error fetching execution '" + execid + "': " + e.getMessage());
-			}
-
-			if (exFlow == null) {
-				ret.put("error", "Cannot find execution '" + execid + "'");
-			}
-			else {
-				if (ajaxName.equals("fetchexecflow")) {
-					ajaxFetchExecutableFlow(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("fetchexecflowupdate")) {
-					ajaxFetchExecutableFlowUpdate(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("cancelFlow")) {
-					ajaxCancelFlow(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("restartFlow")) {
-					ajaxRestartFlow(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("pauseFlow")) {
-					ajaxPauseFlow(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("resumeFlow")) {
-					ajaxResumeFlow(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("fetchExecFlowLogs")) {
-					ajaxFetchExecFlowLogs(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("fetchExecJobLogs")) {
-					ajaxFetchJobLogs(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("fetchExecJobSummary")) {
-					ajaxFetchJobSummary(req, resp, ret, session.getUser(), exFlow);
-				}
-				else if (ajaxName.equals("retryFailedJobs")) {
-					ajaxRestartFailed(req, resp, ret, session.getUser(), exFlow);
-				}
-//				else if (ajaxName.equals("fetchLatestJobStatus")) {
-//					ajaxFetchLatestJobStatus(req, resp, ret, session.getUser(), exFlow);
-//				}
-				else if (ajaxName.equals("flowInfo")) {
-					//String projectName = getParam(req, "project");
-					//Project project = projectManager.getProject(projectName);
-					//String flowName = getParam(req, "flow");
-					ajaxFetchExecutableFlowInfo(req, resp, ret, session.getUser(), exFlow);
-				}
-			}
-		}
-		else if (ajaxName.equals("getRunning")) {
-			String projectName = getParam(req, "project");
-			String flowName = getParam(req, "flow");
-			ajaxGetFlowRunning(req, resp, ret, session.getUser(), projectName, flowName);
-		}
-		else if (ajaxName.equals("flowInfo")) {
-			String projectName = getParam(req, "project");
-			String flowName = getParam(req, "flow");
-			ajaxFetchFlowInfo(req, resp, ret, session.getUser(), projectName, flowName);
-		}
-		else {
-			String projectName = getParam(req, "project");
-			
-			ret.put("project", projectName);
-			if (ajaxName.equals("executeFlow")) {
-				ajaxAttemptExecuteFlow(req, resp, ret, session.getUser());
-			}
-		}
-		if (ret != null) {
-			this.writeJSON(resp, ret);
-		}
-	}
+	}	
 
 //	private void ajaxFetchLatestJobStatus(HttpServletRequest req,HttpServletResponse resp, HashMap<String, Object> ret, User user, ExecutableFlow exFlow) {
 //		Project project = getProjectAjaxByPermission(ret, exFlow.getProjectId(), user, Type.READ);
@@ -732,7 +736,7 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 			return;
 		}
 		
-		ret.put("flow",  flowId);
+		ret.put("flow", flowId);
 		Flow flow = project.getFlow(flowId);
 		if (flow == null) {
 			ret.put("error", "Flow '" + flowId + "' cannot be found in project " + project);
@@ -752,7 +756,7 @@ public class ExecutorServlet extends LoginAbstractAzkabanServlet {
 			return;
 		}
 		
-		ret.put("flow",  flowId);
+		ret.put("flow", flowId);
 		Flow flow = project.getFlow(flowId);
 		if (flow == null) {
 			ret.put("error", "Flow '" + flowId + "' cannot be found in project " + project);
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index c05b38d..662bad0 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -322,7 +322,6 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
 	private void ajaxFetchFlowDetails(Project project, 
       HashMap<String, Object> ret, HttpServletRequest req) 
       throws ServletException {
-		String projectName = getParam(req, "project");
 		String flowName = getParam(req, "flow");
 
 		Flow flow = null;
diff --git a/src/java/azkaban/webapp/servlet/velocity/executionspage.vm b/src/java/azkaban/webapp/servlet/velocity/executionspage.vm
index 41ce8d6..91d1d0c 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executionspage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executionspage.vm
@@ -63,7 +63,7 @@
                 <th>Flow</th>
                 <th>Project</th>
                 <th class="user">User</th>
-                <th class="user">Proxy User</th>
+                <th class="user">Proxy</th>
                 <th class="date">Start Time</th>
                 <th class="date">End Time</th>
                 <th class="elapse">Elapsed</th>
@@ -110,7 +110,7 @@
                 <th>Flow</th>
                 <th>Project</th>
                 <th class="user">User</th>
-                <th class="user">Proxy User</th>
+                <th class="user">Proxy</th>
                 <th class="date">Start Time</th>
                 <th class="date">End Time</th>
                 <th class="elapse">Elapsed</th>
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
index 2863c89..cda497a 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
@@ -165,7 +165,7 @@
 											<table class="table table-striped">
 												<thead>
 													<tr>
-														<th>Name</th>
+														<th class="property-key">Name</th>
 														<th>Value</th>
 													</tr>
 												</thead>
@@ -190,6 +190,7 @@
                 <button type="button" class="btn btn-success" id="schedule-btn">Schedule</button>
               </div>
 #end
+
 #*
 #if ($triggerPlugins.size() > 0)
 	#foreach ($triggerPlugin in $triggerPlugins)
diff --git a/src/java/azkaban/webapp/servlet/velocity/jobpage.vm b/src/java/azkaban/webapp/servlet/velocity/jobpage.vm
index 36c7d9e..5dbec1f 100644
--- a/src/java/azkaban/webapp/servlet/velocity/jobpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/jobpage.vm
@@ -167,23 +167,29 @@
 				<div class="modal-dialog">
 					<div class="modal-content">
 						<div class="modal-header">
-							<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+							<button type="button" class="close" data-dismiss="modal" aria-hidden="true" id="close-btn">&times;</button>
 							<h4 class="modal-title">Edit Job</h4>
 						</div>
 						<div class="modal-body">
 							<h4>Job Essentials</h4>
-							<dl class="dl-horizontal">
-								<dt>Job Name</dt>
-								<dd id="jobName"></dd>
-								<dt>Job Type</dt>
-								<dd id="jobType"></dd>
-							</dl>
+              <table class="table table-bordered table-condensed">
+                <tbody>
+                  <tr>
+                    <td class="property-key">Job Name</td>
+                    <td id="jobName"></td>
+                  </tr>
+                  <tr>
+                    <td class="property-key">Job Type</td>
+                    <td id="jobType"></td>
+                  </tr>
+                </tbody>
+              <table>
 							<h4>General Job Settings</h4>
 							<p><strong>Be Aware:</strong> A job may be shared by multiple flows. The change will be global!</p>
 							<table id="generalProps" class="table table-striped table-bordered">
 								<thead>
 									<tr>
-										<th>Name</th>
+										<th class="property-key">Name</th>
 										<th>Value</th>
 									</tr>
 								</thead>
@@ -197,7 +203,7 @@
 							</table>
 						</div>
 						<div class="modal-footer">
-							<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+							<button type="button" class="btn btn-default" id="cancel-btn" data-dismiss="modal">Cancel</button>
 							<button type="button" class="btn btn-primary" id="set-btn">Set/Change Job Description</button>
 						</div>
 					</div>
diff --git a/src/java/azkaban/webapp/servlet/velocity/nav.vm b/src/java/azkaban/webapp/servlet/velocity/nav.vm
index 5c4f4eb..f709390 100644
--- a/src/java/azkaban/webapp/servlet/velocity/nav.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/nav.vm
@@ -14,6 +14,11 @@
  * the License.
 *#
 
+    <script type="text/javascript">
+      function navMenuClick(url) {
+        window.location.href = url;
+      }
+    </script>
     <div class="navbar navbar-inverse navbar-static-top">
       <div class="container-full">
         <div class="navbar-header">
@@ -35,20 +40,20 @@
         <div class="navbar-collapse collapse">
 #if ($navbar_disabled != 1)
           <ul class="nav navbar-nav">
-						<li#if($current_page == 'all') class="active"#end><a href="$!context/index">Projects</a></li>
-						<li#if($current_page == 'schedule') class="active"#end><a href="$!context/schedule">Scheduling</a></li>
-						<!--<li#if($current_page == 'triggers') class="active"#end><a href="$!context/triggers">Triggers</a></li>-->
-						<li#if($current_page == 'executing') class="active"#end><a href="$!context/executor">Executing</a></li>
-						<li#if($current_page == 'history') class="active"#end><a href="$!context/history">History</a></li>
+						<li#if($current_page == 'all') class="active"#end onClick="navMenuClick('$!context/')"><a href="$!context/index">Projects</a></li>
+						<li#if($current_page == 'schedule') class="active"#end onClick="navMenuClick('$!context/schedule')"><a href="$!context/schedule">Scheduling</a></li>
+						<!--<li#if($current_page == 'triggers') class="active"#end onClick="navMenuClick('$!context/triggers')"><a href="$!context/triggers">Triggers</a></li>-->
+						<li#if($current_page == 'executing') class="active"#end onClick="navMenuClick('$!context/executor')"><a href="$!context/executor">Executing</a></li>
+						<li#if($current_page == 'history') class="active"#end onClick="navMenuClick('$!context/history')"><a href="$!context/history">History</a></li>
 	#foreach ($viewer in $viewers)
 		#if (!$viewer.hidden)
-						<li#if($current_page == $viewer.pluginName) class="active"#end><a href="$!context/$viewer.pluginPath">$viewer.pluginName</a></li>
+						<li#if($current_page == $viewer.pluginName) class="active"#end onClick="navMenuClick('$!context/$viewer.pluginPath')"><a href="$!context/$viewer.pluginPath">$viewer.pluginName</a></li>
 		#end
 	#end
 
 	#foreach ($trigger in $triggerPlugins)
 		#if (!$trigger.hidden)
-						<li#if($current_page == $trigger.pluginName) class="active"#end><a href="$!context/$trigger.pluginPath">$trigger.pluginName</a></li>
+						<li#if($current_page == $trigger.pluginName) class="active"#end onClick="navMenuClick('$!context/$trigger.pluginPath')"><a href="$!context/$trigger.pluginPath">$trigger.pluginName</a></li>
 		#end
 	#end
           </ul>
diff --git a/src/less/navbar.less b/src/less/navbar.less
index 71d9124..3826082 100644
--- a/src/less/navbar.less
+++ b/src/less/navbar.less
@@ -25,13 +25,14 @@
 }
 
 .navbar-enviro {
-  margin: 25px 20px 0px 10px;
+  margin: 30px 20px 0px 12px;
      
   .navbar-enviro-name {
     color: #ff3601;
     font-family: Helvetica, Arial, Sans-Serif;
     font-size: 118.75%;
     font-weight: bold;
+		line-height: 100%;
   }
        
   .navbar-enviro-server {
@@ -80,6 +81,11 @@
     > li > a {
       padding: 0px;
       color: #ccc;
+      &:focus,
+      &:hover {
+        color: #ccc;
+        background-color: transparent;
+      }
     }
 
     > .active {
@@ -92,11 +98,23 @@
       background-color: transparent;
       border-bottom: 1px solid #ff3601;
       &:hover {
+        color: #fff;
         background-color: transparent;
       }
     }
 
-    > .active > .open {
+    > li.dropdown {
+      padding: 0;
+    }
+
+    > li.dropdown > a {
+      padding: 25px 12px 25px 12px;
+    }
+
+    > .open > a,
+    > .open > a:hover,
+    > .open > a:focus {
+      color: #ccc;
       background-color: transparent;
     }
   }
diff --git a/src/less/project.less b/src/less/project.less
index 83946d0..b3ae43f 100644
--- a/src/less/project.less
+++ b/src/less/project.less
@@ -9,8 +9,16 @@
 
 .expanded-flow-job-list {
   .list-group-item {
+    .job-buttons {
+      visibility: hidden;
+    }
+
     &:hover {
       background-color: #f5f5f5;
+
+      .job-buttons {
+        visibility: visible;
+      }
     }
   }
 
diff --git a/src/less/tables.less b/src/less/tables.less
index 6e69260..d67061b 100644
--- a/src/less/tables.less
+++ b/src/less/tables.less
@@ -4,15 +4,25 @@ table.table-properties {
 }
 
 // Flow summary.
-td.property-key {
+.property-key {
   width: 25%;
   font-weight: bold;
 }
 
-td.property-value-half {
+.property-value-half {
   width: 25%;
 }
 
+.editable {
+  .remove-btn {
+    visibility: hidden;
+  }
+
+  &:hover .remove-btn {
+    visibility: visible;
+  }
+}
+
 // Job table.
 #all-jobs {
   .tb-name {
@@ -45,7 +55,7 @@ td.property-value-half {
 .executions-table {
   th {
     &.date {
-      width: 140px;
+      width: 160px;
     }
 
     &.execid {
@@ -75,6 +85,10 @@ td.property-value-half {
     &.action {
       width: 20px;
     }
+
+    &.logs {
+      width: 30px;
+    }
   }
 
   td {
diff --git a/src/web/js/azkaban.flow.execute.view.js b/src/web/js/azkaban.flow.execute.view.js
index d2940e3..2fc3b4b 100644
--- a/src/web/js/azkaban.flow.execute.view.js
+++ b/src/web/js/azkaban.flow.execute.view.js
@@ -326,10 +326,17 @@ azkaban.EditTableView = Backbone.View.extend({
 	
 		var tr = document.createElement("tr");
 		var tdName = document.createElement("td");
+    $(tdName).addClass('property-key');
 		var tdValue = document.createElement("td");
 		
-		var icon = document.createElement("span");
-		$(icon).addClass("removeIcon");
+		var remove = document.createElement("div");
+    $(remove).addClass("pull-right").addClass('remove-btn');
+    var removeBtn = document.createElement("button");
+    $(removeBtn).attr('type', 'button');
+    $(removeBtn).addClass('btn').addClass('btn-xs').addClass('btn-danger');
+    $(removeBtn).text('Delete');
+    $(remove).append(removeBtn);
+
 		var nameData = document.createElement("span");
 		$(nameData).addClass("spanValue");
 		$(nameData).text(name);
@@ -337,13 +344,12 @@ azkaban.EditTableView = Backbone.View.extend({
 		$(valueData).addClass("spanValue");
 		$(valueData).text(value);
 						
-		$(tdName).append(icon);
 		$(tdName).append(nameData);
-		$(tdName).addClass("name");
 		$(tdName).addClass("editable");
 		
 		$(tdValue).append(valueData);
-		$(tdValue).addClass("editable");
+    $(tdValue).append(remove);
+		$(tdValue).addClass("editable").addClass('value');
 		
 		$(tr).addClass("editRow");
 		$(tr).append(tdName);
@@ -361,6 +367,7 @@ azkaban.EditTableView = Backbone.View.extend({
 					
 		var input = document.createElement("input");
 		$(input).attr("type", "text");
+    $(input).addClass('form-control').addClass('input-sm');
 		$(input).css("width", "100%");
 		$(input).val(text);
 		$(curTarget).addClass("editing");
@@ -396,10 +403,15 @@ azkaban.EditTableView = Backbone.View.extend({
 		$(valueData).addClass("spanValue");
 		$(valueData).text(text);
 
-		if ($(parent).hasClass("name")) {
-			var icon = document.createElement("span");
-			$(icon).addClass("removeIcon");
-			$(parent).append(icon);
+		if ($(parent).hasClass("value")) {
+      var remove = document.createElement("div");
+      $(remove).addClass("pull-right").addClass('remove-btn');
+      var removeBtn = document.createElement("button");
+      $(removeBtn).attr('type', 'button');
+      $(removeBtn).addClass('btn').addClass('btn-xs').addClass('btn-danger');
+      $(removeBtn).text('Delete');
+      $(remove).append(removeBtn);
+			$(parent).append(remove);
 		}
 		
 		$(parent).removeClass("editing");
diff --git a/src/web/js/azkaban.jobedit.view.js b/src/web/js/azkaban.jobedit.view.js
index 0e62de1..ba037b0 100644
--- a/src/web/js/azkaban.jobedit.view.js
+++ b/src/web/js/azkaban.jobedit.view.js
@@ -22,9 +22,10 @@ azkaban.JobEditView = Backbone.View.extend({
 		"click" : "closeEditingTarget",
 		"click #set-btn": "handleSet",	
 		"click #cancel-btn": "handleCancel",
+		"click #close-btn": "handleCancel",
 		"click #addRow": "handleAddRow",
 		"click table .editable": "handleEditColumn",
-		"click table .removeIcon": "handleRemoveColumn"
+		"click table .remove-btn": "handleRemoveColumn"
 	},
 	
 	initialize: function(setting) {
@@ -68,30 +69,29 @@ azkaban.JobEditView = Backbone.View.extend({
 		var fetchJobSuccessHandler = function(data) {
 			if (data.error) {
 				alert(data.error);
+				return;
 			}
-			else {
-				document.getElementById('jobName').innerHTML = data.jobName;				
-				document.getElementById('jobType').innerHTML = data.jobType;
-				var generalParams = data.generalParams;
-				var overrideParams = data.overrideParams;
-						
-				/*for (var key in generalParams) {
+			document.getElementById('jobName').innerHTML = data.jobName;				
+			document.getElementById('jobType').innerHTML = data.jobType;
+			var generalParams = data.generalParams;
+			var overrideParams = data.overrideParams;
+					
+			/*for (var key in generalParams) {
+				var row = handleAddRow();
+				var td = $(row).find('span');
+				$(td[1]).text(key);
+				$(td[2]).text(generalParams[key]);
+			}*/
+					
+			mythis.overrideParams = overrideParams;
+			mythis.generalParams = generalParams;
+			
+			for (var okey in overrideParams) {
+				if (okey != 'type' && okey != 'dependencies') {
 					var row = handleAddRow();
 					var td = $(row).find('span');
-					$(td[1]).text(key);
-					$(td[2]).text(generalParams[key]);
-				}*/
-						
-				mythis.overrideParams = overrideParams;
-				mythis.generalParams = generalParams;
-				
-				for (var okey in overrideParams) {
-					if (okey != 'type' && okey != 'dependencies') {
-						var row = handleAddRow();
-						var td = $(row).find('span');
-						$(td[1]).text(okey);
-						$(td[2]).text(overrideParams[okey]);
-					}
+					$(td[0]).text(okey);
+					$(td[1]).text(overrideParams[okey]);
 				}
 			}
 		};
@@ -106,8 +106,8 @@ azkaban.JobEditView = Backbone.View.extend({
 		for (var i = 0; i < editRows.length; ++i) {
 			var row = editRows[i];
 			var td = $(row).find('span');
-			var key = $(td[1]).text();
-			var val = $(td[2]).text();
+			var key = $(td[0]).text();
+			var val = $(td[1]).text();
 
 			if (key && key.length > 0) {
 				jobOverride[key] = val;
@@ -151,23 +151,30 @@ azkaban.JobEditView = Backbone.View.extend({
 	handleAddRow: function(evt) {
 		var tr = document.createElement("tr");
 		var tdName = document.createElement("td");
+    $(tdName).addClass('property-key');
 		var tdValue = document.createElement("td");
 
-		var icon = document.createElement("span");
-		$(icon).addClass("removeIcon");
+		var remove = document.createElement("div");
+    $(remove).addClass("pull-right").addClass('remove-btn');
+    var removeBtn = document.createElement("button");
+    $(removeBtn).attr('type', 'button');
+    $(removeBtn).addClass('btn').addClass('btn-xs').addClass('btn-danger');
+    $(removeBtn).text('Delete');
+    $(remove).append(removeBtn);
+
 		var nameData = document.createElement("span");
 		$(nameData).addClass("spanValue");
 		var valueData = document.createElement("span");
 		$(valueData).addClass("spanValue");
 
-		$(tdName).append(icon);
 		$(tdName).append(nameData);
-		$(tdName).addClass("name");
 		$(tdName).addClass("editable");
 		nameData.myparent = tdName;
 
 		$(tdValue).append(valueData);
-			$(tdValue).addClass("editable");
+    $(tdValue).append(remove);
+		$(tdValue).addClass("editable");
+		$(tdValue).addClass("value");
 		valueData.myparent = tdValue;
 		
 		$(tr).addClass("editRow");
@@ -180,7 +187,6 @@ azkaban.JobEditView = Backbone.View.extend({
 	
 	handleEditColumn: function(evt) {
 		var curTarget = evt.currentTarget;
-	
 		if (this.editingTarget != curTarget) {
 			this.closeEditingTarget(evt);
 		
@@ -189,7 +195,7 @@ azkaban.JobEditView = Backbone.View.extend({
 						
 			var input = document.createElement("input");
 			$(input).attr("type", "text");
-			$(input).css("width", "100%");
+      $(input).addClass("form-control").addClass("input-sm");
 			$(input).val(text);
 			
 			$(curTarget).addClass("editing");
@@ -201,7 +207,6 @@ azkaban.JobEditView = Backbone.View.extend({
 					obj.closeEditingTarget(evt);
 				}
 			});
-			
 			this.editingTarget = curTarget;
 		}
 
@@ -217,28 +222,34 @@ azkaban.JobEditView = Backbone.View.extend({
 	},
 	
 	closeEditingTarget: function(evt) {
-		if (this.editingTarget != null && 
-				this.editingTarget != evt.target && 
-				this.editingTarget != evt.target.myparent) {
-			var input = $(this.editingTarget).children("input")[0];
-			var text = $(input).val();
-			$(input).remove();
-
-			var valueData = document.createElement("span");
-			$(valueData).addClass("spanValue");
-			$(valueData).text(text);
-
-			if ($(this.editingTarget).hasClass("name")) {
-				var icon = document.createElement("span");
-				$(icon).addClass("removeIcon");
-				$(this.editingTarget).append(icon);
-			}
+		if (this.editingTarget == null ||
+				this.editingTarget == evt.target ||
+				this.editingTarget == evt.target.myparent) {
+			return;
+		}
+		var input = $(this.editingTarget).children("input")[0];
+		var text = $(input).val();
+		$(input).remove();
+
+		var valueData = document.createElement("span");
+		$(valueData).addClass("spanValue");
+		$(valueData).text(text);
 
-			$(this.editingTarget).removeClass("editing");
-			$(this.editingTarget).append(valueData);
-			valueData.myparent = this.editingTarget;
-			this.editingTarget = null;
+		if ($(this.editingTarget).hasClass("value")) {
+      var remove = document.createElement("div");
+      $(remove).addClass("pull-right").addClass('remove-btn');
+      var removeBtn = document.createElement("button");
+      $(removeBtn).attr('type', 'button');
+      $(removeBtn).addClass('btn').addClass('btn-xs').addClass('btn-danger');
+      $(removeBtn).text('Delete');
+      $(remove).append(removeBtn);
+			$(this.editingTarget).append(remove);
 		}
+
+		$(this.editingTarget).removeClass("editing");
+		$(this.editingTarget).append(valueData);
+		valueData.myparent = this.editingTarget;
+		this.editingTarget = null;
 	}
 });
 
diff --git a/src/web/js/azkaban.project.view.js b/src/web/js/azkaban.project.view.js
index 073331e..ef01a0e 100644
--- a/src/web/js/azkaban.project.view.js
+++ b/src/web/js/azkaban.project.view.js
@@ -91,10 +91,11 @@ azkaban.FlowTableView = Backbone.View.extend({
 			li.jobName = name;
 
 			if (execAccess) {
-				var hoverMenuDiv = document.createElement("div");
-				$(hoverMenuDiv).addClass("pull-right");
+				var hoverMenuDiv = document.createElement('div');
+				$(hoverMenuDiv).addClass('pull-right');
+        $(hoverMenuDiv).addClass('job-buttons');
 				
-				var divRunJob = document.createElement("button");
+				var divRunJob = document.createElement('button');
         $(divRunJob).attr('type', 'button');
 				$(divRunJob).addClass("btn");
 				$(divRunJob).addClass("btn-success");