Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

424 lignes
12 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import frappe, json, os
  4. from frappe.utils import strip, cint
  5. from frappe.translate import (set_default_language, get_dict, send_translations)
  6. from frappe.geo.country_info import get_country_info
  7. from frappe.utils.password import update_password
  8. from . import install_fixtures
  9. def get_setup_stages(args):
  10. # App setup stage functions should not include frappe.db.commit
  11. # That is done by frappe after successful completion of all stages
  12. stages = [
  13. {
  14. 'status': 'Updating global settings',
  15. 'fail_msg': 'Failed to update global settings',
  16. 'tasks': [
  17. {
  18. 'fn': update_global_settings,
  19. 'args': args,
  20. 'fail_msg': 'Failed to update global settings'
  21. }
  22. ]
  23. }
  24. ]
  25. stages += get_stages_hooks(args) + get_setup_complete_hooks(args)
  26. stages.append({
  27. # post executing hooks
  28. 'status': 'Wrapping up',
  29. 'fail_msg': 'Failed to complete setup',
  30. 'tasks': [
  31. {
  32. 'fn': run_post_setup_complete,
  33. 'args': args,
  34. 'fail_msg': 'Failed to complete setup'
  35. }
  36. ]
  37. })
  38. return stages
  39. @frappe.whitelist()
  40. def setup_complete(args):
  41. """Calls hooks for `setup_wizard_complete`, sets home page as `desktop`
  42. and clears cache. If wizard breaks, calls `setup_wizard_exception` hook"""
  43. # Setup complete: do not throw an exception, let the user continue to desk
  44. if cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
  45. return {'status': 'ok'}
  46. args = parse_args(args)
  47. stages = get_setup_stages(args)
  48. is_background_task = frappe.conf.get('trigger_site_setup_in_background')
  49. if is_background_task:
  50. process_setup_stages.enqueue(stages=stages, user_input=args, is_background_task=True)
  51. return {'status': 'registered'}
  52. else:
  53. return process_setup_stages(stages, args)
  54. @frappe.task()
  55. def process_setup_stages(stages, user_input, is_background_task=False):
  56. try:
  57. frappe.flags.in_setup_wizard = True
  58. current_task = None
  59. for idx, stage in enumerate(stages):
  60. frappe.publish_realtime('setup_task', {"progress": [idx, len(stages)],
  61. "stage_status": stage.get('status')}, user=frappe.session.user)
  62. for task in stage.get('tasks'):
  63. current_task = task
  64. task.get('fn')(task.get('args'))
  65. except Exception:
  66. handle_setup_exception(user_input)
  67. if not is_background_task:
  68. return {'status': 'fail', 'fail': current_task.get('fail_msg')}
  69. frappe.publish_realtime('setup_task',
  70. {'status': 'fail', "fail_msg": current_task.get('fail_msg')}, user=frappe.session.user)
  71. else:
  72. run_setup_success(user_input)
  73. if not is_background_task:
  74. return {'status': 'ok'}
  75. frappe.publish_realtime('setup_task', {"status": 'ok'}, user=frappe.session.user)
  76. finally:
  77. frappe.flags.in_setup_wizard = False
  78. def update_global_settings(args):
  79. if args.language and args.language != "English":
  80. set_default_language(get_language_code(args.lang))
  81. frappe.db.commit()
  82. frappe.clear_cache()
  83. update_system_settings(args)
  84. update_user_name(args)
  85. def run_post_setup_complete(args):
  86. disable_future_access()
  87. frappe.db.commit()
  88. frappe.clear_cache()
  89. def run_setup_success(args):
  90. for hook in frappe.get_hooks("setup_wizard_success"):
  91. frappe.get_attr(hook)(args)
  92. install_fixtures.install()
  93. def get_stages_hooks(args):
  94. stages = []
  95. for method in frappe.get_hooks("setup_wizard_stages"):
  96. stages += frappe.get_attr(method)(args)
  97. return stages
  98. def get_setup_complete_hooks(args):
  99. stages = []
  100. for method in frappe.get_hooks("setup_wizard_complete"):
  101. stages.append({
  102. 'status': 'Executing method',
  103. 'fail_msg': 'Failed to execute method',
  104. 'tasks': [
  105. {
  106. 'fn': frappe.get_attr(method),
  107. 'args': args,
  108. 'fail_msg': 'Failed to execute method'
  109. }
  110. ]
  111. })
  112. return stages
  113. def handle_setup_exception(args):
  114. frappe.db.rollback()
  115. if args:
  116. traceback = frappe.get_traceback()
  117. print(traceback)
  118. for hook in frappe.get_hooks("setup_wizard_exception"):
  119. frappe.get_attr(hook)(traceback, args)
  120. def update_system_settings(args):
  121. number_format = get_country_info(args.get("country")).get("number_format", "#,###.##")
  122. # replace these as float number formats, as they have 0 precision
  123. # and are currency number formats and not for floats
  124. if number_format=="#.###":
  125. number_format = "#.###,##"
  126. elif number_format=="#,###":
  127. number_format = "#,###.##"
  128. system_settings = frappe.get_doc("System Settings", "System Settings")
  129. system_settings.update({
  130. "country": args.get("country"),
  131. "language": get_language_code(args.get("language")) or 'en',
  132. "time_zone": args.get("timezone"),
  133. "float_precision": 3,
  134. 'date_format': frappe.db.get_value("Country", args.get("country"), "date_format"),
  135. 'time_format': frappe.db.get_value("Country", args.get("country"), "time_format"),
  136. 'number_format': number_format,
  137. 'enable_scheduler': 1 if not frappe.flags.in_test else 0,
  138. 'backup_limit': 3 # Default for downloadable backups
  139. })
  140. system_settings.save()
  141. def update_user_name(args):
  142. first_name, last_name = args.get('full_name', ''), ''
  143. if ' ' in first_name:
  144. first_name, last_name = first_name.split(' ', 1)
  145. if args.get("email"):
  146. if frappe.db.exists('User', args.get('email')):
  147. # running again
  148. return
  149. args['name'] = args.get("email")
  150. _mute_emails, frappe.flags.mute_emails = frappe.flags.mute_emails, True
  151. doc = frappe.get_doc({
  152. "doctype":"User",
  153. "email": args.get("email"),
  154. "first_name": first_name,
  155. "last_name": last_name
  156. })
  157. doc.flags.no_welcome_mail = True
  158. doc.insert()
  159. frappe.flags.mute_emails = _mute_emails
  160. update_password(args.get("email"), args.get("password"))
  161. elif first_name:
  162. args.update({
  163. "name": frappe.session.user,
  164. "first_name": first_name,
  165. "last_name": last_name
  166. })
  167. frappe.db.sql("""update `tabUser` SET first_name=%(first_name)s,
  168. last_name=%(last_name)s WHERE name=%(name)s""", args)
  169. if args.get("attach_user"):
  170. attach_user = args.get("attach_user").split(",")
  171. if len(attach_user)==3:
  172. filename, filetype, content = attach_user
  173. _file = frappe.get_doc({
  174. "doctype": "File",
  175. "file_name": filename,
  176. "attached_to_doctype": "User",
  177. "attached_to_name": args.get("name"),
  178. "content": content,
  179. "decode": True})
  180. _file.save()
  181. fileurl = _file.file_url
  182. frappe.db.set_value("User", args.get("name"), "user_image", fileurl)
  183. if args.get('name'):
  184. add_all_roles_to(args.get("name"))
  185. def parse_args(args):
  186. if not args:
  187. args = frappe.local.form_dict
  188. if isinstance(args, str):
  189. args = json.loads(args)
  190. args = frappe._dict(args)
  191. # strip the whitespace
  192. for key, value in args.items():
  193. if isinstance(value, str):
  194. args[key] = strip(value)
  195. return args
  196. def add_all_roles_to(name):
  197. user = frappe.get_doc("User", name)
  198. for role in frappe.db.sql("""select name from tabRole"""):
  199. if role[0] not in ["Administrator", "Guest", "All", "Customer", "Supplier", "Partner", "Employee"]:
  200. d = user.append("roles")
  201. d.role = role[0]
  202. user.save()
  203. def disable_future_access():
  204. frappe.db.set_default('desktop:home_page', 'workspace')
  205. frappe.db.set_value('System Settings', 'System Settings', 'setup_complete', 1)
  206. frappe.db.set_value('System Settings', 'System Settings', 'is_first_startup', 1)
  207. # Enable onboarding after install
  208. frappe.db.set_value('System Settings', 'System Settings', 'enable_onboarding', 1)
  209. if not frappe.flags.in_test:
  210. # remove all roles and add 'Administrator' to prevent future access
  211. page = frappe.get_doc('Page', 'setup-wizard')
  212. page.roles = []
  213. page.append('roles', {'role': 'Administrator'})
  214. page.flags.do_not_update_json = True
  215. page.flags.ignore_permissions = True
  216. page.save()
  217. @frappe.whitelist()
  218. def load_messages(language):
  219. """Load translation messages for given language from all `setup_wizard_requires`
  220. javascript files"""
  221. frappe.clear_cache()
  222. set_default_language(get_language_code(language))
  223. frappe.db.commit()
  224. m = get_dict("page", "setup-wizard")
  225. for path in frappe.get_hooks("setup_wizard_requires"):
  226. # common folder `assets` served from `sites/`
  227. js_file_path = os.path.abspath(frappe.get_site_path("..", *path.strip("/").split("/")))
  228. m.update(get_dict("jsfile", js_file_path))
  229. m.update(get_dict("boot"))
  230. send_translations(m)
  231. return frappe.local.lang
  232. @frappe.whitelist()
  233. def load_languages():
  234. language_codes = frappe.db.sql('select language_code, language_name from tabLanguage order by name', as_dict=True)
  235. codes_to_names = {}
  236. for d in language_codes:
  237. codes_to_names[d.language_code] = d.language_name
  238. return {
  239. "default_language": frappe.db.get_value('Language', frappe.local.lang, 'language_name') or frappe.local.lang,
  240. "languages": sorted(frappe.db.sql_list('select language_name from tabLanguage order by name')),
  241. "codes_to_names": codes_to_names
  242. }
  243. @frappe.whitelist()
  244. def load_country():
  245. from frappe.sessions import get_geo_ip_country
  246. return get_geo_ip_country(frappe.local.request_ip) if frappe.local.request_ip else None
  247. @frappe.whitelist()
  248. def load_user_details():
  249. return {
  250. "full_name": frappe.cache().hget("full_name", "signup"),
  251. "email": frappe.cache().hget("email", "signup")
  252. }
  253. @frappe.whitelist()
  254. def reset_is_first_startup():
  255. frappe.db.set_value('System Settings', 'System Settings', 'is_first_startup', 0)
  256. def prettify_args(args):
  257. # remove attachments
  258. for key, val in args.items():
  259. if isinstance(val, str) and "data:image" in val:
  260. filename = val.split("data:image", 1)[0].strip(", ")
  261. size = round((len(val) * 3 / 4) / 1048576.0, 2)
  262. args[key] = "Image Attached: '{0}' of size {1} MB".format(filename, size)
  263. pretty_args = []
  264. for key in sorted(args):
  265. pretty_args.append("{} = {}".format(key, args[key]))
  266. return pretty_args
  267. def email_setup_wizard_exception(traceback, args):
  268. if not frappe.conf.setup_wizard_exception_email:
  269. return
  270. pretty_args = prettify_args(args)
  271. message = """
  272. #### Traceback
  273. <pre>{traceback}</pre>
  274. ---
  275. #### Setup Wizard Arguments
  276. <pre>{args}</pre>
  277. ---
  278. #### Request Headers
  279. <pre>{headers}</pre>
  280. ---
  281. #### Basic Information
  282. - **Site:** {site}
  283. - **User:** {user}""".format(
  284. site=frappe.local.site,
  285. traceback=traceback,
  286. args="\n".join(pretty_args),
  287. user=frappe.session.user,
  288. headers=frappe.request.headers,
  289. )
  290. frappe.sendmail(recipients=frappe.conf.setup_wizard_exception_email,
  291. sender=frappe.session.user,
  292. subject="Setup failed: {}".format(frappe.local.site),
  293. message=message,
  294. delayed=False)
  295. def log_setup_wizard_exception(traceback, args):
  296. with open('../logs/setup-wizard.log', 'w+') as setup_log:
  297. setup_log.write(traceback)
  298. setup_log.write(json.dumps(args))
  299. def get_language_code(lang):
  300. return frappe.db.get_value('Language', {'language_name':lang})
  301. def enable_twofactor_all_roles():
  302. all_role = frappe.get_doc('Role',{'role_name':'All'})
  303. all_role.two_factor_auth = True
  304. all_role.save(ignore_permissions=True)
  305. def make_records(records, debug=False):
  306. from frappe import _dict
  307. from frappe.modules import scrub
  308. if debug:
  309. print("make_records: in DEBUG mode")
  310. # LOG every success and failure
  311. for record in records:
  312. doctype = record.get("doctype")
  313. condition = record.get('__condition')
  314. if condition and not condition():
  315. continue
  316. doc = frappe.new_doc(doctype)
  317. doc.update(record)
  318. # ignore mandatory for root
  319. parent_link_field = ("parent_" + scrub(doc.doctype))
  320. if doc.meta.get_field(parent_link_field) and not doc.get(parent_link_field):
  321. doc.flags.ignore_mandatory = True
  322. try:
  323. doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
  324. frappe.db.commit()
  325. except frappe.DuplicateEntryError as e:
  326. # print("Failed to insert duplicate {0} {1}".format(doctype, doc.name))
  327. # pass DuplicateEntryError and continue
  328. if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
  329. # make sure DuplicateEntryError is for the exact same doc and not a related doc
  330. frappe.clear_messages()
  331. else:
  332. raise
  333. except Exception as e:
  334. frappe.db.rollback()
  335. exception = record.get('__exception')
  336. if exception:
  337. config = _dict(exception)
  338. if isinstance(e, config.exception):
  339. config.handler()
  340. else:
  341. show_document_insert_error()
  342. else:
  343. show_document_insert_error()
  344. def show_document_insert_error():
  345. print("Document Insert Error")
  346. print(frappe.get_traceback())