Migrated access control setup to wizard framework

This commit is contained in:
Gina Häußge 2015-07-13 18:35:27 +02:00
parent 85edac8191
commit 4193aa4d41
17 changed files with 488 additions and 71 deletions

View file

@ -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",

View file

@ -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',

View file

@ -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

View file

@ -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();

View file

@ -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"
]);
});

View file

@ -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();

View 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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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">&times;</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>

View 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">&times;</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>

View file

@ -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 %}

View file

@ -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>

View file

@ -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' %}