Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

620 wiersze
17 KiB

  1. frappe.provide("frappe.setup");
  2. frappe.provide("frappe.setup.events");
  3. frappe.provide("frappe.ui");
  4. frappe.setup = {
  5. slides: [],
  6. events: {},
  7. data: {},
  8. utils: {},
  9. domains: [],
  10. on: function (event, fn) {
  11. if (!frappe.setup.events[event]) {
  12. frappe.setup.events[event] = [];
  13. }
  14. frappe.setup.events[event].push(fn);
  15. },
  16. add_slide: function (slide) {
  17. frappe.setup.slides.push(slide);
  18. },
  19. remove_slide: function (slide_name) {
  20. frappe.setup.slides = frappe.setup.slides.filter((slide) => slide.name !== slide_name);
  21. },
  22. run_event: function (event) {
  23. $.each(frappe.setup.events[event] || [], function (i, fn) {
  24. fn();
  25. });
  26. }
  27. }
  28. frappe.pages['setup-wizard'].on_page_load = function (wrapper) {
  29. let requires = (frappe.boot.setup_wizard_requires || []);
  30. frappe.require(requires, function () {
  31. frappe.call({
  32. method: "frappe.desk.page.setup_wizard.setup_wizard.load_languages",
  33. freeze: true,
  34. callback: function (r) {
  35. frappe.setup.data.lang = r.message;
  36. frappe.setup.run_event("before_load");
  37. var wizard_settings = {
  38. parent: wrapper,
  39. slides: frappe.setup.slides,
  40. slide_class: frappe.setup.SetupWizardSlide,
  41. unidirectional: 1,
  42. done_state: 1,
  43. }
  44. frappe.wizard = new frappe.setup.SetupWizard(wizard_settings);
  45. frappe.setup.run_event("after_load");
  46. let route = frappe.get_route();
  47. if (route) {
  48. frappe.wizard.show_slide(route[1]);
  49. }
  50. }
  51. });
  52. });
  53. };
  54. frappe.pages['setup-wizard'].on_page_show = function () {
  55. if (frappe.get_route()[1]) {
  56. frappe.wizard && frappe.wizard.show_slide(frappe.get_route()[1]);
  57. }
  58. };
  59. frappe.setup.on("before_load", function () {
  60. // load slides
  61. frappe.setup.slides_settings.forEach((s) => {
  62. if (!(s.name === 'user' && frappe.boot.developer_mode)) {
  63. // if not user slide with developer mode
  64. frappe.setup.add_slide(s);
  65. }
  66. });
  67. });
  68. frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
  69. constructor(args = {}) {
  70. super(args);
  71. $.extend(this, args);
  72. this.page_name = "setup-wizard";
  73. this.welcomed = true;
  74. frappe.set_route("setup-wizard/0");
  75. }
  76. make() {
  77. super.make();
  78. this.container.addClass("container setup-wizard-slide with-form");
  79. this.$next_btn.addClass('action');
  80. this.$complete_btn.addClass('action');
  81. this.setup_keyboard_nav();
  82. }
  83. setup_keyboard_nav() {
  84. $('body').on('keydown', this.handle_enter_press.bind(this));
  85. }
  86. disable_keyboard_nav() {
  87. $('body').off('keydown', this.handle_enter_press.bind(this));
  88. }
  89. handle_enter_press(e) {
  90. if (e.which === frappe.ui.keyCode.ENTER) {
  91. var $target = $(e.target);
  92. if ($target.hasClass('prev-btn')) {
  93. $target.trigger('click');
  94. } else {
  95. this.container.find('.next-btn').trigger('click');
  96. e.preventDefault();
  97. }
  98. }
  99. }
  100. before_show_slide() {
  101. if (!this.welcomed) {
  102. frappe.set_route(this.page_name);
  103. return false;
  104. }
  105. return true;
  106. }
  107. show_slide(id) {
  108. if (id === this.slides.length) {
  109. // show_slide called on last slide
  110. this.action_on_complete();
  111. return;
  112. }
  113. super.show_slide(id);
  114. frappe.set_route(this.page_name, id + "");
  115. }
  116. show_hide_prev_next(id) {
  117. super.show_hide_prev_next(id);
  118. if (id + 1 === this.slides.length) {
  119. this.$next_btn.removeClass("btn-primary").hide();
  120. this.$complete_btn.addClass("btn-primary").show()
  121. .on('click', () => this.action_on_complete());
  122. } else {
  123. this.$next_btn.addClass("btn-primary").show();
  124. this.$complete_btn.removeClass("btn-primary").hide();
  125. }
  126. }
  127. refresh_slides() {
  128. // For Translations, etc.
  129. if (this.in_refresh_slides || !this.current_slide.set_values(true)) {
  130. return;
  131. }
  132. this.in_refresh_slides = true;
  133. this.update_values();
  134. frappe.setup.slides = [];
  135. frappe.setup.run_event("before_load");
  136. frappe.setup.slides = this.get_setup_slides_filtered_by_domain();
  137. this.slides = frappe.setup.slides;
  138. frappe.setup.run_event("after_load");
  139. // re-render all slide, only remake made slides
  140. $.each(this.slide_dict, (id, slide) => {
  141. if (slide.made) {
  142. this.made_slide_ids.push(id);
  143. }
  144. });
  145. this.made_slide_ids.push(this.current_id);
  146. this.setup();
  147. this.show_slide(this.current_id);
  148. this.refresh(this.current_id);
  149. setTimeout(() => {
  150. this.container.find('.form-control').first().focus();
  151. }, 200);
  152. this.in_refresh_slides = false;
  153. }
  154. action_on_complete() {
  155. if (!this.current_slide.set_values()) return;
  156. this.update_values();
  157. this.show_working_state();
  158. this.disable_keyboard_nav();
  159. this.listen_for_setup_stages();
  160. return frappe.call({
  161. method: "frappe.desk.page.setup_wizard.setup_wizard.setup_complete",
  162. args: { args: this.values },
  163. callback: (r) => {
  164. if (r.message.status === 'ok') {
  165. this.post_setup_success();
  166. } else if (r.message.status === 'registered') {
  167. this.update_setup_message(__("starting the setup..."));
  168. } else if (r.message.fail !== undefined) {
  169. this.abort_setup(r.message.fail);
  170. }
  171. },
  172. error: () => this.abort_setup("Error in setup")
  173. });
  174. }
  175. post_setup_success() {
  176. this.set_setup_complete_message(__("Setup Complete"), __("Refreshing..."));
  177. if (frappe.setup.welcome_page) {
  178. localStorage.setItem("session_last_route", frappe.setup.welcome_page);
  179. }
  180. setTimeout(function () {
  181. // Reload
  182. window.location.href = '/app';
  183. }, 2000);
  184. }
  185. abort_setup(fail_msg) {
  186. this.$working_state.find('.state-icon-container').html('');
  187. fail_msg = fail_msg ? fail_msg : __("Failed to complete setup");
  188. this.update_setup_message('Could not start up: ' + fail_msg);
  189. this.$working_state.find('.title').html('Setup failed');
  190. this.$abort_btn.show();
  191. }
  192. listen_for_setup_stages() {
  193. frappe.realtime.on("setup_task", (data) => {
  194. // console.log('data', data);
  195. if (data.stage_status) {
  196. // .html('Process '+ data.progress[0] + ' of ' + data.progress[1] + ': ' + data.stage_status);
  197. this.update_setup_message(data.stage_status);
  198. this.set_setup_load_percent((data.progress[0] + 1) / data.progress[1] * 100);
  199. }
  200. if (data.fail_msg) {
  201. this.abort_setup(data.fail_msg);
  202. }
  203. if (data.status === 'ok') {
  204. this.post_setup_success();
  205. }
  206. })
  207. }
  208. update_setup_message(message) {
  209. this.$working_state.find('.setup-message').html(message);
  210. }
  211. get_setup_slides_filtered_by_domain() {
  212. var filtered_slides = [];
  213. frappe.setup.slides.forEach(function (slide) {
  214. if (frappe.setup.domains) {
  215. let active_domains = frappe.setup.domains;
  216. if (!slide.domains ||
  217. slide.domains.filter(d => active_domains.includes(d)).length > 0) {
  218. filtered_slides.push(slide);
  219. }
  220. } else {
  221. filtered_slides.push(slide);
  222. }
  223. })
  224. return filtered_slides;
  225. }
  226. show_working_state() {
  227. this.container.hide();
  228. frappe.set_route(this.page_name);
  229. this.$working_state = this.get_message(
  230. __("Setting up your system"),
  231. __("Starting Frappe ...")).appendTo(this.parent);
  232. this.attach_abort_button();
  233. this.current_id = this.slides.length;
  234. this.current_slide = null;
  235. }
  236. attach_abort_button() {
  237. this.$abort_btn = $(`<button class='btn btn-secondary btn-xs btn-abort text-muted'>${__('Retry')}</button>`);
  238. this.$working_state.find('.content').append(this.$abort_btn);
  239. this.$abort_btn.on('click', () => {
  240. $(this.parent).find('.setup-in-progress').remove();
  241. this.container.show();
  242. frappe.set_route(this.page_name, this.slides.length - 1);
  243. });
  244. this.$abort_btn.hide();
  245. }
  246. get_message(title, message = "") {
  247. const loading_html = `<div class="progress-chart">
  248. <div class="progress">
  249. <div class="progress-bar"></div>
  250. </div>
  251. </div>`;
  252. return $(`<div class="slides-wrapper container setup-wizard-slide setup-in-progress">
  253. <div class="content text-center">
  254. <h1 class="slide-title title">${title}</h1>
  255. <div class="state-icon-container">${loading_html}</div>
  256. <p class="setup-message text-muted">${message}</p>
  257. </div>
  258. </div>`);
  259. }
  260. set_setup_complete_message(title, message) {
  261. this.$working_state.find('.title').html(title);
  262. this.$working_state.find('.setup-message').html(message);
  263. }
  264. set_setup_load_percent(percent) {
  265. this.$working_state.find('.progress-bar').css({ "width": percent + "%" });
  266. }
  267. };
  268. frappe.setup.SetupWizardSlide = class SetupWizardSlide extends frappe.ui.Slide {
  269. constructor(slide = null) {
  270. super(slide);
  271. }
  272. make() {
  273. super.make();
  274. this.set_init_values();
  275. this.reset_action_button_state();
  276. }
  277. set_init_values() {
  278. var me = this;
  279. // set values from frappe.setup.values
  280. if (frappe.wizard.values && this.fields) {
  281. this.fields.forEach(function (f) {
  282. var value = frappe.wizard.values[f.fieldname];
  283. if (value) {
  284. me.get_field(f.fieldname).set_input(value);
  285. }
  286. });
  287. }
  288. }
  289. };
  290. // Frappe slides settings
  291. // ======================================================
  292. frappe.setup.slides_settings = [
  293. {
  294. // Welcome (language) slide
  295. name: "welcome",
  296. title: __("Hello!"),
  297. fields: [
  298. {
  299. fieldname: "language",
  300. label: __("Your Language"),
  301. fieldtype: "Autocomplete",
  302. placeholder: __('Select Language'),
  303. default: "English",
  304. reqd: 1,
  305. },
  306. {
  307. fieldname: "country",
  308. label: __("Your Country"),
  309. fieldtype: "Autocomplete",
  310. placeholder: __('Select Country'),
  311. reqd: 1,
  312. },
  313. {
  314. fieldtype: "Section Break"
  315. },
  316. {
  317. fieldname: "timezone",
  318. label: __("Time Zone"),
  319. placeholder: __('Select Time Zone'),
  320. fieldtype: "Select",
  321. reqd: 1,
  322. },
  323. { fieldtype: "Column Break" },
  324. {
  325. fieldname: "currency",
  326. label: __("Currency"),
  327. placeholder: __('Select Currency'),
  328. fieldtype: "Select",
  329. reqd: 1,
  330. }
  331. ],
  332. onload: function (slide) {
  333. if (frappe.setup.data.regional_data) {
  334. this.setup_fields(slide);
  335. } else {
  336. frappe.setup.utils.load_regional_data(slide, this.setup_fields);
  337. }
  338. if (!slide.get_value("language")) {
  339. let session_language = frappe.setup.utils.get_language_name_from_code(frappe.boot.lang || navigator.language) || "English";
  340. let language_field = slide.get_field("language");
  341. language_field.set_input(session_language);
  342. if (!frappe.setup._from_load_messages) {
  343. language_field.$input.trigger("change");
  344. }
  345. delete frappe.setup._from_load_messages;
  346. moment.locale("en");
  347. }
  348. frappe.setup.utils.bind_region_events(slide);
  349. frappe.setup.utils.bind_language_events(slide);
  350. },
  351. setup_fields: function (slide) {
  352. frappe.setup.utils.setup_region_fields(slide);
  353. frappe.setup.utils.setup_language_field(slide);
  354. },
  355. },
  356. {
  357. // Profile slide
  358. name: 'user',
  359. title: __("The First User: You"),
  360. icon: "fa fa-user",
  361. fields: [
  362. {
  363. "fieldtype": "Attach Image", "fieldname": "attach_user_image",
  364. label: __("Attach Your Picture"), is_private: 0, align: 'center'
  365. },
  366. {
  367. "fieldname": "full_name", "label": __("Full Name"), "fieldtype": "Data",
  368. reqd: 1
  369. },
  370. {
  371. "fieldname": "email", "label": __("Email Address") + ' (' + __("Will be your login ID") + ')',
  372. "fieldtype": "Data", "options": "Email"
  373. },
  374. { "fieldname": "password", "label": __("Password"), "fieldtype": "Password" }
  375. ],
  376. onload: function (slide) {
  377. if (frappe.session.user !== "Administrator") {
  378. slide.form.fields_dict.email.$wrapper.toggle(false);
  379. slide.form.fields_dict.password.$wrapper.toggle(false);
  380. // remove password field
  381. delete slide.form.fields_dict.password;
  382. if (frappe.boot.user.first_name || frappe.boot.user.last_name) {
  383. slide.form.fields_dict.full_name.set_input(
  384. [frappe.boot.user.first_name, frappe.boot.user.last_name].join(' ').trim());
  385. }
  386. var user_image = frappe.get_cookie("user_image");
  387. var $attach_user_image = slide.form.fields_dict.attach_user_image.$wrapper;
  388. if (user_image) {
  389. $attach_user_image.find(".missing-image").toggle(false);
  390. $attach_user_image.find("img").attr("src", decodeURIComponent(user_image));
  391. $attach_user_image.find(".img-container").toggle(true);
  392. }
  393. delete slide.form.fields_dict.email;
  394. } else {
  395. slide.form.fields_dict.email.df.reqd = 1;
  396. slide.form.fields_dict.email.refresh();
  397. slide.form.fields_dict.password.df.reqd = 1;
  398. slide.form.fields_dict.password.refresh();
  399. frappe.setup.utils.load_user_details(slide, this.setup_fields);
  400. }
  401. },
  402. setup_fields: function (slide) {
  403. if (frappe.setup.data.full_name) {
  404. slide.form.fields_dict.full_name.set_input(frappe.setup.data.full_name);
  405. }
  406. if (frappe.setup.data.email) {
  407. let email = frappe.setup.data.email;
  408. slide.form.fields_dict.email.set_input(email);
  409. if (frappe.get_gravatar(email, 200)) {
  410. var $attach_user_image = slide.form.fields_dict.attach_user_image.$wrapper;
  411. $attach_user_image.find(".missing-image").toggle(false);
  412. $attach_user_image.find("img").attr("src", frappe.get_gravatar(email, 200));
  413. $attach_user_image.find(".img-container").toggle(true);
  414. }
  415. }
  416. },
  417. }
  418. ];
  419. frappe.setup.utils = {
  420. load_regional_data: function (slide, callback) {
  421. frappe.call({
  422. method: "frappe.geo.country_info.get_country_timezone_info",
  423. callback: function (data) {
  424. frappe.setup.data.regional_data = data.message;
  425. callback(slide);
  426. }
  427. });
  428. },
  429. load_user_details: function (slide, callback) {
  430. frappe.call({
  431. method: "frappe.desk.page.setup_wizard.setup_wizard.load_user_details",
  432. freeze: true,
  433. callback: function (r) {
  434. frappe.setup.data.full_name = r.message.full_name;
  435. frappe.setup.data.email = r.message.email;
  436. callback(slide);
  437. }
  438. });
  439. },
  440. setup_language_field: function (slide) {
  441. var language_field = slide.get_field("language");
  442. language_field.df.options = frappe.setup.data.lang.languages;
  443. language_field.set_options();
  444. },
  445. setup_region_fields: function (slide) {
  446. /*
  447. Set a slide's country, timezone and currency fields
  448. */
  449. let data = frappe.setup.data.regional_data;
  450. let country_field = slide.get_field('country');
  451. let translated_countries = [];
  452. Object.keys(data.country_info).sort().forEach(country => {
  453. translated_countries.push({
  454. label: __(country),
  455. value: country
  456. });
  457. });
  458. country_field.set_data(translated_countries);
  459. slide.get_input("currency")
  460. .empty()
  461. .add_options(
  462. frappe.utils.unique(
  463. $.map(data.country_info, opts => opts.currency).sort()
  464. )
  465. );
  466. slide.get_input("timezone").empty()
  467. .add_options(data.all_timezones);
  468. // set values if present
  469. if (frappe.wizard.values.country) {
  470. country_field.set_input(frappe.wizard.values.country);
  471. } else if (data.default_country) {
  472. country_field.set_input(data.default_country);
  473. }
  474. slide.get_field("currency").set_input(frappe.wizard.values.currency);
  475. slide.get_field("timezone").set_input(frappe.wizard.values.timezone);
  476. },
  477. bind_language_events: function (slide) {
  478. slide.get_input("language").unbind("change").on("change", function () {
  479. clearTimeout(slide.language_call_timeout);
  480. slide.language_call_timeout = setTimeout(() => {
  481. var lang = $(this).val() || "English";
  482. frappe._messages = {};
  483. frappe.call({
  484. method: "frappe.desk.page.setup_wizard.setup_wizard.load_messages",
  485. freeze: true,
  486. args: {
  487. language: lang
  488. },
  489. callback: function () {
  490. frappe.setup._from_load_messages = true;
  491. frappe.wizard.refresh_slides();
  492. }
  493. });
  494. }, 500);
  495. });
  496. },
  497. get_language_name_from_code: function (language_code) {
  498. return frappe.setup.data.lang.codes_to_names[language_code] || "English";
  499. },
  500. bind_region_events: function (slide) {
  501. /*
  502. Bind a slide's country, timezone and currency fields
  503. */
  504. slide.get_input("country").on("change", function () {
  505. var country = slide.get_input("country").val();
  506. var $timezone = slide.get_input("timezone");
  507. var data = frappe.setup.data.regional_data;
  508. $timezone.empty();
  509. if (!country) return;
  510. // add country specific timezones first
  511. const timezone_list = data.country_info[country].timezones || [];
  512. $timezone.add_options(timezone_list.sort());
  513. slide.get_field("currency").set_input(data.country_info[country].currency);
  514. slide.get_field("currency").$input.trigger("change");
  515. // add all timezones at the end, so that user has the option to change it to any timezone
  516. $timezone.add_options(data.all_timezones);
  517. slide.get_field("timezone").set_input($timezone.val());
  518. // temporarily set date format
  519. frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
  520. || "dd-mm-yyyy");
  521. });
  522. slide.get_input("currency").on("change", function () {
  523. var currency = slide.get_input("currency").val();
  524. if (!currency) return;
  525. frappe.model.with_doc("Currency", currency, function () {
  526. frappe.provide("locals.:Currency." + currency);
  527. var currency_doc = frappe.model.get_doc("Currency", currency);
  528. var number_format = currency_doc.number_format;
  529. if (number_format === "#.###") {
  530. number_format = "#.###,##";
  531. } else if (number_format === "#,###") {
  532. number_format = "#,###.##";
  533. }
  534. frappe.boot.sysdefaults.number_format = number_format;
  535. locals[":Currency"][currency] = $.extend({}, currency_doc);
  536. });
  537. });
  538. },
  539. };