Эх сурвалжийг харах

HUE-4109 [oozie] First pass at refactoring Coordinator editor to be pluggable

Note: they are 2 TODOs to fix before pushing to master
Romain Rigaux 9 жил өмнө
parent
commit
39400d7

+ 1 - 1
apps/oozie/src/oozie/static/oozie/js/coordinator-editor.ko.js

@@ -109,7 +109,7 @@ var Coordinator = function (vm, coordinator) {
     if (value || !vm.isEditing()) {
       coordCron.disable();
     } else {
-      coordCron.enable();
+      //coordCron.enable(); // TODO REVERT
     }
   });
 

+ 421 - 0
apps/oozie/src/oozie/templates/editor2/common_scheduler.inc.mako

@@ -0,0 +1,421 @@
+## 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.
+<%!
+from desktop.views import _ko
+from django.utils.translation import ugettext as _
+%>
+
+<%def name="import_layout()">
+
+<div class="container-fluid">
+  <div class="row-fluid">
+    <div class="span12 coordinator">
+
+      <div class="card card-home">
+        <h1 class="card-heading simple" style="border-bottom: none"><span data-bind="editable: $root.coordinator.name, editableOptions: {enabled: $root.isEditing(), placement: 'right'}"></span></h1>
+        <div class="card-body muted" style="margin-top: 2px" data-bind="visible: $root.isEditing() || (! $root.isEditing() && $root.coordinator.properties.description)">
+          <span data-bind="editable: $root.coordinator.properties.description, editableOptions: {enabled: $root.isEditing(), placement: 'right', emptytext: '${_ko('Add a description...')}'}"></span>
+        </div>
+      </div>
+
+      <div class="card card-home" style="margin-top: 20px">
+        <h1 class="card-heading simple">${ _('Which workflow to schedule?') }</h1>
+
+        <div class="card-body">
+          <a class="pointer" data-bind="visible: ! coordinator.properties.workflow(), click: showChooseWorkflow">${ _('Choose a workflow...') }</a>
+          <!-- ko if: coordinator.properties.workflow -->
+            <!-- ko if: isEditing -->
+            <a class="pointer" data-bind="click: showChooseWorkflow, text: getWorkflowById(coordinator.properties.workflow()).name"></a>
+
+            <a data-bind="attr: { href: '${ url('oozie:edit_workflow') }?workflow=' + coordinator.properties.workflow() }" target="_blank" title="${ _('Open') }">
+             <i class="fa fa-external-link-square"></i>
+            </a>
+            <!-- /ko -->
+            <!-- ko ifnot: isEditing -->
+            <span data-bind="text: getWorkflowById(coordinator.properties.workflow()).name"></span>
+            <a data-bind="attr: { href: '${ url('oozie:edit_workflow') }?workflow=' + coordinator.properties.workflow() }" target="_blank" title="${ _('Open') }">
+              <i class="fa fa-external-link-square"></i>
+            </a>
+            <!-- /ko -->
+          <!-- /ko -->
+        </div>
+      </div>
+
+      <div class="card card-home" data-bind="visible: coordinator.properties.workflow" style="margin-top: 20px">
+        <h1 class="card-heading simple">${ _('How often?') }
+        </h1>
+
+        <div class="card-body">
+
+          <div class="row-fluid">
+            <div class="span6">
+              <form class="form-horizontal">
+                <div class="control-group" data-bind="visible: ! coordinator.properties.cron_advanced()">
+                  <div class="controls" id="jqCron-container">
+                    <div id="jqCron-instance" style="margin-top: 5px; display: inline-block"></div>
+                  </div>
+                </div>
+                <div class="control-group" data-bind="visible: coordinator.properties.cron_advanced">
+                  <label class="control-label">${ _('Crontab') }</label>
+                  <div class="controls">
+                    <input id="coord-frequency" type="text" data-bind="value: coordinator.properties.cron_frequency, enable: $root.isEditing" name="cron_frequency"/>
+                    <span class="help-inline">
+                      <a data-bind="visible: coordinator.properties.cron_advanced" href="http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html" target="_blank">
+                      <i class="fa fa-question-circle" title="${ _('Check syntax ?') }"></i></a>
+                    </span>
+                  </div>
+                </div>
+                <div class="control-group" style="margin-bottom: 0">
+                  <label class="control-label"></label>
+                  <div class="controls">
+                    <a href="#" data-bind="click: function() { $root.coordinator.showAdvancedFrequencyUI(! $root.coordinator.showAdvancedFrequencyUI()) }">
+                      <i class="fa fa-sliders"></i> <span data-bind="visible: ! coordinator.showAdvancedFrequencyUI()">${ _('Options') }</span>
+                      <span data-bind="visible: coordinator.showAdvancedFrequencyUI">${ _('Hide') }</span>
+                    </a>
+                  </div>
+                </div>
+             </form>
+            </div>
+
+          </div>
+
+          <div data-bind="visible: coordinator.showAdvancedFrequencyUI">
+            <form class="form-horizontal">
+
+              <div class="control-group">
+                <div class="controls">
+                  <label class="checkbox" style="display: inline-block; margin-top: 5px">
+                    <input type="checkbox" name="coordinator.properties.cron_advanced" data-bind="checked: coordinator.properties.cron_advanced, enable: $root.isEditing" /> ${ _('Advanced syntax') }
+                  </label>
+                </div>
+              </div>
+
+              <div class="control-group" style="margin-bottom: 20">
+                <label class="control-label">${ _('Timezone') }</label>
+                <div class="controls">
+                  <select data-bind="options: $root.availableTimezones, select2: { placeholder: '${ _ko("Select a Timezone") }', update: coordinator.properties.timezone, readonly: !$root.isEditing()}" style="width: 180px"></select>
+                  <span class="help-inline"></span>
+                </div>
+              </div>
+
+              <div class="control-group">
+                <label class="control-label">${ _('From') }</label>
+                <div class="controls">
+                  <div class="input-prepend input-group">
+                    <span class="add-on input-group-addon">
+                      <i class="fa fa-calendar"></i>
+                    </span>
+                    <input type="text" class="input-small" data-bind="value: coordinator.properties.startDateUI, datepicker: {}, enable: $root.isEditing" />
+                  </div>
+                  <div class="input-prepend input-group">
+                    <span class="add-on input-group-addon">
+                      <i class="fa fa-clock-o"></i>
+                    </span>
+                    <input type="text" class="input-mini" data-bind="value: coordinator.properties.startTimeUI, timepicker: {}, enable: $root.isEditing" />
+                  </div>
+                  <span class="help-inline"></span>
+                </div>
+              </div>
+              <div class="control-group">
+                <label class="control-label">${ _('To') }</label>
+                <div class="controls">
+                  <div class="input-prepend input-group">
+                    <span class="add-on input-group-addon">
+                      <i class="fa fa-calendar"></i>
+                    </span>
+                    <input type="text" class="input-small" data-bind="value: coordinator.properties.endDateUI, datepicker: {}, enable: $root.isEditing" />
+                  </div>
+                  <div class="input-prepend input-group">
+                    <span class="add-on input-group-addon">
+                      <i class="fa fa-clock-o"></i>
+                    </span>
+                    <input type="text" class="input-mini" data-bind="value: coordinator.properties.endTimeUI, timepicker: {}, enable: $root.isEditing" />
+                  </div>
+                  <span class="help-inline"></span>
+                </div>
+              </div>
+
+            </form>
+
+          </div>
+
+
+        </div>
+      </div>
+
+
+      <div class="card card-home" data-bind="visible: coordinator.properties.workflow()" style="margin-top: 20px; margin-bottom: 20px">
+        <h1 class="card-heading simple">${ _('Workflow Parameters') }</h1>
+
+        <div class="card-body">
+          <span class="muted" data-bind="visible: coordinator.variables().length == 0 && ! isEditing()">${ _('This coordinator has no defined parameters.') }</span>
+
+          <ul data-bind="foreach: coordinator.variables, visible: coordinator.variables().length > 0" class="unstyled">
+            <li style="margin-bottom: 10px">
+              <select data-bind="options: $parent.coordinator.workflowParameters, optionsText: 'name', optionsValue: 'name', select2: { placeholder: '${ _ko("Select a parameter") }', update: workflow_variable, type: 'parameter', readonly: !$root.isEditing()}" style="width: 250px"></select>
+
+              &nbsp;&nbsp;
+
+              <div class="btn-group">
+                <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
+                        aria-expanded="false" data-bind="enable: $root.isEditing">
+                  <!-- ko if: dataset_type() == 'parameter' -->
+                  ${ _('Parameter') }
+                  <!-- /ko -->
+                  <!-- ko if: dataset_type() == 'input_path' -->
+                  ${ _('Input Path') }
+                  <!-- /ko -->
+                  <!-- ko if: dataset_type() == 'output_path' -->
+                  ${ _('Output Path') }
+                  <!-- /ko --> <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu" role="menu" data-bind="foreach: $parent.coordinator.variablesUI">
+                  <!-- ko if: $data != $parent.dataset_type() -->
+                  <li>
+                    <a href="#" data-bind="click: function() { $parent.dataset_type($data) } ">
+                      <!-- ko if: $data == 'parameter' -->
+                      ${ _('Parameter') }
+                      <!-- /ko -->
+                      <!-- ko if: $data == 'input_path' -->
+                      ${ _('Input Path') }
+                      <!-- /ko -->
+                      <!-- ko if: $data == 'output_path' -->
+                      ${ _('Output Path') }
+                      <!-- /ko -->
+                    </a>
+                  </li>
+                  <!-- /ko -->
+                </ul>
+              </div>
+
+              &nbsp;&nbsp;
+
+              <span data-bind="visible: $root.isEditing">
+                <input type="text" class="filechooser-input dataset-input" data-bind="value: dataset_variable, filechooser: dataset_variable, attr: { placeholder:
+                  dataset_type() == 'input_path' ? '${ _ko("Required data path dependency to start the worklow") }' :
+                  dataset_type() == 'output_path' ? '${ _ko("Data path created by the workflow") }' :
+                  'e.g. 1, 2, 3, /data/logs, ${"$"}{coord:nominalTime()}' },
+                  typeahead: { target: dataset_variable, source: datasetTypeaheadSource, triggerOnFocus: true, multipleValues: true, multipleValuesSeparator: '', multipleValuesExtractors: [' ', '/'] }" style="margin-bottom:0; width: 380px" />
+              </span>
+
+              <span data-bind="text: dataset_variable, visible: ! $root.isEditing()"></span>
+
+              <a href="#" data-bind="click: function(){ $root.coordinator.variables.remove(this); }, visible: $root.isEditing">
+                <i class="fa fa-minus"></i>
+              </a>
+
+              <!-- ko if: dataset_type() == 'input_path' || dataset_type() == 'output_path' -->
+
+                <a href="#" data-bind="click: function() { show_advanced(! show_advanced()) }">
+                  <i class="fa fa-sliders"></i>
+                </a>
+
+                <span style="padding-left:100px">
+                  <span data-bind="visible: dataset_variable().length == 0">
+                    e.g. /data/${'$'}{YEAR}/${'$'}{MONTH}/${'$'}{DAY}
+                  </span>
+                  <span data-bind="visible: dataset_variable().length > 0 && instance_choice() != 'range'">
+                    ${ _('Will convert to') }
+                    <a target="_blank" data-bind="text: convertDatasetVariables(dataset_variable(), same_start(), start(), same_frequency(), frequency_unit(), start_instance(), instance_choice()), attr: {'href': '/filebrowser/view=' + convertDatasetVariables(dataset_variable(), same_start(), start(), same_frequency(), frequency_unit(), start_instance(), instance_choice())}"></a>
+                  </span>
+                  </a>
+                </span>
+
+                <div data-bind="visible: show_advanced" style="padding: 20px">
+                  <form class="form-horizontal">
+                    <div class="control-group">
+                      <label class="control-label">${ _('Done flag') }</label>
+                      <div class="controls">
+                        <input type="checkbox" data-bind="checked: use_done_flag, style: {'margin-top': !use_done_flag() ? '9px' : '-1px'}, enable: $root.isEditing" />
+                        <input type="text" data-bind="value: done_flag, visible: use_done_flag, enable: $root.isEditing"/>
+                      </div>
+                    </div>
+                    <div class="control-group">
+                      <label class="control-label">${ _('Same frequency') }</label>
+                      <div class="controls">
+                        <input type="checkbox" data-bind="checked: same_frequency, style: {'margin-top': same_frequency() ? '5px' : '0'}, enable: $root.isEditing" />
+                        <span data-bind="visible: ! same_frequency()">
+                          ${ _('Every') }
+                        </span>
+                        <select data-bind="value: frequency_number, visible: ! same_frequency(), enable: $root.isEditing" style="width: 50px">
+                          % for i in xrange(0, 60):
+                          <option value="${ i }">${ i }</option>
+                          % endfor
+                        </select>
+                        <select data-bind="value: frequency_unit, visible: ! same_frequency(), enable: $root.isEditing" style="width: 100px">
+                          <option value="minutes">${ _('Minutes') }</option>
+                          <option value="hours">${ _('Hours') }</option>
+                          <option value="days" selected="selected">${ _('Days') }</option>
+                          <option value="months">${ _('Months') }</option>
+                        </select>
+                      </div>
+                    </div>
+                    <div class="control-group">
+                      <label class="control-label">${ _('Same start') }</label>
+                      <div class="controls">
+                        <input type="checkbox" data-bind="checked: same_start, style: {'margin-top': same_start() ? '9px' : '-1px'}, enable: $root.isEditing" />
+                        <input type="text" data-bind="value: start, visible: ! same_start(), enable: $root.isEditing"/>
+                      </div>
+                    </div>
+                    <div class="control-group">
+                      <label class="control-label">${ _('Same timezone') }</label>
+                      <div class="controls">
+                        <input type="checkbox" data-bind="checked: same_timezone, style: {'margin-top': same_timezone() ? '5px' : '0'}, enable: $root.isEditing" />
+                        <select data-bind="options: $root.availableTimezones, select2: { placeholder: '${ _ko("Select a Timezone") }', update: timezone}, visible: ! same_timezone(), enable: $root.isEditing" style="width: 180px"></select>
+                      </div>
+                    </div>
+                    <div class="control-group">
+                      <label class="control-label">${ _('Instance') }</label>
+                      <div class="controls">
+                        <div class="btn-group" data-toggle="buttons-radio">
+                          <button id="default-btn" type="button" class="btn"
+                                  data-bind="click: function() { instance_choice('default'); }, css: { active: instance_choice() == 'default' }, enable: $root.isEditing">
+                            ${ _('Default') }
+                          </button>
+                          <button id="single-btn" type="button" class="btn"
+                                  data-bind="click: function() { instance_choice('single'); }, css: { active: instance_choice() == 'single' }, enable: $root.isEditing">
+                            ${ _('Single') }
+                          </button>
+                          <button id="range-btn" type="button" class="btn"
+                                  data-bind="click: function() { instance_choice('range'); }, css: { active: instance_choice() == 'range' }, enable: $root.isEditing">
+                            ${ _('Range') }
+                          </button>
+                        </div>
+                      </div>
+                    </div>
+                    <div class="control-group" data-bind="visible: $.inArray(instance_choice(), ['single', 'range']) != -1">
+                      <label class="control-label">${ _('Start') }</label>
+                      <div class="controls">
+                        <input name="instance_start" type="number" data-bind="value: start_instance, enable: ! is_advanced_start_instance(), enable: $root.isEditing"/>
+                        <label style="display: inline">
+                            &nbsp;
+                            <input type="checkbox" data-bind="checked: is_advanced_start_instance, enable: $root.isEditing" style="margin-top:0">
+                            ${ _('(advanced)') }
+                          </label>
+                          <input type="text" data-bind="value: advanced_start_instance, visible: is_advanced_start_instance(), enable: $root.isEditing"/>
+                      </div>
+                    </div>
+                    <div class="control-group" data-bind="visible: instance_choice() == 'range'">
+                      <label class="control-label">${ _('End') }</label>
+                      <div class="controls">
+                        <input name="instance_end" type="number" data-bind="value: end_instance, enable: ! is_advanced_end_instance(), enable: $root.isEditing"/>
+                        <label style="display: inline">
+                            &nbsp;
+                            <input type="checkbox" data-bind="checked: is_advanced_end_instance, enable: $root.isEditing" style="margin-top:0">
+                            ${ _('(advanced)') }
+                          </label>
+                          <input type="text" data-bind="value: advanced_end_instance, visible: is_advanced_end_instance(), enable: $root.isEditing"/>
+                      </div>
+                    </div>
+                  </form>
+                </div>
+              <!-- /ko -->
+            </li>
+          </ul>
+
+          <a class="pointer" data-bind="click: function() { coordinator.addVariable() }, visible: isEditing">
+            <i class="fa fa-plus"></i> ${ _('Add parameter') }
+          </a>
+
+        </div>
+
+      </div>
+
+      <div class="card card-home" data-bind="visible: coordinator.id() == null && coordinator.properties.workflow()">
+        <div class="card-body">
+          <a href type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }"
+            data-bind="click: $root.save, css: {'btn': true}">
+            ${ _('Save') }
+          </a>
+        </div>
+      </div>
+
+    </div>
+  </div>
+</div>
+
+<div id="chooseWorkflowDemiModal" class="demi-modal fade" data-backdrop="false">
+  <div class="modal-body">
+    <a href="javascript: void(0)" data-dismiss="modal" class="pull-right"><i class="fa fa-times"></i></a>
+    <div style="float: left; margin-right: 10px;text-align: center">
+      <input type="text" data-bind="clearable: $root.workflowModalFilter, valueUpdate:'afterkeydown'" placeholder="${_('Filter workflows')}" class="input" style="float: left" /><br/>
+    </div>
+    <div>
+      <ul data-bind="foreach: $root.filteredModalWorkflows().sort(function (l, r) { return l.name() > r.name() ? 1 : -1 }), visible: $root.filteredModalWorkflows().length > 0"
+          class="unstyled inline fields-chooser" style="height: 100px; overflow-y: auto">
+        <li>
+          <span class="badge badge-info" data-bind="click: selectWorkflow">
+            <span data-bind="text: name(), attr: {'title': uuid()}"></span>
+          </span>
+          <a data-bind="attr: { href: '${ url('oozie:edit_workflow') }?workflow=' + uuid() }" target="_blank" title="${ _('Open') }">
+            <i class="fa fa-external-link-square"></i>
+          </a>
+        </li>
+      </ul>
+      <div class="alert alert-info inline" data-bind="visible: $root.filteredModalWorkflows().length == 0" style="margin-left: 250px;margin-right: 50px; height: 42px;line-height: 42px">
+        ${_('There are no workflows matching your search term.')}
+      </div>
+    </div>
+  </div>
+  <div><a class="pointer demi-modal-chevron" data-dismiss="modal"><i class="fa fa-chevron-up"></i></a></div>
+</div>
+
+<div id="settingsModal" class="modal hide fade">
+  <div class="modal-header" style="padding-bottom: 2px">
+    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+    <h3 id="myModalLabel">${ _('Coordinator Settings') }</h3>
+  </div>
+  <div class="modal-body">
+      <h4>${ _('Submission Parameters') }</h4>
+      <ul data-bind="foreach: coordinator.properties.parameters" class="unstyled">
+        <!-- ko if: ['oozie.use.system.libpath', 'start_date', 'end_date'].indexOf(name()) == -1 -->
+        <li>
+          <input data-bind="value: name"/>
+          <input data-bind="value: value"/>
+          <a href="#" data-bind="click: function(){ $root.coordinator.properties.parameters.remove(this); }">
+            <i class="fa fa-minus"></i>
+          </a>
+        </li>
+        <!-- /ko -->
+      </ul>
+      <a class="pointer" data-bind="click: function(){ $root.coordinator.properties.parameters.push(ko.mapping.fromJS({'name': '', 'value': ''})); }">
+        <i class="fa fa-plus"></i> ${ _('Add parameter') }
+      </a>
+
+      <h4>${ _('Timeout') }</h4>
+      <input data-bind="value: coordinator.properties.timeout" type="number"/>
+
+      <h4>${ _('Concurrency') }</h4>
+      <select data-bind="options: availableSettings, optionsCaption: '${ _ko("Default") }', value: coordinator.properties.concurrency"></select>
+
+      <h4>${ _('Execution') }</h4>
+      <select data-bind="value: coordinator.properties.execution">
+    <option value="FIFO">${ _("FIFO (oldest first)") }</option>
+    <option value="LIFO">${ _("LIFO (newest first)") }</option>
+    <option value="LAST_ONLY">${ _("LAST_ONLY (discards all older materializations)") }</option>
+      </select>
+
+      <h4>${ _('Throttle') }</h4>
+      <select data-bind="options: availableSettings, optionsCaption: '${ _ko("Default") }', value: coordinator.properties.throttle"></select>
+
+      <h4>${ _('SLA Configuration') }</h4>
+      <div class="sla-form" data-bind="with: $root.coordinator.properties">
+        ## ${ utils.slaForm() } ## TODO re-add
+      </div>
+  </div>
+</div>
+
+</%def>

+ 23 - 0
apps/oozie/src/oozie/templates/editor2/common_scheduler.mako

@@ -0,0 +1,23 @@
+## 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.
+<%!
+from desktop.views import _ko
+from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="scheduler" file="common_scheduler.inc.mako" />
+
+${ scheduler.import_layout() }

+ 2 - 397
apps/oozie/src/oozie/templates/editor2/coordinator_editor.mako

@@ -22,6 +22,7 @@ from django.utils.translation import ugettext as _
 <%namespace name="utils" file="../utils.inc.mako" />
 <%namespace name="coordinator_utils" file="coordinator_utils.mako" />
 <%namespace name="layout" file="../navigation-bar.mako" />
+<%namespace name="scheduler" file="common_scheduler.inc.mako" />
 
 ${ commonheader(_("Coordinator Editor"), "Oozie", user) | n,unicode }
 
@@ -86,403 +87,7 @@ ${ layout.menubar(section='coordinators', is_editor=True, pullright=buttons) }
 </script>
 
 
-<div class="container-fluid">
-  <div class="row-fluid">
-    <div class="span12 coordinator">
-
-      <div class="card card-home">
-        <h1 class="card-heading simple" style="border-bottom: none"><span data-bind="editable: $root.coordinator.name, editableOptions: {enabled: $root.isEditing(), placement: 'right'}"></span></h1>
-        <div class="card-body muted" style="margin-top: 2px" data-bind="visible: $root.isEditing() || (! $root.isEditing() && $root.coordinator.properties.description)">
-          <span data-bind="editable: $root.coordinator.properties.description, editableOptions: {enabled: $root.isEditing(), placement: 'right', emptytext: '${_ko('Add a description...')}'}"></span>
-        </div>
-      </div>
-
-      <div class="card card-home" style="margin-top: 20px">
-        <h1 class="card-heading simple">${ _('Which workflow to schedule?') }</h1>
-
-        <div class="card-body">
-          <a class="pointer" data-bind="visible: ! coordinator.properties.workflow(), click: showChooseWorkflow">${ _('Choose a workflow...') }</a>
-          <!-- ko if: coordinator.properties.workflow -->
-            <!-- ko if: isEditing -->
-            <a class="pointer" data-bind="click: showChooseWorkflow, text: getWorkflowById(coordinator.properties.workflow()).name"></a>
-
-            <a data-bind="attr: { href: '${ url('oozie:edit_workflow') }?workflow=' + coordinator.properties.workflow() }" target="_blank" title="${ _('Open') }">
-             <i class="fa fa-external-link-square"></i>
-            </a>
-            <!-- /ko -->
-            <!-- ko ifnot: isEditing -->
-            <span data-bind="text: getWorkflowById(coordinator.properties.workflow()).name"></span>
-            <a data-bind="attr: { href: '${ url('oozie:edit_workflow') }?workflow=' + coordinator.properties.workflow() }" target="_blank" title="${ _('Open') }">
-              <i class="fa fa-external-link-square"></i>
-            </a>
-            <!-- /ko -->
-          <!-- /ko -->
-        </div>
-      </div>
-
-      <div class="card card-home" data-bind="visible: coordinator.properties.workflow" style="margin-top: 20px">
-        <h1 class="card-heading simple">${ _('How often?') }
-        </h1>
-
-        <div class="card-body">
-
-          <div class="row-fluid">
-            <div class="span6">
-              <form class="form-horizontal">
-                <div class="control-group" data-bind="visible: ! coordinator.properties.cron_advanced()">
-                  <div class="controls" id="jqCron-container">
-                    <div id="jqCron-instance" style="margin-top: 5px; display: inline-block"></div>
-                  </div>
-                </div>
-                <div class="control-group" data-bind="visible: coordinator.properties.cron_advanced">
-                  <label class="control-label">${ _('Crontab') }</label>
-                  <div class="controls">
-                    <input id="coord-frequency" type="text" data-bind="value: coordinator.properties.cron_frequency, enable: $root.isEditing" name="cron_frequency"/>
-                    <span class="help-inline">
-                      <a data-bind="visible: coordinator.properties.cron_advanced" href="http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html" target="_blank">
-                      <i class="fa fa-question-circle" title="${ _('Check syntax ?') }"></i></a>
-                    </span>
-                  </div>
-                </div>
-                <div class="control-group" style="margin-bottom: 0">
-                  <label class="control-label"></label>
-                  <div class="controls">
-                    <a href="#" data-bind="click: function() { $root.coordinator.showAdvancedFrequencyUI(! $root.coordinator.showAdvancedFrequencyUI()) }">
-                      <i class="fa fa-sliders"></i> <span data-bind="visible: ! coordinator.showAdvancedFrequencyUI()">${ _('Options') }</span>
-                      <span data-bind="visible: coordinator.showAdvancedFrequencyUI">${ _('Hide') }</span>
-                    </a>
-                  </div>
-                </div>
-             </form>
-            </div>
-
-          </div>
-
-          <div data-bind="visible: coordinator.showAdvancedFrequencyUI">
-            <form class="form-horizontal">
-
-              <div class="control-group">
-                <div class="controls">
-                  <label class="checkbox" style="display: inline-block; margin-top: 5px">
-                    <input type="checkbox" name="coordinator.properties.cron_advanced" data-bind="checked: coordinator.properties.cron_advanced, enable: $root.isEditing" /> ${ _('Advanced syntax') }
-                  </label>
-                </div>
-              </div>
-
-              <div class="control-group" style="margin-bottom: 20">
-                <label class="control-label">${ _('Timezone') }</label>
-                <div class="controls">
-                  <select data-bind="options: $root.availableTimezones, select2: { placeholder: '${ _ko("Select a Timezone") }', update: coordinator.properties.timezone, readonly: !$root.isEditing()}" style="width: 180px"></select>
-                  <span class="help-inline"></span>
-                </div>
-              </div>
-
-              <div class="control-group">
-                <label class="control-label">${ _('From') }</label>
-                <div class="controls">
-                  <div class="input-prepend input-group">
-                    <span class="add-on input-group-addon">
-                      <i class="fa fa-calendar"></i>
-                    </span>
-                    <input type="text" class="input-small" data-bind="value: coordinator.properties.startDateUI, datepicker: {}, enable: $root.isEditing" />
-                  </div>
-                  <div class="input-prepend input-group">
-                    <span class="add-on input-group-addon">
-                      <i class="fa fa-clock-o"></i>
-                    </span>
-                    <input type="text" class="input-mini" data-bind="value: coordinator.properties.startTimeUI, timepicker: {}, enable: $root.isEditing" />
-                  </div>
-                  <span class="help-inline"></span>
-                </div>
-              </div>
-              <div class="control-group">
-                <label class="control-label">${ _('To') }</label>
-                <div class="controls">
-                  <div class="input-prepend input-group">
-                    <span class="add-on input-group-addon">
-                      <i class="fa fa-calendar"></i>
-                    </span>
-                    <input type="text" class="input-small" data-bind="value: coordinator.properties.endDateUI, datepicker: {}, enable: $root.isEditing" />
-                  </div>
-                  <div class="input-prepend input-group">
-                    <span class="add-on input-group-addon">
-                      <i class="fa fa-clock-o"></i>
-                    </span>
-                    <input type="text" class="input-mini" data-bind="value: coordinator.properties.endTimeUI, timepicker: {}, enable: $root.isEditing" />
-                  </div>
-                  <span class="help-inline"></span>
-                </div>
-              </div>
-
-            </form>
-
-          </div>
-
-
-        </div>
-      </div>
-
-
-      <div class="card card-home" data-bind="visible: coordinator.properties.workflow()" style="margin-top: 20px; margin-bottom: 20px">
-        <h1 class="card-heading simple">${ _('Workflow Parameters') }</h1>
-
-        <div class="card-body">
-          <span class="muted" data-bind="visible: coordinator.variables().length == 0 && ! isEditing()">${ _('This coordinator has no defined parameters.') }</span>
-
-          <ul data-bind="foreach: coordinator.variables, visible: coordinator.variables().length > 0" class="unstyled">
-            <li style="margin-bottom: 10px">
-              <select data-bind="options: $parent.coordinator.workflowParameters, optionsText: 'name', optionsValue: 'name', select2: { placeholder: '${ _ko("Select a parameter") }', update: workflow_variable, type: 'parameter', readonly: !$root.isEditing()}" style="width: 250px"></select>
-
-              &nbsp;&nbsp;
-
-              <div class="btn-group">
-                <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
-                        aria-expanded="false" data-bind="enable: $root.isEditing">
-                  <!-- ko if: dataset_type() == 'parameter' -->
-                  ${ _('Parameter') }
-                  <!-- /ko -->
-                  <!-- ko if: dataset_type() == 'input_path' -->
-                  ${ _('Input Path') }
-                  <!-- /ko -->
-                  <!-- ko if: dataset_type() == 'output_path' -->
-                  ${ _('Output Path') }
-                  <!-- /ko --> <span class="caret"></span>
-                </button>
-                <ul class="dropdown-menu" role="menu" data-bind="foreach: $parent.coordinator.variablesUI">
-                  <!-- ko if: $data != $parent.dataset_type() -->
-                  <li>
-                    <a href="#" data-bind="click: function() { $parent.dataset_type($data) } ">
-                      <!-- ko if: $data == 'parameter' -->
-                      ${ _('Parameter') }
-                      <!-- /ko -->
-                      <!-- ko if: $data == 'input_path' -->
-                      ${ _('Input Path') }
-                      <!-- /ko -->
-                      <!-- ko if: $data == 'output_path' -->
-                      ${ _('Output Path') }
-                      <!-- /ko -->
-                    </a>
-                  </li>
-                  <!-- /ko -->
-                </ul>
-              </div>
-
-              &nbsp;&nbsp;
-
-              <span data-bind="visible: $root.isEditing">
-                <input type="text" class="filechooser-input dataset-input" data-bind="value: dataset_variable, filechooser: dataset_variable, attr: { placeholder:
-                  dataset_type() == 'input_path' ? '${ _ko("Required data path dependency to start the worklow") }' :
-                  dataset_type() == 'output_path' ? '${ _ko("Data path created by the workflow") }' :
-                  'e.g. 1, 2, 3, /data/logs, ${"$"}{coord:nominalTime()}' },
-                  typeahead: { target: dataset_variable, source: datasetTypeaheadSource, triggerOnFocus: true, multipleValues: true, multipleValuesSeparator: '', multipleValuesExtractors: [' ', '/'] }" style="margin-bottom:0; width: 380px" />
-              </span>
-
-              <span data-bind="text: dataset_variable, visible: ! $root.isEditing()"></span>
-
-              <a href="#" data-bind="click: function(){ $root.coordinator.variables.remove(this); }, visible: $root.isEditing">
-                <i class="fa fa-minus"></i>
-              </a>
-
-              <!-- ko if: dataset_type() == 'input_path' || dataset_type() == 'output_path' -->
-
-                <a href="#" data-bind="click: function() { show_advanced(! show_advanced()) }">
-                  <i class="fa fa-sliders"></i>
-                </a>
-
-                <span style="padding-left:100px">
-                  <span data-bind="visible: dataset_variable().length == 0">
-                    e.g. /data/${'$'}{YEAR}/${'$'}{MONTH}/${'$'}{DAY}
-                  </span>
-                  <span data-bind="visible: dataset_variable().length > 0 && instance_choice() != 'range'">
-                    ${ _('Will convert to') }
-                    <a target="_blank" data-bind="text: convertDatasetVariables(dataset_variable(), same_start(), start(), same_frequency(), frequency_unit(), start_instance(), instance_choice()), attr: {'href': '/filebrowser/view=' + convertDatasetVariables(dataset_variable(), same_start(), start(), same_frequency(), frequency_unit(), start_instance(), instance_choice())}"></a>
-                  </span>
-                  </a>
-                </span>
-
-                <div data-bind="visible: show_advanced" style="padding: 20px">
-                  <form class="form-horizontal">
-                    <div class="control-group">
-                      <label class="control-label">${ _('Done flag') }</label>
-                      <div class="controls">
-                        <input type="checkbox" data-bind="checked: use_done_flag, style: {'margin-top': !use_done_flag() ? '9px' : '-1px'}, enable: $root.isEditing" />
-                        <input type="text" data-bind="value: done_flag, visible: use_done_flag, enable: $root.isEditing"/>
-                      </div>
-                    </div>
-                    <div class="control-group">
-                      <label class="control-label">${ _('Same frequency') }</label>
-                      <div class="controls">
-                        <input type="checkbox" data-bind="checked: same_frequency, style: {'margin-top': same_frequency() ? '5px' : '0'}, enable: $root.isEditing" />
-                        <span data-bind="visible: ! same_frequency()">
-                          ${ _('Every') }
-                        </span>
-                        <select data-bind="value: frequency_number, visible: ! same_frequency(), enable: $root.isEditing" style="width: 50px">
-                          % for i in xrange(0, 60):
-                          <option value="${ i }">${ i }</option>
-                          % endfor
-                        </select>
-                        <select data-bind="value: frequency_unit, visible: ! same_frequency(), enable: $root.isEditing" style="width: 100px">
-                          <option value="minutes">${ _('Minutes') }</option>
-                          <option value="hours">${ _('Hours') }</option>
-                          <option value="days" selected="selected">${ _('Days') }</option>
-                          <option value="months">${ _('Months') }</option>
-                        </select>
-                      </div>
-                    </div>
-                    <div class="control-group">
-                      <label class="control-label">${ _('Same start') }</label>
-                      <div class="controls">
-                        <input type="checkbox" data-bind="checked: same_start, style: {'margin-top': same_start() ? '9px' : '-1px'}, enable: $root.isEditing" />
-                        <input type="text" data-bind="value: start, visible: ! same_start(), enable: $root.isEditing"/>
-                      </div>
-                    </div>
-                    <div class="control-group">
-                      <label class="control-label">${ _('Same timezone') }</label>
-                      <div class="controls">
-                        <input type="checkbox" data-bind="checked: same_timezone, style: {'margin-top': same_timezone() ? '5px' : '0'}, enable: $root.isEditing" />
-                        <select data-bind="options: $root.availableTimezones, select2: { placeholder: '${ _ko("Select a Timezone") }', update: timezone}, visible: ! same_timezone(), enable: $root.isEditing" style="width: 180px"></select>
-                      </div>
-                    </div>
-                    <div class="control-group">
-                      <label class="control-label">${ _('Instance') }</label>
-                      <div class="controls">
-                        <div class="btn-group" data-toggle="buttons-radio">
-                          <button id="default-btn" type="button" class="btn"
-                                  data-bind="click: function() { instance_choice('default'); }, css: { active: instance_choice() == 'default' }, enable: $root.isEditing">
-                            ${ _('Default') }
-                          </button>
-                          <button id="single-btn" type="button" class="btn"
-                                  data-bind="click: function() { instance_choice('single'); }, css: { active: instance_choice() == 'single' }, enable: $root.isEditing">
-                            ${ _('Single') }
-                          </button>
-                          <button id="range-btn" type="button" class="btn"
-                                  data-bind="click: function() { instance_choice('range'); }, css: { active: instance_choice() == 'range' }, enable: $root.isEditing">
-                            ${ _('Range') }
-                          </button>
-                        </div>
-                      </div>
-                    </div>
-                    <div class="control-group" data-bind="visible: $.inArray(instance_choice(), ['single', 'range']) != -1">
-                      <label class="control-label">${ _('Start') }</label>
-                      <div class="controls">
-                        <input name="instance_start" type="number" data-bind="value: start_instance, enable: ! is_advanced_start_instance(), enable: $root.isEditing"/>
-                        <label style="display: inline">
-                            &nbsp;
-                            <input type="checkbox" data-bind="checked: is_advanced_start_instance, enable: $root.isEditing" style="margin-top:0">
-                            ${ _('(advanced)') }
-                          </label>
-                          <input type="text" data-bind="value: advanced_start_instance, visible: is_advanced_start_instance(), enable: $root.isEditing"/>
-                      </div>
-                    </div>
-                    <div class="control-group" data-bind="visible: instance_choice() == 'range'">
-                      <label class="control-label">${ _('End') }</label>
-                      <div class="controls">
-                        <input name="instance_end" type="number" data-bind="value: end_instance, enable: ! is_advanced_end_instance(), enable: $root.isEditing"/>
-                        <label style="display: inline">
-                            &nbsp;
-                            <input type="checkbox" data-bind="checked: is_advanced_end_instance, enable: $root.isEditing" style="margin-top:0">
-                            ${ _('(advanced)') }
-                          </label>
-                          <input type="text" data-bind="value: advanced_end_instance, visible: is_advanced_end_instance(), enable: $root.isEditing"/>
-                      </div>
-                    </div>
-                  </form>
-                </div>
-              <!-- /ko -->
-            </li>
-          </ul>
-
-          <a class="pointer" data-bind="click: function() { coordinator.addVariable() }, visible: isEditing">
-            <i class="fa fa-plus"></i> ${ _('Add parameter') }
-          </a>
-
-        </div>
-
-      </div>
-
-      <div class="card card-home" data-bind="visible: coordinator.id() == null && coordinator.properties.workflow()">
-        <div class="card-body">
-          <a href type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }"
-            data-bind="click: $root.save, css: {'btn': true}">
-            ${ _('Save') }
-          </a>
-        </div>
-      </div>
-
-    </div>
-  </div>
-</div>
-
-<div id="chooseWorkflowDemiModal" class="demi-modal fade" data-backdrop="false">
-  <div class="modal-body">
-    <a href="javascript: void(0)" data-dismiss="modal" class="pull-right"><i class="fa fa-times"></i></a>
-    <div style="float: left; margin-right: 10px;text-align: center">
-      <input type="text" data-bind="clearable: $root.workflowModalFilter, valueUpdate:'afterkeydown'" placeholder="${_('Filter workflows')}" class="input" style="float: left" /><br/>
-    </div>
-    <div>
-      <ul data-bind="foreach: $root.filteredModalWorkflows().sort(function (l, r) { return l.name() > r.name() ? 1 : -1 }), visible: $root.filteredModalWorkflows().length > 0"
-          class="unstyled inline fields-chooser" style="height: 100px; overflow-y: auto">
-        <li>
-          <span class="badge badge-info" data-bind="click: selectWorkflow">
-            <span data-bind="text: name(), attr: {'title': uuid()}"></span>
-          </span>
-          <a data-bind="attr: { href: '${ url('oozie:edit_workflow') }?workflow=' + uuid() }" target="_blank" title="${ _('Open') }">
-            <i class="fa fa-external-link-square"></i>
-          </a>
-        </li>
-      </ul>
-      <div class="alert alert-info inline" data-bind="visible: $root.filteredModalWorkflows().length == 0" style="margin-left: 250px;margin-right: 50px; height: 42px;line-height: 42px">
-        ${_('There are no workflows matching your search term.')}
-      </div>
-    </div>
-  </div>
-  <div><a class="pointer demi-modal-chevron" data-dismiss="modal"><i class="fa fa-chevron-up"></i></a></div>
-</div>
-
-<div id="settingsModal" class="modal hide fade">
-  <div class="modal-header" style="padding-bottom: 2px">
-    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-    <h3 id="myModalLabel">${ _('Coordinator Settings') }</h3>
-  </div>
-  <div class="modal-body">
-      <h4>${ _('Submission Parameters') }</h4>
-      <ul data-bind="foreach: coordinator.properties.parameters" class="unstyled">
-        <!-- ko if: ['oozie.use.system.libpath', 'start_date', 'end_date'].indexOf(name()) == -1 -->
-        <li>
-          <input data-bind="value: name"/>
-          <input data-bind="value: value"/>
-          <a href="#" data-bind="click: function(){ $root.coordinator.properties.parameters.remove(this); }">
-            <i class="fa fa-minus"></i>
-          </a>
-        </li>
-        <!-- /ko -->
-      </ul>
-      <a class="pointer" data-bind="click: function(){ $root.coordinator.properties.parameters.push(ko.mapping.fromJS({'name': '', 'value': ''})); }">
-        <i class="fa fa-plus"></i> ${ _('Add parameter') }
-      </a>
-
-      <h4>${ _('Timeout') }</h4>
-      <input data-bind="value: coordinator.properties.timeout" type="number"/>
-
-      <h4>${ _('Concurrency') }</h4>
-      <select data-bind="options: availableSettings, optionsCaption: '${ _ko("Default") }', value: coordinator.properties.concurrency"></select>
-
-      <h4>${ _('Execution') }</h4>
-      <select data-bind="value: coordinator.properties.execution">
-		<option value="FIFO">${ _("FIFO (oldest first)") }</option>
-		<option value="LIFO">${ _("LIFO (newest first)") }</option>
-		<option value="LAST_ONLY">${ _("LAST_ONLY (discards all older materializations)") }</option>
-      </select>
-
-      <h4>${ _('Throttle') }</h4>
-      <select data-bind="options: availableSettings, optionsCaption: '${ _ko("Default") }', value: coordinator.properties.throttle"></select>
-
-      <h4>${ _('SLA Configuration') }</h4>
-      <div class="sla-form" data-bind="with: $root.coordinator.properties">
-        ${ utils.slaForm() }
-      </div>
-  </div>
-</div>
+${ scheduler.import_layout() }
 
 
 <div class="submit-modal modal hide"></div>

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

@@ -43,10 +43,10 @@ from oozie.decorators import check_document_access_permission, check_document_mo
 from oozie.forms import ParameterForm
 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, HiveDocumentAction,\
-  WorkflowBuilder
+    find_dollar_variables, find_dollar_braced_variables, WorkflowBuilder
 from oozie.utils import convert_to_server_timezone
 from oozie.views.editor import edit_workflow as old_edit_workflow, edit_coordinator as old_edit_coordinator, edit_bundle as old_edit_bundle
+from desktop.lib import django_mako
 
 
 LOG = logging.getLogger(__name__)
@@ -512,7 +512,17 @@ def edit_coordinator(request):
   if coordinator_id and not filter(lambda a: a['uuid'] == coordinator.data['properties']['workflow'], workflows):
     raise PopupException(_('You don\'t have access to the workflow of this coordinator.'))
 
-  return render('editor2/coordinator_editor.mako', request, {
+  if request.GET.get('format') == 'json':
+    return JsonResponse({
+      'coordinator': coordinator.get_data_for_json(),
+      'credentials': credentials.credentials.keys(),
+      'workflows': workflows,
+      'doc_uuid': doc.uuid if doc else '',
+      'can_edit': doc is None or doc.doc.get().is_editable(request.user),
+      'layout': django_mako.render_to_string('editor2/common_scheduler.mako', {})
+    })
+  else:
+    return render('editor2/coordinator_editor.mako', request, {
       'coordinator_json': coordinator.to_json_for_html(),
       'credentials_json': json.dumps(credentials.credentials.keys(), cls=JSONEncoderForHTML),
       'workflows_json': json.dumps(workflows, cls=JSONEncoderForHTML),

+ 18 - 11
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -1524,17 +1524,6 @@
       });
     };
 
-    self.schedule = function() {
-      logGA('schedule');
-      $.post("/oozie/editor/document/schedule/", {
-        uuid: self.uuid()
-      }, function (data) {
-        window.location.href = data.url;
-      }).fail(function (xhr) {
-        $(document).trigger("error", xhr.responseText);
-      });
-    };
-
     self.clearHistory = function (type) {
       logGA('clearHistory');
       $.post("/notebook/api/clear_history", {
@@ -1846,6 +1835,24 @@
       self.selectedNotebook().parentSavedQueryUuid(null);
       self.saveNotebook();
     };
+
+    self.loadScheduler = function() {
+      logGA('schedule');
+      $.get("/oozie/editor/coordinator/new/", {
+        format: 'json'
+      }, function (data) {
+        $("#schedulerEditor").html(data.layout);
+        var viewModel = new CoordinatorEditorViewModel(data.coordinator, data.credentials, data.workflows, data.can_edit);
+
+        ko.cleanNode($("#schedulerEditor")[0]);
+        ko.applyBindings(viewModel, $("#schedulerEditor")[0]);
+
+        viewModel.coordinator.properties.cron_advanced.valueHasMutated(); // Update jsCron enabled status
+        viewModel.coordinator.tracker().markCurrentStateAsClean();
+      }).fail(function (xhr) {
+        $(document).trigger("error", xhr.responseText);
+      });
+    };
   }
 
 

+ 26 - 1
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -152,6 +152,21 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING
 
 <script src="${ static('desktop/ext/js/download.min.js') }"></script>
 
+## Scheduler
+## ChangeTracker
+<%namespace name="dashboard" file="/common_dashboard.mako" />
+${ dashboard.import_layout() }
+## Duplicating these two for some reason
+<script src="${ static('desktop/ext/js/knockout.min.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/ext/js/knockout-mapping.min.js') }" type="text/javascript" charset="utf-8"></script>
+
+<link href="${ static('desktop/css/jqCron.css') }" rel="stylesheet" type="text/css" />
+<script src="${ static('desktop/js/jqCron.js') }" type="text/javascript"></script>
+<script src="${ static('desktop/ext/js/moment-timezone-with-data.min.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/ext/js/tzdetect.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('oozie/js/coordinator-editor.ko.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('oozie/js/editor2-utils.js') }" type="text/javascript" charset="utf-8"></script>
+
 ${ require.config() }
 
 </%def>
@@ -972,6 +987,7 @@ ${ hueIcons.symbols() }
       <li class="active"><a href="#sessionsTab" data-toggle="tab">${_('Sessions')}</a></li>
       % if ENABLE_QUERY_SCHEDULING.get():
       <li><a href="#scheduleTab" data-toggle="tab">${_('Schedule')}</a></li>
+      <li><a href="#scheduleActionsTab" data-toggle="tab">${_('Executions')}</a></li>
       % endif
     </ul>
 
@@ -1032,13 +1048,22 @@ ${ hueIcons.symbols() }
       <legend><i class="fa fa-calendar"></i> ${ _('Schedule') }</legend>
 
       <!-- ko if: $root.selectedNotebook() && $root.selectedNotebook().id() -->
-        Coord UI
+        <a data-bind="click: $root.loadScheduler">Load scheduler</a>
+
+        [Start / Monitor - Stop - Sync]
+
+        <div id="schedulerEditor">
+        </div>
       <!-- /ko -->
 
       <!-- ko ifnot: $root.selectedNotebook() && $root.selectedNotebook().id() -->
         ${ _('Document needs to be saved first.') }
       <!-- /ko -->
     </div>
+
+    ## To move to 'notification' panel
+    <div class="tab-pane" id="scheduleActionsTab">
+    </div>
     % endif
 
   </div>