Преглед изворни кода

[re-org] setup wizard now in frappe

version-14
Rushabh Mehta пре 9 година
committed by Anand Doshi
родитељ
комит
9e5750f6d8
11 измењених фајлова са 598 додато и 2 уклоњено
  1. +4
    -1
      frappe/boot.py
  2. +0
    -0
      frappe/desk/page/setup_wizard/__init__.py
  3. +70
    -0
      frappe/desk/page/setup_wizard/setup_wizard.css
  4. +396
    -0
      frappe/desk/page/setup_wizard/setup_wizard.js
  5. +22
    -0
      frappe/desk/page/setup_wizard/setup_wizard.json
  6. +70
    -0
      frappe/desk/page/setup_wizard/setup_wizard.py
  7. +7
    -0
      frappe/desk/page/setup_wizard/setup_wizard_message.html
  8. +21
    -0
      frappe/desk/page/setup_wizard/setup_wizard_page.html
  9. +4
    -0
      frappe/hooks.py
  10. +2
    -1
      frappe/translate.py
  11. +2
    -0
      frappe/utils/install.py

+ 4
- 1
frappe/boot.py Прегледај датотеку

@@ -31,7 +31,6 @@ def get_bootinfo():
bootinfo['user_info'] = get_fullnames()
bootinfo['sid'] = frappe.session['sid'];

# home page
bootinfo.modules = {}
for app in frappe.get_installed_apps():
try:
@@ -140,6 +139,10 @@ def add_home_page(bootinfo, docs):
if frappe.session.user=="Guest":
return
home_page = frappe.db.get_default("desktop:home_page")

if home_page == "setup-wizard":
bootinfo.setup_wizard_requires = frappe.get_hooks("setup_wizard_requires")

try:
page = frappe.desk.desk_page.get(home_page)
except (frappe.DoesNotExistError, frappe.PermissionError):


+ 0
- 0
frappe/desk/page/setup_wizard/__init__.py Прегледај датотеку


+ 70
- 0
frappe/desk/page/setup_wizard/setup_wizard.css Прегледај датотеку

@@ -0,0 +1,70 @@
.setup-wizard-slide {
padding-left: 0px;
padding-right: 0px;
}

@media (min-width: 768px) {
.setup-wizard-slide.single-column {
max-width: 500px;
}

.setup-wizard-slide.two-column {
max-width: 768px;
}
}

.setup-wizard-slide .lead {
margin-bottom: 10px;
}

.setup-wizard-slide .form {
margin-top: 20px;
border: 1px solid #d1d8dd;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1);
}

.setup-wizard-slide .footer {
margin: 20px auto;
}

.setup-wizard-progress {
border-bottom: 1px solid #d1d8dd;
padding-bottom: 15px;
margin: -20px auto 20px;
}

.setup-wizard-slide .icon-fixed-width {
vertical-align: middle;
}

.setup-wizard-slide .icon-circle-blank {
font-size: 7px;
}

.setup-wizard-slide .icon-circle {
font-size: 10px;
}

.setup-wizard-slide .frappe-control[data-fieldtype="Attach Image"] {
text-align: center;
}

.setup-wizard-slide .missing-image,
.setup-wizard-slide .attach-image-display {
display: block;
position: relative;
left: 50%;
transform: translate(-50%, 0);
-webkit-transform: translate(-50%, 0);
}

.setup-wizard-slide .missing-image .octicon {
position: relative;
top: 50%;
transform: translate(0px, -50%);
-webkit-transform: translate(0px, -50%);
}

.setup-wizard-message-image {
margin: 15px auto;
}

+ 396
- 0
frappe/desk/page/setup_wizard/setup_wizard.js Прегледај датотеку

@@ -0,0 +1,396 @@
frappe.provide("frappe.wiz");
frappe.provide("frappe.wiz.events");

frappe.wiz = {
slides: [],
events: {},
on: function(event, fn) {
if(!frappe.wiz.events[event]) {
frappe.wiz.events[event] = [];
}
frappe.wiz.events[event].push(fn);
},
add_slide: function(slide) {
frappe.wiz.slides.push(slide);
},
run_event: function(event) {
$.each(frappe.wiz.events[event] || [], function(i, fn) {
fn(frappe.wiz.wizard);
});
}
}

frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
// setup page ui
$(".navbar:first").toggle(false);
$("body").css({"padding-top":"30px"});

frappe.require("/assets/frappe/css/animate.min.css");

$.each(frappe.boot.setup_wizard_requires || [], function(i, path) {
frappe.require(path);
});

// add welcome slide
frappe.wiz.add_slide(frappe.wiz.welcome);
frappe.wiz.add_slide(frappe.wiz.region);

frappe.wiz.run_event("before_load");

var wizard_settings = {
page_name: "setup-wizard",
parent: wrapper,
slides: frappe.wiz.slides,
title: __("Welcome")
}

frappe.wiz.wizard = new frappe.wiz.Wizard(wizard_settings)

}

frappe.pages['setup-wizard'].on_page_show = function(wrapper) {
if(frappe.get_route()[1]) {
frappe.wiz.wizard.show(frappe.get_route()[1]);
}


}

frappe.wiz.Wizard = Class.extend({
init: function(opts) {
$.extend(this, opts);
this.make();
this.slides;
this.slide_dict = {};
this.welcomed = true;
frappe.set_route("setup-wizard/0");
},
make: function() {
this.parent = $('<div class="setup-wizard-wrapper">').appendTo(this.parent);
},
get_message: function(html) {
return $(repl('<div data-state="setup-complete">\
<div style="padding: 40px;" class="text-center">%(html)s</div>\
</div>', {html:html}))
},
show_working: function() {
this.hide_current_slide();
frappe.set_route(this.page_name);
this.current_slide = {"$wrapper": this.get_message(this.working_html()).appendTo(this.parent)};
},
show_complete: function() {
this.hide_current_slide();
this.current_slide = {"$wrapper": this.get_message(this.complete_html()).appendTo(this.parent)};
},
show: function(id) {
if(!this.welcomed) {
frappe.set_route(this.page_name);
return;
}
id = cint(id);
if(this.current_slide && this.current_slide.id===id)
return;
if(!this.slide_dict[id]) {
this.slide_dict[id] = new frappe.wiz.WizardSlide($.extend(this.slides[id], {wiz:this, id:id}));
this.slide_dict[id].make();
}

this.hide_current_slide();

this.current_slide = this.slide_dict[id];
this.current_slide.$wrapper.removeClass("hidden");
},
hide_current_slide: function() {
if(this.current_slide) {
this.current_slide.$wrapper.addClass("hidden");
this.current_slide = null;
}
},
get_values: function() {
var values = {};
$.each(this.slide_dict, function(id, slide) {
$.extend(values, slide.values)
})
return values;
},
working_html: function() {
var msg = $(frappe.render_template("setup_wizard_message", {
image: "/assets/frappe/images/ui/bubble-tea-smile.svg",
title: __("Setting Up"),
message: __('Sit tight while your system is being setup. This may take a few moments.')
}));
msg.find(".setup-wizard-message-image").addClass("animated infinite bounce");
return msg.html();
},

complete_html: function() {
return frappe.render_template("setup_wizard_message", {
image: "/assets/frappe/images/ui/bubble-tea-happy.svg",
title: __('Setup Complete'),
message: ""
});
},

on_complete: function() {
var me = this;
var values = me.get_values();
me.show_working();
return frappe.call({
method: "frappe.desk.page.setup_wizard.setup_wizard.setup_complete",
args: {args: values},
callback: function(r) {
me.show_complete();
if(frappe.wiz.welcome_page) {
localStorage.setItem("session_last_route", frappe.wiz.welcome_page);
}
setTimeout(function() {
window.location = "/desk";
}, 2000);
},
error: function(r) {
var d = msgprint(__("There were errors."));
d.custom_onhide = function() {
frappe.set_route(me.page_name, me.slides.length - 1);
};
}
});
}

});

frappe.wiz.WizardSlide = Class.extend({
init: function(opts) {
$.extend(this, opts);
this.$wrapper = $('<div class="slide-wrapper hidden"></div>')
.appendTo(this.wiz.parent)
.attr("data-slide-id", this.id);
},
make: function() {
var me = this;
if(this.$body) this.$body.remove();

if(this.before_load) {
this.before_load(this);
}

this.$body = $(frappe.render_template("setup_wizard_page", {
help: __(this.help),
title:__(this.title),
main_title:__(this.wiz.title),
step: this.id + 1,
name: this.name,
css_class: this.css_class || "",
slides_count: this.wiz.slides.length
})).appendTo(this.$wrapper);

this.body = this.$body.find(".form")[0];

if(this.fields) {
this.form = new frappe.ui.FieldGroup({
fields: this.fields,
body: this.body,
no_submit_on_enter: true
});
this.form.make();
} else {
$(this.body).html(this.html);
}

if(this.id > 0) {
this.$prev = this.$body.find('.prev-btn').removeClass("hide")
.click(function() {
frappe.set_route(me.wiz.page_name, me.id-1 + "");
})
.css({"margin-right": "10px"});
}
if(this.id+1 < this.wiz.slides.length) {
this.$next = this.$body.find('.next-btn').removeClass("hide")
.click(function() {
me.values = me.form.get_values();
if(me.values===null)
return;
if(me.validate && !me.validate())
return;
frappe.set_route(me.wiz.page_name, me.id+1 + "");
})
} else {
this.$complete = this.$body.find('.complete-btn').removeClass("hide")
.click(function() {
me.values = me.form.get_values();
if(me.values===null)
return;
if(me.validate && !me.validate())
return;
me.wiz.on_complete(me.wiz);
})
}

if(this.onload) {
this.onload(this);
}

},
get_input: function(fn) {
return this.form.get_input(fn);
},
get_field: function(fn) {
return this.form.get_field(fn);
}
});

// language selection
frappe.wiz.welcome = {
name: "welcome",
title: __("Welcome"),
icon: "icon-world",
help: __("Let's prepare the system for first use."),

fields: [
{ fieldname: "language", label: __("Select Your Language"), reqd:1,
fieldtype: "Select" },
],

onload: function(slide) {
if (!frappe.wiz.welcome.data) {
frappe.wiz.welcome.load_languages(slide);
} else {
frappe.wiz.welcome.setup_fields(slide);
}
},

css_class: "single-column",
load_languages: function(slide) {
frappe.call({
method: "frappe.desk.page.setup_wizard.setup_wizard.load_languages",
callback: function(r) {
frappe.wiz.welcome.data = r.message;
frappe.wiz.welcome.setup_fields(slide);

slide.get_field("language")
.set_input(frappe.wiz.welcome.data.default_language || "english");
moment.locale("en");
}
});
},

setup_fields: function(slide) {
var select = slide.get_field("language");
select.df.options = frappe.wiz.welcome.data.languages;
select.refresh();
frappe.wiz.welcome.bind_events(slide);
},

bind_events: function(slide) {
slide.get_input("language").unbind("change").on("change", function() {
var lang = $(this).val() || "english";
frappe._messages = {};
frappe.call({
method: "frappe.desk.page.setup_wizard.setup_wizard.load_messages",
args: {
language: lang
},
callback: function(r) {
// TODO save values!

// re-render all slides
$.each(slide.wiz.slide_dict, function(key, s) {
s.make();
});

// select is re-made after language change
var select = slide.get_field("language");
select.set_input(lang);
}
});
});
}
}

// region selection
frappe.wiz.region = {
title: __("Region"),
icon: "icon-flag",
help: __("Select your Country, Time Zone and Currency"),
fields: [
{ fieldname: "country", label: __("Country"), reqd:1,
fieldtype: "Select" },
{ fieldname: "timezone", label: __("Time Zone"), reqd:1,
fieldtype: "Select" },
{ fieldname: "currency", label: __("Currency"), reqd:1,
fieldtype: "Select" },
],

onload: function(slide) {
frappe.call({
method:"frappe.geo.country_info.get_country_timezone_info",
callback: function(data) {
frappe.wiz.region.data = data.message;
frappe.wiz.region.setup_fields(slide);
frappe.wiz.region.bind_events(slide);
}
});
},
css_class: "single-column",
setup_fields: function(slide) {
var data = frappe.wiz.region.data;

slide.get_input("country").empty()
.add_options([""].concat(keys(data.country_info).sort()));

slide.get_input("currency").empty()
.add_options(frappe.utils.unique([""].concat($.map(data.country_info,
function(opts, country) { return opts.currency; }))).sort());

slide.get_input("timezone").empty()
.add_options([""].concat(data.all_timezones));

if (data.default_country) {
slide.set_input("country", data.default_country);
}
},

bind_events: function(slide) {
slide.get_input("country").on("change", function() {
var country = slide.get_input("country").val();
var $timezone = slide.get_input("timezone");
var data = frappe.wiz.region.data;

$timezone.empty();

// add country specific timezones first
if(country) {
var timezone_list = data.country_info[country].timezones || [];
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
slide.get_field("currency").$input.trigger("change");
}

// add all timezones at the end, so that user has the option to change it to any timezone
$timezone.add_options([""].concat(data.all_timezones));

slide.get_field("timezone").set_input($timezone.val());

// temporarily set date format
frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
|| "dd-mm-yyyy");
});

slide.get_input("currency").on("change", function() {
var currency = slide.get_input("currency").val();
if (!currency) return;
frappe.model.with_doc("Currency", currency, function() {
frappe.provide("locals.:Currency." + currency);
var currency_doc = frappe.model.get_doc("Currency", currency);
var number_format = currency_doc.number_format;
if (number_format==="#.###") {
number_format = "#.###,##";
} else if (number_format==="#,###") {
number_format = "#,###.##"
}

frappe.boot.sysdefaults.number_format = number_format;
locals[":Currency"][currency] = $.extend({}, currency_doc);
});
});
}
};

+ 22
- 0
frappe/desk/page/setup_wizard/setup_wizard.json Прегледај датотеку

@@ -0,0 +1,22 @@
{
"content": null,
"creation": "2013-10-04 13:49:33",
"docstatus": 0,
"doctype": "Page",
"idx": 1,
"modified": "2015-11-09 03:06:14.542388",
"modified_by": "Administrator",
"module": "Desk",
"name": "setup-wizard",
"owner": "Administrator",
"page_name": "setup-wizard",
"roles": [
{
"role": "System Manager"
}
],
"script": null,
"standard": "Yes",
"style": null,
"title": "Setup Wizard"
}

+ 70
- 0
frappe/desk/page/setup_wizard/setup_wizard.py Прегледај датотеку

@@ -0,0 +1,70 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: See license.txt

from __future__ import unicode_literals

import frappe, json
from frappe.utils import strip
from frappe.translate import (set_default_language, get_dict,
get_lang_dict, send_translations, get_language_from_code)

@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"""
args = process_args(args)

try:
for method in frappe.get_hooks("setup_wizard_complete"):
frappe.get_attr(method)(args)

frappe.db.set_default('desktop:home_page', 'desktop')
frappe.db.commit()
frappe.clear_cache()
except:
if args:
traceback = frappe.get_traceback()
for hook in frappe.get_hooks("setup_wizard_exception"):
frappe.get_attr(hook)(traceback, args)

raise

else:
for hook in frappe.get_hooks("setup_wizard_success"):
frappe.get_attr(hook)(args)

def process_args(args):
if not args:
args = frappe.local.form_dict
if isinstance(args, basestring):
args = json.loads(args)

args = frappe._dict(args)

# strip the whitespace
for key, value in args.items():
if isinstance(value, basestring):
args[key] = strip(value)

return args

@frappe.whitelist()
def load_messages(language):
"""Load translation messages for given langauge from all `setup_wizard_requires`
javascript files"""
frappe.clear_cache()
set_default_language(language)
m = get_dict("page", "setup-wizard")
for path in frappe.get_hooks("setup_wizard_requires"):
# common folder `assets` served from `sites/`
m.update(get_dict("jsfile", frappe.get_site_path("..", path)))
m.update(get_dict("boot"))
send_translations(m)
return frappe.local.lang

@frappe.whitelist()
def load_languages():
return {
"default_language": get_language_from_code(frappe.local.lang),
"languages": sorted(get_lang_dict().keys())
}

+ 7
- 0
frappe/desk/page/setup_wizard/setup_wizard_message.html Прегледај датотеку

@@ -0,0 +1,7 @@
<div class="container setup-wizard-slide">
<img class="img-responsive setup-wizard-message-image" src="{%= image %}">

<p class="text-center lead">{%= title %}</p>

<p class="text-center">{%= message %}</p>
</div>

+ 21
- 0
frappe/desk/page/setup_wizard/setup_wizard_page.html Прегледај датотеку

@@ -0,0 +1,21 @@
<div class="container setup-wizard-slide {%= css_class %}" data-slide-name="{%= name %}">
<div class="text-center setup-wizard-progress text-extra-muted">
{% for (var i=0; i < slides_count; i++) { %}
<i class="icon-fixed-width {% if (i+1==step) { %} icon-circle {% } else { %} icon-circle-blank {% } %}"></i>
{% } %}
</div>
<p class="text-center lead">{%= title %}</p>
<div class="row">
<div class="col-sm-12">
{% if (help) { %} <p class="text-center">{%= help %}</p> {% } %}
<div class="form"></div>
</div>
</div>
<div class="footer text-center">
<div>
<a class="prev-btn hide grey small">{%= __("Previous") %}</a>
<a class="next-btn hide btn btn-primary btn-sm">{%= __("Next") %}</a>
<a class="complete-btn hide btn btn-primary btn-sm"><b>{%= __("Complete Setup") %}</b></a>
</div>
</div>
</div>

+ 4
- 0
frappe/hooks.py Прегледај датотеку

@@ -176,3 +176,7 @@ sounds = [
# {"name": "alert", "src": "/assets/frappe/sounds/alert.mp3"},
# {"name": "chime", "src": "/assets/frappe/sounds/chime.mp3"},
]

get_translated_dict = {
("page", "setup-wizard"): "frappe.geo.country_info.get_translated_dict",
}

+ 2
- 1
frappe/translate.py Прегледај датотеку

@@ -306,7 +306,7 @@ def _get_messages_from_page_or_report(doctype, name, module=None):

doc_path = frappe.get_module_path(module, doctype, name)

messages = get_messages_from_file(os.path.join(doc_path, name +".py"))
messages = get_messages_from_file(os.path.join(doc_path, frappe.scrub(name) +".py"))

if os.path.exists(doc_path):
for filename in os.listdir(doc_path):
@@ -362,6 +362,7 @@ def get_messages_from_file(path):
return [(os.path.relpath(" +".join([path, str(pos)]), apps_path),
message) for pos, message in extract_messages_from_code(sourcefile.read(), path.endswith(".py"))]
else:
print "Translate: {0} missing".format(os.path.abspath(path))
return []

def extract_messages_from_code(code, is_py=False):


+ 2
- 0
frappe/utils/install.py Прегледај датотеку

@@ -61,6 +61,8 @@ def after_install():
from frappe.auth import _update_password
_update_password("Administrator", get_admin_password())

# setup wizard now in frappe
frappe.db.set_default('desktop:home_page', 'setup-wizard');

frappe.db.commit()



Loading…
Откажи
Сачувај