Setup stages (#4618)
* setup complete stages * [setup] better setup-in-progress card * restructure setup exception flow * use setup_stages hook * Add message for non-dev mode, fail instead of error * message to not include commits in app setup stages
This commit is contained in:
parent
56bf54322f
commit
d5ad0ff2e8
4 changed files with 207 additions and 187 deletions
|
|
@ -182,39 +182,72 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
|
|||
}
|
||||
|
||||
action_on_complete() {
|
||||
var me = this;
|
||||
if (!this.current_slide.set_values()) return;
|
||||
this.update_values();
|
||||
this.show_working_state();
|
||||
this.disable_keyboard_nav();
|
||||
this.listen_for_setup_stages();
|
||||
|
||||
return frappe.call({
|
||||
method: "frappe.desk.page.setup_wizard.setup_wizard.setup_complete",
|
||||
args: {args: this.values},
|
||||
callback: function() {
|
||||
me.show_setup_complete_state();
|
||||
if(frappe.setup.welcome_page) {
|
||||
localStorage.setItem("session_last_route", frappe.setup.welcome_page);
|
||||
callback: (r) => {
|
||||
if(r.message.status === 'ok') {
|
||||
this.post_setup_success();
|
||||
} else if(r.message.fail !== undefined) {
|
||||
this.abort_setup(r.message.fail);
|
||||
}
|
||||
setTimeout(function() {
|
||||
// Reload
|
||||
window.location.href = '';
|
||||
}, 2000);
|
||||
setTimeout(()=> {
|
||||
$('body').removeClass('setup-state');
|
||||
}, 20000);
|
||||
},
|
||||
error: function() {
|
||||
var d = frappe.msgprint(__("There were errors."));
|
||||
d.custom_onhide = function() {
|
||||
$(me.parent).find('.page-card-container').remove();
|
||||
$('body').removeClass('setup-state');
|
||||
me.container.show();
|
||||
frappe.set_route(me.page_name, me.slides.length - 1);
|
||||
};
|
||||
}
|
||||
error: this.abort_setup.bind(this, "Error in setup", true)
|
||||
});
|
||||
}
|
||||
|
||||
post_setup_success() {
|
||||
this.set_setup_complete_message(__("Setup Complete"), __("Refreshing..."));
|
||||
if(frappe.setup.welcome_page) {
|
||||
localStorage.setItem("session_last_route", frappe.setup.welcome_page);
|
||||
}
|
||||
setTimeout(function() {
|
||||
// Reload
|
||||
window.location.href = '';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
abort_setup(fail_msg, error=false) {
|
||||
this.$working_state.find('.state-icon-container').html('');
|
||||
fail_msg = fail_msg ? fail_msg : __("Failed to complete setup");
|
||||
|
||||
if(error && !frappe.boot.developer_mode) {
|
||||
frappe.msgprint(`Don't worry. It's not you, it's us. We've
|
||||
received the issue details and will get back to you on the solution.
|
||||
Please feel free to contact us on support@erpnext.com in the meantime.`);
|
||||
}
|
||||
|
||||
this.update_setup_message('Could not start up: ' + fail_msg);
|
||||
|
||||
this.$working_state.find('.title').html('Setup failed');
|
||||
|
||||
this.$abort_btn.show();
|
||||
}
|
||||
|
||||
listen_for_setup_stages() {
|
||||
frappe.realtime.on("setup_task", (data) => {
|
||||
// console.log('data', data);
|
||||
if(data.stage_status) {
|
||||
// .html('Process '+ data.progress[0] + ' of ' + data.progress[1] + ': ' + data.stage_status);
|
||||
this.update_setup_message(data.stage_status);
|
||||
this.set_setup_load_percent((data.progress[0]+1)/data.progress[1] * 100);
|
||||
}
|
||||
if(data.fail_msg) {
|
||||
this.abort_setup(data.fail_msg);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
update_setup_message(message) {
|
||||
this.$working_state.find('.setup-message').html(message);
|
||||
}
|
||||
|
||||
get_setup_slides_filtered_by_domain() {
|
||||
var filtered_slides = [];
|
||||
frappe.setup.slides.forEach(function(slide) {
|
||||
|
|
@ -233,51 +266,56 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
|
|||
|
||||
show_working_state() {
|
||||
this.container.hide();
|
||||
$('body').addClass('setup-state');
|
||||
frappe.set_route(this.page_name);
|
||||
|
||||
this.working_state_message = this.get_message(
|
||||
__("Setting Up"),
|
||||
__("Sit tight while your system is being setup. This may take a few moments."),
|
||||
true
|
||||
).appendTo(this.parent);
|
||||
this.$working_state = this.get_message(
|
||||
__("Setting up your system"),
|
||||
__("Starting Frappé ...")).appendTo(this.parent);
|
||||
|
||||
this.attach_abort_button();
|
||||
|
||||
this.current_id = this.slides.length;
|
||||
this.current_slide = null;
|
||||
this.completed_state_message = this.get_message(
|
||||
__("Setup Complete"),
|
||||
__("Refreshing...")
|
||||
);
|
||||
}
|
||||
|
||||
show_setup_complete_state() {
|
||||
this.working_state_message.hide();
|
||||
this.completed_state_message.appendTo(this.parent);
|
||||
attach_abort_button() {
|
||||
this.$abort_btn = $(`<button class='btn btn-default btn-xs text-muted'
|
||||
style="margin-bottom: 30px;">${__('Retry')}</button>`);
|
||||
this.$working_state.find('.content').append(this.$abort_btn);
|
||||
|
||||
this.$abort_btn.on('click', () => {
|
||||
$(this.parent).find('.setup-in-progress').remove();
|
||||
this.container.show();
|
||||
frappe.set_route(this.page_name, this.slides.length - 1);
|
||||
});
|
||||
|
||||
this.$abort_btn.hide();
|
||||
}
|
||||
|
||||
get_message(title, message="", loading=false) {
|
||||
const loading_html = loading
|
||||
? '<div style="width:100%;height:100%" class="lds-rolling state-icon"><div></div></div>'
|
||||
: `<div style="width:100%;height:100%" class="state-icon">
|
||||
<i class="fa fa-check-circle text-success"
|
||||
style="font-size: 64px; margin-top: -8px;"></i>
|
||||
</div>`;
|
||||
get_message(title, message="") {
|
||||
const loading_html = `<div class="progress-chart" style ="width: 150px;">
|
||||
<div class="progress" style="margin-top: 70px; margin-bottom: 0px">
|
||||
<div class="progress-bar" style="width: 2%; background-color: #5e64ff;"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
return $(`<div class="page-card-container" data-state="setup">
|
||||
<div class="page-card">
|
||||
<div class="page-card-head">
|
||||
${loading
|
||||
? `<span class="indicator orange">${title}</span>`
|
||||
: `<span class="indicator green">${title}</span>`
|
||||
}
|
||||
</div>
|
||||
<p>${message}</p>
|
||||
<div class="state-icon-container">
|
||||
${loading_html}
|
||||
</div>
|
||||
return $(`<div class="slides-wrapper setup-wizard-slide setup-in-progress">
|
||||
<div class="content text-center">
|
||||
<p class="title lead">${title}</p>
|
||||
<div class="state-icon-container">${loading_html}</div>
|
||||
<p class="setup-message text-muted" style="margin: 30px 0px;">${message}</p>
|
||||
</div>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
set_setup_complete_message(title, message) {
|
||||
this.$working_state.find('.title').html(title);
|
||||
this.$working_state.find('.setup-message').html(message);
|
||||
}
|
||||
|
||||
set_setup_load_percent(percent) {
|
||||
this.$working_state.find('.progress-bar').css({"width": percent + "%"});
|
||||
}
|
||||
};
|
||||
|
||||
frappe.setup.SetupWizardSlide = class SetupWizardSlide extends frappe.ui.Slide {
|
||||
|
|
|
|||
|
|
@ -13,50 +13,117 @@ from werkzeug.useragents import UserAgent
|
|||
from . import install_fixtures
|
||||
from six import string_types
|
||||
|
||||
def get_setup_stages(args):
|
||||
|
||||
# App setup stage functions should not include frappe.db.commit
|
||||
# That is done by frappe after successful completion of all stages
|
||||
stages = [
|
||||
{
|
||||
'status': 'Updating global settings',
|
||||
'fail_msg': 'Failed to update global settings',
|
||||
'tasks': [
|
||||
{
|
||||
'fn': update_global_settings,
|
||||
'args': args,
|
||||
'fail_msg': 'Failed to update global settings'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
stages += get_stages_hooks(args) + get_setup_complete_hooks(args)
|
||||
|
||||
stages.append({
|
||||
# post executing hooks
|
||||
'status': 'Wrapping up',
|
||||
'fail_msg': 'Failed to complete setup',
|
||||
'tasks': [
|
||||
{
|
||||
'fn': run_post_setup_complete,
|
||||
'args': args,
|
||||
'fail_msg': 'Failed to complete setup'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
return stages
|
||||
|
||||
@frappe.whitelist()
|
||||
def setup_complete(args):
|
||||
"""Calls hooks for `setup_wizard_complete`, sets home page as `desktop`
|
||||
and clears cache. If wizard breaks, calls `setup_wizard_exception` hook"""
|
||||
|
||||
# Setup complete: do not throw an exception, let the user continue to desk
|
||||
if cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
|
||||
# do not throw an exception if setup is already complete
|
||||
# let the user continue to desk
|
||||
return
|
||||
#frappe.throw(_('Setup already complete'))
|
||||
|
||||
args = process_args(args)
|
||||
args = parse_args(args)
|
||||
|
||||
stages = get_setup_stages(args)
|
||||
|
||||
try:
|
||||
if args.language and args.language != "english":
|
||||
set_default_language(get_language_code(args.language))
|
||||
current_task = None
|
||||
for idx, stage in enumerate(stages):
|
||||
frappe.publish_realtime('setup_task', {"progress": [idx, len(stages)],
|
||||
"stage_status": stage.get('status')}, user=frappe.session.user)
|
||||
|
||||
frappe.clear_cache()
|
||||
|
||||
# update system settings
|
||||
update_system_settings(args)
|
||||
update_user_name(args)
|
||||
|
||||
for method in frappe.get_hooks("setup_wizard_complete"):
|
||||
frappe.get_attr(method)(args)
|
||||
|
||||
disable_future_access()
|
||||
|
||||
frappe.db.commit()
|
||||
frappe.clear_cache()
|
||||
except:
|
||||
frappe.db.rollback()
|
||||
if args:
|
||||
traceback = frappe.get_traceback()
|
||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||
frappe.get_attr(hook)(traceback, args)
|
||||
|
||||
raise
|
||||
for task in stage.get('tasks'):
|
||||
current_task = task
|
||||
task.get('fn')(task.get('args'))
|
||||
|
||||
except Exception:
|
||||
handle_setup_exception(args)
|
||||
return {'status': 'fail', 'fail': current_task.get('fail_msg')}
|
||||
else:
|
||||
for hook in frappe.get_hooks("setup_wizard_success"):
|
||||
frappe.get_attr(hook)(args)
|
||||
install_fixtures.install()
|
||||
run_setup_success(args)
|
||||
return {'status': 'ok'}
|
||||
|
||||
def update_global_settings(args):
|
||||
if args.language and args.language != "english":
|
||||
set_default_language(get_language_code(args.lang))
|
||||
frappe.clear_cache()
|
||||
|
||||
update_system_settings(args)
|
||||
update_user_name(args)
|
||||
|
||||
def run_post_setup_complete(args):
|
||||
disable_future_access()
|
||||
frappe.db.commit()
|
||||
frappe.clear_cache()
|
||||
|
||||
def run_setup_success(args):
|
||||
for hook in frappe.get_hooks("setup_wizard_success"):
|
||||
frappe.get_attr(hook)(args)
|
||||
install_fixtures.install()
|
||||
|
||||
def get_stages_hooks(args):
|
||||
stages = []
|
||||
for method in frappe.get_hooks("setup_wizard_stages"):
|
||||
stages += frappe.get_attr(method)(args)
|
||||
return stages
|
||||
|
||||
def get_setup_complete_hooks(args):
|
||||
stages = []
|
||||
for method in frappe.get_hooks("setup_wizard_complete"):
|
||||
stages.append({
|
||||
'status': 'Executing method',
|
||||
'fail_msg': 'Failed to execute method',
|
||||
'tasks': [
|
||||
{
|
||||
'fn': frappe.get_attr(method),
|
||||
'args': args,
|
||||
'fail_msg': 'Failed to execute method'
|
||||
}
|
||||
]
|
||||
})
|
||||
return stages
|
||||
|
||||
def handle_setup_exception(args):
|
||||
frappe.db.rollback()
|
||||
if args:
|
||||
traceback = frappe.get_traceback()
|
||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||
frappe.get_attr(hook)(traceback, args)
|
||||
|
||||
def update_system_settings(args):
|
||||
number_format = get_country_info(args.get("country")).get("number_format", "#,###.##")
|
||||
|
|
@ -126,7 +193,7 @@ def update_user_name(args):
|
|||
if args.get('name'):
|
||||
add_all_roles_to(args.get("name"))
|
||||
|
||||
def process_args(args):
|
||||
def parse_args(args):
|
||||
if not args:
|
||||
args = frappe.local.form_dict
|
||||
if isinstance(args, string_types):
|
||||
|
|
@ -234,14 +301,6 @@ def email_setup_wizard_exception(traceback, args):
|
|||
user_agent = frappe._dict()
|
||||
|
||||
message = """
|
||||
#### Basic Information
|
||||
|
||||
- **Site:** {site}
|
||||
- **User:** {user}
|
||||
- **Browser:** {user_agent.platform} {user_agent.browser} version: {user_agent.version} language: {user_agent.language}
|
||||
- **Browser Languages**: `{accept_languages}`
|
||||
|
||||
---
|
||||
|
||||
#### Traceback
|
||||
|
||||
|
|
@ -257,7 +316,16 @@ def email_setup_wizard_exception(traceback, args):
|
|||
|
||||
#### Request Headers
|
||||
|
||||
<pre>{headers}</pre>""".format(
|
||||
<pre>{headers}</pre>
|
||||
|
||||
---
|
||||
|
||||
#### Basic Information
|
||||
|
||||
- **Site:** {site}
|
||||
- **User:** {user}
|
||||
- **Browser:** {user_agent.platform} {user_agent.browser} version: {user_agent.version} language: {user_agent.language}
|
||||
- **Browser Languages**: `{accept_languages}`""".format(
|
||||
site=frappe.local.site,
|
||||
traceback=traceback,
|
||||
args="\n".join(pretty_args),
|
||||
|
|
@ -268,14 +336,13 @@ def email_setup_wizard_exception(traceback, args):
|
|||
|
||||
frappe.sendmail(recipients=frappe.local.conf.setup_wizard_exception_email,
|
||||
sender=frappe.session.user,
|
||||
subject="Exception in Setup Wizard - {}".format(frappe.local.site),
|
||||
subject="Setup failed: {}".format(frappe.local.site),
|
||||
message=message,
|
||||
delayed=False)
|
||||
|
||||
def get_language_code(lang):
|
||||
return frappe.db.get_value('Language', {'language_name':lang})
|
||||
|
||||
|
||||
def enable_twofactor_all_roles():
|
||||
all_role = frappe.get_doc('Role',{'role_name':'All'})
|
||||
all_role.two_factor_auth = True
|
||||
|
|
|
|||
|
|
@ -279,6 +279,9 @@ select.input-sm {
|
|||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.setup-wizard-slide .progress-bar {
|
||||
background-color: #5e64ff;
|
||||
}
|
||||
.page-card-container {
|
||||
padding: 70px;
|
||||
}
|
||||
|
|
@ -312,49 +315,3 @@ select.input-sm {
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@keyframes lds-rolling {
|
||||
0% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(0deg);
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(360deg);
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes lds-rolling {
|
||||
0% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(0deg);
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(360deg);
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
.lds-rolling {
|
||||
-webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px);
|
||||
transform: translate(-100px, -100px) scale(1) translate(100px, 100px);
|
||||
}
|
||||
.lds-rolling div {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 3px solid #d1d8dd;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
-webkit-animation: lds-rolling 1s linear infinite;
|
||||
animation: lds-rolling 1s linear infinite;
|
||||
top: 50px;
|
||||
left: 50px;
|
||||
}
|
||||
.lds-rolling div:after {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 3px solid #d1d8dd;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -335,6 +335,10 @@ select.input-sm {
|
|||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
background-color: #5e64ff;
|
||||
}
|
||||
}
|
||||
|
||||
.page-card-container {
|
||||
|
|
@ -376,50 +380,4 @@ select.input-sm {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes lds-rolling {
|
||||
0% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(0deg);
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(360deg);
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes lds-rolling {
|
||||
0% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(0deg);
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translate(-50%, -50%) rotate(360deg);
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.lds-rolling {
|
||||
-webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px);
|
||||
transform: translate(-100px, -100px) scale(1) translate(100px, 100px);
|
||||
div {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 3px solid #d1d8dd;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
-webkit-animation: lds-rolling 1s linear infinite;
|
||||
animation: lds-rolling 1s linear infinite;
|
||||
top: 50px;
|
||||
left: 50px;
|
||||
&:after {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 3px solid #d1d8dd;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue