Procházet zdrojové kódy

[oozie] Preliminary work for new dashboard graph

Enrico Berti před 10 roky
rodič
revize
db5709c

+ 60 - 19
apps/oozie/src/oozie/templates/dashboard/list_oozie_workflow.mako

@@ -23,6 +23,8 @@
 
 <%namespace name="layout" file="../navigation-bar.mako" />
 <%namespace name="utils" file="../utils.inc.mako" />
+<%namespace name="workflow" file="../editor2/common_workflow.mako" />
+<%namespace name="dashboard" file="/common_dashboard.mako" />
 
 ${ commonheader(_("Workflow Dashboard"), "oozie", user) | n,unicode }
 ${ layout.menubar(section='workflows', dashboard=True) }
@@ -133,7 +135,7 @@ ${ layout.menubar(section='workflows', dashboard=True) }
       </div>
     </div>
 
-    <div class="span10">
+    <div class="span10" style="margin-left: 2.127659574468085%;">
       <h1 class="card-heading simple card-heading-nopadding card-heading-noborder card-heading-blue" style="margin-bottom: 10px">
         % if oozie_bundle:
           ${ _('Bundle') } <a href="${ oozie_bundle.get_absolute_url() }">${ oozie_bundle.appName }</a> :
@@ -145,12 +147,8 @@ ${ layout.menubar(section='workflows', dashboard=True) }
         ${ _('Workflow') } ${ oozie_workflow.appName }
       </h1>
       <ul class="nav nav-tabs">
-        % if workflow_graph:
-            <li class="active"><a href="#graph" data-toggle="tab">${ _('Graph') }</a></li>
-            <li><a href="#actions" data-toggle="tab">${ _('Actions') }</a></li>
-        % else:
-            <li class="active"><a href="#actions" data-toggle="tab">${ _('Actions') }</a></li>
-        % endif
+        <li class="active"><a href="#graph" data-toggle="tab">${ _('Graph') }</a></li>
+        <li><a href="#actions" data-toggle="tab">${ _('Actions') }</a></li>
         <li><a href="#details" data-toggle="tab">${ _('Details') }</a></li>
         <li><a href="#configuration" data-toggle="tab">${ _('Configuration') }</a></li>
         <li><a href="#log" data-toggle="tab">${ _('Log') }</a></li>
@@ -160,15 +158,15 @@ ${ layout.menubar(section='workflows', dashboard=True) }
         % endif
       </ul>
 
-      <div id="workflow-tab-content" class="tab-content" style="min-height:200px">
-
-        % if workflow_graph:
-            <div id="graph" class="tab-pane active">
-            ${ workflow_graph | n,unicode }
-            </div>
+      <div id="workflow-tab-content" class="tab-content" style="min-height:200px">    
+        <div id="graph" class="tab-pane active">
+        % if layout_json == '':
+        ${ workflow_graph | n,unicode }
+        % else:
+        ${ workflow.render() }
         % endif
-
-        <div id="actions" class="tab-pane ${ utils.if_false(workflow_graph, 'active') }">
+        </div>
+        <div id="actions" class="tab-pane">
           <table class="table table-striped table-condensed selectable">
             <thead>
             <tr>
@@ -347,6 +345,7 @@ ${ layout.menubar(section='workflows', dashboard=True) }
 <script src="/oozie/static/js/dashboard-utils.js" type="text/javascript" charset="utf-8"></script>
 <link rel="stylesheet" href="/oozie/static/css/workflow.css">
 <script src="/static/ext/js/knockout-min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/knockout.mapping-2.3.2.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/datatables-paging-0.1.js" type="text/javascript" charset="utf-8"></script>
 <script src="/static/ext/js/codemirror-3.11.js"></script>
 <link rel="stylesheet" href="/static/ext/css/codemirror.css">
@@ -361,6 +360,17 @@ ${ layout.menubar(section='workflows', dashboard=True) }
 <script src="/static/js/jquery.blueprint.js"></script>
 %endif
 
+% if layout_json != '':
+<link rel="stylesheet" href="/oozie/static/css/common-editor.css">
+<link rel="stylesheet" href="/oozie/static/css/workflow-editor.css">
+
+${ dashboard.import_layout() }
+
+<script src="/oozie/static/js/workflow-editor.ko.js" type="text/javascript" charset="utf-8"></script>
+<script src="/oozie/static/js/workflow-editor.utils.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/jquery/plugins/jquery.curvedarrow.js" type="text/javascript" charset="utf-8"></script>
+%endif
+
 <style type="text/css">
   .CodeMirror.cm-s-default {
     height: 700px;
@@ -383,10 +393,28 @@ ${ layout.menubar(section='workflows', dashboard=True) }
   #slaTable {
     margin-top: 20px;
   }
+
+  % if not workflow_graph:
+  #graph {
+    margin-top: 0;
+  }
+  #graph, #new-node {
+    text-align: left;
+  }
+  % endif
+
 </style>
 
 <script type="text/javascript">
 
+${ utils.slaGlobal() }
+
+  ko.bindingHandlers.editable = { // overwrite the editable bindings with a simple text filler
+    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
+      $(element).text(valueAccessor()());
+    }
+  }
+
   var Action = function (action) {
     return {
       id: action.id,
@@ -416,8 +444,16 @@ ${ layout.menubar(section='workflows', dashboard=True) }
 
   };
 
-  var viewModel = new RunningWorkflowActionsModel([]);
-  ko.applyBindings(viewModel);
+  var actionsViewModel = new RunningWorkflowActionsModel([]);
+  ko.applyBindings(actionsViewModel, $("#actions")[0]);
+
+  % if layout_json != '':
+  var viewModel = new WorkflowEditorViewModel(${ layout_json | n,unicode }, ${ workflow_json | n,unicode }, ${ credentials_json | n,unicode }, ${ workflow_properties_json | n,unicode }, ${ subworkflows_json | n,unicode }, ${ can_edit_json | n,unicode }); 
+  ko.applyBindings(viewModel, $("#graph")[0]);
+  viewModel.isViewer = ko.observable(true);
+  viewModel.init();
+  fullLayout(viewModel);
+  %endif
 
   var CHART_LABELS = {
     NOMINAL_TIME: "${_('Nominal Time')}",
@@ -430,6 +466,10 @@ ${ layout.menubar(section='workflows', dashboard=True) }
   var slaTable;
 
   $(document).ready(function() {
+    % if layout_json != '':
+    drawArrows();
+    %endif
+
     var CURRENT_ZOOM = 1;
     $(document).keydown(function(e) {
       if (e.ctrlKey){
@@ -602,7 +642,7 @@ ${ layout.menubar(section='workflows', dashboard=True) }
       $.getJSON("${ oozie_workflow.get_absolute_url(format='json') }", function (data) {
 
         if (data.actions){
-          viewModel.actions(ko.utils.arrayMap(data.actions, function (action) {
+          actionsViewModel.actions(ko.utils.arrayMap(data.actions, function (action) {
             return new Action(action);
           }));
         }
@@ -630,8 +670,9 @@ ${ layout.menubar(section='workflows', dashboard=True) }
         }
 
         $("#progress .bar").text(data.progress + "%").css("width", data.progress + "%").attr("class", "bar " + getStatusClass(data.status, "bar-"));
-
+        %if layout_json == '':
         $("#graph").html(data.graph);
+        %endif
 
         if (data.status != "RUNNING" && data.status != "PREP"){
           return;

+ 1540 - 0
apps/oozie/src/oozie/templates/editor2/common_workflow.mako

@@ -0,0 +1,1540 @@
+## -*- coding: utf-8 -*-
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you 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.
+
+
+#
+# Include this in order to use the functions:
+# <%namespace name="workflow" file="common_workflow.mako" />
+#
+
+<%!
+  from django.utils.translation import ugettext as _
+%>
+<%namespace name="utils" file="../utils.inc.mako" />
+
+<%def name="render()">
+  
+<div data-bind="css: {'dashboard': true, 'readonly': ! isEditing()}">
+  <!-- ko if: $root.workflow.properties.imported -->
+    <div class="alert alert-warn" style="margin-top: 93px; margin-bottom: 0; border: none; text-align: center">
+      ${ _('This workflow was imported from an old Hue version, save it to create a copy in the new format or') }
+      <a data-bind="attr: { href: '/oozie/edit_workflow/' + $root.workflow.properties.wf1_id() }">${ _('open it in the old editor.') }</a>
+    </div>
+  <!-- /ko -->
+  <div class="container-fluid">
+    <div class="row-fluid" data-bind="template: { name: 'column-template', foreach: oozieColumns}">
+    </div>
+    <div class="clearfix"></div>
+  </div>
+</div>
+
+
+<script type="text/html" id="column-template">
+  <div data-bind="css: klass()" style="min-height: 50px !important;">
+    <div data-bind="template: { name: 'row-template', data: oozieStartRow }, style:{'margin-top' : (typeof $root.workflow.properties.imported != 'undefined' && $root.workflow.properties.imported()) || typeof $root.isViewer != 'undefined' ? '0': '50px'}"></div>
+    <div class="container-fluid" data-bind="visible: $root.isEditing() && oozieRows().length > 0">
+      <div class="row-fluid">
+        <div data-bind="visible: enableOozieDropOnBefore, css: {'span4 offset4': true, 'drop-target': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(x, y){ var _w = $root.addDraggedWidget($data, true); widgetDraggedAdditionalHandler(_w); } }"></div>
+        <div data-bind="visible: ! enableOozieDropOnBefore(), css: {'drop-target drop-target-disabled': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}"></div>
+      </div>
+    </div>
+    <div data-bind="template: { name: 'internal-row-template', foreach: oozieRows}">
+    </div>
+    <div class="container-fluid" data-bind="visible: $root.isEditing() && rows().length > 0">
+      <div class="row-fluid">
+        <div data-bind="visible: enableOozieDropOnAfter, css: {'span4 offset4': true, 'drop-target': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(){ var _w = $root.addDraggedWidget($data, false); widgetDraggedAdditionalHandler(_w); } }">
+          <span data-bind="visible: oozieRows().length == 0">${ _('Drop your action here') }</span>
+        </div>
+        <div data-bind="visible: ! enableOozieDropOnAfter(), css: {'drop-target drop-target-disabled': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}"></div>
+      </div>
+    </div>
+
+    <div data-bind="template: { name: 'row-template', data: oozieEndRow }"></div>
+
+    <div data-bind="template: { name: 'row-template', data: oozieKillRow }" style="margin-top: 60px"></div>
+  </div>
+</script>
+
+
+<script type="text/html" id="internal-column-template">
+  <div data-bind="css: klass(), style: {'minHeight': '50px !important', 'width': percWidth() + '%' }">
+    <div class="container-fluid" data-bind="visible: $root.isEditing()">
+      <div data-bind="visible: ! enableOozieDropOnBefore(), css: {'drop-target drop-target-disabled': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}"></div>
+      <div data-bind="visible: enableOozieDropOnBefore, css: {'drop-target': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(){ var _w = $root.addDraggedWidget($data, true); widgetDraggedAdditionalHandler(_w); } }"></div>
+    </div>
+    <div data-bind="template: { name: 'internal-row-template', foreach: rows}">
+    </div>
+    <div class="container-fluid" data-bind="visible: $root.isEditing()">
+      <div data-bind="visible: ! enableOozieDropOnAfter(), css: {'drop-target drop-target-disabled': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}"></div>
+      <div data-bind="visible: enableOozieDropOnAfter, css: {'drop-target': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(){ var _w = $root.addDraggedWidget($data, false); widgetDraggedAdditionalHandler(_w); } }"></div>
+    </div>
+  </div>
+</script>
+
+
+<script type="text/html" id="row-template">
+  <div class="container-fluid">
+    <div class="row-fluid">
+      <div class="span4 offset4">
+        <div data-bind="css: {'row-fluid': true, 'row-container':true, 'is-editing': $root.isEditing},
+          sortable: { template: 'widget-template', data: widgets, allowDrop: $root.isEditing() && widgets().length < 1, isEnabled: $root.isEditing() && widgets().length < 1,
+          options: {'opacity': 0.7, 'placeholder': 'row-highlight', 'greedy': true,
+              'stop': function(event, ui){},
+              'helper': function(event){lastWindowScrollPosition = $(window).scrollTop(); var _par = $('<div>');_par.addClass('card card-widget');var _title = $('<h2>');_title.addClass('card-heading simple');_title.text($(event.toElement).text());_title.appendTo(_par);_par.height(80);_par.width(180);return _par;}},
+              dragged: function(widget){widgetDraggedAdditionalHandler(widget)}}">
+        </div>
+      </div>
+    </div>
+  </div>
+</script>
+
+<script type="text/html" id="internal-row-template">
+  <div class="container-fluid">
+    <div class="row-fluid" data-bind="visible: $index() > 0 && $root.isEditing() && ! $root.isRowBeforeJoin($data) && ! $root.isRowAfterFork($data)" style="margin-bottom: 10px">
+      <div data-bind="css: {'span1': true, 'offset3andhalf': ($root.isEditing() && $parents.length <= 2 && columns().length == 0), 'offset4': (!$root.isEditing() && $parents.length <= 2 && columns().length == 0), 'readonly': ! $root.isEditing()}"></div>
+      <div data-bind="css: {'span10': ($parents.length > 2 || columns().length > 0), 'span4': ($parents.length <= 2 && columns().length == 0), 'offset3andhalf': (!$root.isEditing() && $parents.length <= 2 && columns().length == 0), 'readonly': ! $root.isEditing()}">
+        <div data-bind="visible: $root.isEditing() && ! enableOozieDropOnBefore(), css: {'drop-target drop-target-disabled': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}"></div>
+        <div style="text-align: left" data-bind="visible: $root.isEditing() && enableOozieDropOnBefore(), css: {'drop-target': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(){ var _w = $root.addDraggedWidget($data); widgetDraggedAdditionalHandler(_w); } }"></div>
+      </div>
+      <div data-bind="css: {'span1': true, 'readonly': ! $root.isEditing()}"></div>
+    </div>
+    <div class="row-fluid" data-bind="style: {'width': columns().length < 5 ? '100%' : (columns().length * 25)+'%' }">
+      <div data-bind="css: {'span1': true, 'offset3andhalf': ($root.isEditing() && $parents.length <= 2 && columns().length == 0), 'offset4': (!$root.isEditing() && $parents.length <= 2 && columns().length == 0), 'readonly': ! $root.isEditing()}">
+        <div data-bind="visible: $root.isEditing() && enableOozieDropOnSide() && !($data.widgets().length > 0 && ['join-widget', 'decision-widget'].indexOf($data.widgets()[0].widgetType()) > -1), css: {'drop-target drop-target-side': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(){ var _w = $root.addSideDraggedWidget($data, true); widgetDraggedAdditionalHandler(_w); } }"></div>
+      </div>
+      <div  data-bind="css: {'span10': ($parents.length > 2 || columns().length > 0), 'span4': ($parents.length <= 2 && columns().length == 0), 'offset3andhalf': (!$root.isEditing() && $parents.length <= 2 && columns().length == 0), 'readonly': ! $root.isEditing()}">
+        <div data-bind="visible: columns().length == 0, css: {'row-fluid': true, 'row-container':true, 'is-editing': $root.isEditing},
+          sortable: { template: 'widget-template', data: widgets, allowDrop: enableOozieDrop, isEnabled: enableOozieDrop,
+          options: {'opacity': 0.7, 'placeholder': 'row-highlight', 'greedy': true,
+              'stop': function(event, ui){},
+              'helper': function(event){lastWindowScrollPosition = $(window).scrollTop();  var _par = $('<div>');_par.addClass('card card-widget');var _title = $('<h2>');_title.addClass('card-heading simple');_title.text($(event.toElement).text());_title.appendTo(_par);_par.height(80);_par.width(180);return _par;}},
+              dragged: function(widget){widgetDraggedAdditionalHandler(widget)}}">
+        </div>
+        <div class="container-fluid" data-bind="visible: columns().length > 0">
+          <div data-bind="css: {'row-fluid': true, 'row-container':true, 'is-editing': $root.isEditing}">
+            <div data-bind="template: { name: 'internal-column-template', foreach: columns}">
+            </div>
+          </div>
+        </div>
+      </div>
+      <div  data-bind="css: {'span1': true, 'readonly no-margin': ! $root.isEditing()}, style: {'margin': ! $root.isEditing() ? '0':''}">
+        <div data-bind="visible: $root.isEditing() && enableOozieDropOnSide() && !($data.widgets().length > 0 && ['join-widget', 'decision-widget'].indexOf($data.widgets()[0].widgetType()) > -1), css: {'drop-target drop-target-side': true, 'drop-target-dragging': $root.isDragging(), 'is-editing': $root.isEditing}, droppable: {enabled: $root.isEditing, onDrop: function(){ var _w = $root.addSideDraggedWidget($data, false); widgetDraggedAdditionalHandler(_w); } }"></div>
+      </div>
+    </div>
+  </div>
+
+</script>
+
+<script type="text/html" id="widget-template">
+  <div data-bind="attr: {'id': 'wdg_'+ id(),}, css: klass() + (ooziePropertiesExpanded()?' expanded-widget':'') + ($root.isRunning() && status() == ''?' widget-running':''),
+      draggable: {data: $data, isEnabled: true, options: {'handle': '.move-widget', 'opacity': 0.7, 'refreshPositions': true, 'start': function(event, ui){ $root.setCurrentlyDraggedWidget($data, event.toElement); $root.isDragging(true); }, 'stop': function(event, ui){ $root.enableSideDrop($data); $root.isDragging(false); }, 'helper': function(event){lastWindowScrollPosition = $(window).scrollTop();  var _par = $('<div>');_par.addClass('card card-widget');var _title = $('<h2>');_title.addClass('card-heading simple');_title.text($(event.currentTarget).find('h2').text());_title.appendTo(_par);_par.css('minHeight', '10px');_par.width(120);return _par;}}}">
+    <h2 class="card-heading simple" data-bind="visible: widgetType() != 'start-widget' && widgetType() != 'end-widget' &&
+        id() != '17c9c895-5a16-7443-bb81-f34b30b21548' && (['fork-widget', 'join-widget', 'decision-widget'].indexOf(widgetType()) == -1 || $root.isEditing())">
+
+      <span data-bind="visible: $root.isEditing() && oozieMovable() && ! oozieExpanded() && ! ooziePropertiesExpanded() && ($root.newAction() == null || $root.newAction().id() != id())">
+        <a href="javascript:void(0)" class="move-widget" title="${ _('Move node') }"><i class="fa fa-arrows"></i></a>
+        &nbsp;
+        <a href="javascript:void(0)" class="move-widget clone-widget" title="${ _('Copy node') }"><i class="fa fa-copy"></i></a>
+        &nbsp;
+      </span>
+
+      <!-- ko if: widgetType() == 'hive-widget' -->
+      <img src="/oozie/static/art/icon_beeswax_48.png" class="widget-icon">
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'hive2-widget' -->
+      <img src="/oozie/static/art/icon_beeswax_48.png" class="widget-icon"><sup style="color: #338bb8; margin-left: -4px">2</sup>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'pig-widget' -->
+      <img src="/oozie/static/art/icon_pig_48.png" class="widget-icon">
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'java-widget' -->
+      <a class="widget-icon"><i class="fa fa-file-code-o"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'sqoop-widget' -->
+      <img src="/oozie/static/art/icon_sqoop_48.png" class="widget-icon">
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'mapreduce-widget' -->
+      <a class="widget-icon"><i class="fa fa-file-archive-o"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'subworkflow-widget' -->
+      <a class="widget-icon"><i class="fa fa-code-fork"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'shell-widget' -->
+      <a class="widget-icon"><i class="fa fa-terminal"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'ssh-widget' -->
+      <a class="widget-icon"><i class="fa fa-tty"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'fs-widget' -->
+      <a class="widget-icon"><i class="fa fa-file-o"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'email-widget' -->
+      <a class="widget-icon"><i class="fa fa-envelope-o"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'streaming-widget' -->
+      <a class="widget-icon"><i class="fa fa-exchange"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'distcp-widget' -->
+      <a class="widget-icon"><i class="fa fa-files-o"></i></a>
+      <!-- /ko -->
+
+      <!-- ko if: widgetType() == 'kill-widget' -->
+      <a class="widget-icon"><i class="fa fa-stop"></i></a>
+      <!-- /ko -->
+
+      <span data-bind="editable: name, editableOptions: {enabled: $root.isEditing(), placement: 'right'}, attr: {'title': id().slice(0, 4)}"></span>
+
+      <!-- ko if: widgetType() == 'decision-widget' -->
+        <div class="inline pull-right" data-bind="visible: $root.isEditing() && $root.workflow.getNodeById(id()) && $root.workflow.getNodeById(id()).children().length <= 1 && ! oozieExpanded() && ! ooziePropertiesExpanded()">
+          <a href="javascript:void(0)" data-bind="click: function(w){addActionDemiModalFieldCancel();$root.removeWidget(w);}"><i class="fa fa-times"></i></a>
+        </div>
+      <!-- /ko -->
+      <!-- ko if: widgetType() != 'decision-widget' -->
+        <div class="inline pull-right" data-bind="visible: $root.isEditing() && (['start-widget', 'end-widget', 'fork-widget', 'join-widget'].indexOf(widgetType()) == -1) && ! oozieExpanded() && ! ooziePropertiesExpanded()">
+          <a href="javascript:void(0)" data-bind="click: function(w){addActionDemiModalFieldCancel();$root.removeWidget(w);}"><i class="fa fa-times"></i></a>
+        </div>
+      <!-- /ko -->
+      <!-- ko if: ooziePropertiesExpanded() -->
+        <div class="inline pull-right">
+          <a href="javascript:void(0)" data-bind="click: toggleProperties"><i class="fa fa-caret-square-o-left"></i></a>
+        </div>
+      <!-- /ko -->
+    </h2>
+    <div class="card-body" style="padding: 0; position: relative">
+      <div class="advanced-triangle-container" data-bind="visible: $root.isEditing() && ! ooziePropertiesExpanded() && oozieMovable(), click: toggleProperties">
+        <div class="advanced-triangle">
+          <a href="javascript:void(0)"><i class="fa fa-cogs"></i></a>
+        </div>
+      </div>
+      <!-- ko if: id() == '17c9c895-5a16-7443-bb81-f34b30b21548' && ooziePropertiesExpanded() -->
+      <div class="advanced-triangle-container" data-bind="visible: $root.isEditing(), click: toggleProperties">
+        <div class="advanced-triangle">
+          <a href="javascript:void(0)"><i class="fa fa-caret-square-o-left"></i></a>
+        </div>
+      </div>
+      <!-- /ko -->
+      <div data-bind="template: { name: function() { return widgetType(); }}" class="widget-main-section"></div>
+      <div data-bind="css: {'widget-statusbar': true, 'widget-statusbar-running': status() == 'running', 'widget-statusbar-failed': status() == 'failed', 'widget-statusbar-success': status() == 'success'}, style: {'width': progress() + '%'}"></div>
+    </div>
+  </div>
+</script>
+
+
+<script type="text/html" id="fork-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="min-height: 40px">
+    <div class="big-icon"><i class="fa fa-sitemap"></i></div>
+    <div data-bind="visible: $root.isEditing" style="padding-left: 10px; padding-bottom: 10px">
+      <a class="pointer" data-bind="click: function() { $root.convertToDecision($parent, $data) }">${_('Convert to Decision')} <i class="fa fa-wrench"></i></a>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="join-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="min-height: 40px">
+    <div class="big-icon"><i class="fa fa-sitemap fa-rotate-180"></i></div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="decision-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="min-height: 40px">
+    <div class="big-icon" data-bind="visible: ! $root.isEditing()"><i class="fa fa-magic"></i></div>
+
+    <div data-bind="visible: $root.isEditing" style="padding: 10px">
+      <a class="custom-popover pull-right" href="javascript:void(0)" target="_blank" data-trigger="click" data-toggle="popover" data-placement="right" rel="popover" 
+        data-html="true" data-content="<strong>${ _('Examples of predicates:') }</strong><br/>
+                <br/>${'${'} fs:fileSize(secondjobOutputDir) gt 10 * GB }
+                <br/>
+                ${"${"} hadoop:counters('secondjob')[RECORDS][REDUCE_OUT] lt 1000000 }
+                <br/>
+                <a href='http://oozie.apache.org/docs/4.1.0/WorkflowFunctionalSpec.html#a4.2_Expression_Language_Functions'>${ _('Click for more') }</a>">
+          <i class="fa fa-question-circle" title="${ _('Click for more info') }"></i>
+      </a>    
+      <ul data-bind="foreach: children" class="unstyled">
+        <li>
+          ${ _('If') } <input type="text" data-bind="value: $data['condition']" />
+          ${ _('go to') }
+          <select data-bind="options: $root.workflow.nodeIds,
+                     optionsText: function(item) {return $root.workflow.nodeNamesMapping()[item]; },
+                     value: $data['to']
+                     ">
+          </select>
+          <a class="pointer" data-bind="click: function(){ $parent.children.remove(this);}">
+            <i class="fa fa-minus"></i>
+          </a>
+        </li>
+      </ul>
+      <a class="pointer" data-bind="click: function(){ children.push({'to': '', 'condition': '${'${'} 1 gt 0 }'});}">
+        ${ _('Jump to another node') } <i class="fa fa-plus"></i>
+      </a>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="kill-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="min-height: 40px">
+    <div class="big-icon" data-bind="visible: id() == '17c9c895-5a16-7443-bb81-f34b30b21548'" title="${ _('It is where we finish if failure!') }"><i class="fa fa-stop"></i></div>
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: $parent.ooziePropertiesExpanded">
+        <h6 class="field-title">${ _('Message') }</h6>
+        <textarea class="span12" data-bind="value: properties.message" />
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="start-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="min-height: 40px;">
+    <div class="big-icon" title="${ _('It is where we start!') }"><i class="fa fa-flag-checkered"></i></div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="end-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="min-height: 40px">
+    <div class="big-icon" title="${ _('It is where we successfully finish!') }"><i class="fa fa-dot-circle-o"></i></div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="common-action-properties">
+  <div class="properties">
+    <h6>${ _('Prepare') }</h6>
+    <ul data-bind="visible: properties.prepares().length > 0, foreach: properties.prepares" class="unstyled">
+      <li>
+        <div style="display: inline-block; width: 60px" data-bind="text: type"></div>
+        <input type="text" class="filechooser-input input-xlarge"
+            data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.prepares.help_text }"/>
+        <a href="#" data-bind="click: function(){ $parent.properties.prepares.remove(this); $(document).trigger('drawArrows') }">
+          <i class="fa fa-minus"></i>
+        </a>
+      </li>
+    </ul>
+    <a class="pointer" data-bind="click: function(){ properties.prepares.push({'type': 'mkdir', 'value': ''}); $(document).trigger('drawArrows') }">
+      ${ _('Directory') } <i class="fa fa-plus"></i>
+    </a>
+    <a class="pointer" data-bind="click: function(){ properties.prepares.push({'type': 'delete', 'value': ''}); $(document).trigger('drawArrows') }">
+      ${ _('Delete') } <i class="fa fa-plus"></i>
+    </a>
+
+    <!-- ko if: properties.job_xml -->
+      <h6>${ _('Job XML') }</h6>
+      <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: properties.job_xml, filechooserOptions: globalFilechooserOptions, attr: { placeholder: $root.workflow_properties.job_xml.help_text }"/>
+    <!-- /ko -->
+
+    <h6>
+      <a class="pointer" data-bind="click: function(){ properties.job_properties.push({'name': '', 'value': ''}); $(document).trigger('drawArrows') }">
+        ${ _('Properties') } <i class="fa fa-plus"></i>
+      </a>
+    </h6>
+    <ul data-bind="visible: properties.job_properties().length > 0, foreach: properties.job_properties" class="unstyled">
+      <li>
+        <input type="text" data-bind="value: name" placeholder="${ _('name, e.g. mapred.job.queue.name') }"/>
+        <input type="text" class="filechooser-input input-xlarge" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.job_properties.help_text }"/>
+        <a href="#" data-bind="click: function(){ $parent.properties.job_properties.remove(this); $(document).trigger('drawArrows') }">
+          <i class="fa fa-minus"></i>
+        </a>
+      </li>
+    </ul>
+    <em data-bind="visible: properties.job_properties().length == 0">${ _('No properties defined.') }</em>
+
+    <h6>
+      <a class="pointer" data-bind="click: function(){ properties.archives.push(ko.mapping.fromJS({'name': ''})); $(document).trigger('drawArrows') }">
+        ${ _('Archives') } <i class="fa fa-plus"></i>
+      </a>
+    </h6>
+    <ul data-bind="visible: properties.archives().length > 0, foreach: properties.archives" class="unstyled">
+      <li>
+        <input type="text" class="filechooser-input input-xlarge" data-bind="filechooser: name(), filechooserFilter: 'zip,tar,tgz,tar.gz', filechooserOptions: globalFilechooserOptions, value: name, attr: { placeholder: $root.workflow_properties.archives.help_text }"/>
+        <span data-bind='template: { name: "common-fs-link", data: { path: name(), with_label: false} }'></span>
+        <a href="#" data-bind="click: function(){ $parent.properties.archives.remove(this); $(document).trigger('drawArrows') }">
+          <i class="fa fa-minus"></i>
+        </a>
+      </li>
+    </ul>
+    <em data-bind="visible: properties.archives().length == 0">${ _('No archives defined.') }</em>
+  </div>
+</script>
+
+
+<script type="text/html" id="common-properties-pig-arguments">
+  <h6>
+    <a class="pointer" data-bind="click: function(){ properties.arguments.push({'value': ''}); $(document).trigger('drawArrows') }">
+      ${ _('Arguments') } <i class="fa fa-plus"></i>
+    </a>
+  </h6>
+  <ul class="unstyled" data-bind="visible: properties.arguments().length > 0, foreach: properties.arguments">
+    <li>
+      <input type="text" class="input-medium" data-bind="value: value, attr: { placeholder: $root.workflow_properties.arguments.help_text }"/>
+      <a href="#" data-bind="click: function(){ $parent.properties.arguments.remove(this); $(document).trigger('drawArrows') }">
+        <i class="fa fa-minus"></i>
+      </a>
+    </li>
+  </ul>
+  <em data-bind="visible: properties.arguments().length == 0">${ _('No arguments defined.') }</em>
+</script>
+
+
+<script type="text/html" id="common-properties-arguments">
+  <h6>
+    <a class="pointer" data-bind="click: function(){ properties.arguments.push({'value': ''}); $(document).trigger('drawArrows') }">
+      ${ _('Arguments') } <i class="fa fa-plus"></i>
+    </a>
+  </h6>
+  <ul class="unstyled" data-bind="visible: properties.arguments().length > 0, foreach: properties.arguments">
+    <li style="margin-bottom: 3px">
+      <input type="text" class="span11" data-bind="value: value, attr: { placeholder: $root.workflow_properties.arguments.help_text }"/>
+      <a href="#" data-bind="click: function(){ $parent.properties.arguments.remove(this); $(document).trigger('drawArrows') }">
+        <i class="fa fa-minus"></i>
+      </a>
+    </li>
+  </ul>
+</script>
+
+
+<script type="text/html" id="common-properties-files">
+  <h6>
+    <a class="pointer" data-bind="click: function(){ properties.files.push(ko.mapping.fromJS({'value': ''})); $(document).trigger('drawArrows') }">
+      ${ _('Files') } <i class="fa fa-plus"></i>
+    </a>
+  </h6>
+  <ul class="unstyled" data-bind="foreach: properties.files">
+    <li style="margin-bottom: 3px">
+      <input type="text" class="filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions"/>
+      <span data-bind='template: { name: "common-fs-link", data: { path: value(), with_label: false} }'></span>
+      <a href="#" data-bind="click: function(){ $parent.properties.files.remove(this); $(document).trigger('drawArrows') }">
+        <i class="fa fa-minus"></i>
+      </a>
+    </li>
+  </ul>
+</script>
+
+
+<script type="text/html" id="common-properties-parameters">
+  <h6>
+    <a class="pointer" data-bind="click: function(){ properties.parameters.push(ko.mapping.fromJS({'value': ''})); $(document).trigger('drawArrows')}">
+      ${ _('Parameters') } <i class="fa fa-plus"></i>
+    </a>
+  </h6>
+  <ul class="unstyled" data-bind="foreach: properties.parameters">
+    <li style="margin-bottom: 3px">
+      <input type="text" class="filechooser-input seventy" data-bind="value: value, filechooser: value, filechooserOptions: globalFilechooserOptions, filechooserPrefixSeparator: '=', attr: { placeholder: ' ${ _("Fill me up!") }' }, typeahead: { target: value, source: $parent.actionParametersUI, sourceSuffix: '=', triggerOnFocus: true }"/>
+      <span data-bind='template: { name: "param-fs-link", data: {path: value()} }'></span>
+      <a href="#" data-bind="click: function(){ $parent.properties.parameters.remove(this); $(document).trigger('drawArrows') }">
+        <i class="fa fa-minus"></i>
+      </a>
+    </li>
+  </ul>
+</script>
+
+
+<script type="text/html" id="common-action-transition">
+  <!-- ko if: children().length == 2 -->
+  OK -->
+  <select data-bind="options: $root.workflow.nodeIds,
+      optionsText: function(item) {return $root.workflow.nodeNamesMapping()[item]; },
+      value: children()[0]['to']
+      ">
+  </select>
+  <br/>
+  KO -->
+  <select data-bind="options: $root.workflow.nodeIds,
+     optionsText: function(item) {return $root.workflow.nodeNamesMapping()[item]; },
+   value: children()[1]['error']
+     ">
+  </select>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="common-action-credentials">
+  <em data-bind="visible: $root.credentials() == null || $root.credentials().length == 0">${ _('No available credentials.') }</em>
+  <select data-bind="visible: $root.credentials() != null && $root.credentials().length > 0, options: $root.credentials, value: properties.credentials" size="5" multiple="true"></select>
+</script>
+
+
+<script type="text/html" id="common-action-sla">
+  <div data-bind="with: properties">
+     ${ utils.slaForm() }
+  </div>
+</script>
+
+
+<script type="text/html" id="common-fs-link">
+<!-- ko if: $data.path.length > 0 -->
+  <!-- ko if: with_label -->
+    <a data-bind="attr: {href: '/filebrowser/view' + ($data.path.indexOf('/') == -1 ? $root.workflow.properties.deployment_dir() + '/' : '') + $data.path , title: '${ _('Open') } '+ $data.path }" target="_blank">
+      <span data-bind="text: $data.path.split('/').pop()"></span>
+    </a>
+  <!-- /ko -->
+
+   <!-- ko if: ! with_label -->
+     <a data-bind="attr: {href: '/filebrowser/view' + ($data.path.indexOf('/') == -1 ? $root.workflow.properties.deployment_dir() + '/' : '') + $data.path }" target="_blank" title="${ _('Open') }">
+       <i class="fa fa-external-link-square"></i>
+     </a>
+   <!-- /ko -->
+ <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="param-fs-link">
+  <!-- ko if: path.split('=', 2)[1] && path.split('=', 2)[1].charAt(0) == '/' -->
+    <a data-bind="attr: {href: '/filebrowser/view' + $data.path.split('=', 2)[1] }" target="_blank" title="${ _('Open') }">
+      <i class="fa fa-external-link-square"></i>
+    </a>
+  <!-- /ko -->
+</script>
+
+
+
+<script type="text/html" id="hive-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.script_path(), with_label: true} }'></span>
+    </div>
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <input type="text" style="width:25%" class="filechooser-input" data-bind="filechooser: properties.script_path, filechooserOptions: globalFilechooserOptions, attr: { placeholder:  $root.workflow_properties.script_path.help_text }"/>
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.script_path(), with_label: false}}'></span>
+
+        &nbsp;&nbsp;
+
+        <span data-bind="text: $root.workflow_properties.hive_xml.label"></span>      
+        <input type="text" style="width:25%" class="input-large filechooser-input" data-bind="filechooser: properties.hive_xml, filechooserOptions: globalFilechooserOptions, attr: { placeholder: $root.workflow_properties.hive_xml.help_text }"/>
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-parameters' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+      </div>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+          <br/>
+          <br/>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="hive2-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.script_path(), with_label: true} }'></span>
+    </div>
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <input type="text" class="filechooser-input" data-bind="filechooser: properties.script_path, filechooserOptions: globalFilechooserOptions, attr: { placeholder:  $root.workflow_properties.script_path.help_text }"/>
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.script_path(), with_label: false}}'></span>
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-parameters' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+      </div>
+    </div>
+
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="text: $root.workflow_properties.jdbc_url.label"></span>
+          <input type="text" data-bind="value: properties.jdbc_url" />
+          <br/>
+          <span data-bind="text: $root.workflow_properties.password.label"></span>
+          <input type="text" data-bind="value: properties.password" />
+          <br/>
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+          <br/>
+          <br/>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="pig-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.script_path(), with_label: true} }'></span>
+    </div>
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <input type="text" class="filechooser-input" data-bind="filechooser: properties.script_path, filechooserOptions: globalFilechooserOptions, hdfsAutocomplete: properties.script_path, attr: { placeholder:  $root.workflow_properties.script_path.help_text }" />
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.script_path(), with_label: false}}'></span>
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-parameters' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+
+      </div>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="template: { name: 'common-properties-pig-arguments' }"></span>
+          <br/>
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="java-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <pan data-bind="text: properties.main_class" />
+    </div>
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <span data-bind="text: $root.workflow_properties.jar_path.label" style="display: inline-block; width: 75px"></span>
+        <input type="text" class="filechooser-input input-xlarge"
+            data-bind="filechooser: properties.jar_path, filechooserOptions: globalFilechooserOptions, attr: { placeholder: $root.workflow_properties.jar_path.help_text }"
+        />
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.jar_path(), with_label: false}}'></span>
+
+        <br/>
+        <span data-bind="text: $root.workflow_properties.main_class.label" style="display: inline-block; width: 75px"></span>
+        <input type="text" class="input-xlarge" data-bind="value: properties.main_class, attr: { placeholder: $root.workflow_properties.main_class.help_text }" />
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-arguments' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+
+      </div>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <h6>
+            <a class="pointer" data-bind="click: function(){ properties.java_opts.push({'value': ''}); }">
+              <span data-bind="text: $root.workflow_properties.java_opts.label"></span> <i class="fa fa-plus"></i>
+            </a>
+          </h6>
+          <ul class="unstyled" data-bind="foreach: properties.java_opts">
+            <li>
+              <input type="text" data-bind="value: value, attr: { placeholder: $root.workflow_properties.java_opts.help_text }" class="input-xlarge"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.java_opts.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+
+          <span data-bind="text: $root.workflow_properties.capture_output.label"></span>
+          <input type="checkbox" data-bind="checked: properties.capture_output" />
+          <br/>
+          <br/>
+
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+          <br/>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="sqoop-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <span data-bind="text: $root.workflow_properties.command.label"></span>
+        <input type="text" data-bind="value: properties.command" />
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-parameters' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <a href="javascript:void(0)">
+        <span type="text" data-bind="text: properties.command().slice(0, 70), attr: { title: properties.command() }" />...
+      </a>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="mapreduce-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <span data-bind="text: $root.workflow_properties.jar_path.label"></span>
+        <input type="text" class="filechooser-input" data-bind="filechooser: properties.jar_path, filechooserOptions: globalFilechooserOptions, value: properties.jar_path" />
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.jar_path(), with_label: false} }'></span>
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.job_properties.push({'name': '', 'value': ''}); $(document).trigger('drawArrows') }">
+            ${ _('Properties') } <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="visible: properties.job_properties().length > 0, foreach: properties.job_properties" class="unstyled">
+          <li>
+            <input type="text" class="span4" data-bind="value: name" placeholder="${ _('name, e.g. mapred.job.queue.name') }"/>
+            <input type="text" class="filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.job_properties.help_text }"/>
+            <a href="#" data-bind="click: function(){ $parent.properties.job_properties.remove(this); $(document).trigger('drawArrows') }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+        
+        <div class="span12" data-bind="template: { name: 'common-properties-files' }"></div>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.jar_path(), with_label: true} }'></span>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+      <div class="properties">
+        <h6>${ _('Prepare') }</h6>
+        <ul data-bind="visible: properties.prepares().length > 0, foreach: properties.prepares" class="unstyled">
+          <li>
+            <div style="display: inline-block; width: 60px" data-bind="text: type"></div>
+            <input type="text" class="filechooser-input input-xlarge"
+                data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.prepares.help_text }"/>
+            <a href="#" data-bind="click: function(){ $parent.properties.prepares.remove(this); $(document).trigger('drawArrows') }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+        <a class="pointer" data-bind="click: function(){ properties.prepares.push({'type': 'mkdir', 'value': ''}); $(document).trigger('drawArrows') }">
+          ${ _('Directory') } <i class="fa fa-plus"></i>
+        </a>
+        <a class="pointer" data-bind="click: function(){ properties.prepares.push({'type': 'delete', 'value': ''}); $(document).trigger('drawArrows') }">
+          ${ _('Delete') } <i class="fa fa-plus"></i>
+        </a>
+    
+        <!-- ko if: properties.job_xml -->
+          <h6>${ _('Job XML') }</h6>
+          <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: properties.job_xml, filechooserOptions: globalFilechooserOptions, attr: { placeholder: $root.workflow_properties.job_xml.help_text }"/>
+        <!-- /ko -->
+    
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.archives.push(ko.mapping.fromJS({'name': ''})); $(document).trigger('drawArrows') }">
+            ${ _('Archives') } <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="visible: properties.archives().length > 0, foreach: properties.archives" class="unstyled">
+          <li>
+            <input type="text" class="filechooser-input input-xlarge" data-bind="filechooser: name(), filechooserFilter: 'zip,tar,tgz,tar.gz', filechooserOptions: globalFilechooserOptions, value: name, attr: { placeholder: $root.workflow_properties.archives.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: { path: name(), with_label: false} }'></span>
+            <a href="#" data-bind="click: function(){ $parent.properties.archives.remove(this); $(document).trigger('drawArrows') }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+        <em data-bind="visible: properties.archives().length == 0">${ _('No archives defined.') }</em>
+      </div>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="subworkflow-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <select data-bind="options: $root.subworfklows, optionsText: 'name', optionsValue: 'value', value: properties.workflow"></select>
+        <span data-bind="visible: properties.workflow().length > 0">
+          <a href="#" data-bind="attr: { href: '${ url('oozie:edit_workflow') }' + '?workflow=' + properties.workflow() }" target="_blank" title="${ _('Open') }">
+            <i class="fa fa-external-link-square"></i>
+          </a>
+        </span>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <!-- ko if: $root.getSubWorkflow(properties.workflow()) -->
+        <span data-bind="with: $root.getSubWorkflow(properties.workflow())">
+          <a href="#" data-bind="attr: { href: '${ url('oozie:edit_workflow') }' + '?workflow=' + $data.value() }" target="_blank" title="${ _('Open') }">
+            <span data-bind="text: $data.name"></span>
+          </a>
+        </span>
+      <!-- /ko -->
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="text: $root.workflow_properties.propagate_configuration.label"></span>
+          <input type="checkbox" data-bind="checked: properties.propagate_configuration" />
+
+          <br/>
+
+          <h6>
+            <a class="pointer" data-bind="click: function(){ properties.job_properties.push({'name': '', 'value': ''}); $(document).trigger('drawArrows') }">
+              ${ _('Properties') } <i class="fa fa-plus"></i>
+            </a>
+          </h6>
+          <ul data-bind="visible: properties.job_properties().length > 0, foreach: properties.job_properties" class="unstyled">
+            <li>
+              <input type="text" data-bind="value: name"/>
+              <input type="text" data-bind="value: value"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.job_properties.remove(this); $(document).trigger('drawArrows') }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <em data-bind="visible: properties.job_properties().length == 0">${ _('No properties defined.') }</em>
+
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="shell-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <input type="text" data-bind="value: properties.shell_command" />
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.shell_command(), with_label: false} }'></span>
+
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-arguments' }"></div>
+          <div class="span6" data-bind="template: { name: 'common-properties-files' }"></div>
+        </div>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.shell_command(), with_label: true} }'></span>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="text: $root.workflow_properties.capture_output.label"></span>
+          <input type="checkbox" data-bind="checked: properties.capture_output" />
+          <br/>
+
+          <h6>
+            <a class="pointer" data-bind="click: function(){ properties.env_var.push({'value': ''}); }">
+              <span data-bind="text: $root.workflow_properties.env_var.label"></span> <i class="fa fa-plus"></i>
+            </a>
+          </h6>
+          <ul data-bind="foreach: properties.env_var" class="unstyled">
+            <li>
+              <input type="text" class="input-xlarge" data-bind="value: value, attr: { placeholder: $root.workflow_properties.env_var.help_text }"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.env_var.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="ssh-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <span data-bind="text: $root.workflow_properties.host.label"></span>
+        <input type="text" data-bind="value: properties.host" />
+        <br/>
+
+        <span data-bind="text: $root.workflow_properties.ssh_command.label"></span>
+        <input type="text" data-bind="value: properties.ssh_command" />
+        <div class="row-fluid">
+          <div class="span6" data-bind="template: { name: 'common-properties-arguments' }"></div>
+        </div>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind="text: properties.host" />
+      <span data-bind="text: properties.ssh_command().slice(0, 75)" />
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="text: $root.workflow_properties.capture_output.label"></span>
+          <input type="checkbox" data-bind="checked: properties.capture_output" />
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="fs-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind="text: '${ _("Delete") }', visible: properties.deletes().length > 0"></span>
+      <ul data-bind="foreach: properties.deletes" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: true} }, visible: value().length > 0'></span>
+        </li>
+      </ul>
+
+      <span data-bind="text: '${ _("Create") }', visible: properties.mkdirs().length > 0 || properties.touchzs().length > 0"></span>
+      <ul data-bind="foreach: properties.mkdirs" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: true} }, visible: value().length > 0'></span>
+        </li>
+      </ul>
+      <ul data-bind="foreach: properties.touchzs" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: true} }, visible: value().length > 0'></span>
+        </li>
+      </ul>
+
+      <span data-bind="text: '${ _("Move") }', visible: properties.moves().length > 0"></span>
+      <ul data-bind="foreach: properties.moves" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: source(), with_label: true} }, visible: source().length > 0'></span>
+          ${ _('to') }
+          <span data-bind='template: { name: "common-fs-link", data: {path: destination(), with_label: true} }, visible: destination().length > 0'></span>
+        </li>
+      </ul>
+
+      <span data-bind="text: '${ _("Change permissions") }', visible: properties.chmods().length > 0"></span>
+      <ul data-bind="foreach: properties.chmods" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: true} }, visible: value().length > 0'></span>
+          ${ _('to') }
+          <span data-bind="text: permissions"/>
+          <span data-bind="visible: ! dir_files(), text: '${ _('for directories') }'"/>
+          <span data-bind="visible: dir_files(), text: '${ _('for directories and files') }'"/>
+          <span data-bind="visible: recursive, text: '${ _('recursivelt') }'"/>
+        </li>
+      </ul>
+
+      <span data-bind="text: '${ _("Change groups") }', visible: properties.chgrps().length > 0"></span>
+      <ul data-bind="foreach: properties.chgrps" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: true} }, visible: value().length > 0'></span>
+          ${ _('to') }
+          <span data-bind="text: group"/>
+          <span data-bind="visible: ! dir_files(), text: '${ _('for directories') }'"/>
+          <span data-bind="visible: dir_files(), text: '${ _('for directories and files') }'"/>
+          <span data-bind="visible: recursive, text: '${ _('recursively') }'"/>
+        </li>
+      </ul>
+    </div>
+
+
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.deletes.push(ko.mapping.fromJS({'value': ''})); }">
+            <span data-bind="text: $root.workflow_properties.deletes.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.deletes" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.deletes.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: false} }, visible: value().length > 0'></span>
+            <a href="#" data-bind="click: function(){ $parent.properties.deletes.remove(this); }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.mkdirs.push(ko.mapping.fromJS({'value': ''})); }">
+            <span data-bind="text: $root.workflow_properties.mkdirs.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.mkdirs" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.mkdirs.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: false} }, visible: value().length > 0'></span>
+            <a href="#" data-bind="click: function(){ $parent.properties.mkdirs.remove(this); }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.touchzs.push(ko.mapping.fromJS({'value': ''})); }">
+            <span data-bind="text: $root.workflow_properties.touchzs.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.touchzs" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.touchzs.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: false} }, visible: value().length > 0'></span>
+            <a href="#" data-bind="click: function(){ $parent.properties.touchzs.remove(this); }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.moves.push(ko.mapping.fromJS({'source': '', 'destination': ''})); }">
+            <span data-bind="text: $root.workflow_properties.moves.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.moves" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: source, filechooserOptions: globalFilechooserOptions, value: source" placeholder="${ _('Source path') }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: source(), with_label: false} }, visible: source().length > 0'></span>
+
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: destination, filechooserOptions: globalFilechooserOptions, value: destination" placeholder="${ _('New destination path') }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: destination(), with_label: false} }, visible: destination().length > 0'></span>
+            <a href="#" data-bind="click: function(){ $parent.properties.moves.remove(this); }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+      </div>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.chmods.push(ko.mapping.fromJS({'value': '', 'permissions': '755', 'dir_files': false, 'recursive': false})); }">
+            <span data-bind="text: $root.workflow_properties.chmods.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.chmods" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.chmods.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: false} }, visible: value().length > 0'></span>
+
+            <input type="text" class="input-small" data-bind="value: permissions" placeholder="${ _('755, -rwxrw-rw-') }"/>
+            ${ _('Only for directories') }
+            <input type="checkbox" data-bind="checked: dir_files"/>
+            ${ _('Recursive to sub directories') }
+            <input type="checkbox" data-bind="checked: recursive"/>
+            <a href="#" data-bind="click: function(){ $parent.properties.chmods.remove(this); }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.chgrps.push(ko.mapping.fromJS({'value': '', 'group': '', 'dir_files': false, 'recursive': false})); }">
+            <span data-bind="text: $root.workflow_properties.chgrps.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.chgrps" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.chgrps.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: false} }, visible: value().length > 0'></span>
+
+            <input type="text" class="input-small" data-bind="value: group" placeholder="${ _('e.g. newgroup') }"/>
+            ${ _('Only for directories') }
+            <input type="checkbox" data-bind="checked: dir_files"/>
+            ${ _('Recursive to sub directories') }
+            <input type="checkbox" data-bind="checked: recursive"/>
+            <a href="#" data-bind="click: function(){ $parent.properties.chgrps.remove(this); }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="email-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <span data-bind="text: $root.workflow_properties.to.label" style="display: inline-block; width: 80px"></span>
+        <input type="text" data-bind="value: properties.to, attr: { placeholder: $root.workflow_properties.to.help_text }" />
+        <br/>
+
+        <span data-bind="text: $root.workflow_properties.subject.label" style="display: inline-block; width: 80px"></span>
+        <input type="text" data-bind="value: properties.subject, attr: { placeholder: $root.workflow_properties.subject.help_text }" />
+        <br/>
+
+        <span data-bind="text: $root.workflow_properties.body.label" style="display: inline-block; width: 80px"></span>
+        <textarea data-bind="value: properties.body, attr: { placeholder: $root.workflow_properties.body.help_text }"></textarea>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      ${ _('To') }
+      <span data-bind="text: properties.to"/>
+      <br/>
+      ${ _('About') }
+      <span data-bind="text: properties.subject"/>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="text: $root.workflow_properties.cc.label"></span>
+          <input type="text" data-bind="value: properties.cc, attr: { placeholder: $root.workflow_properties.cc.help_text }" />
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="streaming-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+        <span data-bind="text: $root.workflow_properties.mapper.label"></span>
+        <input type="text" data-bind="value: properties.mapper" />
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.mapper(), with_label: false} }'></span>
+        <br/>
+
+        <span data-bind="text: $root.workflow_properties.reducer.label"></span
+        <input type="text" data-bind="value: properties.reducer" />
+        <span data-bind='template: { name: "common-fs-link", data: {path: properties.reducer(), with_label: false} }'></span>
+        
+        <div data-bind="template: { name: 'common-properties-files' }"></div>
+      </div>
+    </div>
+
+    <div data-bind="visible: ! $root.isEditing()">
+      <span data-bind="text: $root.workflow_properties.mapper.label, visible: properties.mapper().length > 0"></span>
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.mapper(), with_label: true} }, visible: properties.mapper().length > 0'></span>
+      </br>
+      <span data-bind="text: $root.workflow_properties.reducer.label, visible: properties.reducer().length > 0"></span>
+      <span data-bind='template: { name: "common-fs-link", data: {path: properties.reducer(), with_label: true} }, visible: properties.reducer().length > 0'></span>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+          <span data-bind="template: { name: 'common-action-properties' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+
+<script type="text/html" id="distcp-widget">
+  <!-- ko if: $root.workflow.getNodeById(id()) -->
+  <div class="row-fluid" data-bind="with: $root.workflow.getNodeById(id())" style="padding: 10px">
+    <div data-bind="visible: $root.isEditing">
+      <div data-bind="visible: ! $parent.ooziePropertiesExpanded()" class="nowrap">
+
+        <h6>
+          <a class="pointer" data-bind="click: function(){ properties.distcp_parameters.push(ko.mapping.fromJS({'value': ''}));}">
+            <span data-bind="text: $root.workflow_properties.distcp_parameters.label"></span> <i class="fa fa-plus"></i>
+          </a>
+        </h6>
+        <ul data-bind="foreach: properties.distcp_parameters" class="unstyled">
+          <li>
+            <input type="text" class="input-xlarge filechooser-input" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.distcp_parameters.help_text }"/>
+            <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: false} }, visible: value().length > 0'></span>
+            <a href="#" data-bind="click: function(){ $parent.properties.distcp_parameters.remove(this);  }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+        </ul>
+
+      </div>
+    </div>
+    
+    <div data-bind="visible: ! $root.isEditing()">
+      ${ _('Parameters') }
+      <ul data-bind="foreach: properties.distcp_parameters" class="unstyled">
+        <li>
+          <span data-bind='template: { name: "common-fs-link", data: {path: value(), with_label: true} }, visible: value().length > 0'></span>
+        </li>
+      </ul>
+    </div>
+
+    <div data-bind="visible: $parent.ooziePropertiesExpanded">
+      <ul class="nav nav-tabs">
+        <li class="active"><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
+      </ul>
+      <div class="tab-content">
+        <div class="tab-pane active" data-bind="attr: { id: 'properties-' + id() }">
+
+     <h6>${ _('Prepare') }</h6>
+       <ul data-bind="visible: properties.prepares().length > 0, foreach: properties.prepares" class="unstyled">
+         <li>
+           <div style="display: inline-block; width: 60px" data-bind="text: type"></div>
+           <input type="text" class="filechooser-input input-xlarge"
+                data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.prepares.help_text }"/>
+            <a href="#" data-bind="click: function(){ $parent.properties.prepares.remove(this); $(document).trigger('drawArrows') }">
+              <i class="fa fa-minus"></i>
+            </a>
+          </li>
+       </ul>
+       <a class="pointer" data-bind="click: function(){ properties.prepares.push({'type': 'mkdir', 'value': ''}); $(document).trigger('drawArrows') }">
+         ${ _('Directory') } <i class="fa fa-plus"></i>
+       </a>
+       <a class="pointer" data-bind="click: function(){ properties.prepares.push({'type': 'delete', 'value': ''}); $(document).trigger('drawArrows') }">
+         ${ _('Delete') } <i class="fa fa-plus"></i>
+       </a>
+    
+       <h6>
+         <a class="pointer" data-bind="click: function(){ properties.job_properties.push({'name': '', 'value': ''}); $(document).trigger('drawArrows') }">
+           ${ _('Properties') } <i class="fa fa-plus"></i>
+         </a>
+       </h6>
+           <ul data-bind="visible: properties.job_properties().length > 0, foreach: properties.job_properties" class="unstyled">
+           <li>
+             <input type="text" data-bind="value: name" placeholder="${ _('name, e.g. mapred.job.queue.name') }"/>
+             <input type="text" class="filechooser-input input-xlarge" data-bind="filechooser: value, filechooserOptions: globalFilechooserOptions, value: value, attr: { placeholder: $root.workflow_properties.job_properties.help_text }"/>
+             <a href="#" data-bind="click: function(){ $parent.properties.job_properties.remove(this); $(document).trigger('drawArrows') }">
+               <i class="fa fa-minus"></i>
+              </a>
+             </li>
+           </ul>
+           <em data-bind="visible: properties.job_properties().length == 0">${ _('No properties defined.') }</em>
+
+           <h6>             
+             <span data-bind="text: $root.workflow_properties.java_opts.label"></span>             
+           </h6>
+           <input type="text" class="input-xlarge" data-bind="value: properties.java_opts, attr: { placeholder: $root.workflow_properties.java_opts.help_text }" />
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
+        </div>
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- /ko -->
+</script>
+
+</%def>
+
+

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 8 - 1506
apps/oozie/src/oozie/templates/editor2/workflow_editor.mako


+ 42 - 6
apps/oozie/src/oozie/views/dashboard.py

@@ -37,15 +37,17 @@ from desktop.lib.i18n import smart_str, smart_unicode
 from desktop.lib.rest.http_client import RestException
 from desktop.lib.view_util import format_duration_in_millis
 from desktop.log.access import access_warn
+from desktop.models import Document, Document2
 
 from liboozie.oozie_api import get_oozie
+from liboozie.credentials import Credentials
 from liboozie.submittion import Submission
 from liboozie.types import Workflow as OozieWorkflow, Coordinator as CoordinatorWorkflow, Bundle as BundleWorkflow
 
 from oozie.conf import OOZIE_JOBS_COUNT, ENABLE_CRON_SCHEDULING, ENABLE_V2
 from oozie.forms import RerunForm, ParameterForm, RerunCoordForm, RerunBundleForm, UpdateEndTimeForm
-from oozie.models import Workflow, Job, utc_datetime_format, Bundle, Coordinator, get_link, History as OldHistory
-from oozie.models2 import History
+from oozie.models import Workflow as OldWorkflow, Job, utc_datetime_format, Bundle, Coordinator, get_link, History as OldHistory
+from oozie.models2 import History, Workflow, WORKFLOW_NODE_PROPERTIES
 from oozie.settings import DJANGO_APPS
 
 
@@ -55,6 +57,12 @@ def get_history():
   else:
     return OldHistory
 
+def get_workflow():
+  if ENABLE_V2.get():
+    return Workflow
+  else:
+    return OldWorkflow
+
 
 LOG = logging.getLogger(__name__)
 MAX_COORD_ACTIONS = 250
@@ -72,6 +80,15 @@ Permissions checking happens by calling:
   * check_job_edition_permission()
 """
 
+def _get_workflows(user):
+  return [{
+        'name': workflow.name,
+        'owner': workflow.owner.username,
+        'value': workflow.uuid,
+        'id': workflow.id
+      } for workflow in [d.content_object for d in Document.objects.get_docs(user, Document2, extra='workflow2')]
+    ]  
+
 
 def manage_oozie_jobs(request, job_id, action):
   if request.method != 'POST':
@@ -232,6 +249,10 @@ def list_oozie_workflow(request, job_id):
   if oozie_bundle is not None:
     setattr(oozie_workflow, 'oozie_bundle', oozie_bundle)
 
+  workflow_data = None
+  credentials = None
+  doc = None
+
   if ENABLE_V2.get():
     # To update with the new History document model
     hue_coord = get_history().get_coordinator_from_config(oozie_workflow.conf_dict)
@@ -243,8 +264,16 @@ def list_oozie_workflow(request, job_id):
     if hue_workflow:
       workflow_graph = hue_workflow.gen_status_graph(oozie_workflow)
       full_node_list = hue_workflow.nodes
+      workflow_id = hue_workflow.id  
+      wid = {
+        'id': workflow_id
+      }
+      doc = Document2.objects.get(type='oozie-workflow2', **wid)
+      new_workflow = get_workflow()(document=doc)
+      workflow_data = new_workflow.get_data()
+      credentials = Credentials()
     else:
-      workflow_graph, full_node_list = '', None    
+      workflow_graph, full_node_list = OldWorkflow.gen_status_graph_from_xml(request.user, oozie_workflow)
   else:
     history = get_history().cross_reference_submission_history(request.user, job_id)
 
@@ -258,7 +287,7 @@ def list_oozie_workflow(request, job_id):
       workflow_graph = hue_workflow.gen_status_graph(oozie_workflow)
       full_node_list = hue_workflow.node_list
     else:
-      workflow_graph, full_node_list = Workflow.gen_status_graph_from_xml(request.user, oozie_workflow)
+      workflow_graph, full_node_list = get_workflow().gen_status_graph_from_xml(request.user, oozie_workflow)
 
   parameters = oozie_workflow.conf_dict.copy()
 
@@ -295,7 +324,14 @@ def list_oozie_workflow(request, job_id):
     'hue_coord': hue_coord,
     'parameters': parameters,
     'has_job_edition_permission': has_job_edition_permission,
-    'workflow_graph': workflow_graph
+    'workflow_graph': workflow_graph,
+    'layout_json': json.dumps(workflow_data['layout'], cls=JSONEncoderForHTML) if workflow_data else '',
+    'workflow_json': json.dumps(workflow_data['workflow'], cls=JSONEncoderForHTML) if workflow_data else '',
+    'credentials_json': json.dumps(credentials.credentials.keys(), cls=JSONEncoderForHTML) if credentials else '',
+    'workflow_properties_json': json.dumps(WORKFLOW_NODE_PROPERTIES, cls=JSONEncoderForHTML),
+    'doc1_id': doc.doc.get().id if doc else -1,
+    'subworkflows_json': json.dumps(_get_workflows(request.user), cls=JSONEncoderForHTML),
+    'can_edit_json': json.dumps(doc is None or doc.doc.get().is_editable(request.user))
   })
 
 
@@ -688,7 +724,7 @@ def submit_external_job(request, application_path):
     if params_form.is_valid():
       mapping = dict([(param['name'], param['value']) for param in params_form.cleaned_data])
       application_name = os.path.basename(application_path)
-      application_class = Bundle if application_name == 'bundle.xml' else Coordinator if application_name == 'coordinator.xml' else Workflow
+      application_class = Bundle if application_name == 'bundle.xml' else Coordinator if application_name == 'coordinator.xml' else get_workflow()
       mapping[application_class.get_application_path_key()] = application_path
 
       try:

+ 3 - 3
apps/oozie/src/oozie/views/editor2.py

@@ -37,7 +37,7 @@ from liboozie.submission2 import Submission
 
 from oozie.decorators import check_document_access_permission, check_document_modify_permission
 from oozie.forms import ParameterForm
-from oozie.models import Workflow as OlfWorklow, Coordinator as OldCoordinator, Bundle as OldBundle, Job
+from oozie.models import Workflow as OldWorklow, Coordinator as OldCoordinator, Bundle as OldBundle, Job
 from oozie.models2 import Node, Workflow, Coordinator, Bundle, NODES, WORKFLOW_NODE_PROPERTIES, import_workflow_from_hue_3_7,\
     find_dollar_variables, find_dollar_braced_variables
 from oozie.views.editor import edit_workflow as old_edit_workflow, edit_coordinator as old_edit_coordinator, edit_bundle as old_edit_bundle
@@ -50,7 +50,7 @@ LOG = logging.getLogger(__name__)
 def list_editor_workflows(request):  
   workflows = [d.content_object.to_dict() for d in Document.objects.get_docs(request.user, Document2, extra='workflow2')]
 
-  workflows_v1 = [job.doc.get().to_dict() for job in Document.objects.available(OlfWorklow, request.user) if job.managed]
+  workflows_v1 = [job.doc.get().to_dict() for job in Document.objects.available(OldWorklow, request.user) if job.managed]
   if workflows_v1:
     workflows.extend(workflows_v1)
   
@@ -134,7 +134,7 @@ def delete_job(request):
     else: # Old version
       job = Job.objects.can_read_or_exception(request, job['object_id'])
       Job.objects.can_edit_or_exception(request, job)
-      OlfWorklow.objects.destroy(job, request.fs)      
+      OldWorklow.objects.destroy(job, request.fs)      
 
   response = {}
   request.info(_('Document deleted.') if len(jobs) > 1 else _('Document deleted.'))

+ 2 - 6
apps/oozie/static/css/workflow-editor.css

@@ -75,11 +75,11 @@
   margin-left: 0.5%;
 }
 
-.row-fluid .span10 {
+.dashboard .row-fluid .span10 {
   width: 90%;
 }
 
-.row-fluid .span1 {
+.dashboard .row-fluid .span1 {
   width: 4.5%;
 }
 
@@ -182,10 +182,6 @@
   padding: 5px;
 }
 
-body {
-  background-color: #FFFFFF;
-}
-
 .ui-sortable {
   min-height: 10px !important;
 }

+ 105 - 0
apps/oozie/static/js/workflow-editor.utils.js

@@ -0,0 +1,105 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you 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 linkWidgets(fromId, toId) {
+  var _from = $("#wdg_" + (typeof fromId == "function" ? fromId() : fromId));
+  var _to = $("#wdg_" + (typeof toId == "function" ? toId() : toId));
+  if (_from.length > 0 && _to.length > 0) {
+    var _fromCenter = {
+      x: _from.position().left + _from.outerWidth() / 2,
+      y: _from.position().top + _from.outerHeight() + 3
+    }
+
+    var _toCenter = {
+      x: _to.position().left + _to.outerWidth() / 2,
+      y: _to.position().top - 5
+    }
+
+    var _curveCoords = {};
+
+    if (_fromCenter.x == _toCenter.x) {
+      _curveCoords.x = _fromCenter.x;
+      _curveCoords.y = _fromCenter.y + (_toCenter.y - _fromCenter.y) / 2;
+    } else {
+      if (_fromCenter.x > _toCenter.x) {
+        _fromCenter.x = _fromCenter.x - 5;
+        _toCenter.x = _toCenter.x + 5;
+      } else {
+        _fromCenter.x = _fromCenter.x + 5;
+        _toCenter.x = _toCenter.x - 5;
+      }
+      _curveCoords.x = _fromCenter.x - (_fromCenter.x - _toCenter.x) / 4;
+      _curveCoords.y = _fromCenter.y + (_toCenter.y - _fromCenter.y) / 2;
+    }
+
+    $(document.body).curvedArrow({
+      p0x: _fromCenter.x,
+      p0y: _fromCenter.y,
+      p1x: _curveCoords.x,
+      p1y: _curveCoords.y,
+      p2x: _toCenter.x,
+      p2y: _toCenter.y,
+      lineWidth: 2,
+      size: 10,
+      strokeStyle: viewModel.isEditing() ? '#e5e5e5' : '#dddddd'
+    });
+  }
+}
+
+function drawArrows() {
+  $("canvas").remove();
+  if (viewModel.oozieColumns()[0].rows().length > 3) {
+    var _links = viewModel.workflow.linkMapping();
+    Object.keys(_links).forEach(function(id) {
+      if (_links[id].length > 0) {
+        _links[id].forEach(function(nextId) {
+          linkWidgets(id, nextId);
+        });
+      }
+    });
+  }
+}
+
+function toggleProperties(widget) {
+  if (widget.oozieMovable()) {
+    var _el = $("#wdg_" + widget.id());
+    if (!widget.ooziePropertiesExpanded()) {
+      setLastExpandedWidget(widget);
+      _el.find(".prop-editor").show();
+      widget.ooziePropertiesExpanded(true);
+    } else {
+      if (widget.oozieExpanded()) {
+        exposeOverlayClickHandler();
+      } else {
+        _el.find(".prop-editor").hide();
+        widget.ooziePropertiesExpanded(false);
+      }
+    }
+    $(document).trigger("drawArrows");
+  }
+}
+
+var _linkMappingTimeout = -1;
+$(document).on("drawArrows", function(){
+  window.clearTimeout(_linkMappingTimeout);
+  if (typeof renderChangeables != 'undefined'){
+  	_linkMappingTimeout = window.setTimeout(renderChangeables, 25);	
+  }
+});
+
+$(document).on("removeArrows", function(){
+  $("canvas").remove();
+});

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů