azkaban-uncached
Changes
src/java/azkaban/webapp/servlet/velocity/projectpage.vm 326(+189 -137)
src/web/css/bootstrap-fileinput.css 93(+93 -0)
src/web/js/bootstrap.fileinput.js 179(+179 -0)
Details
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel2.vm b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel2.vm
new file mode 100644
index 0000000..f338e2d
--- /dev/null
+++ b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel2.vm
@@ -0,0 +1,185 @@
+#*
+ * Copyright 2012 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+*#
+
+<script type="text/javascript" src="${context}/js/azkaban.layout.js"></script>
+<script type="text/javascript" src="${context}/js/svgNavigate.js"></script>
+<script type="text/javascript" src="${context}/js/azkaban.context.menu.js"></script>
+<script type="text/javascript" src="${context}/js/azkaban.common.utils.js"></script>
+<script type="text/javascript" src="${context}/js/azkaban.svg.graph.view.js"></script>
+<script type="text/javascript" src="${context}/js/azkaban.flow.execute.view.js"></script>
+
+<div id="modalBackground" class="modalBackground2">
+<div id="execute-flow-panel" class="modal modalContainer2">
+ <h3 id="execute-flow-panel-title"></h3>
+ <a title="Close" class="modal-close closeExecPanel">x</a>
+ <div id="execute-message" class="message">
+ </div>
+
+ <div class="panel">
+ <div id="executionGraphOptions">
+ <div id="graphOptions" class="sideMenu">
+ <h3 id="flowOption" viewpanel="svgDivCustom">Flow View</h3>
+ <div>
+ <p>Right click on the jobs to disable and enable jobs in the flow.</p>
+ </div>
+ <h3 viewpanel="notificationPanel">Notification</h3>
+ <div>
+ <p>Change the addresses where success and failure emails will be sent.</p>
+ </div>
+ <h3 viewpanel="failureOptions">Failure Options</h3>
+ <div>
+ <p>Select flow behavior when a failure is detected.</p>
+ </div>
+ <h3 viewpanel="concurrentPanel">Concurrent</h3>
+ <div>
+ <p>Change the behavior of the flow if it is already running.</p>
+ </div>
+ <h3 viewpanel="flowParametersPanel">Flow Parameters</h3>
+ <div>
+ <p>Add temporary flow parameters that are used to override global properties for each job.</p>
+ </div>
+ </div>
+ </div>
+ <div id="executionGraphOptionsPanel" class="rightPanel">
+ <div id="svgDivCustom" class="svgDiv sidePanel" >
+ <svg class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
+ </svg>
+ </div>
+ <div id="notificationPanel" class="sidePanel">
+ <div>
+ <h4>Notify on Failure</h4>
+ <p>On a job failure, notify on either the first failure, and/or when the failed flow finishes.</p>
+ <input id="notifyFailureFirst" class="checkbox" type="checkbox" name="notify" value="first" checked /> <label for="notify">First Failure</label>
+ <input id="notifyFailureLast" class="checkbox" type="checkbox" name="notify" value="last"></input> <label for="notify">Flow Finished</label>
+
+
+ <h4>Failure Emails</h4>
+ <div>
+ <input id="overrideFailureEmails" type="checkbox" name="overrideFailureEmails" value="overrideFailureEmails" />
+ <label for="overrideFailureEmails">Override flow email settings</label>
+ </div>
+ <p>Notify these addresses on failure. Comma, space or semi-colon delimited list.</p>
+ <textarea id="failureEmails"></textarea>
+ </div>
+
+ <div>
+ <h4>Success Emails</h4>
+ <div>
+ <input id="overrideSuccessEmails" type="checkbox" name="overrideSuccessEmails" value="overrideSuccessEmails" />
+ <label for="overrideSuccessEmails">Override flow email settings</label>
+ </div>
+ <p>Notify when the flow finishes successfully. Comma, space or semi-colon delimited list.</p>
+ <textarea id="successEmails"></textarea>
+ </div>
+ </div>
+ <div id="failureOptions" class="failureOptions sidePanel">
+ <h4>Failure Options</h4>
+ <p>When a failure first occurs in the flow, select the execution behavior.</p>
+ <ul>
+ <li><span class="bold">Finish Current Running</span> finishes only the currently running jobs. It will not start any new jobs.</p></li>
+ <li><span class="bold">Cancel All</span> immediately kills all jobs and fails the flow.</p></li>
+ <li><span class="bold">Finish All Possible</span> will keep executing jobs as long as its dependencies are met.</p></li>
+ </ul>
+
+ <select id="failureAction" name="failureAction">
+ <option value="finishCurrent">Finish Current Running</option>
+ <option value="cancelImmediately">Cancel All</option>
+ <option value="finishPossible">Finish All Possible</option>
+ </select>
+ </div>
+ <div id="concurrentPanel" class="sidePanel">
+ <h4>Concurrent Execution Options</h4>
+ <p>If the flow is currently running, these are the options that can be set.</p>
+
+ <input id="skip" class="radio" type="radio" name="concurrent" value="skip" checked /><label for="skip">Skip Execution</label>
+ <p>Do not run flow if it is already running.</p>
+
+ <input id="ignore" class="radio" type="radio" name="concurrent" value="ignore" checked /><label for="ignore">Run Concurrently</label>
+ <p>Run the flow anyways. Previous execution is unaffected.</p>
+
+ <input id="pipeline" class="radio" type="radio" name="concurrent" value="pipeline" /><label for="pipeline">Pipeline</label>
+ <select id="pipelineLevel" name="pipelineLevel">
+ <option value="1">Level 1</option>
+ <option value="2">Level 2</option>
+ </select>
+ <p>Pipeline the flow, so the current execution will not be overrun.</p>
+ <ul>
+ <li>Level 1: block job A until the previous flow job A has completed.</li>
+ <li>Level 2: block job A until the previous flow job A's children have completed.</li>
+ </ul>
+ <!--
+ <input id="queue" class="radio" type="radio" name="concurrent" value="queue" /><label for="queue">Queue Job</label>
+ <select id="queueLevel" name="queueLevel">
+ <option value="1">1</option>
+ <option value="2">2</option>
+ </select>
+ <p>Queue up to 2. Wait until the previous execution has completed before running.</p>
+ -->
+ </div>
+ <div id="flowParametersPanel" class="sidePanel">
+ <h4>Flow Property Override</h4>
+ <div id="editTable" class="tableDiv">
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr id="addRow" class="addRow"><td id="addRow-col" colspan="2"><span class="addIcon"></span><a>Add Row</a></td></tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="actions">
+ #if(!$show_schedule || $show_schedule == 'true')
+ <a class="btn2" id="schedule-btn">Schedule</a>
+ #end
+
+#*
+ #if( $triggerPlugins.size() > 0 )
+ #foreach( $triggerPlugin in $triggerPlugins )
+ <a class="btn2" id=set-$triggerPlugin.pluginName>$triggerPlugin.pluginName</a>
+ #end
+ #end
+*#
+ <a class="yes btn1" id="execute-btn">Execute</a>
+ <a class="no simplemodal-close btn3 closeExecPanel">Cancel</a>
+ </div>
+</div>
+</div>
+
+#if(!$show_schedule || $show_schedule == 'true')
+#parse( "azkaban/webapp/servlet/velocity/schedulepanel.vm" )
+#end
+
+#*
+#if( $triggerPlugins.size() > 0 )
+ #foreach( $triggerPlugin in $triggerPlugins )
+ #set ($prefix = $triggerPlugin.pluginName )
+ #set ($webpath = $triggerPlugin.pluginPath )
+ #parse( $triggerPlugin.inputPanelVM )
+ #end
+#end
+*#
+
+<div id="contextMenu">
+
+</div>
diff --git a/src/java/azkaban/webapp/servlet/velocity/index.vm b/src/java/azkaban/webapp/servlet/velocity/index.vm
index 86427cc..2887e91 100644
--- a/src/java/azkaban/webapp/servlet/velocity/index.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/index.vm
@@ -174,7 +174,7 @@
## Modal dialog to be displayed when the user sesion is invalid. -->
- <div class="modal fade">
+ <div class="modal fade" id="invalid-session-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
diff --git a/src/java/azkaban/webapp/servlet/velocity/javascript.vm b/src/java/azkaban/webapp/servlet/velocity/javascript.vm
index 2b2c316..40b35fd 100644
--- a/src/java/azkaban/webapp/servlet/velocity/javascript.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/javascript.vm
@@ -16,6 +16,7 @@
<script type="text/javascript" src="${context}/js/jquery/jquery-1.9.1.js"></script>
<script type="text/javascript" src="${context}/js/bootstrap.min.js"></script>
+ <script type="text/javascript" src="${context}/js/bootstrap-fileinput.js"></script>
<script type="text/javascript" src="${context}/js/underscore-1.4.4-min.js"></script>
<script type="text/javascript" src="${context}/js/namespace.js"></script>
<script type="text/javascript" src="${context}/js/backbone-0.9.10-min.js"></script>
diff --git a/src/java/azkaban/webapp/servlet/velocity/messagedialog2.vm b/src/java/azkaban/webapp/servlet/velocity/messagedialog2.vm
new file mode 100644
index 0000000..f50118a
--- /dev/null
+++ b/src/java/azkaban/webapp/servlet/velocity/messagedialog2.vm
@@ -0,0 +1,34 @@
+#*
+ * Copyright 2012 LinkedIn Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+*#
+
+ <script type="text/javascript" src="${context}/js/azkaban.message.dialog2.view.js"></script>
+
+ <div class="modal fade" id="azkaban-message-dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title" id="azkaban-message-dialog-title"></h4>
+ </div>
+ <div class="modal-body">
+ <p id="azkaban-message-dialog-text"></p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-primary" id="continue-btn">Continue</button>
+ </div>
+ </div>
+ </div>
+ </div>
src/java/azkaban/webapp/servlet/velocity/projectpage.vm 326(+189 -137)
diff --git a/src/java/azkaban/webapp/servlet/velocity/projectpage.vm b/src/java/azkaban/webapp/servlet/velocity/projectpage.vm
index ef3d281..7d11650 100644
--- a/src/java/azkaban/webapp/servlet/velocity/projectpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/projectpage.vm
@@ -15,22 +15,15 @@
*#
<!DOCTYPE html>
-<html>
+<html lang="en">
<head>
-#parse( "azkaban/webapp/servlet/velocity/style.vm" )
- <script type="text/javascript" src="${context}/js/jquery/jquery-1.9.1.js"></script>
- <script type="text/javascript" src="${context}/js/jqueryui/jquery-ui-1.10.1.custom.js"></script>
-
- <script type="text/javascript" src="${context}/js/underscore-1.4.4-min.js"></script>
- <script type="text/javascript" src="${context}/js/namespace.js"></script>
- <script type="text/javascript" src="${context}/js/backbone-0.9.10-min.js"></script>
- <script type="text/javascript" src="${context}/js/jquery.simplemodal-1.4.4.js"></script>
+
+#parse ("azkaban/webapp/servlet/velocity/style2.vm")
+#parse ("azkaban/webapp/servlet/velocity/javascript.vm")
+
<script type="text/javascript" src="${context}/js/azkaban.ajax.utils.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.nav.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.project.view.js"></script>
-
- <link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
-
<script type="text/javascript">
var contextURL = "${context}";
var currentTime = ${currentTime};
@@ -39,149 +32,208 @@
var successMessage = null;
var projectId = ${project.id};
-
var execAccess = ${exec};
var projectName = "$project.name";
</script>
</head>
<body>
-#set($current_page="all")
-#parse( "azkaban/webapp/servlet/velocity/nav.vm" )
- <div class="messaging"><p id="messageClose">X</p><p id="message"></p></div>
- <div class="content">
-#if($errorMsg)
- <div class="box-error-message"><pre>$errorMsg</pre></div>
-#else
-#if($error_message != "null")
- <div class="box-error-message"><pre>$error_message</pre></div>
-#elseif($success_message != "null")
- <div class="box-success-message"><pre>$success_message</pre></div>
-#end
+#set ($current_page="all")
+#parse ("azkaban/webapp/servlet/velocity/nav2.vm")
- <div id="all-jobs-content">
- <div class="section-hd">
- <h2><a href="${context}/manager?project=${project.name}">Project <span>$project.name</span></a></h2>
- <a id="project-upload-btn" class="btn1 projectupload">Upload</a>
- <a id="project-permission-btn" class="btn5 projectpermission" href="${context}/manager?project=${project.name}&permissions">Permissions</a>
- #if($admin)
- <a id="project-logs-btn" class="btn2" href="${context}/manager?project=${project.name}&logs">Project Logs</a>
- <a id="project-delete-btn" class="btn6">Delete Project</a>
- #end
- </div><!-- end .section-hd -->
- </div>
+ <div class="container">
- <div id="project-users">
- <table class="user-table">
- <tr><td class="first">Project Admins:</td><td>$admins</td></tr>
- <tr><td class="first">Your Permissions:</td><td>$userpermission.toString()</td></tr>
- </table>
- </div>
+## Page error or success message.
- <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.createTimestamp)</td></tr>
- <tr><td class="first">Modified Date:</td><td>$utils.formatDate($project.lastModifiedTimestamp)</td></tr>
- <tr><td class="first">Modified by:</td><td>$project.lastModifiedUser</td></tr>
- <tr><td class="first">Description:</td><td id="pdescription">$project.description</td>
- #if($admin)
- <td><div id="edit" class="btn5">Edit Description</div></td>
- #end
- </tr>
- </table>
+#if ($errorMsg)
+ <div class="panel panel-danger">
+ <div class="panel-heading">Error</div>
+ <div class="panel-body">
+ $errorMsg
</div>
-
- <div id="flow-tabs">
- <table id="all-jobs" class="all-jobs job-table">
- <thead>
- <tr>
- <th class="tb-name">Flow Name</th>
- </tr>
- </thead>
- <tbody>
-#if($flows)
-#foreach($flow in $flows)
- <tr class="row flowrow">
- <td class="tb-name" flow="${flow.id}" project="${project.name}">
- <div class="jobfolder expand" id="${flow.id}">
- <span class="state-icon"></span>
- <a href="${context}/manager?project=${project.name}&flow=${flow.id}">${flow.id}</a>
- </div>
- #if (${exec})
- <div class="job-hover-menu">
- <div class="btn1 executeFlow" flowId="${flow.id}">Execute Flow</div>
- </div>
- #end
- </td>
- </tr>
- <tr class="childrow" id="${flow.id}-child" style="display: none;">
- <td class="expandedFlow">
- <table class="innerTable">
- <thead>
- <tr><th class="tb-name">Jobs</th></tr>
- </thead>
- <tbody id="${flow.id}-tbody">
- </tbody>
- </table>
- </td>
- </tr>
-#end
-#else
- <tr><td class="last">No flows uploaded to this project.</td></tr>
-#end
- </tbody>
- </table>
</div>
-#end
- </div>
-
- <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" /></dd>
- <input type="hidden" name="project" value="$project.name" />
- <input type="hidden" name="action" value="upload" />
- </dl>
- </fieldset>
- </form>
+#else
+ #if ($error_message != "null")
+ <div class="alert alert-danger">$error_message</div>
+ #elseif ($success_message != "null")
+ <div class="alert alert-success">$success_message</div>
+ #end
+
+## Alert message
+
+ <div class="alert alert-dismissable" id="messaging">
+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
+ <p id="messaging-message"></p>
</div>
- <div class="actions">
- <a class="yes btn2" id="upload-btn" href="#">Upload</a>
- <a class="no simplemodal-close btn3" href="#">Cancel</a>
+
+ <div class="page-header">
+ <h1><a href="${context}/manager?project=${project.name}">Project <small>$project.name</small></a></h1>
+ <p>$project.description</p>
</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 class="row">
+ <div class="col-lg-8">
+ <div class="panel panel-default">
+ <div class="panel-heading">Flows</div>
+ <div class="panel-body">
+
+ #if ($flows)
+ #foreach ($flow in $flows)
+ <div class="panel panel-info" flow="${flow.id}" project="${project.name}">
+ <div class="panel-heading" id="${flow.id}">
+ <span>+</span>
+ <a href="${context}/manager?project=${project.name}&flow=${flow.id}">${flow.id}</a>
+ </div>
+ <div class="panel-body">
+ <div class="panel panel-default">
+ <div class="panel-heading">Jobs</div>
+ <div class="panel-body" id="${flow.id}-body">
+ </div>
+ </div>
+ </div>
+ <div class="panel-footer">
+ #if (${exec})
+ <button type="button" class="btn btn-primary" flowId="${flow.id}">Execute Flow</button>
+ #end
+ </div>
+ </div>
+ #end
+ #else
+ <p>No flows uploaded to this project</p>
+ #end
+ </div>
+ </div>
+ </div><!-- /col-lg-8 -->
+
+ <div class="col-lg-4">
+ <div class="well">
+ <table>
+ <tbody>
+ <tr><th>Name:</td><td>$project.name</td></tr>
+ <tr><th>Created Date:</td><td>$utils.formatDate($project.createTimestamp)</td></tr>
+ <tr><th>Modified Date:</td><td>$utils.formatDate($project.lastModifiedTimestamp)</td></tr>
+ <tr><th>Modified by:</td><td>$project.lastModifiedUser</td></tr>
+ </tbody>
+ </table>
+
+ <hr>
+
+ <table>
+ <tbody>
+ <tr><th>Project Admins:</td><td>$admins</td></tr>
+ <tr><th>Your Permissions:</td><td>$userpermission.toString()</td></tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="well">
+ <button id="project-upload-btn" class="btn btn-primary">Upload</button>
+ <button id="project-delete-btn" class="btn btn-danger">Delete Project</button>
+ </div>
+
+ <div class="well well-sm">
+ <ul class="nav nav-pills nav-stacked">
+ <li class="active"><a href="${context}/manager?project=${project.name}">Project</a></li>
+ <li><a id="project-permission-btn" href="${context}/manager?project=${project.name}&permissions">Permissions</a></li>
+ #if ($admin)
+ <li><a id="project-logs-btn" href="${context}/manager?project=${project.name}&logs">Project Logs</a></li>
+ #end
+ </ul>
+ </div>
+ </div><!-- /col-lg-4 -->
+ </div><!-- /row -->
+
+## Upload project modal
+
+ <div class="modal fade" id="upload-project-modal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">Upload Project Files</h4>
+ </div>
+ <div class="modal-body">
+ <div class="alert alert-danger" id="modal-error-msg">$error_msg</div>
+ <form id="upload-form" enctype="multipart/form-data" method="post" action="$!context/manager">
+ <fieldset class="form-horizontal">
+ <div class="form-group">
+ <label for="path" class="col-sm-2 control-label">Job Archive</label>
+ <div class="col-sm-10">
+ <div class="fileinput fileinput-new" data-provides="datainput">
+ <div class="form-control uneditable-input span3" data-trigger="fileinput">
+ <i class="glyphicon glyphicon-file fileinput-exists"></i>
+ <span class="fileinput-filename"></span>
+ </div>
+ <span class="input-group-addon btn btn-default btn-file">
+ <span class="fileinput-new">Select file</span>
+ <span class="fileinput-exists">Change</span>
+ <input type="file" name="...">
+ </span>
+ <a href="#" class="input-group-addon btn btn-default fileinput-exists" data-dismiss="fileinput">Remove</a>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+ <div class="modal-footer">
+ <input type="hidden" name="project" value="$project.name">
+ <input type="hidden" name="action" value="upload">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button type="button" class="btn btn-primary" id="upload-btn">Upload</button>
+ </div>
+ </div>
</div>
</div>
- </div>
- <div id="delete-project" class="modal">
- <h3>Delete Project</h3>
- <div class="warn">
- <div class="warning-icon"></div>
- <div class="warning-message"><p>Warning: This project will be deleted and may not be recoverable.</p></div>
+
+## Modal dialog to be displayed when the user sesion is invalid. -->
+
+ <div class="modal fade" id="invalid-session-modal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">Invalid Session</h4>
+ </div>
+ <div class="modal-body">
+ <p>Session has expired. Please re-login.</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-primary" id="login-btn">Re-login</button>
+ </div>
+ </div>
+ </div>
</div>
- <form id="delete-form">
- <input type="hidden" name="project" value="$project.name" />
- <input type="hidden" name="delete" value="true" />
- </form>
+
+## Delete project modal.
- <div class="actions">
- <a class="no simplemodal-close btn3" href="#">Cancel</a>
- <a class="yes btn6" id="delete-btn" href="#">Yes</a>
+ <div class="modal fade" id="delete-project-modal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title">Delete Project</h4>
+ </div>
+ <div class="modal-body">
+ <p>Warning: This project will be deleted and may not be recoverable.</p>
+ </div>
+ <div class="modal-footer">
+ <input type="hidden" name="project" value="$project.name">
+ <input type="hidden" name="delete" value="true">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button type="button" class="btn btn-primary" id="delete-btn">Yes</button>
+ </div>
+ </div>
+ </div>
</div>
- </div>
- #parse( "azkaban/webapp/servlet/velocity/flowexecutionpanel.vm" )
- #parse( "azkaban/webapp/servlet/velocity/messagedialog.vm" )
+
+ #parse ("azkaban/webapp/servlet/velocity/flowexecutionpanel2.vm")
+ #parse ("azkaban/webapp/servlet/velocity/messagedialog2.vm")
+#end
+
+#parse ("azkaban/webapp/servlet/velocity/footer.vm")
+
+ </div><!-- /container -->
</body>
-
</html>
diff --git a/src/java/azkaban/webapp/servlet/velocity/style2.vm b/src/java/azkaban/webapp/servlet/velocity/style2.vm
index 8411f84..226ea13 100644
--- a/src/java/azkaban/webapp/servlet/velocity/style2.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/style2.vm
@@ -20,6 +20,7 @@
<link rel="shortcut icon" href="${context}/favicon.ico" />
<!-- Bootstrap core CSS -->
<link href="/css/bootstrap.min.css" rel="stylesheet">
+ <link href="/css/bootstrap-fileinput.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
src/web/css/bootstrap-fileinput.css 93(+93 -0)
diff --git a/src/web/css/bootstrap-fileinput.css b/src/web/css/bootstrap-fileinput.css
new file mode 100644
index 0000000..1929506
--- /dev/null
+++ b/src/web/css/bootstrap-fileinput.css
@@ -0,0 +1,93 @@
+
+.fileinput {
+ display: inline-block;
+ margin-bottom: 9px;
+}
+
+.fileinput .uneditable-input {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ cursor: text;
+}
+
+.fileinput .thumbnail {
+ display: inline-block;
+ margin-bottom: 5px;
+ overflow: hidden;
+ text-align: center;
+ vertical-align: middle;
+}
+
+.fileinput .thumbnail > img {
+ max-height: 100%;
+}
+
+.fileinput .btn {
+ vertical-align: middle;
+}
+
+.fileinput-exists .fileinput-new,
+.fileinput-new .fileinput-exists {
+ display: none;
+}
+
+.fileinput-inline .fileinput-controls {
+ display: inline;
+}
+
+.fileinput .uneditable-input {
+ white-space: normal;
+}
+
+.fileinput-new .input-group .btn-file {
+ border-radius: 0 4px 4px 0;
+}
+
+.fileinput-new .input-group .btn-file.btn-xs,
+.fileinput-new .input-group .btn-file.btn-sm {
+ border-radius: 0 3px 3px 0;
+}
+
+.fileinput-new .input-group .btn-file.btn-lg {
+ border-radius: 0 6px 6px 0;
+}
+
+.form-group.has-warning .fileinput .uneditable-input {
+ color: #c09853;
+ border-color: #faebcc;
+}
+
+.form-group.has-warning .fileinput .fileinput-preview {
+ color: #c09853;
+}
+
+.form-group.has-warning .fileinput .thumbnail {
+ border-color: #faebcc;
+}
+
+.form-group.has-error .fileinput .uneditable-input {
+ color: #b94a48;
+ border-color: #ebccd1;
+}
+
+.form-group.has-error .fileinput .fileinput-preview {
+ color: #b94a48;
+}
+
+.form-group.has-error .fileinput .thumbnail {
+ border-color: #ebccd1;
+}
+
+.form-group.has-success .fileinput .uneditable-input {
+ color: #468847;
+ border-color: #d6e9c6;
+}
+
+.form-group.has-success .fileinput .fileinput-preview {
+ color: #468847;
+}
+
+.form-group.has-success .fileinput .thumbnail {
+ border-color: #d6e9c6;
+}
src/web/js/bootstrap.fileinput.js 179(+179 -0)
diff --git a/src/web/js/bootstrap.fileinput.js b/src/web/js/bootstrap.fileinput.js
new file mode 100644
index 0000000..88c26c2
--- /dev/null
+++ b/src/web/js/bootstrap.fileinput.js
@@ -0,0 +1,179 @@
+/* ===========================================================
+ * Bootstrap: fileinput.js v3.0.0-p7
+ * http://jasny.github.com/bootstrap/javascript.html#fileinput
+ * ===========================================================
+ * Copyright 2012 Jasny BV, Netherlands.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
++function ($) { "use strict";
+
+ var isIE = window.navigator.appName == 'Microsoft Internet Explorer'
+
+ // FILEUPLOAD PUBLIC CLASS DEFINITION
+ // =================================
+
+ var Fileupload = function (element, options) {
+ this.$element = $(element)
+
+ this.$input = this.$element.find(':file')
+ if (this.$input.length === 0) return
+
+ this.name = this.$input.attr('name') || options.name
+
+ this.$hidden = this.$element.find('input[type=hidden][name="'+this.name+'"]')
+ if (this.$hidden.length === 0) {
+ this.$hidden = $('<input type="hidden" />')
+ this.$element.prepend(this.$hidden)
+ }
+
+ this.$preview = this.$element.find('.fileinput-preview')
+ var height = this.$preview.css('height')
+ if (this.$preview.css('display') != 'inline' && height != '0px' && height != 'none') this.$preview.css('line-height', height)
+
+ this.original = {
+ exists: this.$element.hasClass('fileinput-exists'),
+ preview: this.$preview.html(),
+ hiddenVal: this.$hidden.val()
+ }
+
+ this.listen()
+ }
+
+ Fileupload.prototype.listen = function() {
+ this.$input.on('change.bs.fileinput', $.proxy(this.change, this))
+ $(this.$input[0].form).on('reset.bs.fileinput', $.proxy(this.reset, this))
+
+ this.$element.find('[data-trigger="fileinput"]').on('click.bs.fileinput', $.proxy(this.trigger, this))
+ this.$element.find('[data-dismiss="fileinput"]').on('click.bs.fileinput', $.proxy(this.clear, this))
+ },
+
+ Fileupload.prototype.change = function(e) {
+ if (e.target.files === undefined) e.target.files = e.target && e.target.value ? [ {name: e.target.value.replace(/^.+\\/, '')} ] : []
+ if (e.target.files.length === 0) return
+
+ this.$hidden.val('')
+ this.$hidden.attr('name', '')
+ this.$input.attr('name', this.name)
+
+ var file = e.target.files[0]
+
+ if (this.$preview.length > 0 && (typeof file.type !== "undefined" ? file.type.match('image.*') : file.name.match(/\.(gif|png|jpe?g)$/i)) && typeof FileReader !== "undefined") {
+ var reader = new FileReader()
+ var preview = this.$preview
+ var element = this.$element
+
+ reader.onload = function(re) {
+ var $img = $('<img>').attr('src', re.target.result)
+ e.target.files[0].result = re.target.result
+
+ element.find('.fileinput-filename').text(file.name)
+
+ // if parent has max-height, using `(max-)height: 100%` on child doesn't take padding and border into account
+ if (preview.css('max-height') != 'none') $img.css('max-height', parseInt(preview.css('max-height'), 10) - parseInt(preview.css('padding-top'), 10) - parseInt(preview.css('padding-bottom'), 10) - parseInt(preview.css('border-top'), 10) - parseInt(preview.css('border-bottom'), 10))
+
+ preview.html($img)
+ element.addClass('fileinput-exists').removeClass('fileinput-new')
+
+ element.trigger('change.bs.fileinput', e.target.files)
+ }
+
+ reader.readAsDataURL(file)
+ } else {
+ this.$element.find('.fileinput-filename').text(file.name)
+ this.$preview.text(file.name)
+
+ this.$element.addClass('fileinput-exists').removeClass('fileinput-new')
+
+ this.$element.trigger('change.bs.fileinput')
+ }
+ },
+
+ Fileupload.prototype.clear = function(e) {
+ if (e) e.preventDefault()
+
+ this.$hidden.val('')
+ this.$hidden.attr('name', this.name)
+ this.$input.attr('name', '')
+
+ //ie8+ doesn't support changing the value of input with type=file so clone instead
+ if (isIE) {
+ var inputClone = this.$input.clone(true);
+ this.$input.after(inputClone);
+ this.$input.remove();
+ this.$input = inputClone;
+ } else {
+ this.$input.val('')
+ }
+
+ this.$preview.html('')
+ this.$element.find('.fileinput-filename').text('')
+ this.$element.addClass('fileinput-new').removeClass('fileinput-exists')
+
+ if (e !== false) {
+ this.$input.trigger('change')
+ this.$element.trigger('clear.bs.fileinput')
+ }
+ },
+
+ Fileupload.prototype.reset = function() {
+ this.clear(false)
+
+ this.$hidden.val(this.original.hiddenVal)
+ this.$preview.html(this.original.preview)
+ this.$element.find('.fileinput-filename').text('')
+
+ if (this.original.exists) this.$element.addClass('fileinput-exists').removeClass('fileinput-new')
+ else this.$element.addClass('fileinput-new').removeClass('fileinput-exists')
+
+ this.$element.trigger('reset.bs.fileinput')
+ },
+
+ Fileupload.prototype.trigger = function(e) {
+ this.$input.trigger('click')
+ e.preventDefault()
+ }
+
+
+ // FILEUPLOAD PLUGIN DEFINITION
+ // ===========================
+
+ $.fn.fileinput = function (options) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('fileinput')
+ if (!data) $this.data('fileinput', (data = new Fileupload(this, options)))
+ if (typeof options == 'string') data[options]()
+ })
+ }
+
+ $.fn.fileinput.Constructor = Fileupload
+
+
+ // FILEUPLOAD DATA-API
+ // ==================
+
+ $(document).on('click.fileinput.data-api', '[data-provides="fileinput"]', function (e) {
+ var $this = $(this)
+ if ($this.data('fileinput')) return
+ $this.fileinput($this.data())
+
+ var $target = $(e.target).closest('[data-dismiss="fileinput"],[data-trigger="fileinput"]');
+ if ($target.length > 0) {
+ e.preventDefault()
+ $target.trigger('click.bs.fileinput')
+ }
+ })
+
+}(window.jQuery);