Ver código fonte

HUE-8550 [jb] Use the context selector component in the job browser

Johan Ahlen 7 anos atrás
pai
commit
67a4af1

+ 230 - 256
apps/jobbrowser/src/jobbrowser/templates/job_browser.mako

@@ -79,9 +79,8 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
   </a>
 % endif
 
-
-% if not is_mini:
-<div class="navbar hue-title-bar">
+  % if not is_mini:
+  <div class="navbar hue-title-bar">
     <div class="navbar-inner">
       <div class="container-fluid">
         <div class="nav-collapse">
@@ -98,241 +97,245 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
               </li>
             <!-- /ko -->
           </ul>
-          <span class="pull-right">
-            <!-- ko if: availableComputes().length > 1 -->
-              <div data-bind="component: { name: 'hue-drop-down', params: { value: compute, entries: availableComputes, labelAttribute: 'name', searchable: true, linkTitle: '${ _ko('Active clusters') }' } }"></div>
-            <!-- /ko -->
-          </span>
+          <div class="pull-right" style="padding-top: 15px">
+            <!-- ko component: {
+              name: 'hue-context-selector',
+              params: {
+                sourceType: 'jobs',
+                compute: compute,
+                showDatabases: false,
+                showNamespaces: false
+              }
+            } --><!-- /ko -->
+          </div>
           % if not hiveserver2_impersonation_enabled:
             <div class="pull-right label label-warning" style="margin-top: 16px">${ _("Hive jobs are running as the 'hive' user") }</div>
           % endif
         </div>
       </div>
     </div>
-</div>
-% endif
+  </div>
+  % endif
 
 
-<script type="text/html" id="apps-list${ SUFFIX }">
-  <table data-bind="attr: {id: tableId}" class="datatables table table-condensed status-border-container">
-    <thead>
-    <tr>
-      <th width="1%" class="vertical-align-middle">
-        <div class="select-all hue-checkbox fa" data-bind="hueCheckAll: { allValues: apps, selectedValues: selectedJobs }"></div>
-      </th>
-      <th width="20%">${_('Name')}</th>
-      <th width="6%">${_('User')}</th>
-      <th width="6%">${_('Type')}</th>
-      <th width="5%">${_('Status')}</th>
-      <th width="3%">${_('Progress')}</th>
-      <th width="5%">${_('Group')}</th>
-      <th width="10%" data-bind="text: $root.interface() != 'schedules' ? '${_('Started')}' : '${_('Modified')}'"></th>
-      <th width="6%">${_('Duration')}</th>
-      <th width="15%">${_('Id')}</th>
-    </tr>
-    </thead>
-    <tbody data-bind="foreach: apps">
-      <tr class="status-border pointer" data-bind="css: {'completed': apiStatus() == 'SUCCEEDED', 'info': apiStatus() == 'PAUSED', 'running': apiStatus() == 'RUNNING', 'failed': apiStatus() == 'FAILED'}, click: fetchJob">
-        <td data-bind="click: function() {}, clickBubble: false">
-          <div class="hue-checkbox fa" data-bind="click: function() {}, clickBubble: false, multiCheck: '#' + $parent.tableId, value: $data, hueChecked: $parent.selectedJobs"></div>
-        </td>
-        <td data-bind="text: name"></td>
-        <td data-bind="text: user"></td>
-        <td data-bind="text: type"></td>
-        <td data-bind="text: status"></td>
-        <td data-bind="text: $root.formatProgress(progress)"></td>
-        <td data-bind="text: queue"></td>
-        <td data-bind="moment: {data: submitted, format: 'LLL'}"></td>
-        <td data-bind="text: duration().toHHMMSS()"></td>
-        <td data-bind="text: id"></td>
+  <script type="text/html" id="apps-list${ SUFFIX }">
+    <table data-bind="attr: {id: tableId}" class="datatables table table-condensed status-border-container">
+      <thead>
+      <tr>
+        <th width="1%" class="vertical-align-middle">
+          <div class="select-all hue-checkbox fa" data-bind="hueCheckAll: { allValues: apps, selectedValues: selectedJobs }"></div>
+        </th>
+        <th width="20%">${_('Name')}</th>
+        <th width="6%">${_('User')}</th>
+        <th width="6%">${_('Type')}</th>
+        <th width="5%">${_('Status')}</th>
+        <th width="3%">${_('Progress')}</th>
+        <th width="5%">${_('Group')}</th>
+        <th width="10%" data-bind="text: $root.interface() != 'schedules' ? '${_('Started')}' : '${_('Modified')}'"></th>
+        <th width="6%">${_('Duration')}</th>
+        <th width="15%">${_('Id')}</th>
       </tr>
-    </tbody>
-  </table>
-</script>
-
+      </thead>
+      <tbody data-bind="foreach: apps">
+        <tr class="status-border pointer" data-bind="css: {'completed': apiStatus() == 'SUCCEEDED', 'info': apiStatus() == 'PAUSED', 'running': apiStatus() == 'RUNNING', 'failed': apiStatus() == 'FAILED'}, click: fetchJob">
+          <td data-bind="click: function() {}, clickBubble: false">
+            <div class="hue-checkbox fa" data-bind="click: function() {}, clickBubble: false, multiCheck: '#' + $parent.tableId, value: $data, hueChecked: $parent.selectedJobs"></div>
+          </td>
+          <td data-bind="text: name"></td>
+          <td data-bind="text: user"></td>
+          <td data-bind="text: type"></td>
+          <td data-bind="text: status"></td>
+          <td data-bind="text: $root.formatProgress(progress)"></td>
+          <td data-bind="text: queue"></td>
+          <td data-bind="moment: {data: submitted, format: 'LLL'}"></td>
+          <td data-bind="text: duration().toHHMMSS()"></td>
+          <td data-bind="text: id"></td>
+        </tr>
+      </tbody>
+    </table>
+  </script>
+
+
+  <div class="main-content">
+    <div class="vertical-full container-fluid" data-bind="style: { 'padding-left' : $root.isLeftPanelVisible() ? '0' : '20px' }">
+      <div class="vertical-full">
+        <div class="vertical-full row-fluid panel-container">
+          % if not is_embeddable:
+          <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
+            <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
+              <i class="fa fa-chevron-left"></i>
+            </a>
+            <div class="assist" data-bind="component: {
+                name: 'assist-panel',
+                params: {
+                  user: '${user.username}',
+                  sql: {
+                    navigationSettings: {
+                      openItem: false,
+                      showStats: true
+                    }
+                  },
+                  visibleAssistPanels: ['sql']
+                }
+              }"></div>
+          </div>
+          <div class="resizer" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable(), splitDraggable : { appName: 'notebook', leftPanelVisible: $root.isLeftPanelVisible }"><div class="resize-bar">&nbsp;</div></div>
+          % endif
 
-<div class="main-content">
-  <div class="vertical-full container-fluid" data-bind="style: { 'padding-left' : $root.isLeftPanelVisible() ? '0' : '20px' }">
-    <div class="vertical-full">
-      <div class="vertical-full row-fluid panel-container">
-        % if not is_embeddable:
-        <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
-          <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
-            <i class="fa fa-chevron-left"></i>
-          </a>
-          <div class="assist" data-bind="component: {
-              name: 'assist-panel',
-              params: {
-                user: '${user.username}',
-                sql: {
-                  navigationSettings: {
-                    openItem: false,
-                    showStats: true
-                  }
-                },
-                visibleAssistPanels: ['sql']
-              }
-            }"></div>
-        </div>
-        <div class="resizer" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable(), splitDraggable : { appName: 'notebook', leftPanelVisible: $root.isLeftPanelVisible }"><div class="resize-bar">&nbsp;</div></div>
-        % endif
+          <div class="content-panel">
+            <div class="content-panel-inner">
+              <!-- ko if: $root.job() -->
+              <div data-bind="template: { name: 'breadcrumbs${ SUFFIX }' }"></div>
+              <!-- /ko -->
 
-        <div class="content-panel">
-          <div class="content-panel-inner">
-            <!-- ko if: $root.job() -->
-            <div data-bind="template: { name: 'breadcrumbs${ SUFFIX }' }"></div>
-            <!-- /ko -->
+              <!-- ko if: interface() !== 'slas' && interface() !== 'oozie-info' -->
+              <!-- ko if: !$root.job() -->
+              <form class="form-inline">
+                <!-- ko if: !$root.isMini() && interface() == 'queries' -->
+                  ${ _('Impala queries from') }
+                <!-- /ko -->
+                <input type="text" class="input-large" data-bind="clearable: jobs.textFilter, valueUpdate: 'afterkeydown'" placeholder="${_('Filter by id, name, user...')}" />
+                <!-- ko if: jobs.statesValuesFilter -->
+                <span data-bind="foreach: jobs.statesValuesFilter">
+                  <label class="checkbox">
+                    <div class="pull-left margin-left-5 status-border status-content" data-bind="css: value, hueCheckbox: checked"></div>
+                    <div class="inline-block" data-bind="text: name, toggle: checked"></div>
+                  </label>
+                </span>
+                <!-- /ko -->
 
-            <!-- ko if: interface() !== 'slas' && interface() !== 'oozie-info' -->
-            <!-- ko if: !$root.job() -->
-            <form class="form-inline">
-              <!-- ko if: !$root.isMini() && interface() == 'queries' -->
-                ${ _('Impala queries from') }
-              <!-- /ko -->
-              <input type="text" class="input-large" data-bind="clearable: jobs.textFilter, valueUpdate: 'afterkeydown'" placeholder="${_('Filter by id, name, user...')}" />
-              <!-- ko if: jobs.statesValuesFilter -->
-              <span data-bind="foreach: jobs.statesValuesFilter">
-                <label class="checkbox">
-                  <div class="pull-left margin-left-5 status-border status-content" data-bind="css: value, hueCheckbox: checked"></div>
-                  <div class="inline-block" data-bind="text: name, toggle: checked"></div>
-                </label>
-              </span>
-              <!-- /ko -->
+                <!-- ko ifnot: $root.isMini -->
+                <!-- ko if: $root.interface() !== 'schedules' && $root.interface() !== 'bundles' -->
+                  ${_('in the last')} <input class="input-mini no-margin" type="number" min="1" max="3650" data-bind="value: jobs.timeValueFilter">
+                  <select class="input-small no-margin" data-bind="value: jobs.timeUnitFilter, options: jobs.timeUnitFilterUnits, optionsText: 'name', optionsValue: 'value'">
+                    <option value="days">${_('days')}</option>
+                    <option value="hours">${_('hours')}</option>
+                    <option value="minutes">${_('minutes')}</option>
+                  </select>
+
+                  <a class="btn" title="${ _('Refresh') }" data-bind="click: jobs.updateJobs">
+                    <i class="fa fa-refresh"></i>
+                  </a>
 
-              <!-- ko ifnot: $root.isMini -->
-              <!-- ko if: $root.interface() !== 'schedules' && $root.interface() !== 'bundles' -->
-                ${_('in the last')} <input class="input-mini no-margin" type="number" min="1" max="3650" data-bind="value: jobs.timeValueFilter">
-                <select class="input-small no-margin" data-bind="value: jobs.timeUnitFilter, options: jobs.timeUnitFilterUnits, optionsText: 'name', optionsValue: 'value'">
-                  <option value="days">${_('days')}</option>
-                  <option value="hours">${_('hours')}</option>
-                  <option value="minutes">${_('minutes')}</option>
-                </select>
-
-                <a class="btn" title="${ _('Refresh') }" data-bind="click: jobs.updateJobs">
-                  <i class="fa fa-refresh"></i>
-                </a>
-                
-                <a class="btn" title="${ _('Create cluster') }" data-bind="visible: $root.compute() && $root.compute()['type'].indexOf('altus') >= 0, click: jobs.createCluster">
-                  <i class="fa fa-plus"></i>
-                </a>
-              <!-- /ko -->
+                  <a class="btn" title="${ _('Create cluster') }" data-bind="visible: $root.compute() && $root.compute()['type'].indexOf('altus') >= 0, click: jobs.createCluster">
+                    <i class="fa fa-plus"></i>
+                  </a>
+                <!-- /ko -->
 
-              <div data-bind="template: { name: 'job-actions${ SUFFIX }', 'data': jobs }" class="pull-right"></div>
-              <!-- /ko -->
-            </form>
-
-            <div data-bind="visible: jobs.showJobCountBanner" class="pull-center alert alert-warning">${ _("Showing oldest %s jobs. Use days filter to get the recent ones.") % MAX_JOB_FETCH.get() }</div>
-
-            <div class="card card-small">
-              <!-- ko hueSpinner: { spin: jobs.loadingJobs(), center: true, size: 'xlarge' } --><!-- /ko -->
-              <!-- ko ifnot: jobs.loadingJobs() -->
-                <!-- ko if: $root.isMini -->
-                <ul class="unstyled status-border-container" id="jobsTable" data-bind="foreach: jobs.apps">
-                  <li class="status-border pointer" data-bind="css: {'completed': apiStatus() == 'SUCCEEDED', 'info': apiStatus() === 'PAUSED', 'running': apiStatus() === 'RUNNING', 'failed': apiStatus() == 'FAILED'}, click: fetchJob">
-                    <span class="muted pull-left" data-bind="momentFromNow: {data: submitted, interval: 10000, titleFormat: 'LLL'}"></span><span class="muted">&nbsp;-&nbsp;</span><span class="muted" data-bind="text: status"></span></td>
-                    <span class="muted pull-right" data-bind="text: duration().toHHMMSS()"></span>
-                    <div class="clearfix"></div>
-                    <strong class="pull-left" data-bind="text: type"></strong>
-                    <div class="inline-block pull-right"><i class="fa fa-user muted"></i> <span data-bind="text: user"></span></div>
-                    <div class="clearfix"></div>
-                    <div class="pull-left" data-bind="ellipsis: {data: name(), length: 40 }"></div>
-                    <div class="pull-right muted" data-bind="text: id"></div>
-                    <div class="clearfix"></div>
-                  </li>
-                  <div class="status-bar status-background" data-bind="css: {'running': isRunning()}, style: {'width': progress() + '%'}"></div>
-                </ul>
+                <div data-bind="template: { name: 'job-actions${ SUFFIX }', 'data': jobs }" class="pull-right"></div>
                 <!-- /ko -->
-                <!-- ko ifnot: $root.isMini -->
-                  ${ _('Running') }
-                  <div data-bind="template: { name: 'apps-list${ SUFFIX }', data: {apps: jobs.runningApps, tableId: 'runningJobsTable', selectedJobs: jobs.selectedJobs} }"></div>
+              </form>
+
+              <div data-bind="visible: jobs.showJobCountBanner" class="pull-center alert alert-warning">${ _("Showing oldest %s jobs. Use days filter to get the recent ones.") % MAX_JOB_FETCH.get() }</div>
+
+              <div class="card card-small">
+                <!-- ko hueSpinner: { spin: jobs.loadingJobs(), center: true, size: 'xlarge' } --><!-- /ko -->
+                <!-- ko ifnot: jobs.loadingJobs() -->
+                  <!-- ko if: $root.isMini -->
+                  <ul class="unstyled status-border-container" id="jobsTable" data-bind="foreach: jobs.apps">
+                    <li class="status-border pointer" data-bind="css: {'completed': apiStatus() == 'SUCCEEDED', 'info': apiStatus() === 'PAUSED', 'running': apiStatus() === 'RUNNING', 'failed': apiStatus() == 'FAILED'}, click: fetchJob">
+                      <span class="muted pull-left" data-bind="momentFromNow: {data: submitted, interval: 10000, titleFormat: 'LLL'}"></span><span class="muted">&nbsp;-&nbsp;</span><span class="muted" data-bind="text: status"></span></td>
+                      <span class="muted pull-right" data-bind="text: duration().toHHMMSS()"></span>
+                      <div class="clearfix"></div>
+                      <strong class="pull-left" data-bind="text: type"></strong>
+                      <div class="inline-block pull-right"><i class="fa fa-user muted"></i> <span data-bind="text: user"></span></div>
+                      <div class="clearfix"></div>
+                      <div class="pull-left" data-bind="ellipsis: {data: name(), length: 40 }"></div>
+                      <div class="pull-right muted" data-bind="text: id"></div>
+                      <div class="clearfix"></div>
+                    </li>
+                    <div class="status-bar status-background" data-bind="css: {'running': isRunning()}, style: {'width': progress() + '%'}"></div>
+                  </ul>
+                  <!-- /ko -->
+                  <!-- ko ifnot: $root.isMini -->
+                    ${ _('Running') }
+                    <div data-bind="template: { name: 'apps-list${ SUFFIX }', data: {apps: jobs.runningApps, tableId: 'runningJobsTable', selectedJobs: jobs.selectedJobs} }"></div>
 
-                  ${ _('Completed') }
-                  <div data-bind="template: { name: 'apps-list${ SUFFIX }', data: {apps: jobs.finishedApps, tableId: 'completedJobsTable', selectedJobs: jobs.selectedJobs} }"></div>
+                    ${ _('Completed') }
+                    <div data-bind="template: { name: 'apps-list${ SUFFIX }', data: {apps: jobs.finishedApps, tableId: 'completedJobsTable', selectedJobs: jobs.selectedJobs} }"></div>
+                  <!-- /ko -->
                 <!-- /ko -->
+              </div>
               <!-- /ko -->
-            </div>
-            <!-- /ko -->
-
-          <!-- ko if: $root.job() -->
-          <!-- ko with: $root.job() -->
-            <!-- ko if: mainType() == 'jobs' -->
-              <div class="jb-panel" data-bind="template: { name: 'job-page${ SUFFIX }' }"></div>
-            <!-- /ko -->
 
-            <!-- ko if: mainType() == 'queries' -->
-              <div class="jb-panel" data-bind="template: { name: 'queries-page${ SUFFIX }' }"></div>
-            <!-- /ko -->
+              <!-- ko if: $root.job() -->
+              <!-- ko with: $root.job() -->
+                <!-- ko if: mainType() == 'jobs' -->
+                  <div class="jb-panel" data-bind="template: { name: 'job-page${ SUFFIX }' }"></div>
+                <!-- /ko -->
 
-            <!-- ko if: mainType() == 'workflows' -->
-              <!-- ko if: type() == 'workflow' -->
-                <div class="jb-panel" data-bind="template: { name: 'workflow-page${ SUFFIX }' }"></div>
-              <!-- /ko -->
+                <!-- ko if: mainType() == 'queries' -->
+                  <div class="jb-panel" data-bind="template: { name: 'queries-page${ SUFFIX }' }"></div>
+                <!-- /ko -->
 
-              <!-- ko if: type() == 'workflow-action' -->
-                <div class="jb-panel" data-bind="template: { name: 'workflow-action-page${ SUFFIX }' }"></div>
-              <!-- /ko -->
-            <!-- /ko -->
+                <!-- ko if: mainType() == 'workflows' -->
+                  <!-- ko if: type() == 'workflow' -->
+                    <div class="jb-panel" data-bind="template: { name: 'workflow-page${ SUFFIX }' }"></div>
+                  <!-- /ko -->
 
-            <!-- ko if: mainType() == 'schedules' -->
-              <div class="jb-panel" data-bind="template: { name: 'schedule-page${ SUFFIX }' }"></div>
-            <!-- /ko -->
+                  <!-- ko if: type() == 'workflow-action' -->
+                    <div class="jb-panel" data-bind="template: { name: 'workflow-action-page${ SUFFIX }' }"></div>
+                  <!-- /ko -->
+                <!-- /ko -->
 
-            <!-- ko if: mainType() == 'bundles' -->
-              <div class="jb-panel" data-bind="template: { name: 'bundle-page${ SUFFIX }' }"></div>
-            <!-- /ko -->
+                <!-- ko if: mainType() == 'schedules' -->
+                  <div class="jb-panel" data-bind="template: { name: 'schedule-page${ SUFFIX }' }"></div>
+                <!-- /ko -->
 
-            <!-- ko if: mainType().startsWith('dataeng-job') -->
-              <div data-bind="template: { name: 'dataeng-job-page${ SUFFIX }' }"></div>
-            <!-- /ko -->
+                <!-- ko if: mainType() == 'bundles' -->
+                  <div class="jb-panel" data-bind="template: { name: 'bundle-page${ SUFFIX }' }"></div>
+                <!-- /ko -->
 
-            <!-- ko if: mainType() == 'livy-sessions' -->
-              <div class="jb-panel" data-bind="template: { name: 'livy-session-page${ SUFFIX }' }"></div>
-            <!-- /ko -->
+                <!-- ko if: mainType().startsWith('dataeng-job') -->
+                  <div data-bind="template: { name: 'dataeng-job-page${ SUFFIX }' }"></div>
+                <!-- /ko -->
 
-          <!-- /ko -->
-          <!-- /ko -->
+                <!-- ko if: mainType() == 'livy-sessions' -->
+                  <div class="jb-panel" data-bind="template: { name: 'livy-session-page${ SUFFIX }' }"></div>
+                <!-- /ko -->
+              <!-- /ko -->
+              <!-- /ko -->
 
-          <div data-bind="template: { name: 'pagination${ SUFFIX }', data: $root.jobs }, visible: !$root.job() && !jobs.loadingJobs()"></div>
-          <!-- /ko -->
+              <div data-bind="template: { name: 'pagination${ SUFFIX }', data: $root.jobs }, visible: !$root.job() && !jobs.loadingJobs()"></div>
+              <!-- /ko -->
 
-          % if not is_mini:
-          <!-- ko if: interface() === 'slas' -->
-            <!-- ko hueSpinner: { spin: slasLoading(), center: true, size: 'xlarge' } --><!-- /ko -->
-          <!-- /ko -->
-          <div id="slas" data-bind="visible: interface() === 'slas'"></div>
+              % if not is_mini:
+              <!-- ko if: interface() === 'slas' -->
+                <!-- ko hueSpinner: { spin: slasLoading(), center: true, size: 'xlarge' } --><!-- /ko -->
+              <!-- /ko -->
+              <div id="slas" data-bind="visible: interface() === 'slas'"></div>
 
-          <!-- ko if: interface() === 'oozie-info' -->
-            <!-- ko hueSpinner: { spin: oozieInfoLoading(), center: true, size: 'xlarge' } --><!-- /ko -->
-          <!-- /ko -->
-          <div id="oozieInfo" data-bind="visible: interface() === 'oozie-info'"></div>
-          %endif
+              <!-- ko if: interface() === 'oozie-info' -->
+                <!-- ko hueSpinner: { spin: oozieInfoLoading(), center: true, size: 'xlarge' } --><!-- /ko -->
+              <!-- /ko -->
+              <div id="oozieInfo" data-bind="visible: interface() === 'oozie-info'"></div>
+              %endif
+            </div>
+          </div>
         </div>
       </div>
-
     </div>
   </div>
-</div>
-</div>
 
-<!-- ko if: $root.job() -->
-  <div id="rerun-modal${ SUFFIX }" class="modal hide" data-bind="htmlUnsecure: $root.job().rerunModalContent"></div>
-<!-- /ko -->
+  <!-- ko if: $root.job() -->
+    <div id="rerun-modal${ SUFFIX }" class="modal hide" data-bind="htmlUnsecure: $root.job().rerunModalContent"></div>
+  <!-- /ko -->
 
-<!-- ko if: ($root.job() && $root.job().hasKill()) || (!$root.job() && $root.jobs.hasKill()) -->
-  <div id="killModal${ SUFFIX }" class="modal hide">
-    <div class="modal-header">
-      <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
-      <h2 class="modal-title">${_('Confirm Kill')}</h2>
-    </div>
-    <div class="modal-body">
-      <p>${_('Are you sure you want to kill the selected job(s)?')}</p>
-    </div>
-    <div class="modal-footer">
-      <a class="btn" data-dismiss="modal">${_('No')}</a>
-      <a id="killJobBtn" class="btn btn-danger disable-feedback" data-dismiss="modal" data-bind="click: function(){ if (job()) { job().control('kill'); } else { jobs.control('kill'); } }">${_('Yes')}</a>
+  <!-- ko if: ($root.job() && $root.job().hasKill()) || (!$root.job() && $root.jobs.hasKill()) -->
+    <div id="killModal${ SUFFIX }" class="modal hide">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
+        <h2 class="modal-title">${_('Confirm Kill')}</h2>
+      </div>
+      <div class="modal-body">
+        <p>${_('Are you sure you want to kill the selected job(s)?')}</p>
+      </div>
+      <div class="modal-footer">
+        <a class="btn" data-dismiss="modal">${_('No')}</a>
+        <a id="killJobBtn" class="btn btn-danger disable-feedback" data-dismiss="modal" data-bind="click: function(){ if (job()) { job().control('kill'); } else { jobs.control('kill'); } }">${_('Yes')}</a>
+      </div>
     </div>
-  </div>
-<!-- /ko -->
+  <!-- /ko -->
 </div>
 
 
@@ -496,7 +499,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 
 <script type="text/html" id="job-mapreduce-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css: {'span2': !$root.isMini(), 'span12': $root.isMini()}">
       <div class="sidebar-nav">
@@ -613,15 +615,12 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           <div data-bind="template: { name: 'render-page-counters${ SUFFIX }', data: properties['counters'] }"></div>
         </div>
       </div>
-
     </div>
   </div>
-
 </script>
 
 
 <script type="text/html" id="job-mapreduce-task-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -711,11 +710,9 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       </div>
     </div>
   </div>
-
 </script>
 
 <script type="text/html" id="job-oozie-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -797,11 +794,9 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
     </div>
   </div>
-
 </script>
 
 <script type="text/html" id="job-oozie-attempt-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -833,11 +828,9 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
     <div data-bind="css: {'span10': !$root.isMini(), 'span12': $root.isMini() }">
     </div>
   </div>
-
 </script>
 
 <script type="text/html" id="job-mapreduce-task-attempt-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -899,11 +892,10 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
     </div>
   </div>
-
 </script>
 
 <script type="text/html" id="job-spark-page${ SUFFIX }">
-   <div class="row-fluid">
+  <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
         <ul class="nav nav-list">
@@ -1012,14 +1004,12 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           </table>
         </div>
       </div>
-
     </div>
   </div>
 </script>
 
 
 <script type="text/html" id="job-spark-executor-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -1074,10 +1064,8 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           <pre data-bind="html: logs, logScroller: logs"></pre>
         </div>
       </div>
-
     </div>
   </div>
-
 </script>
 
 
@@ -1110,7 +1098,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 
 <script type="text/html" id="queries-page${ SUFFIX }">
-
   <div class="row-fluid" data-jobType="queries">
     <!-- ko if: id() -->
     <div data-bind="css: {'span2': !$root.isMini(), 'span12': $root.isMini() }">
@@ -1277,7 +1264,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 
 <script type="text/html" id="livy-session-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css: {'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -1348,7 +1334,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
             </tbody>
           </table>
         </div>
-
       </div>
     </div>
   </div>
@@ -1394,7 +1379,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 
 <script type="text/html" id="workflow-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -1600,12 +1584,10 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       </div>
     </div>
   </div>
-
 </script>
 
 
 <script type="text/html" id="schedule-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -1731,7 +1713,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 
 <script type="text/html" id="bundle-page${ SUFFIX }">
-
   <div class="row-fluid">
     <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
       <div class="sidebar-nav">
@@ -1835,7 +1816,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       </div>
     </div>
   </div>
-
 </script>
 
 <script type="text/html" id="render-properties${ SUFFIX }">
@@ -1969,29 +1949,26 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
   % else:
   <div id="job-mapreduce-page-metadata-template${ SUFFIX }" style="overflow-y: hidden; height: 400px;">
   %endif
-  <table id="jobbrowserJobMetadataTable" class="table table-condensed">
-    <thead>
-    <tr>
-      <th>${ _('Name') }</th>
-      <th width="50%">${ _('Value') }</th>
-    </tr>
-    </thead>
-    <tbody data-bind="foreachVisible: { data: property, minHeight: 20, container: '#job-mapreduce-page-metadata-template${ SUFFIX }'}">
+    <table id="jobbrowserJobMetadataTable" class="table table-condensed">
+      <thead>
       <tr>
-        <td data-bind="text: name"></td>
-        <td>
-          <!-- ko template: { name: 'link-or-text${ SUFFIX }', data: { name: name, value: value } } --><!-- /ko -->
-        </td>
+        <th>${ _('Name') }</th>
+        <th width="50%">${ _('Value') }</th>
       </tr>
-    </tbody>
-  </table>
+      </thead>
+      <tbody data-bind="foreachVisible: { data: property, minHeight: 20, container: '#job-mapreduce-page-metadata-template${ SUFFIX }'}">
+        <tr>
+          <td data-bind="text: name"></td>
+          <td>
+            <!-- ko template: { name: 'link-or-text${ SUFFIX }', data: { name: name, value: value } } --><!-- /ko -->
+          </td>
+        </tr>
+      </tbody>
+    </table>
   </div>
   <!-- /ko -->
 </script>
 
-
-
-
 <script type="text/html" id="link-or-text${ SUFFIX }">
   <!-- ko if: typeof $data.value === 'string' -->
     <!-- ko if: $data.name.indexOf('logs') > -1 -->
@@ -2768,28 +2745,25 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       self.clusterType = ko.observable();
       self.isMini = ko.observable(false);
 
-      self.availableComputes = ko.observableArray();
       self.compute = ko.observable();
-    
-      ContextCatalog.getComputes({ sourceType: 'jobs' }).done(self.availableComputes);
 
       self.availableInterfaces = ko.pureComputed(function () {
         var jobsInterfaceCondition = function () {
           return self.appConfig() && self.appConfig()['browser'] && self.appConfig()['browser']['interpreter_names'].indexOf('yarn') != -1 && self.clusterType() != '${ ANALYTIC_DB }' && (!self.compute() || self.compute()['type'].indexOf('altus') == -1);
-        }
+        };
         var dataEngInterfaceCondition = function () {
           // return self.appConfig() && self.appConfig()['browser'] && self.appConfig()['browser']['interpreter_names'].indexOf('dataeng') != -1;
           return self.compute() && self.compute()['type'].indexOf('altus') >= 0;
-        }
+        };
         var schedulerInterfaceCondition = function () {
           return '${ user.has_hue_permission(action="access", app="oozie") }' == 'True' && self.clusterType() != '${ ANALYTIC_DB }' && (!self.compute() || self.compute()['type'].indexOf('altus') == -1);
-        }
+        };
         var livyInterfaceCondition = function () {
           return self.appConfig() && self.appConfig()['editor'] && self.appConfig()['editor']['interpreter_names'].indexOf('pyspark') != -1 && (!self.compute() || self.compute()['type'].indexOf('altus') == -1);
-        }
+        };
         var queryInterfaceCondition = function () {
           return '${ ENABLE_QUERY_BROWSER.get() }' == 'True' && self.appConfig() && self.appConfig()['editor'] && self.appConfig()['editor']['interpreter_names'].indexOf('impala') != -1 && (!self.compute() || self.compute()['type'].indexOf('altus') == -1);
-        }
+        };
 
         var interfaces = [
           {'interface': 'jobs', 'label': '${ _ko('Jobs') }', 'condition': jobsInterfaceCondition},
@@ -2824,7 +2798,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
             }
           });
         }
-      }
+      };
 
       self.oozieInfoLoadedOnce = false;
       self.oozieInfoLoading = ko.observable(true);
@@ -2843,7 +2817,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
             }
           });
         }
-      }
+      };
 
       self.interface = ko.observable();
       self.isValidInterface = function(name) {
@@ -2905,7 +2879,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
             updateJobsInterval = setInterval(self.jobs.updateJobs, 20000, 'jobbrowser');
           }
         }
-      }
+      };
 
       self.breadcrumbs = ko.observableArray([]);
       self.resetBreadcrumbs = function(extraCrumbs) {
@@ -2914,7 +2888,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           crumbs = crumbs.concat(extraCrumbs);
         }
         self.breadcrumbs(crumbs);
-      }
+      };
 
       self.resetBreadcrumbs();
 
@@ -2935,7 +2909,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           return Math.round(progress*100)/100 + '%';
         }
         return progress;
-      }
+      };
 
       self.load = function() {
         var h = window.location.hash;

+ 8 - 8
desktop/core/src/desktop/templates/ko_components/ko_context_selector.mako

@@ -81,7 +81,7 @@ from desktop.views import _ko
        *   } --><!-- /ko -->
        *
        * @param {Object} params
-       * @param {ko.observable} params.sourceType
+       * @param {ko.observable|string} params.sourceType
        * @param {ko.observable} [params.compute]
        * @param {ko.observable} [params.namespace]
        * @param {ko.observable} [params.database]
@@ -106,7 +106,7 @@ from desktop.views import _ko
 
         if (params.compute) {
           self.loadingComputes(true);
-          self.lastComputesPromise = ContextCatalog.getComputes({ sourceType: self.sourceType() }).done(function (computes) {
+          self.lastComputesPromise = ContextCatalog.getComputes({ sourceType: ko.unwrap(self.sourceType) }).done(function (computes) {
             self.availableComputes(computes);
             if (!self.compute() || !computes.some(function (compute) {
               if (compute.id === self.compute().id) {
@@ -133,7 +133,7 @@ from desktop.views import _ko
         self.reloadNamespaces = function () {
           if (params.namespace) {
             self.loadingNamespaces(true);
-            self.lastNamespacePromise = ContextCatalog.getNamespaces({ sourceType: self.sourceType() }).done(function (context) {
+            self.lastNamespacePromise = ContextCatalog.getNamespaces({ sourceType: ko.unwrap(self.sourceType) }).done(function (context) {
               self.availableNamespaces(context.namespaces);
               if (!self.namespace() || !context.namespaces.some(function (namespace) {
                 if (namespace.id === self.namespace().id) {
@@ -164,7 +164,7 @@ from desktop.views import _ko
               window.clearTimeout(updateDatabaseThrottle);
               updateDatabaseThrottle = window.setTimeout(function () {
                 DataCatalog.getEntry({
-                  sourceType: self.sourceType(),
+                  sourceType: ko.unwrap(self.sourceType),
                   namespace: self.namespace(),
                   compute: self.compute(),
                   path: [],
@@ -189,7 +189,7 @@ from desktop.views import _ko
                     self.loadingDatabases(false);
 
                     huePubSub.publish('assist.set.database', {
-                      source: self.sourceType(),
+                      source: ko.unwrap(self.sourceType),
                       namespace: self.namespace(),
                       name: self.database()
                     });
@@ -197,7 +197,7 @@ from desktop.views import _ko
                 });
               }, 10);
             });
-          } else {
+          } else if (self.database) {
             self.availableDatabases([]);
             self.database(undefined);
           }
@@ -250,7 +250,7 @@ from desktop.views import _ko
         if (self.database) {
           huePubSub.subscribe('data.catalog.entry.refreshed', function (details) {
             if (details.entry.isSource()) {
-              if (self.sourceType() === details.entry.getSourceType()) {
+              if (ko.unwrap(self.sourceType) === details.entry.getSourceType()) {
                 self.updateDatabases();
               }
             }
@@ -259,7 +259,7 @@ from desktop.views import _ko
 
         if (self.namespace) {
           huePubSub.subscribe('context.catalog.namespaces.refreshed', function (sourceType) {
-            if (self.sourceType() === sourceType) {
+            if (ko.unwrap(self.sourceType) === sourceType) {
               self.reloadNamespaces();
             }
           });