Migrated access control setup to wizard framework
This commit is contained in:
parent
85edac8191
commit
4193aa4d41
17 changed files with 488 additions and 71 deletions
|
|
@ -795,7 +795,7 @@ class Server():
|
|||
"js/lib/jquery/jquery.fileupload.js",
|
||||
"js/lib/jquery/jquery.slimscroll.min.js",
|
||||
"js/lib/jquery/jquery.qrcode.min.js",
|
||||
"js/lib/jquery/jquery.bootstrap.wizard.min.js",
|
||||
"js/lib/jquery/jquery.bootstrap.wizard.js",
|
||||
"js/lib/moment-with-locales.min.js",
|
||||
"js/lib/pusher.color.min.js",
|
||||
"js/lib/detectmobilebrowser.js",
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ def collect_plugin_assets(enable_gcodeviewer=True, enable_timelapse=True, prefer
|
|||
'js/app/viewmodels/appearance.js',
|
||||
'js/app/viewmodels/connection.js',
|
||||
'js/app/viewmodels/control.js',
|
||||
'js/app/viewmodels/firstrun.js',
|
||||
'js/app/viewmodels/firstrun_wizard.js',
|
||||
'js/app/viewmodels/files.js',
|
||||
'js/app/viewmodels/loginstate.js',
|
||||
'js/app/viewmodels/navigation.js',
|
||||
|
|
|
|||
|
|
@ -178,9 +178,9 @@ def index():
|
|||
|
||||
template_sorting["wizard"] = dict(add="custom_insert", key="name", custom_insert_entries=lambda missing: dict(), custom_insert_order=custom_insert_order)
|
||||
templates["wizard"]["entries"] = dict(
|
||||
firstrunstart=(gettext("Start"), dict(template="dialogs/wizard/firstrunstart.jinja2", _div="firstrun_start")),
|
||||
firstrunend=(gettext("Finish"), dict(template="dialogs/wizard/firstrunend.jinja2", _div="firstrun_end")),
|
||||
access=(gettext("Access Control"), dict(template="dialogs/wizard/accesscontrol.jinja2", _div="firstrun_dialog", custom_bindings=True))
|
||||
firstrunstart=(gettext("Start"), dict(template="dialogs/wizard/firstrun_start.jinja2", _div="wizard_firstrun_start")),
|
||||
firstrunend=(gettext("Finish"), dict(template="dialogs/wizard/firstrun_end.jinja2", _div="wizard_firstrun_end")),
|
||||
access=(gettext("Access Control"), dict(template="dialogs/wizard/firstrun_acl.jinja2", _div="wizard_firstrun_acl", custom_bindings=True))
|
||||
)
|
||||
|
||||
# extract data from template plugins
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -434,11 +434,38 @@ function hideOfflineOverlay() {
|
|||
$("#offline_overlay").hide();
|
||||
}
|
||||
|
||||
function showConfirmationDialog(message, onacknowledge) {
|
||||
function showMessageDialog(message, options) {
|
||||
options = options || {};
|
||||
|
||||
var messageDialog = $("#message_dialog");
|
||||
|
||||
var title = options.title || "";
|
||||
var close = options.close || gettext("Close");
|
||||
|
||||
$(".message_dialog_title", messageDialog).text(title);
|
||||
$(".message_dialog_message", messageDialog).text(message);
|
||||
$(".message_dialog_close", messageDialog).text(close);
|
||||
|
||||
messageDialog.modal("show");
|
||||
}
|
||||
|
||||
function showConfirmationDialog(message, onacknowledge, options) {
|
||||
options = options || {};
|
||||
|
||||
var confirmationDialog = $("#confirmation_dialog");
|
||||
var confirmationDialogAck = $(".confirmation_dialog_acknowledge", confirmationDialog);
|
||||
|
||||
var title = options.title || gettext("Are you sure?");
|
||||
var question = options.question || gettext("Are you sure you want to proceed?");
|
||||
var cancel = options.cancel || gettext("Cancel");
|
||||
var proceed = options.proceed || gettext("Proceed");
|
||||
|
||||
$(".confirmation_dialog_message", confirmationDialog).text(message);
|
||||
$(".confirmation_dialog_title", confirmationDialog).text(title);
|
||||
$(".confirmation_dialog_question", confirmationDialog).text(question);
|
||||
$(".confirmation_dialog_cancel", confirmationDialog).text(cancel);
|
||||
|
||||
var confirmationDialogAck = $(".confirmation_dialog_acknowledge", confirmationDialog);
|
||||
confirmationDialogAck.text(proceed);
|
||||
confirmationDialogAck.unbind("click");
|
||||
confirmationDialogAck.bind("click", function (e) {
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
$(function() {
|
||||
function FirstRunViewModel() {
|
||||
function AclWizardViewModel() {
|
||||
var self = this;
|
||||
|
||||
self.username = ko.observable(undefined);
|
||||
self.password = ko.observable(undefined);
|
||||
self.confirmedPassword = ko.observable(undefined);
|
||||
|
||||
self.setup = ko.observable(false);
|
||||
self.decision = ko.observable();
|
||||
|
||||
self.passwordMismatch = ko.computed(function() {
|
||||
return self.password() != self.confirmedPassword();
|
||||
});
|
||||
|
|
@ -35,21 +38,13 @@ $(function() {
|
|||
};
|
||||
|
||||
self.disableAccessControl = function() {
|
||||
$("#confirmation_dialog .confirmation_dialog_message").html(gettext("If you disable Access Control <strong>and</strong> your OctoPrint installation is accessible from the internet, your printer <strong>will be accessible by everyone - that also includes the bad guys!</strong>"));
|
||||
$("#confirmation_dialog .confirmation_dialog_acknowledge").unbind("click");
|
||||
$("#confirmation_dialog .confirmation_dialog_acknowledge").click(function(e) {
|
||||
e.preventDefault();
|
||||
$("#confirmation_dialog").modal("hide");
|
||||
|
||||
var message = gettext("If you disable Access Control <strong>and</strong> your OctoPrint installation is accessible from the internet, your printer <strong>will be accessible by everyone - that also includes the bad guys!</strong>");
|
||||
showConfirmationDialog(message, function(e) {
|
||||
var data = {
|
||||
"ac": false
|
||||
};
|
||||
self._sendData(data, function() {
|
||||
// if the user indeed disables access control, we'll need to reload the page for this to take effect
|
||||
//location.reload(true); // TODO: clear cache doesn't work properly, needs a better way, same issue with reloading plugins
|
||||
});
|
||||
self._sendData(data);
|
||||
});
|
||||
$("#confirmation_dialog").modal("show");
|
||||
};
|
||||
|
||||
self._sendData = function(data, callback) {
|
||||
|
|
@ -59,30 +54,31 @@ $(function() {
|
|||
dataType: "json",
|
||||
data: data,
|
||||
success: function() {
|
||||
self.closeDialog();
|
||||
self.setup(true);
|
||||
self.decision(data.ac);
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.showDialog = function() {
|
||||
$("#first_run_dialog").modal("show");
|
||||
};
|
||||
|
||||
self.closeDialog = function() {
|
||||
$("#first_run_dialog").modal("hide");
|
||||
};
|
||||
|
||||
self.onAllBound = function(allViewModels) {
|
||||
if (CONFIG_FIRST_RUN) {
|
||||
self.showDialog();
|
||||
self.onWizardTabChange = function(current, next) {
|
||||
if (!current || !_.startsWith(current, "wizard_firstrun_acl") || self.setup()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
showMessageDialog(gettext("You haven't yet set up access control. You need to either setup a username and password and click \"Keep Access Control Enabled\" or click \"Disable Access Control\" before continuing"), {title: "Please set up Access Control"});
|
||||
return false;
|
||||
};
|
||||
|
||||
self.onWizardFinished = function() {
|
||||
if (!self.decision()) {
|
||||
return "reload";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
OCTOPRINT_VIEWMODELS.push([
|
||||
FirstRunViewModel,
|
||||
AclWizardViewModel,
|
||||
[],
|
||||
"#first_run_dialog"
|
||||
"#wizard_firstrun_acl"
|
||||
]);
|
||||
});
|
||||
|
|
@ -28,17 +28,57 @@ $(function() {
|
|||
nextSelector: ".button-next",
|
||||
previousSelector: ".button-previous",
|
||||
finishSelector: ".button-finish",
|
||||
onTabClick: function() {
|
||||
// we don't allow clicking on the tabs
|
||||
return false;
|
||||
},
|
||||
onTabShow: function(tab, navigation, index) {
|
||||
var total = navigation.find("li").length;
|
||||
var current = index+1;
|
||||
if (index < 0 || tab.length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (current >= total) {
|
||||
var total = self.wizardDialog.bootstrapWizard("navigationLength");
|
||||
|
||||
if (index == total) {
|
||||
self.wizardDialog.find(".button-next").hide();
|
||||
self.wizardDialog.find(".button-finish").show().removeClass("disabled");
|
||||
} else {
|
||||
self.wizardDialog.find(".button-finish").hide();
|
||||
self.wizardDialog.find(".button-next").show();
|
||||
}
|
||||
|
||||
var active = tab[0].id;
|
||||
if (active != undefined) {
|
||||
_.each(allViewModels, function(viewModel) {
|
||||
if (viewModel.hasOwnProperty("onAfterWizardTabChange")) {
|
||||
viewModel.onAfterWizardTabChange(active);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onTabChange: function(tab, navigation, index, nextTabIndex, nextTab) {
|
||||
var current, next;
|
||||
|
||||
if (index == undefined || index < 0 ||
|
||||
nextTabIndex == undefined || nextTabIndex < 0 ||
|
||||
index == nextTabIndex ||
|
||||
tab.length == 0 || nextTab.length == 0) {
|
||||
// let's ignore that nonsense
|
||||
return;
|
||||
}
|
||||
|
||||
current = tab[0].id;
|
||||
next = nextTab[0].id;
|
||||
|
||||
if (current != undefined && next != undefined) {
|
||||
var result = true;
|
||||
_.each(allViewModels, function(viewModel) {
|
||||
if (viewModel.hasOwnProperty("onWizardTabChange")) {
|
||||
result = result && viewModel.onWizardTabChange(current, next);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
self.showDialog();
|
||||
|
|
|
|||
341
src/octoprint/static/js/lib/jquery/jquery.bootstrap.wizard.js
Normal file
341
src/octoprint/static/js/lib/jquery/jquery.bootstrap.wizard.js
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/*!
|
||||
* jQuery twitter bootstrap wizard plugin
|
||||
* Examples and documentation at: http://github.com/VinceG/twitter-bootstrap-wizard
|
||||
* version 1.0
|
||||
* Requires jQuery v1.3.2 or later
|
||||
* Supports Bootstrap 2.2.x, 2.3.x, 3.0
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
* Authors: Vadim Vincent Gabriel (http://vadimg.com), Jason Gill (www.gilluminate.com)
|
||||
*/
|
||||
;(function($) {
|
||||
var bootstrapWizardCreate = function(elem, options) {
|
||||
var element = $(elem);
|
||||
var obj = this;
|
||||
|
||||
// selector skips any 'li' elements that do not contain a child with a tab data-toggle
|
||||
var baseItemSelector = 'li:has([data-toggle="tab"])';
|
||||
var historyStack = [];
|
||||
|
||||
// Merge options with defaults
|
||||
var $settings = $.extend({}, $.fn.bootstrapWizard.defaults, options);
|
||||
var $activeTab = null;
|
||||
var $navigation = null;
|
||||
|
||||
this.rebindClick = function(selector, fn) {
|
||||
selector.unbind('click', fn).bind('click', fn);
|
||||
};
|
||||
|
||||
this.fixNavigationButtons = function() {
|
||||
// Get the current active tab
|
||||
if(!$activeTab.length) {
|
||||
// Select first one
|
||||
$navigation.find('a:first').tab('show');
|
||||
$activeTab = $navigation.find(baseItemSelector + ':first');
|
||||
}
|
||||
|
||||
// See if we're currently in the first/last then disable the previous and last buttons
|
||||
$($settings.previousSelector, element).toggleClass('disabled', (obj.firstIndex() >= obj.currentIndex()));
|
||||
$($settings.nextSelector, element).toggleClass('disabled', (obj.currentIndex() >= obj.navigationLength()));
|
||||
$($settings.nextSelector, element).toggleClass('hidden', (obj.currentIndex() >= obj.navigationLength() && $($settings.finishSelector, element).length > 0));
|
||||
$($settings.lastSelector, element).toggleClass('hidden', (obj.currentIndex() >= obj.navigationLength() && $($settings.finishSelector, element).length > 0));
|
||||
$($settings.finishSelector, element).toggleClass('hidden', (obj.currentIndex() < obj.navigationLength()));
|
||||
$($settings.backSelector, element).toggleClass('disabled', (historyStack.length == 0));
|
||||
$($settings.backSelector, element).toggleClass('hidden', (obj.currentIndex() >= obj.navigationLength() && $($settings.finishSelector, element).length > 0));
|
||||
|
||||
// We are unbinding and rebinding to ensure single firing and no double-click errors
|
||||
obj.rebindClick($($settings.nextSelector, element), obj.next);
|
||||
obj.rebindClick($($settings.previousSelector, element), obj.previous);
|
||||
obj.rebindClick($($settings.lastSelector, element), obj.last);
|
||||
obj.rebindClick($($settings.firstSelector, element), obj.first);
|
||||
obj.rebindClick($($settings.finishSelector, element), obj.finish);
|
||||
obj.rebindClick($($settings.backSelector, element), obj.back);
|
||||
|
||||
if($settings.onTabShow && typeof $settings.onTabShow === 'function' && $settings.onTabShow($activeTab, $navigation, obj.currentIndex())===false){
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
this.next = function(e) {
|
||||
// If we clicked the last then dont activate this
|
||||
if(element.hasClass('last')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if($settings.onNext && typeof $settings.onNext === 'function' && $settings.onNext($activeTab, $navigation, obj.nextIndex())===false){
|
||||
return false;
|
||||
}
|
||||
|
||||
var formerIndex = obj.currentIndex();
|
||||
$index = obj.nextIndex();
|
||||
|
||||
// Did we click the last button
|
||||
if($index > obj.navigationLength()) {
|
||||
} else {
|
||||
historyStack.push(formerIndex);
|
||||
$navigation.find(baseItemSelector + ':eq(' + $index + ') a').tab('show');
|
||||
}
|
||||
};
|
||||
|
||||
this.previous = function(e) {
|
||||
// If we clicked the first then dont activate this
|
||||
if(element.hasClass('first')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if($settings.onPrevious && typeof $settings.onPrevious === 'function' && $settings.onPrevious($activeTab, $navigation, obj.previousIndex())===false){
|
||||
return false;
|
||||
}
|
||||
|
||||
var formerIndex = obj.currentIndex();
|
||||
$index = obj.previousIndex();
|
||||
|
||||
if($index < 0) {
|
||||
} else {
|
||||
historyStack.push(formerIndex);
|
||||
$navigation.find(baseItemSelector + ':eq(' + $index + ') a').tab('show');
|
||||
}
|
||||
};
|
||||
|
||||
this.first = function (e) {
|
||||
if($settings.onFirst && typeof $settings.onFirst === 'function' && $settings.onFirst($activeTab, $navigation, obj.firstIndex())===false){
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the element is disabled then we won't do anything
|
||||
if(element.hasClass('disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
historyStack.push(obj.currentIndex());
|
||||
$navigation.find(baseItemSelector + ':eq(0) a').tab('show');
|
||||
};
|
||||
|
||||
this.last = function(e) {
|
||||
if($settings.onLast && typeof $settings.onLast === 'function' && $settings.onLast($activeTab, $navigation, obj.lastIndex())===false){
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the element is disabled then we won't do anything
|
||||
if(element.hasClass('disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
historyStack.push(obj.currentIndex());
|
||||
$navigation.find(baseItemSelector + ':eq(' + obj.navigationLength() + ') a').tab('show');
|
||||
};
|
||||
|
||||
this.finish = function (e) {
|
||||
if ($settings.onFinish && typeof $settings.onFinish === 'function') {
|
||||
$settings.onFinish($activeTab, $navigation, obj.lastIndex());
|
||||
}
|
||||
};
|
||||
|
||||
this.back = function () {
|
||||
if (historyStack.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var formerIndex = historyStack.pop();
|
||||
if ($settings.onBack && typeof $settings.onBack === 'function' && $settings.onBack($activeTab, $navigation, formerIndex) === false) {
|
||||
historyStack.push(formerIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
element.find(baseItemSelector + ':eq(' + formerIndex + ') a').tab('show');
|
||||
};
|
||||
|
||||
this.currentIndex = function() {
|
||||
return $navigation.find(baseItemSelector).index($activeTab);
|
||||
};
|
||||
|
||||
this.firstIndex = function() {
|
||||
return 0;
|
||||
};
|
||||
|
||||
this.lastIndex = function() {
|
||||
return obj.navigationLength();
|
||||
};
|
||||
this.getIndex = function(e) {
|
||||
return $navigation.find(baseItemSelector).index(e);
|
||||
};
|
||||
this.nextIndex = function() {
|
||||
return $navigation.find(baseItemSelector).index($activeTab) + 1;
|
||||
};
|
||||
this.previousIndex = function() {
|
||||
return $navigation.find(baseItemSelector).index($activeTab) - 1;
|
||||
};
|
||||
this.navigationLength = function() {
|
||||
return $navigation.find(baseItemSelector).length - 1;
|
||||
};
|
||||
this.activeTab = function() {
|
||||
return $activeTab;
|
||||
};
|
||||
this.tabForIndex = function(idx) {
|
||||
return $navigation.find(baseItemSelector + ':eq('+idx+')').length ? $navigation.find(baseItemSelector + ':eq('+idx+')') : null;
|
||||
};
|
||||
this.nextTab = function() {
|
||||
return $navigation.find(baseItemSelector + ':eq('+(obj.currentIndex()+1)+')').length ? $navigation.find(baseItemSelector + ':eq('+(obj.currentIndex()+1)+')') : null;
|
||||
};
|
||||
this.previousTab = function() {
|
||||
if(obj.currentIndex() <= 0) {
|
||||
return null;
|
||||
}
|
||||
return $navigation.find(baseItemSelector + ':eq('+parseInt(obj.currentIndex()-1)+')');
|
||||
};
|
||||
this.show = function(index) {
|
||||
var tabToShow = isNaN(index) ?
|
||||
element.find(baseItemSelector + ' a[href=#' + index + ']') :
|
||||
element.find(baseItemSelector + ':eq(' + index + ') a');
|
||||
if (tabToShow.length > 0) {
|
||||
historyStack.push(obj.currentIndex());
|
||||
tabToShow.tab('show');
|
||||
}
|
||||
};
|
||||
this.disable = function (index) {
|
||||
$navigation.find(baseItemSelector + ':eq('+index+')').addClass('disabled');
|
||||
};
|
||||
this.enable = function(index) {
|
||||
$navigation.find(baseItemSelector + ':eq('+index+')').removeClass('disabled');
|
||||
};
|
||||
this.hide = function(index) {
|
||||
$navigation.find(baseItemSelector + ':eq('+index+')').hide();
|
||||
};
|
||||
this.display = function(index) {
|
||||
$navigation.find(baseItemSelector + ':eq('+index+')').show();
|
||||
};
|
||||
this.remove = function(args) {
|
||||
var $index = args[0];
|
||||
var $removeTabPane = typeof args[1] != 'undefined' ? args[1] : false;
|
||||
var $item = $navigation.find(baseItemSelector + ':eq('+$index+')');
|
||||
|
||||
// Remove the tab pane first if needed
|
||||
if($removeTabPane) {
|
||||
var $href = $item.find('a').attr('href');
|
||||
$($href).remove();
|
||||
}
|
||||
|
||||
// Remove menu item
|
||||
$item.remove();
|
||||
};
|
||||
|
||||
var innerTabClick = function (e) {
|
||||
// Get the index of the clicked tab
|
||||
var $ul = $navigation.find(baseItemSelector);
|
||||
var clickedIndex = $ul.index($(e.currentTarget).parent(baseItemSelector));
|
||||
var $clickedTab = $( $ul[clickedIndex] );
|
||||
if($settings.onTabClick && typeof $settings.onTabClick === 'function' && $settings.onTabClick($activeTab, $navigation, obj.currentIndex(), clickedIndex, $clickedTab)===false){
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var innerTabShow = function(e) {
|
||||
var $element = $(e.target).parent();
|
||||
var nextTab = $navigation.find(baseItemSelector).index($element);
|
||||
|
||||
// If it's disabled then do not change
|
||||
if($element.hasClass('disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if($settings.onTabChange && typeof $settings.onTabChange === 'function' && $settings.onTabChange($activeTab, $navigation, obj.currentIndex(), nextTab, $element)===false){
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var innerTabShown = function (e) {
|
||||
$activeTab = $(e.target).parent(); // activated tab
|
||||
obj.fixNavigationButtons();
|
||||
};
|
||||
|
||||
this.resetWizard = function() {
|
||||
|
||||
// remove the existing handlers
|
||||
$('a[data-toggle="tab"]', $navigation).off('click', innerTabClick);
|
||||
$('a[data-toggle="tab"]', $navigation).off('show', innerTabShow);
|
||||
$('a[data-toggle="tab"]', $navigation).off('shown', innerTabShown);
|
||||
|
||||
// reset elements based on current state of the DOM
|
||||
$navigation = element.find('ul:first', element);
|
||||
$activeTab = $navigation.find(baseItemSelector + '.active', element);
|
||||
|
||||
// re-add handlers
|
||||
$('a[data-toggle="tab"]', $navigation).on('click', innerTabClick);
|
||||
$('a[data-toggle="tab"]', $navigation).on('show', innerTabShow);
|
||||
$('a[data-toggle="tab"]', $navigation).on('shown', innerTabShown);
|
||||
|
||||
obj.fixNavigationButtons();
|
||||
};
|
||||
|
||||
$navigation = element.find('ul:first', element);
|
||||
$activeTab = $navigation.find(baseItemSelector + '.active', element);
|
||||
|
||||
if(!$navigation.hasClass($settings.tabClass)) {
|
||||
$navigation.addClass($settings.tabClass);
|
||||
}
|
||||
|
||||
// Load onInit
|
||||
if($settings.onInit && typeof $settings.onInit === 'function'){
|
||||
$settings.onInit($activeTab, $navigation, 0);
|
||||
}
|
||||
|
||||
// Load onShow
|
||||
if($settings.onShow && typeof $settings.onShow === 'function'){
|
||||
$settings.onShow($activeTab, $navigation, obj.nextIndex());
|
||||
}
|
||||
|
||||
$('a[data-toggle="tab"]', $navigation).on('click', innerTabClick);
|
||||
|
||||
// attach to both show and show.bs.tab to support Bootstrap versions 2.3.2 and 3.0.0
|
||||
$('a[data-toggle="tab"]', $navigation).on('show', innerTabShow);
|
||||
|
||||
// attach to both shown and shown.bs.tab to support Bootstrap versions 2.3.2 and 3.0.0
|
||||
$('a[data-toggle="tab"]', $navigation).on('shown', innerTabShown);
|
||||
};
|
||||
$.fn.bootstrapWizard = function(options) {
|
||||
//expose methods
|
||||
if (typeof options == 'string') {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if(args.length === 1) {
|
||||
args.toString();
|
||||
}
|
||||
return this.data('bootstrapWizard')[options](args);
|
||||
}
|
||||
return this.each(function(index){
|
||||
var element = $(this);
|
||||
// Return early if this element already has a plugin instance
|
||||
if (element.data('bootstrapWizard')) return;
|
||||
// pass options to plugin constructor
|
||||
var wizard = new bootstrapWizardCreate(element, options);
|
||||
// Store plugin object in this element's data
|
||||
element.data('bootstrapWizard', wizard);
|
||||
// and then trigger initial change
|
||||
wizard.fixNavigationButtons();
|
||||
});
|
||||
};
|
||||
|
||||
// expose options
|
||||
$.fn.bootstrapWizard.defaults = {
|
||||
tabClass: 'nav nav-pills',
|
||||
nextSelector: '.wizard li.next',
|
||||
previousSelector: '.wizard li.previous',
|
||||
firstSelector: '.wizard li.first',
|
||||
lastSelector: '.wizard li.last',
|
||||
finishSelector: '.wizard li.finish',
|
||||
backSelector: '.wizard li.back',
|
||||
onShow: null,
|
||||
onInit: null,
|
||||
onNext: null,
|
||||
onPrevious: null,
|
||||
onLast: null,
|
||||
onFirst: null,
|
||||
onFinish: null,
|
||||
onBack: null,
|
||||
onTabChange: null,
|
||||
onTabClick: null,
|
||||
onTabShow: null,
|
||||
onBeforeTabShow: null
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/*!
|
||||
* jQuery twitter bootstrap wizard plugin
|
||||
* Examples and documentation at: http://github.com/VinceG/twitter-bootstrap-wizard
|
||||
* version 1.0
|
||||
* Requires jQuery v1.3.2 or later
|
||||
* Supports Bootstrap 2.2.x, 2.3.x, 3.0
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
* Authors: Vadim Vincent Gabriel (http://vadimg.com), Jason Gill (www.gilluminate.com)
|
||||
*/
|
||||
(function(e){var n=function(d,k){d=e(d);var a=this,g=[],c=e.extend({},e.fn.bootstrapWizard.defaults,k),f=null,b=null;this.rebindClick=function(h,a){h.unbind("click",a).bind("click",a)};this.fixNavigationButtons=function(){f.length||(b.find("a:first").tab("show"),f=b.find('li:has([data-toggle="tab"]):first'));e(c.previousSelector,d).toggleClass("disabled",a.firstIndex()>=a.currentIndex());e(c.nextSelector,d).toggleClass("disabled",a.currentIndex()>=a.navigationLength());e(c.backSelector,d).toggleClass("disabled",
|
||||
0==g.length);a.rebindClick(e(c.nextSelector,d),a.next);a.rebindClick(e(c.previousSelector,d),a.previous);a.rebindClick(e(c.lastSelector,d),a.last);a.rebindClick(e(c.firstSelector,d),a.first);a.rebindClick(e(c.backSelector,d),a.back);if(c.onTabShow&&"function"===typeof c.onTabShow&&!1===c.onTabShow(f,b,a.currentIndex()))return!1};this.next=function(h){if(d.hasClass("last")||c.onNext&&"function"===typeof c.onNext&&!1===c.onNext(f,b,a.nextIndex()))return!1;h=a.currentIndex();$index=a.nextIndex();$index>
|
||||
a.navigationLength()||(g.push(h),b.find('li:has([data-toggle="tab"]):eq('+$index+") a").tab("show"))};this.previous=function(h){if(d.hasClass("first")||c.onPrevious&&"function"===typeof c.onPrevious&&!1===c.onPrevious(f,b,a.previousIndex()))return!1;h=a.currentIndex();$index=a.previousIndex();0>$index||(g.push(h),b.find('li:has([data-toggle="tab"]):eq('+$index+") a").tab("show"))};this.first=function(h){if(c.onFirst&&"function"===typeof c.onFirst&&!1===c.onFirst(f,b,a.firstIndex())||d.hasClass("disabled"))return!1;
|
||||
g.push(a.currentIndex());b.find('li:has([data-toggle="tab"]):eq(0) a').tab("show")};this.last=function(h){if(c.onLast&&"function"===typeof c.onLast&&!1===c.onLast(f,b,a.lastIndex())||d.hasClass("disabled"))return!1;g.push(a.currentIndex());b.find('li:has([data-toggle="tab"]):eq('+a.navigationLength()+") a").tab("show")};this.back=function(){if(0==g.length)return null;var a=g.pop();if(c.onBack&&"function"===typeof c.onBack&&!1===c.onBack(f,b,a))return g.push(a),!1;d.find('li:has([data-toggle="tab"]):eq('+
|
||||
a+") a").tab("show")};this.currentIndex=function(){return b.find('li:has([data-toggle="tab"])').index(f)};this.firstIndex=function(){return 0};this.lastIndex=function(){return a.navigationLength()};this.getIndex=function(a){return b.find('li:has([data-toggle="tab"])').index(a)};this.nextIndex=function(){return b.find('li:has([data-toggle="tab"])').index(f)+1};this.previousIndex=function(){return b.find('li:has([data-toggle="tab"])').index(f)-1};this.navigationLength=function(){return b.find('li:has([data-toggle="tab"])').length-
|
||||
1};this.activeTab=function(){return f};this.nextTab=function(){return b.find('li:has([data-toggle="tab"]):eq('+(a.currentIndex()+1)+")").length?b.find('li:has([data-toggle="tab"]):eq('+(a.currentIndex()+1)+")"):null};this.previousTab=function(){return 0>=a.currentIndex()?null:b.find('li:has([data-toggle="tab"]):eq('+parseInt(a.currentIndex()-1)+")")};this.show=function(b){b=isNaN(b)?d.find('li:has([data-toggle="tab"]) a[href=#'+b+"]"):d.find('li:has([data-toggle="tab"]):eq('+b+") a");0<b.length&&
|
||||
(g.push(a.currentIndex()),b.tab("show"))};this.disable=function(a){b.find('li:has([data-toggle="tab"]):eq('+a+")").addClass("disabled")};this.enable=function(a){b.find('li:has([data-toggle="tab"]):eq('+a+")").removeClass("disabled")};this.hide=function(a){b.find('li:has([data-toggle="tab"]):eq('+a+")").hide()};this.display=function(a){b.find('li:has([data-toggle="tab"]):eq('+a+")").show()};this.remove=function(a){var c="undefined"!=typeof a[1]?a[1]:!1;a=b.find('li:has([data-toggle="tab"]):eq('+a[0]+
|
||||
")");c&&(c=a.find("a").attr("href"),e(c).remove());a.remove()};var l=function(d){var g=b.find('li:has([data-toggle="tab"])');d=g.index(e(d.currentTarget).parent('li:has([data-toggle="tab"])'));g=e(g[d]);if(c.onTabClick&&"function"===typeof c.onTabClick&&!1===c.onTabClick(f,b,a.currentIndex(),d,g))return!1},m=function(d){$element=e(d.target).parent();d=b.find('li:has([data-toggle="tab"])').index($element);if($element.hasClass("disabled")||c.onTabChange&&"function"===typeof c.onTabChange&&!1===c.onTabChange(f,
|
||||
b,a.currentIndex(),d))return!1;f=$element;a.fixNavigationButtons()};this.resetWizard=function(){e('a[data-toggle="tab"]',b).off("click",l);e('a[data-toggle="tab"]',b).off("shown shown.bs.tab",m);b=d.find("ul:first",d);f=b.find('li:has([data-toggle="tab"]).active',d);e('a[data-toggle="tab"]',b).on("click",l);e('a[data-toggle="tab"]',b).on("shown shown.bs.tab",m);a.fixNavigationButtons()};b=d.find("ul:first",d);f=b.find('li:has([data-toggle="tab"]).active',d);b.hasClass(c.tabClass)||b.addClass(c.tabClass);
|
||||
if(c.onInit&&"function"===typeof c.onInit)c.onInit(f,b,0);if(c.onShow&&"function"===typeof c.onShow)c.onShow(f,b,a.nextIndex());e('a[data-toggle="tab"]',b).on("click",l);e('a[data-toggle="tab"]',b).on("shown shown.bs.tab",m)};e.fn.bootstrapWizard=function(d){if("string"==typeof d){var k=Array.prototype.slice.call(arguments,1);1===k.length&&k.toString();return this.data("bootstrapWizard")[d](k)}return this.each(function(a){a=e(this);if(!a.data("bootstrapWizard")){var g=new n(a,d);a.data("bootstrapWizard",
|
||||
g);g.fixNavigationButtons()}})};e.fn.bootstrapWizard.defaults={tabClass:"nav nav-pills",nextSelector:".wizard li.next",previousSelector:".wizard li.previous",firstSelector:".wizard li.first",lastSelector:".wizard li.last",backSelector:".wizard li.back",onShow:null,onInit:null,onNext:null,onPrevious:null,onLast:null,onFirst:null,onBack:null,onTabChange:null,onTabClick:null,onTabShow:null}})(jQuery);
|
||||
|
|
@ -690,6 +690,13 @@ ul.dropdown-menu li a {
|
|||
margin-left: 0;
|
||||
}
|
||||
|
||||
#wizard_firstrun_acl {
|
||||
.acl_decision {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#settings_appearance_managelanguagesdialog_emptylist {
|
||||
overflow: hidden;
|
||||
|
|
@ -753,6 +760,10 @@ ul.dropdown-menu li a {
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.overflow_visible {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<div id="confirmation_dialog" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">×</a>
|
||||
<h3>{{ _('Are you sure?') }}</h3>
|
||||
<h3 class="confirmation_dialog_title">{{ _('Are you sure?') }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="confirmation_dialog_message"></p>
|
||||
<p>{{ _('Are you sure you want to proceed?') }}</p>
|
||||
<p class="confirmation_dialog_question">{{ _('Are you sure you want to proceed?') }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn" data-dismiss="modal" aria-hidden="true">{{ _('Cancel') }}</a>
|
||||
<a href="#" class="btn btn-danger confirmation_dialog_acknowledge">{{ _('Proceed') }}</a>
|
||||
<a href="#" class="confirmation_dialog_cancel btn" data-dismiss="modal" aria-hidden="true">{{ _('Cancel') }}</a>
|
||||
<a href="#" class="confirmation_dialog_acknowledge btn btn-danger">{{ _('Proceed') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
12
src/octoprint/templates/dialogs/message.jinja2
Normal file
12
src/octoprint/templates/dialogs/message.jinja2
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<div id="message_dialog" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<a href="#" class="close" data-dismiss="modal" aria-hidden="true">×</a>
|
||||
<h3 class="message_dialog_title"></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="message_dialog_message"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="message_dialog_close btn" data-dismiss="modal" aria-hidden="true">{{ _('Close') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
<div class="tabbable row-fluid">
|
||||
<div class="span3 scrollable" id="wizard_dialog_menu">
|
||||
<ul class="nav nav-list" id="wizardTabs">
|
||||
{% set mark_active = True %}
|
||||
{% for key in templates.wizard.order %}
|
||||
{% set entry, data = templates.wizard.entries[key] %}
|
||||
{% if data is none %}
|
||||
|
|
@ -15,7 +16,7 @@
|
|||
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<li id="{{ data._div }}_link"
|
||||
{% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
|
||||
class="{% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
class="{% if mark_active %}active{% set mark_active = False %}{% endif %} {% if "classes_link" in data %}{{ data.classes_link|join(' ') }}{% elif "classes" in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
{% if "styles_link" in data %} style="{{ data.styles_link|join(', ') }}" {% elif "styles" in data %} style="{{ data.styles|join(', ') }}" {% endif %}
|
||||
>
|
||||
<a href="#{{ data._div }}" data-toggle="tab">{{ entry }}</a>
|
||||
|
|
@ -26,13 +27,14 @@
|
|||
</ul>
|
||||
</div>
|
||||
<div class="tab-content span9 scrollable" id="wizard_dialog_content">
|
||||
{% set mark_active = True %}
|
||||
{% for key in templates.wizard.order %}
|
||||
{% set entry, data = templates.wizard.entries[key] %}
|
||||
{% if data is not none %}
|
||||
{% if "custom_bindings" not in data or data["custom_bindings"] %}<!-- ko allowBindings: false -->{% endif %}
|
||||
<div id="{{ data._div }}"
|
||||
{% if "data_bind" in data %}data-bind="{{ data.data_bind }}"{% endif %}
|
||||
class="tab-pane {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
class="tab-pane {% if mark_active %}active{% set mark_active = False %}{% endif %} {% if classes_content in data %}{{ data.classes_content|join(' ') }}{% elif classes in data %}{{ data.classes|join(' ') }}{% endif %}"
|
||||
{% if "styles_content" in data %} style="{{ data.styles_content|join(', ') }}" {% elif styles in data %} style="{{ data.styles|join(', ') }}" {% endif %}
|
||||
>
|
||||
{% include data.template ignore missing %}
|
||||
|
|
|
|||
|
|
@ -18,19 +18,19 @@
|
|||
<div class="control-group" data-bind="css: {success: validUsername()}">
|
||||
<label class="control-label" for="first_run_username">{{ _('Username') }}</label>
|
||||
<div class="controls">
|
||||
<input type="text" class="input-medium" data-bind="value: username, valueUpdate: 'afterkeydown'">
|
||||
<input type="text" class="input-medium" data-bind="value: username, valueUpdate: 'afterkeydown', enable: !setup(), css: {disabled: setup()}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="css: {success: validPassword()}">
|
||||
<label class="control-label" for="first_run_username">{{ _('Password') }}</label>
|
||||
<div class="controls">
|
||||
<input type="password" class="input-medium" data-bind="value: password, valueUpdate: 'afterkeydown'">
|
||||
<input type="password" class="input-medium" data-bind="value: password, valueUpdate: 'afterkeydown', enable: !setup(), css: {disabled: setup()}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group" data-bind="css: {error: passwordMismatch(), success: validPassword() && !passwordMismatch()}">
|
||||
<label class="control-label" for="first_run_username">{{ _('Confirm Password') }}</label>
|
||||
<div class="controls">
|
||||
<input type="password" class="input-medium" data-bind="value: confirmedPassword, valueUpdate: 'afterkeydown'">
|
||||
<input type="password" class="input-medium" data-bind="value: confirmedPassword, valueUpdate: 'afterkeydown', enable: !setup(), css: {disabled: setup()}">
|
||||
<span class="help-inline" data-bind="visible: passwordMismatch()">{{ _('Passwords do not match') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -45,6 +45,15 @@
|
|||
</p>{% endtrans %}
|
||||
|
||||
<div class="row-fluid">
|
||||
<a href="#" class="btn btn-danger span6" data-bind="click: disableAccessControl">{{ _('Disable Access Control') }}</a>
|
||||
<a href="#" class="btn btn-primary span6" data-bind="click: keepAccessControl, enable: validData(), css: {disabled: !validData()}">{{ _('Keep Access Control Enabled') }}</a>
|
||||
<a href="#" class="btn btn-danger span6" data-bind="click: function() { if(!setup()){disableAccessControl()}}, enable: !setup(), css: {disabled: setup()}">{{ _('Disable Access Control') }}</a>
|
||||
<a href="#" class="btn btn-primary span6" data-bind="click: function() { if(!setup()){keepAccessControl()}}, enable: !setup() && validData(), css: {disabled: !validData() || setup()}">{{ _('Keep Access Control Enabled') }}</a>
|
||||
</div>
|
||||
|
||||
<div class="acl_decision" style="display: none" data-bind="visible: setup()">
|
||||
<div class="text-center" style="display: none" data-bind="visible: decision()">{% trans %}
|
||||
Access control is <strong class="text-success">enabled</strong>.
|
||||
{% endtrans %}</div>
|
||||
<div class="text-center" style="display: none" data-bind="visible: !decision()">{% trans %}
|
||||
Access control is <strong class="text-danger">disabled</strong>.
|
||||
{% endtrans %}</div>
|
||||
</div>
|
||||
|
|
@ -125,6 +125,7 @@
|
|||
|
||||
<!-- Dialogs -->
|
||||
{% include 'dialogs/confirmation.jinja2' %}
|
||||
{% include 'dialogs/message.jinja2' %}
|
||||
{% include 'dialogs/firstrun.jinja2' %}
|
||||
{% include 'dialogs/settings.jinja2' %}
|
||||
{% include 'dialogs/slicing.jinja2' %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue