You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 54 KiB

fix: Backport missing commits from version-13 to develop (#12845) * feat(logger): allow max_size and file_count params (cherry picked from commit 94116ae93c416958619e98aa47ad979481ec9dd1) * chore: Typo (cherry picked from commit f04ce5dd7a7957d46ef12d2d5e2216bb93e753e1) * fix(get_logger): allow None for module (cherry picked from commit a87531e1d0a808f69ce9d12a7e2fc9a9bb962641) * revert: "fix(image-with-blur): Set width, height in CSS" (bp #11170) (#11179) Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> * fix: Log site only if exists (cherry picked from commit 4b93f75eba40625bba5b83cca2470aa179e3f082) * fix: check if is_standard is "Yes" * fix: whitelist functions called through search widget (bp #11153) (#11187) (cherry picked from commit 645331799af44feb4629ccc50f214278579be200) Co-authored-by: Prssanna Desai <prssud@gmail.com> * feat: don't load user translation in make_dict_from_messages (bp #11120) (#11188) (cherry picked from commit 9b267027b5ce2c0674a4dd1cb476d5b80b873632) Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> * fix: Permission query in Dashboard Chart (bp #10981) (#11191) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> * fix: Dashboard fixes (bp #11186) (#11199) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> * test: Make email accounts in email_account test (bp #11139) (#11150) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * feat: Add validate_and_sanitize_search_inputs decorator (#11194) (#11204) * feat: Add validate_and_sanitize_search_inputs decorator Co-authored-by: Nabin Hait <nabinhait@gmail.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> * refactor: Move validate_and_sanitize_search_inputs to init * refactor: Move validate_and_sanitize_search_inputs to search.py * test: validate_and_sanitize_search_inputs decorator * chore: Add wrapt module * refactor: Use @wrapt to define validate_and_sanitize_search_inputs decorator * test: Add a case to make sure frappe.call works as well Co-authored-by: Nabin Hait <nabinhait@gmail.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> (cherry picked from commit cd1ab8e23c4a1de4bbd5137c34a864af53788237) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Validate and sanitise whitelisted search methods using decorator (#11197) (#11205) * fix: Validate and sanitize whitelisted search methods using decorator Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> * refactor: Replace decorator name Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> (cherry picked from commit 21a0cafe6209334906441c07c7f2e085afc82ade) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * chore: Add change log for v13.beta.4 (#11207) * bumped to version 13.0.0-beta.4 * fix: set samesite none for mobile (#11266) Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore: Fix Version * refactor: catch exceptions (cherry picked from commit 64f913fa36721f7477a03507f780ab6b03ff56be) * feat: catch exceptions on tokenizing values in get_value (cherry picked from commit 23975d5a70e24d3f90dd4c6e24231fa3d1752d10) * fix: contact not showing in form (bp #11363) (#11379) Co-authored-by: Afshan <afshan13k@gmail.com> * fix: boot if module is not present in desk (bp #11470) (#11471) (cherry picked from commit 40748d7fa88c18f64600081b986d95f3b5cf0721) Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> * fix: oauth code pointing to older desktop page * feat: Add module field in get_desk_sidebar_items (bp #11781) (#11782) (cherry picked from commit 00310ea783980229dd7708694a0f7369bf337f25) Co-authored-by: Sumit Bhanushali <sumit.bhanushali63@gmail.com> * fix: customize options in desk page rtl layout (cherry picked from commit e1316814a22cb9bb7ea37fd73194af2e61a7d5d2) * fix(Help Article): show 0 instead of undefined when value not set in dashboard (cherry picked from commit 6e04e0d6cb0bea862baa7c03ce17f00c34a77b1c) * fix: add colon between key and value (cherry picked from commit 218431372aa55c2d525a0984aea753983c5bba89) * fix: disable chart form condition (#11844) (cherry picked from commit 6e931e6b2a21417efb604c34bd48e007dfd19c33) * fix: shorten number card percentage stat (bp #11846) (#11848) (cherry picked from commit 8d70e3ec5b94ee5413e51845997506cf89d405d5) Co-authored-by: prssanna <prssud@gmail.com> * fix: Max slices for aggregate charts (bp #11808) (#11847) Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Navbar logo height and width (bp #11822) (#11849) (cherry picked from commit 25052afc9b6284e832f996eaaad41a61a6dba9f9) Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com> * fix: Email password prompt missing field name for submit button (bp #11840) (#11850) (cherry picked from commit 8aca710c850a7a169d45f2c4ebd0494d038167f0) Co-authored-by: Andy Zhu <andy007yan@gmail.com> * fix: Remove @ from relevance query (bp #11837) (#11851) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Show total text with value if first column is numeric (bp #11813) (#11852) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Show custom message for invalid login credentials (bp #11853) (#11855) Co-authored-by: Suraj Shetty <surajshetty3416@gmail.com> * fix: Update child values for existing rows (bp #11737) (#11860) Co-authored-by: Faris Ansari <netchamp.faris@gmail.com> * fix: Doctype query in create new shortcut (bp #11864) (#11865) Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> * fix(minor): minor fixes in document_naming.py and mandatory * fix: Total Row in Checkbox Column Reports * fix: Check of `get_datatable_options` exists in report settings * fix: enable disable save when navigating between docs (bp #11867) (#11876) Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> * chore: Add Release Notes for v13-beta-6 (#11882) * fix(pdf): PDF generation shouldn't fail in background jobs and tests (bp #11792) (#11886) Co-authored-by: Aditya Hase <aditya@adityahase.com> * fix: Remove scrolling on focusout event for touchscreen devices (bp #11888) (#11889) (cherry picked from commit 59c5437af7d5768fa3301dbf13e5378596fc3d3d) Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com> * fix: Clear cache after updating defaults manually (cherry picked from commit 823aec2d872314ca5add8e1e5c5ed81ba932e57e) * test: Added tests for bench remove-from-installed-apps (cherry picked from commit cb44492febb27deb167af374c4c76c5bde3f357d) # Conflicts: # frappe/tests/test_commands.py * chore: Resolve conflicts * chore: update version * bumped to version 13.0.0-beta.6 * refactor: Move get_build_version to utils.py (cherry picked from commit 01312889f8834facfa7ab8f111a4334db6c2b47b) * fix(website): Bust cache by passing build_version to link and script sources (cherry picked from commit 13175a8bc4615ec8eab31045413c2fe5c4052a04) * fix(website): Bust cache by passing build_version to link and script src (bp #11903) (#11909) Co-authored-by: Suraj Shetty <surajshetty3416@gmail.com> * fix: create auto_repeat field if docfield/custom field does not exist * feat: check if auto_repeat field is already present * chore: Bump version to 13.0.0-beta.7 * chore: Bump version to 13.0.0-beta.7 * fix(query-report): Show scrollbar for datatable Show report-wrapper before rendering datatable (cherry picked from commit e7fb4d0ef3bff2c6da3b77038a90d6b0849f031a) * fix(Email): use set_header to set Message-Id in header (cherry picked from commit 8c1187840094bc900553f84ebdc9bcb15e82e2c5) * chore: Bump version to 13.0.0-beta.8 * fix: Reload server script via patch (#12079) * fix(workflow): Update modified timestamp (bp #12093) (#12094) To sync changes in https://github.com/frappe/frappe/pull/11787 after migrate (cherry picked from commit 41a3b8c2842ff066194970cffb2fa8cb446e9246) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Link attachment in webform for new file (bp #12097) (bp #12102) (#12103) (cherry picked from commit 85d6a640358fa0337aaac405e71b7d14395fb298) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit a6705135dcb8679d3f82deacf1cd93476182d867) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * fix(patch): Remove Package Publish Tool doctypes (#12113) (#12124) (cherry picked from commit bc9d6cff2e2a3be17101f0957f771ab9e4f5b377) Co-authored-by: Faris Ansari <netchampfaris@users.noreply.github.com> * chore: Added change log (#12127) * fix: beta version tag from version-13-beta * bumped to version 13.0.0-beta.9 * fix: validate user permissions while user has select perms (#12287) * feat: ability to set default desk page (#12286) * feat: ability to set default desk page * chore: add description * fix: validate single default page * fix: error message Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> * fix: cannot extend a desk page with developer mode off (bp #12294) (#12295) Co-authored-by: Saqib <nextchamp.saqib@gmail.com> * chore: Add release notes for v13-beta-10 (#12296) * bumped to version 13.0.0-beta.10 * perf: optimise set_cors_headers (bp #12460) (#12462) (cherry picked from commit 68c6fc4a4417c6c36132ef25ec96af522d351dd6) Co-authored-by: Sagar Vora <sagar@resilient.tech> * fix: ignore notify update in patch execution * chore: Bump to v13.0.0-beta.11 * fix: allow on submit for child table not working (bp #12381) (#12472) Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: hasnain2808@gmail.com <hasnain2808@gmail.com> * Revert "chore: Bump to v13.0.0-beta.11" This reverts commit 0ccdd9317324786eca77e39a7b5a58bff8a6a4de. * bumped to version 13.0.0-beta.11 * fix: uncomment conditions that handle guest access for desk (cherry picked from commit aa8f7cc3c5470811d4851b7d4a80c8332d2aa541) * fix: don't show social login icon if not set (cherry picked from commit 91cb71f0c9f4d3469b99ccf160b62d8ebe5e76a2) * chore: update frappe charts (cherry picked from commit 89b9c29802e2f8c505a615971d06b8b6b4d9ca9d) * fix: missing bracket in get_inner_group_button (cherry picked from commit 882ea6e3d141a8144aa9f45a69f10598dd3f3670) * fix: toast red indicator icon (cherry picked from commit dcbce242a92d5fd924382f756436c8fe452288bb) * fix: web form buttons (cherry picked from commit 1937a1e558e7e5cb377369ad87fef279a711fe39) * fix: web form style (cherry picked from commit 72192e4a1028fae116ef0a63eb90a2cd3f5640ca) * refactor: common form styles (cherry picked from commit ec004cc1b750abc558e55f6bc719f846e941c211) * fix: commonify quill styles for website and desk (cherry picked from commit 1581c99da72ad0d3ccf8f6138e12d7c2d38b8960) * fix: more button in web list (cherry picked from commit 9386dbc1d316128d447223f771159a9e7ac6ade7) * fix: web form print button (cherry picked from commit 4246f8f3d8f3c901cb45ca4003028d7010c4622a) * fix: select field condition to check if value is selected (cherry picked from commit 97f7f6345bdcedc8c3a51b0af89cf6b3934f884f) * fix: use single statememt Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit 7d1e04fac3569653fcdb3cb99720bda3ad0728b8) * feat: Client Script for List views * fix: remove print statement * fix: rename field to simply "view" * fix: List view formatters should override standard formatters * fix: Don't add child table button for List script * fix: DocType and Apply To should be set only once * fix: Hide Apply To for Single doctypes * fix: Reset form boilerplate if view is List * chore(sider): add sider default config for flake8 + ignore tab errors (W191) (bp #12587) (#12598) Co-authored-by: Mohammad Hasnain Mohsin Rajan <hasnain2808@gmail.com> * test: encoding @ since we encode each url (bp #12567) (#12599) Co-authored-by: hasnain2808@gmail.com <hasnain2808@gmail.com> * fix: Average Chart compute inaccurate average (bp #12254) (#12595) Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: shariquerik <sharique.rik@gmail.com> Co-authored-by: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Co-authored-by: prssanna <prssud@gmail.com> Co-authored-by: Suraj Shetty <surajshetty3416@gmail.com> Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * feat: Add quoting parameter in the UnicodeWriter constructor (bp #12578) (#12602) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Co-authored-by: Britlog <info@britlog.com> * fix: Remove list.min.css from hook (bp #12606) (#12607) (cherry picked from commit cf2eacbb3a6a20eaec11d20b39d5f25ae03c5504) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Redesign Fixes (bp #12591) (#12611) Co-authored-by: prssanna <prssud@gmail.com> * chore: Update frappe-charts (bp #12617) (#12618) (cherry picked from commit 19a092feecd27660712ae1609d0a84fb585be517) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * revert: "fix: include space and tab in special characters to match to encode url" (bp #12619) (#12620) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit 67f34644f701e6672fa3f1e204836e77f924a29c) Co-authored-by: Prssanna Desai <prssud@gmail.com> * fix: Double signature in emails (bp #12520) (#12622) Co-authored-by: pateljannat <pateljannat2308@gmail.com> * revert: "test: encoding @ since we encode each url (bp #12567)" (#12626) This reverts commit 907d418cfdfc04a0891be1ce86522d462b8fd9ef. * fix: include space and tab in special characters to match to encode url (bp #12632) (#12634) (cherry picked from commit 6827b6b3a083a4a48f94dc754cab012aab98313e) Co-authored-by: prssanna <prssud@gmail.com> * fix: Auth failing (bp #12637) (#12638) Co-authored-by: leela <leela.vadlamudi@gmail.com> * fix: Remove 'ignore_user_type' from user filter (cherry picked from commit d9bb0c7a9931393c37d1befdf944d3ebdfe16218) * feat: allowed the auto name in Customize Form (cherry picked from commit 2e70c1c77cb88f7a9b87a3728ebc16612d74df0f) * feat: allowed the auto name in Customize Form (cherry picked from commit 607dcce21bfccca2252b597d3f045d8cc44bdf3a) * fix: Make scrolling separate for sidebar and main content (cherry picked from commit 6e22902ae14026ff4a2cf8c9201ad89640ed1fbc) * fix: Mobile view for workspace (cherry picked from commit 228cbb1a17a82ba94c9384ac2d8770a7530b4140) * fix(Workspace): Remove forced scrollbar (bp #12655) (#12656) (cherry picked from commit 8be8d4ba554563b4aec5b27b47b0c22580428ad9) Co-authored-by: Aditya Hase <aditya@adityahase.com> * fix: Login with username fails (bp #12658) (#12663) With the recent refactoring we missed to include username check in auth flow. Added test cases along with the fix. (cherry picked from commit 29840260352649c274c642c8e0677379a8258d86) Co-authored-by: Leela vadlamudi <leela@users.noreply.github.com> * feat: flag to delete docs permanently * feat: flag to delete attached files permanently * fix: remove redundant code * fix: auto delete prepared reports permanently * chore: update docstring for delete_doc * fix: update description for prepared_report_expiry_period - remove duplicate field * chore: Remove unused code (bp #12671) (#12675) (cherry picked from commit c4b0f54943696fb7b4534f9eb18ea65a4c97ae69) Co-authored-by: Sagar Vora <sagar@resilient.tech> * fix: Hide tooltip on route change (bp #12579) (#12684) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Format currency values in dashboard chart (cherry picked from commit 8fcee4e5f8d69ffbe1b5da606cdd5024a20bc2b1) * fix: missing help links in navbar help dropdown (bp #12657) (#12691) Co-authored-by: prssanna <prssud@gmail.com> * fix: Typography for markdown content (bp #12692) (#12698) Co-authored-by: Faris Ansari <netchamp.faris@gmail.com> * fix: Always validate file URLs (bp #12685) (#12704) * feat: validate file urls * fix: validate file url while downloading content * fix: cleaner validation * fix: added separate validation for web URLs Co-authored-by: Saurabh <saurabh6790@gmail.com> Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit bbeb24123230593d7b5b237dd0eb0e21f9248f85) Co-authored-by: Sagar Vora <sagar@resilient.tech> * fix: Read only mode for color input - fix style color readonly field - fix case where color control is hidden (cherry picked from commit 0d20d1a5d55104c07e3003d09814968ecd2281a4) * fix: Set color control as Data while adding filter (cherry picked from commit b22e5ddcb2d91c8b2fc69f6caefa5be052081219) * fix: Add version link to version content (cherry picked from commit f202953654b42e34c32fe5240ed6cf68ea5186c6) * fix: awesomplete style in page form (cherry picked from commit 1c0e5c20c9b5509688f6f11980356411619b9e79) * fix: render info type comments on form timeline (cherry picked from commit e286cbfc8bcc830096c53c853b3eb5f5c78ef735) * fix: toggle modal scroll for field select in modal (cherry picked from commit 64d2d99ba8b1eac311c3bd63ab7634cfa95a6ba7) * fix: don't show apply filter button if no filter button (cherry picked from commit ab65f7a7f050bc823b14472791e555a72aec8701) * fix: check if report result exists before formatting (cherry picked from commit c0e949e8093255101f2f4b0592c696e8f0f10f04) * feat: don't render filters, sort, add doc button for dashbaord view (cherry picked from commit e22542a2b143f797fb1600897d57d50b1e07923b) * style: fix formatting (cherry picked from commit 7fe570c297eb5cbbc46acde82637138db2251fe4) * fix: render map view in Views menu (cherry picked from commit 8af1616656b18b02b5d891ec4ab3cdb388a1e662) * fix: translation syntax (cherry picked from commit a638618788612825c0ef526a1204f6c6025a877e) * style: Remove unnecessary space (cherry picked from commit fdb94a5177112f345ea2629ee63e6ce003372d58) * fix: pull modules from Module Def instead of desktop config file (bp #12631) (#12723) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit 875056bb15233b03645e535e8427c05201398932) Co-authored-by: Prssanna Desai <prssud@gmail.com> * fix: System Notification fix (bp #12719) (#12724) (cherry picked from commit 93e6cc4ac129a6880ebd09f1d7c02ae35101e1ab) Co-authored-by: prssanna <prssud@gmail.com> * fix: enable fetch from for Check fieldtype (bp #12727) (#12728) (cherry picked from commit 8d3e0c244027c3edb0574465932240707a659818) Co-authored-by: prssanna <prssud@gmail.com> * fix(refactor): lockdown frappe.desk.reportview (cherry picked from commit a2ffea53f278d6101ec430bd7c026ef0c9204226) * fix(minor): fieldnames can't have commas (cherry picked from commit 511a5ddc4bbb9bf3652b75c7f1ba644be6ac2d7e) * fix(minor): functions must end with bracket (cherry picked from commit 0c995940c126ba8685111bbba5d2b2d3b5249de2) * fix(minor): handle as fieldnames (cherry picked from commit 0def97543b2d16c75acb9449d7558a4ced39b8c1) * fix(minor): make group_by validation tighter (cherry picked from commit c9b367933a3de7e587acd12060a6da53cce4de3f) * fix(report): move count, aggregation to serverside (cherry picked from commit 6d978a1df0b7c7cbebf244fa134a7fde6ad88a79) * fix(minor): disallow comments in fields (cherry picked from commit f1f64772a5bfae07583c8a9217f6d895ba46c6af) * fix(minor): lockdown frappe.client.get_list (cherry picked from commit 2248c6c410d8cd9db18e6bc71942a3e42d9af3d8) * fix(minor): tests and linting (cherry picked from commit d959fc7310fc0315d7983a4eb919a8bd69b741e1) * fix(minor): return value in reportview.py (cherry picked from commit 1aa8adadcb7eae05b452f78ef5d30cfea8840213) * fix(minor): tests (cherry picked from commit 868228bdea0b043402d53223685a28eaa2760980) * fix(minor): tests (cherry picked from commit 82399ddfba3d03b08491aa82d23aec45df9e5663) * fix(reportview): test (cherry picked from commit 337bdc976c2a2c60c50b0924c0d9fad0d8935c6a) * Update frappe/desk/reportview.py Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit 375cdea1458ee6ef24d76b3120d4378c36312d55) * fix: first column padding in datatable (bp #12733) (#12734) (cherry picked from commit 1c3688e0ed8524888f83fb267901d18cf02a5046) Co-authored-by: Prssanna Desai <prssud@gmail.com> * fix: copy paste rows more than 50 (one page) (bp #12548) (#12736) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Co-authored-by: Fisher Yu <12823863+szufisher@users.noreply.github.com> * refactor: Grid paste code (bp #12633) (#12737) Co-authored-by: Suraj Shetty <surajshetty3416@gmail.com> * fix: Miscellaneous changes (bp #12730) (#12738) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * chore: add semicolon * fix: use pathname on window.location * fix: checkbox bleed (cherry picked from commit b12714ae20f4e23ee8c1fa14b1b122db6a3efb2b) * fix: Make authentication check mandatory even in case of 2FA (cherry picked from commit 2227b910d3a18f47f2971fcbec894394c215c0c9) * refactor: Cleanup name confusion Using `delete_session` name for a function and also as a method name is confusing. Cleaned that up. (cherry picked from commit 9200192c1cbb2125894aa59150f361d031d55afb) * fix: Track 2FA OTP attempts using login tracker (cherry picked from commit 1f6f02fd5a7f80842094ce43fefc0e4bb70260d7) * test: OTP atempt tracker tests (cherry picked from commit 8fcb97ae31522a36d1fc2a10607aa0702ac9bcdb) * fix: change z-index even when list is empty (bp #12745) (#12747) (cherry picked from commit bbcfee9b488e7c5d57d46e592754095495caa74c) Co-authored-by: hasnain2808@gmail.com <hasnain2808@gmail.com> * fix: get frappe.client.get_value() to work when as_dict is false (bp #12739) (#12749) (cherry picked from commit 193dbec47cf7f31bd0bd94afe986f36a3d67aa7c) Co-authored-by: Walstan Baptista <38958184+walstanb@users.noreply.github.com> * fix: Return correct value from frappe.db.count (bp #12748) (#12750) (cherry picked from commit b279ca2c0f30cbaad6064e1e10aa1228c24363d4) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Chart style in Dark Mode (bp #12751) (#12752) (cherry picked from commit 8d07768a07beea9d07ac554c0a7c87c01bb6ca81) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Miscellaneous changes (bp #12754) (#12755) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: only focus on first input if form is new (bp #12758) (#12759) (cherry picked from commit 4fb544cb0a1cf373c775e59bd62f8b8d745ed4c0) Co-authored-by: prssanna <prssud@gmail.com> * fix: Grid validation - Use separate docfields list for each child table row to avoid cross row validation issue (cherry picked from commit be857c753bf9b721bd18dc881f67ef8d1f9e0e55) * test: Add a test case for grid validation (cherry picked from commit 108c728298bb55cf536b9fe21fa919b3e2fe0c6c) * chore: Remove change logs of beta releases * fix: users currently viewing a form (cherry picked from commit 0952e5d2d00e734ead14c78de590abaf9a952e2c) * chore: Version 13 change log * fix: also emit doc close event on doc change (cherry picked from commit 987b3e23ca5d13d4418be7c021ec8eccd18a066a) * fix: get_route_options_for_new_doc in link field - Used correct df where get_route_options_for_new_doc function is attached It stopped working after https://github.com/frappe/frappe/pull/12744 (cherry picked from commit e25fae3f8f925d9f82caaf1c9b9b9bf0094cc132) * fix: get_count API endpoint Make get_count work with join queries (cherry picked from commit 566f8ba12af9f9acc11839f1478cf7b7eb3d6e17) * fix: fix patch to pluralize list view setting (cherry picked from commit 872151f32e0fe04340da722e10a0910019c0166e) * fix: list view setting patch (cherry picked from commit 86ddc1b2a3aa849eb83e4f3e5e457d01fedde1dc) * chore: Bump version to v13.0.0 * fix: Reload website_theme_ignore_app before Website Theme (cherry picked from commit aaea55ed7de6a0b7adca469d19bc5abf9706b46f) * fix: Check if df.options exists before setting docfields Fixes: https://github.com/frappe/frappe/issues/12793 (cherry picked from commit a71066f3a42f0888e967f51e44886f7db9c4dd8f) * chore: Bump version to v13.0.1 * fix: Handle exception while building version comment (bp #12801) (#12802) (cherry picked from commit 13e7f453fcc70e90fd5dfff4d81bc3ddddc2b1d5) Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> * fix: Patch fixes for v13 upgrade (#12806) * fix: ZeroDivision error in progress (bp #12586) (#12808) Co-authored-by: Ankush Menat <ankush@iwebnotes.com> * bumped to version 13.0.2 Co-authored-by: Gavin D'souza <gavin18d@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Shivam Mishra <scmmishra@users.noreply.github.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Prssanna Desai <prssud@gmail.com> Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Co-authored-by: Saurabh <saurabh6790@gmail.com> Co-authored-by: Afshan <afshan13k@gmail.com> Co-authored-by: Sagar Vora <sagar@resilient.tech> Co-authored-by: Suraj Shetty <surajshetty3416@gmail.com> Co-authored-by: Sumit Bhanushali <sumit.bhanushali63@gmail.com> Co-authored-by: Abhishek Balam <abhishekbalam96@gmail.com> Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com> Co-authored-by: Andy Zhu <andy007yan@gmail.com> Co-authored-by: Faris Ansari <netchamp.faris@gmail.com> Co-authored-by: Rushabh Mehta <rmehta@gmail.com> Co-authored-by: marination <maricadsouza221197@gmail.com> Co-authored-by: Aditya Hase <aditya@adityahase.com> Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com> Co-authored-by: Saurabh <saurabh@erpnext.com> Co-authored-by: Faris Ansari <netchampfaris@users.noreply.github.com> Co-authored-by: Saqib <nextchamp.saqib@gmail.com> Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com> Co-authored-by: Administrator <Administrator> Co-authored-by: hasnain2808@gmail.com <hasnain2808@gmail.com> Co-authored-by: shariquerik <sharique.rik@gmail.com> Co-authored-by: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Co-authored-by: Britlog <info@britlog.com> Co-authored-by: pateljannat <pateljannat2308@gmail.com> Co-authored-by: leela <leela.vadlamudi@gmail.com> Co-authored-by: Anupam <anupamvns0099@gmail.com> Co-authored-by: Leela vadlamudi <leela@users.noreply.github.com> Co-authored-by: Fisher Yu <12823863+szufisher@users.noreply.github.com> Co-authored-by: Walstan Baptista <38958184+walstanb@users.noreply.github.com> Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
4 years ago
10 years ago
12 years ago
12 years ago
12 years ago
10 years ago
10 years ago
12 years ago
12 years ago
12 years ago
11 years ago
10 years ago
12 years ago
10 years ago
11 years ago
10 years ago
Postgres support for Frappe (#5919) * [start] postgres * [wip] started refactoring db_schema * Add psycopg2 to requirements.txt * Add support for Postgres SQL - Separate frameworkSQL, database, schema, setup_db file for mariaDB and postgres - WIP * Remove quotes from sql to make it compatible with postgres as well * Moved some code from db_schema to database.py * Move code from db_schema to schema.py Add other required refactoring * Add schema chages * Remove redundant code in file * Add invalid column name exception class to exceptions.py * Add back tick in query wherever needed and replace ifnull with coalesce * Update get_column_description code in database.py file * Remove a print statement * Add keys to get on_duplicate query * Add bactick wherever necessary - Remove db_schema.py file * Remove DATE_SUB as it is incompatible with postgres - Fix prepare_filter_condition * Add backtick and quotes wherever necessary - Move get_database_size to frappe.db namespace - fix some left out bugs and errors * Add code to create key and unique index - added mysql and posgres in their respective database.py * Add more bacticks in queries and fix some errors - Pass keys to on_duplicate_update method - Replace MONTH with EXTRACT function - Remove DATEDIFF and CURDATE usage * Cast state value to int in toggle_two_factor_auth - since two_factor_auth has the datatype of Int * Refactor - Replace Timediff with normal arithmetic operator - Add MAX_COLUMN_LENGTH - Remove Redundant code - Add regexp character constant - Move create_help_table to database.py - Add get_full_text_search_condition method - Inherit MariaDBTable from DBTable * Replace Database instance with get_db method * Move db_manager to separate file * Refactor - Remove some unwanted code - Separate alter table code for postgres and mysql - Replace data_type with column_type in database.py * Make fulltext search changes in global_search.py * Add empty string check * Add root_password to site config * Create cli command for postgres console * Move setup of help database to setup_db.py * Add get_database_list method * Fix exception handling - Replace bad_field handler with missing_column handler * Fix tests and sql queries * Fix import error * Fix typo db -> database * Fix error with make_table in help.py * Try test for postgres * Remove pyhton 2.7 version to try postgres travis test * Add test fixes * Add db_type to the config of test_site_postgres * Enable query debug to check the reason for travis fail * Add backticks to check if the test passes * Update travis.yml - Add postgres addon * Try appending 'd_' to hash for db_name - since postgres does not support dbname starting with a number * Try adding db_type for global help to make travis work * Add print statements to debug travis failure * Enable transaction and remove debug flag * Fix help table creation query (postgres) * Fix import issue * Add some checks to prevent errors - Some doctypes used to get called even before they are created * Try fixes * Update travis config * Fix create index for help table * Remove unused code * Fix queries and update travis config * Fix ifnull replace logic (regex) * Add query fixes and code cleanup * Fix typo - get_column_description -> get_table_columns_description * Fix tests - Replace double quotes in query with single quote * Replace psycopg2 with psycopg2-binary to avoid warnings - http://initd.org/psycopg/docs/install.html#binary-install-from-pypi * Add multisql api * Add few multisql queries * Remove print statements * Remove get_fulltext_search_condition method and replace with multi query * Remove text slicing in create user * Set default for 'values' argument in multisql * Fix incorrect queries and remove few debug flags - Fix multisql bug * Force delete user to fix test - Fix Import error - Fix incorrect query * Fix query builder bug * Fix bad query * Fix query (minor) * Convert boolean text to int since is_private has datatype of int - Some query changes like removed double quotes and replace with interpolated string to pass multiple value pass in one of the query * Extend database class from an object to support python 2 * Fix query - Add quotes around value passed to the query for variable comparision * Try setting host_name for each test site - To avoid "RemoteDisconnected" error while testing data migration test - Update travis.yml to add hosts - Remove unwanted commit in setup_help_database * Set site hostname to data migration connector (in test file) - To connect the same site host * Fix duplicate entry issue - the problem is in naming series file. In previous commits I unknowingly changed a part of a series query due to which series were not getting reset * Replace few sql queries with orm methods * Fix codacy * Fix 'Doctype Sessions not found' issue * Fix bugs induced during codacy fixes * Fix Notification Test - Use ORM instead of raw sql * Set Date fallback value to 0001-01-01 - 0000-00-00 is invalid date in Postgres - 0001-01-01 works in both * Fix date filter method * Replace double quotes with single quote for literal value * Remove print statement * Replace double quotes with single * Fix tests - Replace few raw sql with ORM * Separate query for postgres - update_fields_to_fetch_query * Fix tests - replace locate with strpos for postgres * Fix tests - Skip test for datediff - convert bytes to str in escape method * Remove TestBot * Skip fieldname extraction * Replace docshare raw sql with ORM * Fix typo * Fix ancestor query test * Fix test data migration * Remove hardcoded hostname * Add default option and option list for db_type * Remove frappe.async module * Remove a debug flag from test * Fix codacy * fix import issue * Convert classmethod to static method * Convert few instance methods to static methods * Remove some unused imports * Fix codacy - Add exception type - Replace few instance methods with static methods - Remove unsued import * Fix codacy * Remove unused code * Remove some unused codes - Convert some instance methods to static function * Fix a issue with query modification * Fix add_index query * Fix query * Fix update_auth patch * Fix a issue with exception handling * Add try catch to a reload_doc * Add try-catch to file_manager_hook patch * import update_gravatar to set_user_gravatar patch * Undo all the wrong patch fixes * Fix db_setup code 😪 - previously it was not restoring db from source SQL which is why few old patched were breaking (because they were getting different schema structure) * Fix typo ! * Fix exception(is_missing_column) handling * Add deleted code - This code is only used in a erpnext patch. Can be moved to that patch file * Fix codacy * Replace a mariadb specific function in a query used in validate_series * Remove a debug flag * Revert changes (rename_parent_and_child) * Fix validate_one_root method * Fix date format issue * Fix codacy - Disable a pylint for variable argument warning - Convert an instance method to static method * Add bandit.yml The Codacy seems to use Bandit which generates warning for every subprocess import and its usage during pytest Since we have carefully used subprocess (avoided user input), warnings needs to be avoided. This can be removed if we have any alternative for subprocess usage. * Skip start_process_with_partial_path check * Fix typo * Add python 2.7 test * Move python versions in travis.yml * Add python versions to jobs * Overwrite python version inheritance for postgres in travis.yml * Add quotes around python version in .travis.yml * Add quotes around the name of the job * Try a travis fix * Try .travis.yml fix * Import missing subprocess * Refactor travis.yml * Refactor travis.yml - move install and tests commands to separate files - Use matrix to build combination of python version and db type * Make install.sh and run-tests.sh executable * Add sudo required to travis.yml to allow sudo cmmands in shell files * Load nvm * Remove verbose flag from scripts * Remove command-trace-print flag * Change to build dir in before script * Add absolute path for scripts * Fix tests * Fix typo * Fix codacy - fixes - "echo won't expand escape sequences." warning * Append (_) underscore instead of 'd' for db_name * Remove printf and use mysql execute flag
6 years ago
Postgres support for Frappe (#5919) * [start] postgres * [wip] started refactoring db_schema * Add psycopg2 to requirements.txt * Add support for Postgres SQL - Separate frameworkSQL, database, schema, setup_db file for mariaDB and postgres - WIP * Remove quotes from sql to make it compatible with postgres as well * Moved some code from db_schema to database.py * Move code from db_schema to schema.py Add other required refactoring * Add schema chages * Remove redundant code in file * Add invalid column name exception class to exceptions.py * Add back tick in query wherever needed and replace ifnull with coalesce * Update get_column_description code in database.py file * Remove a print statement * Add keys to get on_duplicate query * Add bactick wherever necessary - Remove db_schema.py file * Remove DATE_SUB as it is incompatible with postgres - Fix prepare_filter_condition * Add backtick and quotes wherever necessary - Move get_database_size to frappe.db namespace - fix some left out bugs and errors * Add code to create key and unique index - added mysql and posgres in their respective database.py * Add more bacticks in queries and fix some errors - Pass keys to on_duplicate_update method - Replace MONTH with EXTRACT function - Remove DATEDIFF and CURDATE usage * Cast state value to int in toggle_two_factor_auth - since two_factor_auth has the datatype of Int * Refactor - Replace Timediff with normal arithmetic operator - Add MAX_COLUMN_LENGTH - Remove Redundant code - Add regexp character constant - Move create_help_table to database.py - Add get_full_text_search_condition method - Inherit MariaDBTable from DBTable * Replace Database instance with get_db method * Move db_manager to separate file * Refactor - Remove some unwanted code - Separate alter table code for postgres and mysql - Replace data_type with column_type in database.py * Make fulltext search changes in global_search.py * Add empty string check * Add root_password to site config * Create cli command for postgres console * Move setup of help database to setup_db.py * Add get_database_list method * Fix exception handling - Replace bad_field handler with missing_column handler * Fix tests and sql queries * Fix import error * Fix typo db -> database * Fix error with make_table in help.py * Try test for postgres * Remove pyhton 2.7 version to try postgres travis test * Add test fixes * Add db_type to the config of test_site_postgres * Enable query debug to check the reason for travis fail * Add backticks to check if the test passes * Update travis.yml - Add postgres addon * Try appending 'd_' to hash for db_name - since postgres does not support dbname starting with a number * Try adding db_type for global help to make travis work * Add print statements to debug travis failure * Enable transaction and remove debug flag * Fix help table creation query (postgres) * Fix import issue * Add some checks to prevent errors - Some doctypes used to get called even before they are created * Try fixes * Update travis config * Fix create index for help table * Remove unused code * Fix queries and update travis config * Fix ifnull replace logic (regex) * Add query fixes and code cleanup * Fix typo - get_column_description -> get_table_columns_description * Fix tests - Replace double quotes in query with single quote * Replace psycopg2 with psycopg2-binary to avoid warnings - http://initd.org/psycopg/docs/install.html#binary-install-from-pypi * Add multisql api * Add few multisql queries * Remove print statements * Remove get_fulltext_search_condition method and replace with multi query * Remove text slicing in create user * Set default for 'values' argument in multisql * Fix incorrect queries and remove few debug flags - Fix multisql bug * Force delete user to fix test - Fix Import error - Fix incorrect query * Fix query builder bug * Fix bad query * Fix query (minor) * Convert boolean text to int since is_private has datatype of int - Some query changes like removed double quotes and replace with interpolated string to pass multiple value pass in one of the query * Extend database class from an object to support python 2 * Fix query - Add quotes around value passed to the query for variable comparision * Try setting host_name for each test site - To avoid "RemoteDisconnected" error while testing data migration test - Update travis.yml to add hosts - Remove unwanted commit in setup_help_database * Set site hostname to data migration connector (in test file) - To connect the same site host * Fix duplicate entry issue - the problem is in naming series file. In previous commits I unknowingly changed a part of a series query due to which series were not getting reset * Replace few sql queries with orm methods * Fix codacy * Fix 'Doctype Sessions not found' issue * Fix bugs induced during codacy fixes * Fix Notification Test - Use ORM instead of raw sql * Set Date fallback value to 0001-01-01 - 0000-00-00 is invalid date in Postgres - 0001-01-01 works in both * Fix date filter method * Replace double quotes with single quote for literal value * Remove print statement * Replace double quotes with single * Fix tests - Replace few raw sql with ORM * Separate query for postgres - update_fields_to_fetch_query * Fix tests - replace locate with strpos for postgres * Fix tests - Skip test for datediff - convert bytes to str in escape method * Remove TestBot * Skip fieldname extraction * Replace docshare raw sql with ORM * Fix typo * Fix ancestor query test * Fix test data migration * Remove hardcoded hostname * Add default option and option list for db_type * Remove frappe.async module * Remove a debug flag from test * Fix codacy * fix import issue * Convert classmethod to static method * Convert few instance methods to static methods * Remove some unused imports * Fix codacy - Add exception type - Replace few instance methods with static methods - Remove unsued import * Fix codacy * Remove unused code * Remove some unused codes - Convert some instance methods to static function * Fix a issue with query modification * Fix add_index query * Fix query * Fix update_auth patch * Fix a issue with exception handling * Add try catch to a reload_doc * Add try-catch to file_manager_hook patch * import update_gravatar to set_user_gravatar patch * Undo all the wrong patch fixes * Fix db_setup code 😪 - previously it was not restoring db from source SQL which is why few old patched were breaking (because they were getting different schema structure) * Fix typo ! * Fix exception(is_missing_column) handling * Add deleted code - This code is only used in a erpnext patch. Can be moved to that patch file * Fix codacy * Replace a mariadb specific function in a query used in validate_series * Remove a debug flag * Revert changes (rename_parent_and_child) * Fix validate_one_root method * Fix date format issue * Fix codacy - Disable a pylint for variable argument warning - Convert an instance method to static method * Add bandit.yml The Codacy seems to use Bandit which generates warning for every subprocess import and its usage during pytest Since we have carefully used subprocess (avoided user input), warnings needs to be avoided. This can be removed if we have any alternative for subprocess usage. * Skip start_process_with_partial_path check * Fix typo * Add python 2.7 test * Move python versions in travis.yml * Add python versions to jobs * Overwrite python version inheritance for postgres in travis.yml * Add quotes around python version in .travis.yml * Add quotes around the name of the job * Try a travis fix * Try .travis.yml fix * Import missing subprocess * Refactor travis.yml * Refactor travis.yml - move install and tests commands to separate files - Use matrix to build combination of python version and db type * Make install.sh and run-tests.sh executable * Add sudo required to travis.yml to allow sudo cmmands in shell files * Load nvm * Remove verbose flag from scripts * Remove command-trace-print flag * Change to build dir in before script * Add absolute path for scripts * Fix tests * Fix typo * Fix codacy - fixes - "echo won't expand escape sequences." warning * Append (_) underscore instead of 'd' for db_name * Remove printf and use mysql execute flag
6 years ago
6 years ago
Postgres support for Frappe (#5919) * [start] postgres * [wip] started refactoring db_schema * Add psycopg2 to requirements.txt * Add support for Postgres SQL - Separate frameworkSQL, database, schema, setup_db file for mariaDB and postgres - WIP * Remove quotes from sql to make it compatible with postgres as well * Moved some code from db_schema to database.py * Move code from db_schema to schema.py Add other required refactoring * Add schema chages * Remove redundant code in file * Add invalid column name exception class to exceptions.py * Add back tick in query wherever needed and replace ifnull with coalesce * Update get_column_description code in database.py file * Remove a print statement * Add keys to get on_duplicate query * Add bactick wherever necessary - Remove db_schema.py file * Remove DATE_SUB as it is incompatible with postgres - Fix prepare_filter_condition * Add backtick and quotes wherever necessary - Move get_database_size to frappe.db namespace - fix some left out bugs and errors * Add code to create key and unique index - added mysql and posgres in their respective database.py * Add more bacticks in queries and fix some errors - Pass keys to on_duplicate_update method - Replace MONTH with EXTRACT function - Remove DATEDIFF and CURDATE usage * Cast state value to int in toggle_two_factor_auth - since two_factor_auth has the datatype of Int * Refactor - Replace Timediff with normal arithmetic operator - Add MAX_COLUMN_LENGTH - Remove Redundant code - Add regexp character constant - Move create_help_table to database.py - Add get_full_text_search_condition method - Inherit MariaDBTable from DBTable * Replace Database instance with get_db method * Move db_manager to separate file * Refactor - Remove some unwanted code - Separate alter table code for postgres and mysql - Replace data_type with column_type in database.py * Make fulltext search changes in global_search.py * Add empty string check * Add root_password to site config * Create cli command for postgres console * Move setup of help database to setup_db.py * Add get_database_list method * Fix exception handling - Replace bad_field handler with missing_column handler * Fix tests and sql queries * Fix import error * Fix typo db -> database * Fix error with make_table in help.py * Try test for postgres * Remove pyhton 2.7 version to try postgres travis test * Add test fixes * Add db_type to the config of test_site_postgres * Enable query debug to check the reason for travis fail * Add backticks to check if the test passes * Update travis.yml - Add postgres addon * Try appending 'd_' to hash for db_name - since postgres does not support dbname starting with a number * Try adding db_type for global help to make travis work * Add print statements to debug travis failure * Enable transaction and remove debug flag * Fix help table creation query (postgres) * Fix import issue * Add some checks to prevent errors - Some doctypes used to get called even before they are created * Try fixes * Update travis config * Fix create index for help table * Remove unused code * Fix queries and update travis config * Fix ifnull replace logic (regex) * Add query fixes and code cleanup * Fix typo - get_column_description -> get_table_columns_description * Fix tests - Replace double quotes in query with single quote * Replace psycopg2 with psycopg2-binary to avoid warnings - http://initd.org/psycopg/docs/install.html#binary-install-from-pypi * Add multisql api * Add few multisql queries * Remove print statements * Remove get_fulltext_search_condition method and replace with multi query * Remove text slicing in create user * Set default for 'values' argument in multisql * Fix incorrect queries and remove few debug flags - Fix multisql bug * Force delete user to fix test - Fix Import error - Fix incorrect query * Fix query builder bug * Fix bad query * Fix query (minor) * Convert boolean text to int since is_private has datatype of int - Some query changes like removed double quotes and replace with interpolated string to pass multiple value pass in one of the query * Extend database class from an object to support python 2 * Fix query - Add quotes around value passed to the query for variable comparision * Try setting host_name for each test site - To avoid "RemoteDisconnected" error while testing data migration test - Update travis.yml to add hosts - Remove unwanted commit in setup_help_database * Set site hostname to data migration connector (in test file) - To connect the same site host * Fix duplicate entry issue - the problem is in naming series file. In previous commits I unknowingly changed a part of a series query due to which series were not getting reset * Replace few sql queries with orm methods * Fix codacy * Fix 'Doctype Sessions not found' issue * Fix bugs induced during codacy fixes * Fix Notification Test - Use ORM instead of raw sql * Set Date fallback value to 0001-01-01 - 0000-00-00 is invalid date in Postgres - 0001-01-01 works in both * Fix date filter method * Replace double quotes with single quote for literal value * Remove print statement * Replace double quotes with single * Fix tests - Replace few raw sql with ORM * Separate query for postgres - update_fields_to_fetch_query * Fix tests - replace locate with strpos for postgres * Fix tests - Skip test for datediff - convert bytes to str in escape method * Remove TestBot * Skip fieldname extraction * Replace docshare raw sql with ORM * Fix typo * Fix ancestor query test * Fix test data migration * Remove hardcoded hostname * Add default option and option list for db_type * Remove frappe.async module * Remove a debug flag from test * Fix codacy * fix import issue * Convert classmethod to static method * Convert few instance methods to static methods * Remove some unused imports * Fix codacy - Add exception type - Replace few instance methods with static methods - Remove unsued import * Fix codacy * Remove unused code * Remove some unused codes - Convert some instance methods to static function * Fix a issue with query modification * Fix add_index query * Fix query * Fix update_auth patch * Fix a issue with exception handling * Add try catch to a reload_doc * Add try-catch to file_manager_hook patch * import update_gravatar to set_user_gravatar patch * Undo all the wrong patch fixes * Fix db_setup code 😪 - previously it was not restoring db from source SQL which is why few old patched were breaking (because they were getting different schema structure) * Fix typo ! * Fix exception(is_missing_column) handling * Add deleted code - This code is only used in a erpnext patch. Can be moved to that patch file * Fix codacy * Replace a mariadb specific function in a query used in validate_series * Remove a debug flag * Revert changes (rename_parent_and_child) * Fix validate_one_root method * Fix date format issue * Fix codacy - Disable a pylint for variable argument warning - Convert an instance method to static method * Add bandit.yml The Codacy seems to use Bandit which generates warning for every subprocess import and its usage during pytest Since we have carefully used subprocess (avoided user input), warnings needs to be avoided. This can be removed if we have any alternative for subprocess usage. * Skip start_process_with_partial_path check * Fix typo * Add python 2.7 test * Move python versions in travis.yml * Add python versions to jobs * Overwrite python version inheritance for postgres in travis.yml * Add quotes around python version in .travis.yml * Add quotes around the name of the job * Try a travis fix * Try .travis.yml fix * Import missing subprocess * Refactor travis.yml * Refactor travis.yml - move install and tests commands to separate files - Use matrix to build combination of python version and db type * Make install.sh and run-tests.sh executable * Add sudo required to travis.yml to allow sudo cmmands in shell files * Load nvm * Remove verbose flag from scripts * Remove command-trace-print flag * Change to build dir in before script * Add absolute path for scripts * Fix tests * Fix typo * Fix codacy - fixes - "echo won't expand escape sequences." warning * Append (_) underscore instead of 'd' for db_name * Remove printf and use mysql execute flag
6 years ago
6 years ago
6 years ago
6 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
12 years ago
10 years ago
10 years ago
Postgres support for Frappe (#5919) * [start] postgres * [wip] started refactoring db_schema * Add psycopg2 to requirements.txt * Add support for Postgres SQL - Separate frameworkSQL, database, schema, setup_db file for mariaDB and postgres - WIP * Remove quotes from sql to make it compatible with postgres as well * Moved some code from db_schema to database.py * Move code from db_schema to schema.py Add other required refactoring * Add schema chages * Remove redundant code in file * Add invalid column name exception class to exceptions.py * Add back tick in query wherever needed and replace ifnull with coalesce * Update get_column_description code in database.py file * Remove a print statement * Add keys to get on_duplicate query * Add bactick wherever necessary - Remove db_schema.py file * Remove DATE_SUB as it is incompatible with postgres - Fix prepare_filter_condition * Add backtick and quotes wherever necessary - Move get_database_size to frappe.db namespace - fix some left out bugs and errors * Add code to create key and unique index - added mysql and posgres in their respective database.py * Add more bacticks in queries and fix some errors - Pass keys to on_duplicate_update method - Replace MONTH with EXTRACT function - Remove DATEDIFF and CURDATE usage * Cast state value to int in toggle_two_factor_auth - since two_factor_auth has the datatype of Int * Refactor - Replace Timediff with normal arithmetic operator - Add MAX_COLUMN_LENGTH - Remove Redundant code - Add regexp character constant - Move create_help_table to database.py - Add get_full_text_search_condition method - Inherit MariaDBTable from DBTable * Replace Database instance with get_db method * Move db_manager to separate file * Refactor - Remove some unwanted code - Separate alter table code for postgres and mysql - Replace data_type with column_type in database.py * Make fulltext search changes in global_search.py * Add empty string check * Add root_password to site config * Create cli command for postgres console * Move setup of help database to setup_db.py * Add get_database_list method * Fix exception handling - Replace bad_field handler with missing_column handler * Fix tests and sql queries * Fix import error * Fix typo db -> database * Fix error with make_table in help.py * Try test for postgres * Remove pyhton 2.7 version to try postgres travis test * Add test fixes * Add db_type to the config of test_site_postgres * Enable query debug to check the reason for travis fail * Add backticks to check if the test passes * Update travis.yml - Add postgres addon * Try appending 'd_' to hash for db_name - since postgres does not support dbname starting with a number * Try adding db_type for global help to make travis work * Add print statements to debug travis failure * Enable transaction and remove debug flag * Fix help table creation query (postgres) * Fix import issue * Add some checks to prevent errors - Some doctypes used to get called even before they are created * Try fixes * Update travis config * Fix create index for help table * Remove unused code * Fix queries and update travis config * Fix ifnull replace logic (regex) * Add query fixes and code cleanup * Fix typo - get_column_description -> get_table_columns_description * Fix tests - Replace double quotes in query with single quote * Replace psycopg2 with psycopg2-binary to avoid warnings - http://initd.org/psycopg/docs/install.html#binary-install-from-pypi * Add multisql api * Add few multisql queries * Remove print statements * Remove get_fulltext_search_condition method and replace with multi query * Remove text slicing in create user * Set default for 'values' argument in multisql * Fix incorrect queries and remove few debug flags - Fix multisql bug * Force delete user to fix test - Fix Import error - Fix incorrect query * Fix query builder bug * Fix bad query * Fix query (minor) * Convert boolean text to int since is_private has datatype of int - Some query changes like removed double quotes and replace with interpolated string to pass multiple value pass in one of the query * Extend database class from an object to support python 2 * Fix query - Add quotes around value passed to the query for variable comparision * Try setting host_name for each test site - To avoid "RemoteDisconnected" error while testing data migration test - Update travis.yml to add hosts - Remove unwanted commit in setup_help_database * Set site hostname to data migration connector (in test file) - To connect the same site host * Fix duplicate entry issue - the problem is in naming series file. In previous commits I unknowingly changed a part of a series query due to which series were not getting reset * Replace few sql queries with orm methods * Fix codacy * Fix 'Doctype Sessions not found' issue * Fix bugs induced during codacy fixes * Fix Notification Test - Use ORM instead of raw sql * Set Date fallback value to 0001-01-01 - 0000-00-00 is invalid date in Postgres - 0001-01-01 works in both * Fix date filter method * Replace double quotes with single quote for literal value * Remove print statement * Replace double quotes with single * Fix tests - Replace few raw sql with ORM * Separate query for postgres - update_fields_to_fetch_query * Fix tests - replace locate with strpos for postgres * Fix tests - Skip test for datediff - convert bytes to str in escape method * Remove TestBot * Skip fieldname extraction * Replace docshare raw sql with ORM * Fix typo * Fix ancestor query test * Fix test data migration * Remove hardcoded hostname * Add default option and option list for db_type * Remove frappe.async module * Remove a debug flag from test * Fix codacy * fix import issue * Convert classmethod to static method * Convert few instance methods to static methods * Remove some unused imports * Fix codacy - Add exception type - Replace few instance methods with static methods - Remove unsued import * Fix codacy * Remove unused code * Remove some unused codes - Convert some instance methods to static function * Fix a issue with query modification * Fix add_index query * Fix query * Fix update_auth patch * Fix a issue with exception handling * Add try catch to a reload_doc * Add try-catch to file_manager_hook patch * import update_gravatar to set_user_gravatar patch * Undo all the wrong patch fixes * Fix db_setup code 😪 - previously it was not restoring db from source SQL which is why few old patched were breaking (because they were getting different schema structure) * Fix typo ! * Fix exception(is_missing_column) handling * Add deleted code - This code is only used in a erpnext patch. Can be moved to that patch file * Fix codacy * Replace a mariadb specific function in a query used in validate_series * Remove a debug flag * Revert changes (rename_parent_and_child) * Fix validate_one_root method * Fix date format issue * Fix codacy - Disable a pylint for variable argument warning - Convert an instance method to static method * Add bandit.yml The Codacy seems to use Bandit which generates warning for every subprocess import and its usage during pytest Since we have carefully used subprocess (avoided user input), warnings needs to be avoided. This can be removed if we have any alternative for subprocess usage. * Skip start_process_with_partial_path check * Fix typo * Add python 2.7 test * Move python versions in travis.yml * Add python versions to jobs * Overwrite python version inheritance for postgres in travis.yml * Add quotes around python version in .travis.yml * Add quotes around the name of the job * Try a travis fix * Try .travis.yml fix * Import missing subprocess * Refactor travis.yml * Refactor travis.yml - move install and tests commands to separate files - Use matrix to build combination of python version and db type * Make install.sh and run-tests.sh executable * Add sudo required to travis.yml to allow sudo cmmands in shell files * Load nvm * Remove verbose flag from scripts * Remove command-trace-print flag * Change to build dir in before script * Add absolute path for scripts * Fix tests * Fix typo * Fix codacy - fixes - "echo won't expand escape sequences." warning * Append (_) underscore instead of 'd' for db_name * Remove printf and use mysql execute flag
6 years ago
10 years ago
Python 3 compatible print statements (#3199) * changes print statements in file to python 3 compatible style using `__future__` * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * changes deprecated md5 module to hashlib * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements
8 years ago
10 years ago
10 years ago
10 years ago
7 years ago
7 years ago
7 years ago
7 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
6 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
Python 3 compatible print statements (#3199) * changes print statements in file to python 3 compatible style using `__future__` * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * changes deprecated md5 module to hashlib * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements * adds python 3 style for print statements
8 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
Data Migration Tool (for hub) (#4144) * migration tool * custom field for primary key added * foreign key and multiple linking F_key issue resolved * refined code * many-to-one mapping temp fix * added support for pre-process + cleaned up code * [various] fixes to setup wizard for developer mode, frappe.enqueue_doc, share with assign * Refactor data migration module * added migration for hub * Add "Skip errors" in data import tool * move db_set to document.py * Add Data Migration Run * Dynamic Migration ID * move run() from Mapping to Run * Push Deleted Documents * fixes * [migration] doc operation counts * insert and update instead of push in connection * fix count and total_pages, skip sync if total_pages is 0 * [migration] child tables * fix complete() * [page] remove required libs * Add sidebar.js, rename old sidebar.js to form_sidebar.js * [minor] get_empty_state fixes * svg in icon * remove image check * fix codacy * fix is_child_table check * [connector] add get_list() * Add test for Data Migration Run * fix test * truncate tabNote * fix test * sync todo with event to fix test * fix db count * [mapping] export Mapping to json * Add docs for Data Migration Tool * [migration] pull data as list, test case * [hub] remove mapping export to files * Pull refactor * [test] * Add comments * [mapping] exec in mapping formula * fix codacy * fix codacy * Remove exec for pre-process and post-process * Add pre and post process for Push * Remove formula * fixes * [refactor] add failed_log to pull, handle error in pull * [test] Push, pull, update * Fix codacy, fix insert_doc for pull * Set migration id on successful insert * fix update_doc * fix update_doc * method is a function * child table mapping * Refactor logging * fix update_doc again * fix hostname, password * update docs, minors * Remove assign_if_none * Remove error handling from connection methods * [refactor] Data migration run * Break push stages into methods * Migration run refactor - fix test - add separate fields for logging * fix codacy * fix hostname password * fix test
7 years ago
8 years ago
Data Migration Tool (for hub) (#4144) * migration tool * custom field for primary key added * foreign key and multiple linking F_key issue resolved * refined code * many-to-one mapping temp fix * added support for pre-process + cleaned up code * [various] fixes to setup wizard for developer mode, frappe.enqueue_doc, share with assign * Refactor data migration module * added migration for hub * Add "Skip errors" in data import tool * move db_set to document.py * Add Data Migration Run * Dynamic Migration ID * move run() from Mapping to Run * Push Deleted Documents * fixes * [migration] doc operation counts * insert and update instead of push in connection * fix count and total_pages, skip sync if total_pages is 0 * [migration] child tables * fix complete() * [page] remove required libs * Add sidebar.js, rename old sidebar.js to form_sidebar.js * [minor] get_empty_state fixes * svg in icon * remove image check * fix codacy * fix is_child_table check * [connector] add get_list() * Add test for Data Migration Run * fix test * truncate tabNote * fix test * sync todo with event to fix test * fix db count * [mapping] export Mapping to json * Add docs for Data Migration Tool * [migration] pull data as list, test case * [hub] remove mapping export to files * Pull refactor * [test] * Add comments * [mapping] exec in mapping formula * fix codacy * fix codacy * Remove exec for pre-process and post-process * Add pre and post process for Push * Remove formula * fixes * [refactor] add failed_log to pull, handle error in pull * [test] Push, pull, update * Fix codacy, fix insert_doc for pull * Set migration id on successful insert * fix update_doc * fix update_doc * method is a function * child table mapping * Refactor logging * fix update_doc again * fix hostname, password * update docs, minors * Remove assign_if_none * Remove error handling from connection methods * [refactor] Data migration run * Break push stages into methods * Migration run refactor - fix test - add separate fields for logging * fix codacy * fix hostname password * fix test
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824
  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. """
  4. Frappe - Low Code Open Source Framework in Python and JS
  5. Frappe, pronounced fra-pay, is a full stack, batteries-included, web
  6. framework written in Python and Javascript with MariaDB as the database.
  7. It is the framework which powers ERPNext. It is pretty generic and can
  8. be used to build database driven apps.
  9. Read the documentation: https://frappeframework.com/docs
  10. """
  11. import os, warnings
  12. _dev_server = os.environ.get('DEV_SERVER', False)
  13. if _dev_server:
  14. warnings.simplefilter('always', DeprecationWarning)
  15. warnings.simplefilter('always', PendingDeprecationWarning)
  16. from werkzeug.local import Local, release_local
  17. import sys, importlib, inspect, json
  18. import typing
  19. from past.builtins import cmp
  20. import click
  21. # Local application imports
  22. from .exceptions import *
  23. from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader)
  24. from .utils.lazy_loader import lazy_import
  25. # Lazy imports
  26. faker = lazy_import('faker')
  27. __version__ = '14.0.0-dev'
  28. __title__ = "Frappe Framework"
  29. local = Local()
  30. controllers = {}
  31. class _dict(dict):
  32. """dict like object that exposes keys as attributes"""
  33. def __getattr__(self, key):
  34. ret = self.get(key)
  35. if not ret and key.startswith("__"):
  36. raise AttributeError()
  37. return ret
  38. def __setattr__(self, key, value):
  39. self[key] = value
  40. def __getstate__(self):
  41. return self
  42. def __setstate__(self, d):
  43. self.update(d)
  44. def update(self, d):
  45. """update and return self -- the missing dict feature in python"""
  46. super(_dict, self).update(d)
  47. return self
  48. def copy(self):
  49. return _dict(dict(self).copy())
  50. def _(msg, lang=None, context=None):
  51. """Returns translated string in current lang, if exists.
  52. Usage:
  53. _('Change')
  54. _('Change', context='Coins')
  55. """
  56. from frappe.translate import get_full_dict
  57. from frappe.utils import strip_html_tags, is_html
  58. if not hasattr(local, 'lang'):
  59. local.lang = lang or 'en'
  60. if not lang:
  61. lang = local.lang
  62. non_translated_string = msg
  63. if is_html(msg):
  64. msg = strip_html_tags(msg)
  65. # msg should always be unicode
  66. msg = as_unicode(msg).strip()
  67. translated_string = ''
  68. if context:
  69. string_key = '{msg}:{context}'.format(msg=msg, context=context)
  70. translated_string = get_full_dict(lang).get(string_key)
  71. if not translated_string:
  72. translated_string = get_full_dict(lang).get(msg)
  73. # return lang_full_dict according to lang passed parameter
  74. return translated_string or non_translated_string
  75. def as_unicode(text, encoding='utf-8'):
  76. '''Convert to unicode if required'''
  77. if isinstance(text, str):
  78. return text
  79. elif text==None:
  80. return ''
  81. elif isinstance(text, bytes):
  82. return str(text, encoding)
  83. else:
  84. return str(text)
  85. def get_lang_dict(fortype, name=None):
  86. """Returns the translated language dict for the given type and name.
  87. :param fortype: must be one of `doctype`, `page`, `report`, `include`, `jsfile`, `boot`
  88. :param name: name of the document for which assets are to be returned."""
  89. from frappe.translate import get_dict
  90. return get_dict(fortype, name)
  91. def set_user_lang(user, user_language=None):
  92. """Guess and set user language for the session. `frappe.local.lang`"""
  93. from frappe.translate import get_user_lang
  94. local.lang = get_user_lang(user)
  95. # local-globals
  96. db = local("db")
  97. conf = local("conf")
  98. form = form_dict = local("form_dict")
  99. request = local("request")
  100. response = local("response")
  101. session = local("session")
  102. user = local("user")
  103. flags = local("flags")
  104. error_log = local("error_log")
  105. debug_log = local("debug_log")
  106. message_log = local("message_log")
  107. lang = local("lang")
  108. # This if block is never executed when running the code. It is only used for
  109. # telling static code analyzer where to find dynamically defined attributes.
  110. if typing.TYPE_CHECKING:
  111. from frappe.database.mariadb.database import MariaDBDatabase
  112. from frappe.database.postgres.database import PostgresDatabase
  113. db: typing.Union[MariaDBDatabase, PostgresDatabase]
  114. # end: static analysis hack
  115. def init(site, sites_path=None, new_site=False):
  116. """Initialize frappe for the current site. Reset thread locals `frappe.local`"""
  117. if getattr(local, "initialised", None):
  118. return
  119. if not sites_path:
  120. sites_path = '.'
  121. local.error_log = []
  122. local.message_log = []
  123. local.debug_log = []
  124. local.realtime_log = []
  125. local.flags = _dict({
  126. "currently_saving": [],
  127. "redirect_location": "",
  128. "in_install_db": False,
  129. "in_install_app": False,
  130. "in_import": False,
  131. "in_test": False,
  132. "mute_messages": False,
  133. "ignore_links": False,
  134. "mute_emails": False,
  135. "has_dataurl": False,
  136. "new_site": new_site
  137. })
  138. local.rollback_observers = []
  139. local.before_commit = []
  140. local.test_objects = {}
  141. local.site = site
  142. local.sites_path = sites_path
  143. local.site_path = os.path.join(sites_path, site)
  144. local.all_apps = None
  145. local.request_ip = None
  146. local.response = _dict({"docs":[]})
  147. local.task_id = None
  148. local.conf = _dict(get_site_config())
  149. local.lang = local.conf.lang or "en"
  150. local.lang_full_dict = None
  151. local.module_app = None
  152. local.app_modules = None
  153. local.system_settings = _dict()
  154. local.user = None
  155. local.user_perms = None
  156. local.session = None
  157. local.role_permissions = {}
  158. local.valid_columns = {}
  159. local.new_doc_templates = {}
  160. local.link_count = {}
  161. local.jenv = None
  162. local.jloader =None
  163. local.cache = {}
  164. local.document_cache = {}
  165. local.meta_cache = {}
  166. local.form_dict = _dict()
  167. local.session = _dict()
  168. local.dev_server = _dev_server
  169. setup_module_map()
  170. local.initialised = True
  171. def connect(site=None, db_name=None, set_admin_as_user=True):
  172. """Connect to site database instance.
  173. :param site: If site is given, calls `frappe.init`.
  174. :param db_name: Optional. Will use from `site_config.json`.
  175. :param set_admin_as_user: Set Administrator as current user.
  176. """
  177. from frappe.database import get_db
  178. if site:
  179. init(site)
  180. local.db = get_db(user=db_name or local.conf.db_name)
  181. if set_admin_as_user:
  182. set_user("Administrator")
  183. def connect_replica():
  184. from frappe.database import get_db
  185. user = local.conf.db_name
  186. password = local.conf.db_password
  187. if local.conf.different_credentials_for_replica:
  188. user = local.conf.replica_db_name
  189. password = local.conf.replica_db_password
  190. local.replica_db = get_db(host=local.conf.replica_host, user=user, password=password)
  191. # swap db connections
  192. local.primary_db = local.db
  193. local.db = local.replica_db
  194. def get_site_config(sites_path=None, site_path=None):
  195. """Returns `site_config.json` combined with `sites/common_site_config.json`.
  196. `site_config` is a set of site wide settings like database name, password, email etc."""
  197. config = {}
  198. sites_path = sites_path or getattr(local, "sites_path", None)
  199. site_path = site_path or getattr(local, "site_path", None)
  200. if sites_path:
  201. common_site_config = os.path.join(sites_path, "common_site_config.json")
  202. if os.path.exists(common_site_config):
  203. try:
  204. config.update(get_file_json(common_site_config))
  205. except Exception as error:
  206. click.secho("common_site_config.json is invalid", fg="red")
  207. print(error)
  208. if site_path:
  209. site_config = os.path.join(site_path, "site_config.json")
  210. if os.path.exists(site_config):
  211. try:
  212. config.update(get_file_json(site_config))
  213. except Exception as error:
  214. click.secho("{0}/site_config.json is invalid".format(local.site), fg="red")
  215. print(error)
  216. elif local.site and not local.flags.new_site:
  217. raise IncorrectSitePath("{0} does not exist".format(local.site))
  218. return _dict(config)
  219. def get_conf(site=None):
  220. if hasattr(local, 'conf'):
  221. return local.conf
  222. else:
  223. # if no site, get from common_site_config.json
  224. with init_site(site):
  225. return local.conf
  226. class init_site:
  227. def __init__(self, site=None):
  228. '''If site==None, initialize it for empty site ('') to load common_site_config.json'''
  229. self.site = site or ''
  230. def __enter__(self):
  231. init(self.site)
  232. return local
  233. def __exit__(self, type, value, traceback):
  234. destroy()
  235. def destroy():
  236. """Closes connection and releases werkzeug local."""
  237. if db:
  238. db.close()
  239. release_local(local)
  240. # memcache
  241. redis_server = None
  242. def cache():
  243. """Returns redis connection."""
  244. global redis_server
  245. if not redis_server:
  246. from frappe.utils.redis_wrapper import RedisWrapper
  247. redis_server = RedisWrapper.from_url(conf.get('redis_cache')
  248. or "redis://localhost:11311")
  249. return redis_server
  250. def get_traceback():
  251. """Returns error traceback."""
  252. from frappe.utils import get_traceback
  253. return get_traceback()
  254. def errprint(msg):
  255. """Log error. This is sent back as `exc` in response.
  256. :param msg: Message."""
  257. msg = as_unicode(msg)
  258. if not request or (not "cmd" in local.form_dict) or conf.developer_mode:
  259. print(msg)
  260. error_log.append({"exc": msg})
  261. def print_sql(enable=True):
  262. return cache().set_value('flag_print_sql', enable)
  263. def log(msg):
  264. """Add to `debug_log`.
  265. :param msg: Message."""
  266. if not request:
  267. if conf.get("logging") or False:
  268. print(repr(msg))
  269. debug_log.append(as_unicode(msg))
  270. def msgprint(msg, title=None, raise_exception=0, as_table=False, as_list=False, indicator=None, alert=False, primary_action=None, is_minimizable=None, wide=None):
  271. """Print a message to the user (via HTTP response).
  272. Messages are sent in the `__server_messages` property in the
  273. response JSON and shown in a pop-up / modal.
  274. :param msg: Message.
  275. :param title: [optional] Message title.
  276. :param raise_exception: [optional] Raise given exception and show message.
  277. :param as_table: [optional] If `msg` is a list of lists, render as HTML table.
  278. :param as_list: [optional] If `msg` is a list, render as un-ordered list.
  279. :param primary_action: [optional] Bind a primary server/client side action.
  280. :param is_minimizable: [optional] Allow users to minimize the modal
  281. :param wide: [optional] Show wide modal
  282. """
  283. from frappe.utils import strip_html_tags
  284. msg = safe_decode(msg)
  285. out = _dict(message=msg)
  286. def _raise_exception():
  287. if raise_exception:
  288. if flags.rollback_on_exception:
  289. db.rollback()
  290. import inspect
  291. if inspect.isclass(raise_exception) and issubclass(raise_exception, Exception):
  292. raise raise_exception(msg)
  293. else:
  294. raise ValidationError(msg)
  295. if flags.mute_messages:
  296. _raise_exception()
  297. return
  298. if as_table and type(msg) in (list, tuple):
  299. out.as_table = 1
  300. if as_list and type(msg) in (list, tuple) and len(msg) > 1:
  301. out.as_list = 1
  302. if flags.print_messages and out.message:
  303. print(f"Message: {strip_html_tags(out.message)}")
  304. if title:
  305. out.title = title
  306. if not indicator and raise_exception:
  307. indicator = 'red'
  308. if indicator:
  309. out.indicator = indicator
  310. if is_minimizable:
  311. out.is_minimizable = is_minimizable
  312. if alert:
  313. out.alert = 1
  314. if raise_exception:
  315. out.raise_exception = 1
  316. if primary_action:
  317. out.primary_action = primary_action
  318. if wide:
  319. out.wide = wide
  320. message_log.append(json.dumps(out))
  321. if raise_exception and hasattr(raise_exception, '__name__'):
  322. local.response['exc_type'] = raise_exception.__name__
  323. _raise_exception()
  324. def clear_messages():
  325. local.message_log = []
  326. def get_message_log():
  327. log = []
  328. for msg_out in local.message_log:
  329. log.append(json.loads(msg_out))
  330. return log
  331. def clear_last_message():
  332. if len(local.message_log) > 0:
  333. local.message_log = local.message_log[:-1]
  334. def throw(msg, exc=ValidationError, title=None, is_minimizable=None, wide=None, as_list=False):
  335. """Throw execption and show message (`msgprint`).
  336. :param msg: Message.
  337. :param exc: Exception class. Default `frappe.ValidationError`"""
  338. msgprint(msg, raise_exception=exc, title=title, indicator='red', is_minimizable=is_minimizable, wide=wide, as_list=as_list)
  339. def emit_js(js, user=False, **kwargs):
  340. if user == False:
  341. user = session.user
  342. publish_realtime('eval_js', js, user=user, **kwargs)
  343. def create_folder(path, with_init=False):
  344. """Create a folder in the given path and add an `__init__.py` file (optional).
  345. :param path: Folder path.
  346. :param with_init: Create `__init__.py` in the new folder."""
  347. from frappe.utils import touch_file
  348. if not os.path.exists(path):
  349. os.makedirs(path)
  350. if with_init:
  351. touch_file(os.path.join(path, "__init__.py"))
  352. def set_user(username):
  353. """Set current user.
  354. :param username: **User** name to set as current user."""
  355. local.session.user = username
  356. local.session.sid = username
  357. local.cache = {}
  358. local.form_dict = _dict()
  359. local.jenv = None
  360. local.session.data = _dict()
  361. local.role_permissions = {}
  362. local.new_doc_templates = {}
  363. local.user_perms = None
  364. def get_user():
  365. from frappe.utils.user import UserPermissions
  366. if not local.user_perms:
  367. local.user_perms = UserPermissions(local.session.user)
  368. return local.user_perms
  369. def get_roles(username=None):
  370. """Returns roles of current user."""
  371. if not local.session:
  372. return ["Guest"]
  373. import frappe.permissions
  374. return frappe.permissions.get_roles(username or local.session.user)
  375. def get_request_header(key, default=None):
  376. """Return HTTP request header.
  377. :param key: HTTP header key.
  378. :param default: Default value."""
  379. return request.headers.get(key, default)
  380. def sendmail(recipients=[], sender="", subject="No Subject", message="No Message",
  381. as_markdown=False, delayed=True, reference_doctype=None, reference_name=None,
  382. unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, add_unsubscribe_link=1,
  383. attachments=None, content=None, doctype=None, name=None, reply_to=None, queue_separately=False,
  384. cc=[], bcc=[], message_id=None, in_reply_to=None, send_after=None, expose_recipients=None,
  385. send_priority=1, communication=None, retry=1, now=None, read_receipt=None, is_notification=False,
  386. inline_images=None, template=None, args=None, header=None, print_letterhead=False, with_container=False):
  387. """Send email using user's default **Email Account** or global default **Email Account**.
  388. :param recipients: List of recipients.
  389. :param sender: Email sender. Default is current user or default outgoing account.
  390. :param subject: Email Subject.
  391. :param message: (or `content`) Email Content.
  392. :param as_markdown: Convert content markdown to HTML.
  393. :param delayed: Send via scheduled email sender **Email Queue**. Don't send immediately. Default is true
  394. :param send_priority: Priority for Email Queue, default 1.
  395. :param reference_doctype: (or `doctype`) Append as communication to this DocType.
  396. :param reference_name: (or `name`) Append as communication to this document name.
  397. :param unsubscribe_method: Unsubscribe url with options email, doctype, name. e.g. `/api/method/unsubscribe`
  398. :param unsubscribe_params: Unsubscribe paramaters to be loaded on the unsubscribe_method [optional] (dict).
  399. :param attachments: List of attachments.
  400. :param reply_to: Reply-To Email Address.
  401. :param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email.
  402. :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To.
  403. :param send_after: Send after the given datetime.
  404. :param expose_recipients: Display all recipients in the footer message - "This email was sent to"
  405. :param communication: Communication link to be set in Email Queue record
  406. :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id
  407. :param template: Name of html template from templates/emails folder
  408. :param args: Arguments for rendering the template
  409. :param header: Append header in email
  410. :param with_container: Wraps email inside a styled container
  411. """
  412. text_content = None
  413. if template:
  414. message, text_content = get_email_from_template(template, args)
  415. message = content or message
  416. if as_markdown:
  417. from frappe.utils import md_to_html
  418. message = md_to_html(message)
  419. if not delayed:
  420. now = True
  421. from frappe.email.doctype.email_queue.email_queue import QueueBuilder
  422. builder = QueueBuilder(recipients=recipients, sender=sender,
  423. subject=subject, message=message, text_content=text_content,
  424. reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, add_unsubscribe_link=add_unsubscribe_link,
  425. unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message,
  426. attachments=attachments, reply_to=reply_to, cc=cc, bcc=bcc, message_id=message_id, in_reply_to=in_reply_to,
  427. send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, queue_separately=queue_separately,
  428. communication=communication, read_receipt=read_receipt, is_notification=is_notification,
  429. inline_images=inline_images, header=header, print_letterhead=print_letterhead, with_container=with_container)
  430. # build email queue and send the email if send_now is True.
  431. builder.process(send_now=now)
  432. whitelisted = []
  433. guest_methods = []
  434. xss_safe_methods = []
  435. allowed_http_methods_for_whitelisted_func = {}
  436. def whitelist(allow_guest=False, xss_safe=False, methods=None):
  437. """
  438. Decorator for whitelisting a function and making it accessible via HTTP.
  439. Standard request will be `/api/method/[path.to.method]`
  440. :param allow_guest: Allow non logged-in user to access this method.
  441. :param methods: Allowed http method to access the method.
  442. Use as:
  443. @frappe.whitelist()
  444. def myfunc(param1, param2):
  445. pass
  446. """
  447. if not methods:
  448. methods = ['GET', 'POST', 'PUT', 'DELETE']
  449. def innerfn(fn):
  450. global whitelisted, guest_methods, xss_safe_methods, allowed_http_methods_for_whitelisted_func
  451. # get function from the unbound / bound method
  452. # this is needed because functions can be compared, but not methods
  453. method = None
  454. if hasattr(fn, '__func__'):
  455. method = fn
  456. fn = method.__func__
  457. whitelisted.append(fn)
  458. allowed_http_methods_for_whitelisted_func[fn] = methods
  459. if allow_guest:
  460. guest_methods.append(fn)
  461. if xss_safe:
  462. xss_safe_methods.append(fn)
  463. return method or fn
  464. return innerfn
  465. def is_whitelisted(method):
  466. from frappe.utils import sanitize_html
  467. is_guest = session['user'] == 'Guest'
  468. if method not in whitelisted or is_guest and method not in guest_methods:
  469. throw(_("Not permitted"), PermissionError)
  470. if is_guest and method not in xss_safe_methods:
  471. # strictly sanitize form_dict
  472. # escapes html characters like <> except for predefined tags like a, b, ul etc.
  473. for key, value in form_dict.items():
  474. if isinstance(value, str):
  475. form_dict[key] = sanitize_html(value)
  476. def read_only():
  477. def innfn(fn):
  478. def wrapper_fn(*args, **kwargs):
  479. if conf.read_from_replica:
  480. connect_replica()
  481. try:
  482. retval = fn(*args, **get_newargs(fn, kwargs))
  483. except:
  484. raise
  485. finally:
  486. if local and hasattr(local, 'primary_db'):
  487. local.db.close()
  488. local.db = local.primary_db
  489. return retval
  490. return wrapper_fn
  491. return innfn
  492. def only_for(roles, message=False):
  493. """Raise `frappe.PermissionError` if the user does not have any of the given **Roles**.
  494. :param roles: List of roles to check."""
  495. if local.flags.in_test:
  496. return
  497. if not isinstance(roles, (tuple, list)):
  498. roles = (roles,)
  499. roles = set(roles)
  500. myroles = set(get_roles())
  501. if not roles.intersection(myroles):
  502. if message:
  503. msgprint(_('This action is only allowed for {}').format(bold(', '.join(roles))), _('Not Permitted'))
  504. raise PermissionError
  505. def get_domain_data(module):
  506. try:
  507. domain_data = get_hooks('domains')
  508. if module in domain_data:
  509. return _dict(get_attr(get_hooks('domains')[module][0] + '.data'))
  510. else:
  511. return _dict()
  512. except ImportError:
  513. if local.flags.in_test:
  514. return _dict()
  515. else:
  516. raise
  517. def clear_cache(user=None, doctype=None):
  518. """Clear **User**, **DocType** or global cache.
  519. :param user: If user is given, only user cache is cleared.
  520. :param doctype: If doctype is given, only DocType cache is cleared."""
  521. import frappe.cache_manager
  522. if doctype:
  523. frappe.cache_manager.clear_doctype_cache(doctype)
  524. reset_metadata_version()
  525. elif user:
  526. frappe.cache_manager.clear_user_cache(user)
  527. else: # everything
  528. from frappe import translate
  529. frappe.cache_manager.clear_user_cache()
  530. frappe.cache_manager.clear_domain_cache()
  531. translate.clear_cache()
  532. reset_metadata_version()
  533. local.cache = {}
  534. local.new_doc_templates = {}
  535. for fn in get_hooks("clear_cache"):
  536. get_attr(fn)()
  537. local.role_permissions = {}
  538. def only_has_select_perm(doctype, user=None, ignore_permissions=False):
  539. if ignore_permissions:
  540. return False
  541. if not user:
  542. user = local.session.user
  543. import frappe.permissions
  544. permissions = frappe.permissions.get_role_permissions(doctype, user=user)
  545. if permissions.get('select') and not permissions.get('read'):
  546. return True
  547. else:
  548. return False
  549. def has_permission(doctype=None, ptype="read", doc=None, user=None, verbose=False, throw=False):
  550. """Raises `frappe.PermissionError` if not permitted.
  551. :param doctype: DocType for which permission is to be check.
  552. :param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`.
  553. :param doc: [optional] Checks User permissions for given doc.
  554. :param user: [optional] Check for given user. Default: current user."""
  555. if not doctype and doc:
  556. doctype = doc.doctype
  557. import frappe.permissions
  558. out = frappe.permissions.has_permission(doctype, ptype, doc=doc, verbose=verbose, user=user, raise_exception=throw)
  559. if throw and not out:
  560. if doc:
  561. frappe.throw(_("No permission for {0}").format(doc.doctype + " " + doc.name))
  562. else:
  563. frappe.throw(_("No permission for {0}").format(doctype))
  564. return out
  565. def has_website_permission(doc=None, ptype='read', user=None, verbose=False, doctype=None):
  566. """Raises `frappe.PermissionError` if not permitted.
  567. :param doctype: DocType for which permission is to be check.
  568. :param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`.
  569. :param doc: Checks User permissions for given doc.
  570. :param user: [optional] Check for given user. Default: current user."""
  571. if not user:
  572. user = session.user
  573. if doc:
  574. if isinstance(doc, str):
  575. doc = get_doc(doctype, doc)
  576. doctype = doc.doctype
  577. if doc.flags.ignore_permissions:
  578. return True
  579. # check permission in controller
  580. if hasattr(doc, 'has_website_permission'):
  581. return doc.has_website_permission(ptype, user, verbose=verbose)
  582. hooks = (get_hooks("has_website_permission") or {}).get(doctype, [])
  583. if hooks:
  584. for method in hooks:
  585. result = call(method, doc=doc, ptype=ptype, user=user, verbose=verbose)
  586. # if even a single permission check is Falsy
  587. if not result:
  588. return False
  589. # else it is Truthy
  590. return True
  591. else:
  592. return False
  593. def is_table(doctype):
  594. """Returns True if `istable` property (indicating child Table) is set for given DocType."""
  595. def get_tables():
  596. return db.sql_list("select name from tabDocType where istable=1")
  597. tables = cache().get_value("is_table", get_tables)
  598. return doctype in tables
  599. def get_precision(doctype, fieldname, currency=None, doc=None):
  600. """Get precision for a given field"""
  601. from frappe.model.meta import get_field_precision
  602. return get_field_precision(get_meta(doctype).get_field(fieldname), doc, currency)
  603. def generate_hash(txt=None, length=None):
  604. """Generates random hash for given text + current timestamp + random string."""
  605. import hashlib, time
  606. from .utils import random_string
  607. digest = hashlib.sha224(((txt or "") + repr(time.time()) + repr(random_string(8))).encode()).hexdigest()
  608. if length:
  609. digest = digest[:length]
  610. return digest
  611. def reset_metadata_version():
  612. """Reset `metadata_version` (Client (Javascript) build ID) hash."""
  613. v = generate_hash()
  614. cache().set_value("metadata_version", v)
  615. return v
  616. def new_doc(doctype, parent_doc=None, parentfield=None, as_dict=False):
  617. """Returns a new document of the given DocType with defaults set.
  618. :param doctype: DocType of the new document.
  619. :param parent_doc: [optional] add to parent document.
  620. :param parentfield: [optional] add against this `parentfield`."""
  621. from frappe.model.create_new import get_new_doc
  622. return get_new_doc(doctype, parent_doc, parentfield, as_dict=as_dict)
  623. def set_value(doctype, docname, fieldname, value=None):
  624. """Set document value. Calls `frappe.client.set_value`"""
  625. import frappe.client
  626. return frappe.client.set_value(doctype, docname, fieldname, value)
  627. def get_cached_doc(*args, **kwargs):
  628. if args and len(args) > 1 and isinstance(args[1], str):
  629. key = get_document_cache_key(args[0], args[1])
  630. # local cache
  631. doc = local.document_cache.get(key)
  632. if doc:
  633. return doc
  634. # redis cache
  635. doc = cache().hget('document_cache', key)
  636. if doc:
  637. doc = get_doc(doc)
  638. local.document_cache[key] = doc
  639. return doc
  640. # database
  641. doc = get_doc(*args, **kwargs)
  642. return doc
  643. def get_document_cache_key(doctype, name):
  644. return '{0}::{1}'.format(doctype, name)
  645. def clear_document_cache(doctype, name):
  646. cache().hdel("last_modified", doctype)
  647. key = get_document_cache_key(doctype, name)
  648. if key in local.document_cache:
  649. del local.document_cache[key]
  650. cache().hdel('document_cache', key)
  651. def get_cached_value(doctype, name, fieldname, as_dict=False):
  652. doc = get_cached_doc(doctype, name)
  653. if isinstance(fieldname, str):
  654. if as_dict:
  655. throw('Cannot make dict for single fieldname')
  656. return doc.get(fieldname)
  657. values = [doc.get(f) for f in fieldname]
  658. if as_dict:
  659. return _dict(zip(fieldname, values))
  660. return values
  661. def get_doc(*args, **kwargs):
  662. """Return a `frappe.model.document.Document` object of the given type and name.
  663. :param arg1: DocType name as string **or** document JSON.
  664. :param arg2: [optional] Document name as string.
  665. Examples:
  666. # insert a new document
  667. todo = frappe.get_doc({"doctype":"ToDo", "description": "test"})
  668. todo.insert()
  669. # open an existing document
  670. todo = frappe.get_doc("ToDo", "TD0001")
  671. """
  672. import frappe.model.document
  673. doc = frappe.model.document.get_doc(*args, **kwargs)
  674. # set in cache
  675. if args and len(args) > 1:
  676. key = get_document_cache_key(args[0], args[1])
  677. local.document_cache[key] = doc
  678. cache().hset('document_cache', key, doc.as_dict())
  679. return doc
  680. def get_last_doc(doctype, filters=None, order_by="creation desc"):
  681. """Get last created document of this type."""
  682. d = get_all(
  683. doctype,
  684. filters=filters,
  685. limit_page_length=1,
  686. order_by=order_by,
  687. pluck="name"
  688. )
  689. if d:
  690. return get_doc(doctype, d[0])
  691. else:
  692. raise DoesNotExistError
  693. def get_single(doctype):
  694. """Return a `frappe.model.document.Document` object of the given Single doctype."""
  695. return get_doc(doctype, doctype)
  696. def get_meta(doctype, cached=True):
  697. """Get `frappe.model.meta.Meta` instance of given doctype name."""
  698. import frappe.model.meta
  699. return frappe.model.meta.get_meta(doctype, cached=cached)
  700. def get_meta_module(doctype):
  701. import frappe.modules
  702. return frappe.modules.load_doctype_module(doctype)
  703. def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False,
  704. ignore_permissions=False, flags=None, ignore_on_trash=False, ignore_missing=True, delete_permanently=False):
  705. """Delete a document. Calls `frappe.model.delete_doc.delete_doc`.
  706. :param doctype: DocType of document to be delete.
  707. :param name: Name of document to be delete.
  708. :param force: Allow even if document is linked. Warning: This may lead to data integrity errors.
  709. :param ignore_doctypes: Ignore if child table is one of these.
  710. :param for_reload: Call `before_reload` trigger before deleting.
  711. :param ignore_permissions: Ignore user permissions.
  712. :param delete_permanently: Do not create a Deleted Document for the document."""
  713. import frappe.model.delete_doc
  714. frappe.model.delete_doc.delete_doc(doctype, name, force, ignore_doctypes, for_reload,
  715. ignore_permissions, flags, ignore_on_trash, ignore_missing, delete_permanently)
  716. def delete_doc_if_exists(doctype, name, force=0):
  717. """Delete document if exists."""
  718. if db.exists(doctype, name):
  719. delete_doc(doctype, name, force=force)
  720. def reload_doctype(doctype, force=False, reset_permissions=False):
  721. """Reload DocType from model (`[module]/[doctype]/[name]/[name].json`) files."""
  722. reload_doc(scrub(db.get_value("DocType", doctype, "module")), "doctype", scrub(doctype),
  723. force=force, reset_permissions=reset_permissions)
  724. def reload_doc(module, dt=None, dn=None, force=False, reset_permissions=False):
  725. """Reload Document from model (`[module]/[doctype]/[name]/[name].json`) files.
  726. :param module: Module name.
  727. :param dt: DocType name.
  728. :param dn: Document name.
  729. :param force: Reload even if `modified` timestamp matches.
  730. """
  731. import frappe.modules
  732. return frappe.modules.reload_doc(module, dt, dn, force=force, reset_permissions=reset_permissions)
  733. @whitelist()
  734. def rename_doc(*args, **kwargs):
  735. """
  736. Renames a doc(dt, old) to doc(dt, new) and updates all linked fields of type "Link"
  737. Calls `frappe.model.rename_doc.rename_doc`
  738. """
  739. kwargs.pop('ignore_permissions', None)
  740. kwargs.pop('cmd', None)
  741. from frappe.model.rename_doc import rename_doc
  742. return rename_doc(*args, **kwargs)
  743. def get_module(modulename):
  744. """Returns a module object for given Python module name using `importlib.import_module`."""
  745. return importlib.import_module(modulename)
  746. def scrub(txt):
  747. """Returns sluggified string. e.g. `Sales Order` becomes `sales_order`."""
  748. return txt.replace(' ', '_').replace('-', '_').lower()
  749. def unscrub(txt):
  750. """Returns titlified string. e.g. `sales_order` becomes `Sales Order`."""
  751. return txt.replace('_', ' ').replace('-', ' ').title()
  752. def get_module_path(module, *joins):
  753. """Get the path of the given module name.
  754. :param module: Module name.
  755. :param *joins: Join additional path elements using `os.path.join`."""
  756. module = scrub(module)
  757. return get_pymodule_path(local.module_app[module] + "." + module, *joins)
  758. def get_app_path(app_name, *joins):
  759. """Return path of given app.
  760. :param app: App name.
  761. :param *joins: Join additional path elements using `os.path.join`."""
  762. return get_pymodule_path(app_name, *joins)
  763. def get_site_path(*joins):
  764. """Return path of current site.
  765. :param *joins: Join additional path elements using `os.path.join`."""
  766. return os.path.join(local.site_path, *joins)
  767. def get_pymodule_path(modulename, *joins):
  768. """Return path of given Python module name.
  769. :param modulename: Python module name.
  770. :param *joins: Join additional path elements using `os.path.join`."""
  771. if not "public" in joins:
  772. joins = [scrub(part) for part in joins]
  773. return os.path.join(os.path.dirname(get_module(scrub(modulename)).__file__ or ''), *joins)
  774. def get_module_list(app_name):
  775. """Get list of modules for given all via `app/modules.txt`."""
  776. return get_file_items(os.path.join(os.path.dirname(get_module(app_name).__file__), "modules.txt"))
  777. def get_all_apps(with_internal_apps=True, sites_path=None):
  778. """Get list of all apps via `sites/apps.txt`."""
  779. if not sites_path:
  780. sites_path = local.sites_path
  781. apps = get_file_items(os.path.join(sites_path, "apps.txt"), raise_not_found=True)
  782. if with_internal_apps:
  783. for app in get_file_items(os.path.join(local.site_path, "apps.txt")):
  784. if app not in apps:
  785. apps.append(app)
  786. if "frappe" in apps:
  787. apps.remove("frappe")
  788. apps.insert(0, 'frappe')
  789. return apps
  790. def get_installed_apps(sort=False, frappe_last=False):
  791. """Get list of installed apps in current site."""
  792. if getattr(flags, "in_install_db", True):
  793. return []
  794. if not db:
  795. connect()
  796. if not local.all_apps:
  797. local.all_apps = cache().get_value('all_apps', get_all_apps)
  798. installed = json.loads(db.get_global("installed_apps") or "[]")
  799. if sort:
  800. installed = [app for app in local.all_apps if app in installed]
  801. if frappe_last:
  802. if 'frappe' in installed:
  803. installed.remove('frappe')
  804. installed.append('frappe')
  805. return installed
  806. def get_doc_hooks():
  807. '''Returns hooked methods for given doc. It will expand the dict tuple if required.'''
  808. if not hasattr(local, 'doc_events_hooks'):
  809. hooks = get_hooks('doc_events', {})
  810. out = {}
  811. for key, value in hooks.items():
  812. if isinstance(key, tuple):
  813. for doctype in key:
  814. append_hook(out, doctype, value)
  815. else:
  816. append_hook(out, key, value)
  817. local.doc_events_hooks = out
  818. return local.doc_events_hooks
  819. def get_hooks(hook=None, default=None, app_name=None):
  820. """Get hooks via `app/hooks.py`
  821. :param hook: Name of the hook. Will gather all hooks for this name and return as a list.
  822. :param default: Default if no hook found.
  823. :param app_name: Filter by app."""
  824. def load_app_hooks(app_name=None):
  825. hooks = {}
  826. for app in [app_name] if app_name else get_installed_apps(sort=True):
  827. app = "frappe" if app=="webnotes" else app
  828. try:
  829. app_hooks = get_module(app + ".hooks")
  830. except ImportError:
  831. if local.flags.in_install_app:
  832. # if app is not installed while restoring
  833. # ignore it
  834. pass
  835. print('Could not find app "{0}"'.format(app_name))
  836. if not request:
  837. sys.exit(1)
  838. raise
  839. for key in dir(app_hooks):
  840. if not key.startswith("_"):
  841. append_hook(hooks, key, getattr(app_hooks, key))
  842. return hooks
  843. no_cache = conf.developer_mode or False
  844. if app_name:
  845. hooks = _dict(load_app_hooks(app_name))
  846. else:
  847. if no_cache:
  848. hooks = _dict(load_app_hooks())
  849. else:
  850. hooks = _dict(cache().get_value("app_hooks", load_app_hooks))
  851. if hook:
  852. return hooks.get(hook) or (default if default is not None else [])
  853. else:
  854. return hooks
  855. def append_hook(target, key, value):
  856. '''appends a hook to the the target dict.
  857. If the hook key, exists, it will make it a key.
  858. If the hook value is a dict, like doc_events, it will
  859. listify the values against the key.
  860. '''
  861. if isinstance(value, dict):
  862. # dict? make a list of values against each key
  863. target.setdefault(key, {})
  864. for inkey in value:
  865. append_hook(target[key], inkey, value[inkey])
  866. else:
  867. # make a list
  868. target.setdefault(key, [])
  869. if not isinstance(value, list):
  870. value = [value]
  871. target[key].extend(value)
  872. def setup_module_map():
  873. """Rebuild map of all modules (internal)."""
  874. _cache = cache()
  875. if conf.db_name:
  876. local.app_modules = _cache.get_value("app_modules")
  877. local.module_app = _cache.get_value("module_app")
  878. if not (local.app_modules and local.module_app):
  879. local.module_app, local.app_modules = {}, {}
  880. for app in get_all_apps(True):
  881. if app == "webnotes":
  882. app = "frappe"
  883. local.app_modules.setdefault(app, [])
  884. for module in get_module_list(app):
  885. module = scrub(module)
  886. local.module_app[module] = app
  887. local.app_modules[app].append(module)
  888. if conf.db_name:
  889. _cache.set_value("app_modules", local.app_modules)
  890. _cache.set_value("module_app", local.module_app)
  891. def get_file_items(path, raise_not_found=False, ignore_empty_lines=True):
  892. """Returns items from text file as a list. Ignores empty lines."""
  893. import frappe.utils
  894. content = read_file(path, raise_not_found=raise_not_found)
  895. if content:
  896. content = frappe.utils.strip(content)
  897. return [
  898. p.strip() for p in content.splitlines()
  899. if (not ignore_empty_lines) or (p.strip() and not p.startswith("#"))
  900. ]
  901. else:
  902. return []
  903. def get_file_json(path):
  904. """Read a file and return parsed JSON object."""
  905. with open(path, 'r') as f:
  906. return json.load(f)
  907. def read_file(path, raise_not_found=False):
  908. """Open a file and return its content as Unicode."""
  909. if isinstance(path, str):
  910. path = path.encode("utf-8")
  911. if os.path.exists(path):
  912. with open(path, "r") as f:
  913. return as_unicode(f.read())
  914. elif raise_not_found:
  915. raise IOError("{} Not Found".format(path))
  916. else:
  917. return None
  918. def get_attr(method_string):
  919. """Get python method object from its name."""
  920. app_name = method_string.split(".")[0]
  921. if not local.flags.in_install and app_name not in get_installed_apps():
  922. throw(_("App {0} is not installed").format(app_name), AppNotInstalledError)
  923. modulename = '.'.join(method_string.split('.')[:-1])
  924. methodname = method_string.split('.')[-1]
  925. return getattr(get_module(modulename), methodname)
  926. def call(fn, *args, **kwargs):
  927. """Call a function and match arguments."""
  928. if isinstance(fn, str):
  929. fn = get_attr(fn)
  930. newargs = get_newargs(fn, kwargs)
  931. return fn(*args, **newargs)
  932. def get_newargs(fn, kwargs):
  933. if hasattr(fn, 'fnargs'):
  934. fnargs = fn.fnargs
  935. else:
  936. fnargs = inspect.getfullargspec(fn).args
  937. fnargs.extend(inspect.getfullargspec(fn).kwonlyargs)
  938. varkw = inspect.getfullargspec(fn).varkw
  939. newargs = {}
  940. for a in kwargs:
  941. if (a in fnargs) or varkw:
  942. newargs[a] = kwargs.get(a)
  943. newargs.pop("ignore_permissions", None)
  944. newargs.pop("flags", None)
  945. return newargs
  946. def make_property_setter(args, ignore_validate=False, validate_fields_for_doctype=True):
  947. """Create a new **Property Setter** (for overriding DocType and DocField properties).
  948. If doctype is not specified, it will create a property setter for all fields with the
  949. given fieldname"""
  950. args = _dict(args)
  951. if not args.doctype_or_field:
  952. args.doctype_or_field = 'DocField'
  953. if not args.property_type:
  954. args.property_type = db.get_value('DocField',
  955. {'parent': 'DocField', 'fieldname': args.property}, 'fieldtype') or 'Data'
  956. if not args.doctype:
  957. doctype_list = db.sql_list('select distinct parent from tabDocField where fieldname=%s', args.fieldname)
  958. else:
  959. doctype_list = [args.doctype]
  960. for doctype in doctype_list:
  961. if not args.property_type:
  962. args.property_type = db.get_value('DocField',
  963. {'parent': doctype, 'fieldname': args.fieldname}, 'fieldtype') or 'Data'
  964. ps = get_doc({
  965. 'doctype': "Property Setter",
  966. 'doctype_or_field': args.doctype_or_field,
  967. 'doc_type': doctype,
  968. 'field_name': args.fieldname,
  969. 'row_name': args.row_name,
  970. 'property': args.property,
  971. 'value': args.value,
  972. 'property_type': args.property_type or "Data",
  973. '__islocal': 1
  974. })
  975. ps.flags.ignore_validate = ignore_validate
  976. ps.flags.validate_fields_for_doctype = validate_fields_for_doctype
  977. ps.validate_fieldtype_change()
  978. ps.insert()
  979. def import_doc(path):
  980. """Import a file using Data Import."""
  981. from frappe.core.doctype.data_import.data_import import import_doc
  982. import_doc(path)
  983. def copy_doc(doc, ignore_no_copy=True):
  984. """ No_copy fields also get copied."""
  985. import copy
  986. def remove_no_copy_fields(d):
  987. for df in d.meta.get("fields", {"no_copy": 1}):
  988. if hasattr(d, df.fieldname):
  989. d.set(df.fieldname, None)
  990. fields_to_clear = ['name', 'owner', 'creation', 'modified', 'modified_by']
  991. if not local.flags.in_test:
  992. fields_to_clear.append("docstatus")
  993. if not isinstance(doc, dict):
  994. d = doc.as_dict()
  995. else:
  996. d = doc
  997. newdoc = get_doc(copy.deepcopy(d))
  998. newdoc.set("__islocal", 1)
  999. for fieldname in (fields_to_clear + ['amended_from', 'amendment_date']):
  1000. newdoc.set(fieldname, None)
  1001. if not ignore_no_copy:
  1002. remove_no_copy_fields(newdoc)
  1003. for i, d in enumerate(newdoc.get_all_children()):
  1004. d.set("__islocal", 1)
  1005. for fieldname in fields_to_clear:
  1006. d.set(fieldname, None)
  1007. if not ignore_no_copy:
  1008. remove_no_copy_fields(d)
  1009. return newdoc
  1010. def compare(val1, condition, val2):
  1011. """Compare two values using `frappe.utils.compare`
  1012. `condition` could be:
  1013. - "^"
  1014. - "in"
  1015. - "not in"
  1016. - "="
  1017. - "!="
  1018. - ">"
  1019. - "<"
  1020. - ">="
  1021. - "<="
  1022. - "not None"
  1023. - "None"
  1024. """
  1025. import frappe.utils
  1026. return frappe.utils.compare(val1, condition, val2)
  1027. def respond_as_web_page(title, html, success=None, http_status_code=None, context=None,
  1028. indicator_color=None, primary_action='/', primary_label = None, fullpage=False,
  1029. width=None, template='message'):
  1030. """Send response as a web page with a message rather than JSON. Used to show permission errors etc.
  1031. :param title: Page title and heading.
  1032. :param message: Message to be shown.
  1033. :param success: Alert message.
  1034. :param http_status_code: HTTP status code
  1035. :param context: web template context
  1036. :param indicator_color: color of indicator in title
  1037. :param primary_action: route on primary button (default is `/`)
  1038. :param primary_label: label on primary button (default is "Home")
  1039. :param fullpage: hide header / footer
  1040. :param width: Width of message in pixels
  1041. :param template: Optionally pass view template
  1042. """
  1043. local.message_title = title
  1044. local.message = html
  1045. local.response['type'] = 'page'
  1046. local.response['route'] = template
  1047. local.no_cache = 1
  1048. if http_status_code:
  1049. local.response['http_status_code'] = http_status_code
  1050. if not context:
  1051. context = {}
  1052. if not indicator_color:
  1053. if success:
  1054. indicator_color = 'green'
  1055. elif http_status_code and http_status_code > 300:
  1056. indicator_color = 'red'
  1057. else:
  1058. indicator_color = 'blue'
  1059. context['indicator_color'] = indicator_color
  1060. context['primary_label'] = primary_label
  1061. context['primary_action'] = primary_action
  1062. context['error_code'] = http_status_code
  1063. context['fullpage'] = fullpage
  1064. if width:
  1065. context['card_width'] = width
  1066. local.response['context'] = context
  1067. def redirect_to_message(title, html, http_status_code=None, context=None, indicator_color=None):
  1068. """Redirects to /message?id=random
  1069. Similar to respond_as_web_page, but used to 'redirect' and show message pages like success, failure, etc. with a detailed message
  1070. :param title: Page title and heading.
  1071. :param message: Message to be shown.
  1072. :param http_status_code: HTTP status code.
  1073. Example Usage:
  1074. frappe.redirect_to_message(_('Thank you'), "<div><p>You will receive an email at test@example.com</p></div>")
  1075. """
  1076. message_id = generate_hash(length=8)
  1077. message = {
  1078. 'context': context or {},
  1079. 'http_status_code': http_status_code or 200
  1080. }
  1081. message['context'].update({
  1082. 'header': title,
  1083. 'title': title,
  1084. 'message': html
  1085. })
  1086. if indicator_color:
  1087. message['context'].update({
  1088. "indicator_color": indicator_color
  1089. })
  1090. cache().set_value("message_id:{0}".format(message_id), message, expires_in_sec=60)
  1091. location = '/message?id={0}'.format(message_id)
  1092. if not getattr(local, 'is_ajax', False):
  1093. local.response["type"] = "redirect"
  1094. local.response["location"] = location
  1095. else:
  1096. return location
  1097. def build_match_conditions(doctype, as_condition=True):
  1098. """Return match (User permissions) for given doctype as list or SQL."""
  1099. import frappe.desk.reportview
  1100. return frappe.desk.reportview.build_match_conditions(doctype, as_condition=as_condition)
  1101. def get_list(doctype, *args, **kwargs):
  1102. """List database query via `frappe.model.db_query`. Will also check for permissions.
  1103. :param doctype: DocType on which query is to be made.
  1104. :param fields: List of fields or `*`.
  1105. :param filters: List of filters (see example).
  1106. :param order_by: Order By e.g. `modified desc`.
  1107. :param limit_page_start: Start results at record #. Default 0.
  1108. :param limit_page_length: No of records in the page. Default 20.
  1109. Example usage:
  1110. # simple dict filter
  1111. frappe.get_list("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"})
  1112. # filter as a list of lists
  1113. frappe.get_list("ToDo", fields="*", filters = [["modified", ">", "2014-01-01"]])
  1114. # filter as a list of dicts
  1115. frappe.get_list("ToDo", fields="*", filters = {"description": ("like", "test%")})
  1116. """
  1117. import frappe.model.db_query
  1118. return frappe.model.db_query.DatabaseQuery(doctype).execute(*args, **kwargs)
  1119. def get_all(doctype, *args, **kwargs):
  1120. """List database query via `frappe.model.db_query`. Will **not** check for permissions.
  1121. Parameters are same as `frappe.get_list`
  1122. :param doctype: DocType on which query is to be made.
  1123. :param fields: List of fields or `*`. Default is: `["name"]`.
  1124. :param filters: List of filters (see example).
  1125. :param order_by: Order By e.g. `modified desc`.
  1126. :param limit_start: Start results at record #. Default 0.
  1127. :param limit_page_length: No of records in the page. Default 20.
  1128. Example usage:
  1129. # simple dict filter
  1130. frappe.get_all("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"})
  1131. # filter as a list of lists
  1132. frappe.get_all("ToDo", fields=["*"], filters = [["modified", ">", "2014-01-01"]])
  1133. # filter as a list of dicts
  1134. frappe.get_all("ToDo", fields=["*"], filters = {"description": ("like", "test%")})
  1135. """
  1136. kwargs["ignore_permissions"] = True
  1137. if not "limit_page_length" in kwargs:
  1138. kwargs["limit_page_length"] = 0
  1139. return get_list(doctype, *args, **kwargs)
  1140. def get_value(*args, **kwargs):
  1141. """Returns a document property or list of properties.
  1142. Alias for `frappe.db.get_value`
  1143. :param doctype: DocType name.
  1144. :param filters: Filters like `{"x":"y"}` or name of the document. `None` if Single DocType.
  1145. :param fieldname: Column name.
  1146. :param ignore: Don't raise exception if table, column is missing.
  1147. :param as_dict: Return values as dict.
  1148. :param debug: Print query in error log.
  1149. """
  1150. return db.get_value(*args, **kwargs)
  1151. def as_json(obj, indent=1):
  1152. from frappe.utils.response import json_handler
  1153. return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': '))
  1154. def are_emails_muted():
  1155. from frappe.utils import cint
  1156. return flags.mute_emails or cint(conf.get("mute_emails") or 0) or False
  1157. def get_test_records(doctype):
  1158. """Returns list of objects from `test_records.json` in the given doctype's folder."""
  1159. from frappe.modules import get_doctype_module, get_module_path
  1160. path = os.path.join(get_module_path(get_doctype_module(doctype)), "doctype", scrub(doctype), "test_records.json")
  1161. if os.path.exists(path):
  1162. with open(path, "r") as f:
  1163. return json.loads(f.read())
  1164. else:
  1165. return []
  1166. def format_value(*args, **kwargs):
  1167. """Format value with given field properties.
  1168. :param value: Value to be formatted.
  1169. :param df: (Optional) DocField object with properties `fieldtype`, `options` etc."""
  1170. import frappe.utils.formatters
  1171. return frappe.utils.formatters.format_value(*args, **kwargs)
  1172. def format(*args, **kwargs):
  1173. """Format value with given field properties.
  1174. :param value: Value to be formatted.
  1175. :param df: (Optional) DocField object with properties `fieldtype`, `options` etc."""
  1176. import frappe.utils.formatters
  1177. return frappe.utils.formatters.format_value(*args, **kwargs)
  1178. def get_print(doctype=None, name=None, print_format=None, style=None,
  1179. html=None, as_pdf=False, doc=None, output=None, no_letterhead=0, password=None):
  1180. """Get Print Format for given document.
  1181. :param doctype: DocType of document.
  1182. :param name: Name of document.
  1183. :param print_format: Print Format name. Default 'Standard',
  1184. :param style: Print Format style.
  1185. :param as_pdf: Return as PDF. Default False.
  1186. :param password: Password to encrypt the pdf with. Default None"""
  1187. from frappe.website.render import build_page
  1188. from frappe.utils.pdf import get_pdf
  1189. local.form_dict.doctype = doctype
  1190. local.form_dict.name = name
  1191. local.form_dict.format = print_format
  1192. local.form_dict.style = style
  1193. local.form_dict.doc = doc
  1194. local.form_dict.no_letterhead = no_letterhead
  1195. options = None
  1196. if password:
  1197. options = {'password': password}
  1198. if not html:
  1199. html = build_page("printview")
  1200. if as_pdf:
  1201. return get_pdf(html, output = output, options = options)
  1202. else:
  1203. return html
  1204. def attach_print(doctype, name, file_name=None, print_format=None,
  1205. style=None, html=None, doc=None, lang=None, print_letterhead=True, password=None):
  1206. from frappe.utils import scrub_urls
  1207. if not file_name: file_name = name
  1208. file_name = file_name.replace(' ','').replace('/','-')
  1209. print_settings = db.get_singles_dict("Print Settings")
  1210. _lang = local.lang
  1211. #set lang as specified in print format attachment
  1212. if lang: local.lang = lang
  1213. local.flags.ignore_print_permissions = True
  1214. no_letterhead = not print_letterhead
  1215. kwargs = dict(
  1216. print_format=print_format,
  1217. style=style,
  1218. html=html,
  1219. doc=doc,
  1220. no_letterhead=no_letterhead,
  1221. password=password
  1222. )
  1223. content = ''
  1224. if int(print_settings.send_print_as_pdf or 0):
  1225. ext = ".pdf"
  1226. kwargs["as_pdf"] = True
  1227. content = get_print(doctype, name, **kwargs)
  1228. else:
  1229. ext = ".html"
  1230. content = scrub_urls(get_print(doctype, name, **kwargs)).encode('utf-8')
  1231. out = {
  1232. "fname": file_name + ext,
  1233. "fcontent": content
  1234. }
  1235. local.flags.ignore_print_permissions = False
  1236. #reset lang to original local lang
  1237. local.lang = _lang
  1238. return out
  1239. def publish_progress(*args, **kwargs):
  1240. """Show the user progress for a long request
  1241. :param percent: Percent progress
  1242. :param title: Title
  1243. :param doctype: Optional, for document type
  1244. :param docname: Optional, for document name
  1245. :param description: Optional description
  1246. """
  1247. import frappe.realtime
  1248. return frappe.realtime.publish_progress(*args, **kwargs)
  1249. def publish_realtime(*args, **kwargs):
  1250. """Publish real-time updates
  1251. :param event: Event name, like `task_progress` etc.
  1252. :param message: JSON message object. For async must contain `task_id`
  1253. :param room: Room in which to publish update (default entire site)
  1254. :param user: Transmit to user
  1255. :param doctype: Transmit to doctype, docname
  1256. :param docname: Transmit to doctype, docname
  1257. :param after_commit: (default False) will emit after current transaction is committed
  1258. """
  1259. import frappe.realtime
  1260. return frappe.realtime.publish_realtime(*args, **kwargs)
  1261. def local_cache(namespace, key, generator, regenerate_if_none=False):
  1262. """A key value store for caching within a request
  1263. :param namespace: frappe.local.cache[namespace]
  1264. :param key: frappe.local.cache[namespace][key] used to retrieve value
  1265. :param generator: method to generate a value if not found in store
  1266. """
  1267. if namespace not in local.cache:
  1268. local.cache[namespace] = {}
  1269. if key not in local.cache[namespace]:
  1270. local.cache[namespace][key] = generator()
  1271. elif local.cache[namespace][key]==None and regenerate_if_none:
  1272. # if key exists but the previous result was None
  1273. local.cache[namespace][key] = generator()
  1274. return local.cache[namespace][key]
  1275. def enqueue(*args, **kwargs):
  1276. '''
  1277. Enqueue method to be executed using a background worker
  1278. :param method: method string or method object
  1279. :param queue: (optional) should be either long, default or short
  1280. :param timeout: (optional) should be set according to the functions
  1281. :param event: this is passed to enable clearing of jobs from queues
  1282. :param is_async: (optional) if is_async=False, the method is executed immediately, else via a worker
  1283. :param job_name: (optional) can be used to name an enqueue call, which can be used to prevent duplicate calls
  1284. :param kwargs: keyword arguments to be passed to the method
  1285. '''
  1286. import frappe.utils.background_jobs
  1287. return frappe.utils.background_jobs.enqueue(*args, **kwargs)
  1288. def task(**task_kwargs):
  1289. def decorator_task(f):
  1290. f.enqueue = lambda **fun_kwargs: enqueue(f, **task_kwargs, **fun_kwargs)
  1291. return f
  1292. return decorator_task
  1293. def enqueue_doc(*args, **kwargs):
  1294. '''
  1295. Enqueue method to be executed using a background worker
  1296. :param doctype: DocType of the document on which you want to run the event
  1297. :param name: Name of the document on which you want to run the event
  1298. :param method: method string or method object
  1299. :param queue: (optional) should be either long, default or short
  1300. :param timeout: (optional) should be set according to the functions
  1301. :param kwargs: keyword arguments to be passed to the method
  1302. '''
  1303. import frappe.utils.background_jobs
  1304. return frappe.utils.background_jobs.enqueue_doc(*args, **kwargs)
  1305. def get_doctype_app(doctype):
  1306. def _get_doctype_app():
  1307. doctype_module = local.db.get_value("DocType", doctype, "module")
  1308. return local.module_app[scrub(doctype_module)]
  1309. return local_cache("doctype_app", doctype, generator=_get_doctype_app)
  1310. loggers = {}
  1311. log_level = None
  1312. def logger(module=None, with_more_info=False, allow_site=True, filter=None, max_size=100_000, file_count=20):
  1313. '''Returns a python logger that uses StreamHandler'''
  1314. from frappe.utils.logger import get_logger
  1315. return get_logger(module=module, with_more_info=with_more_info, allow_site=allow_site, filter=filter, max_size=max_size, file_count=file_count)
  1316. def log_error(message=None, title=_("Error")):
  1317. '''Log error to Error Log'''
  1318. # AI ALERT:
  1319. # the title and message may be swapped
  1320. # the better API for this is log_error(title, message), and used in many cases this way
  1321. # this hack tries to be smart about whats a title (single line ;-)) and fixes it
  1322. if message:
  1323. if '\n' in title:
  1324. error, title = title, message
  1325. else:
  1326. error = message
  1327. else:
  1328. error = get_traceback()
  1329. return get_doc(dict(doctype='Error Log', error=as_unicode(error),
  1330. method=title)).insert(ignore_permissions=True)
  1331. def get_desk_link(doctype, name):
  1332. html = '<a href="/app/Form/{doctype}/{name}" style="font-weight: bold;">{doctype_local} {name}</a>'
  1333. return html.format(
  1334. doctype=doctype,
  1335. name=name,
  1336. doctype_local=_(doctype)
  1337. )
  1338. def bold(text):
  1339. return '<b>{0}</b>'.format(text)
  1340. def safe_eval(code, eval_globals=None, eval_locals=None):
  1341. '''A safer `eval`'''
  1342. whitelisted_globals = {
  1343. "int": int,
  1344. "float": float,
  1345. "long": int,
  1346. "round": round
  1347. }
  1348. UNSAFE_ATTRIBUTES = {
  1349. # Generator Attributes
  1350. "gi_frame", "gi_code",
  1351. # Coroutine Attributes
  1352. "cr_frame", "cr_code", "cr_origin",
  1353. # Async Generator Attributes
  1354. "ag_code", "ag_frame",
  1355. # Traceback Attributes
  1356. "tb_frame", "tb_next",
  1357. # Format Attributes
  1358. "format", "format_map",
  1359. }
  1360. for attribute in UNSAFE_ATTRIBUTES:
  1361. if attribute in code:
  1362. throw('Illegal rule {0}. Cannot use "{1}"'.format(bold(code), attribute))
  1363. if '__' in code:
  1364. throw('Illegal rule {0}. Cannot use "__"'.format(bold(code)))
  1365. if not eval_globals:
  1366. eval_globals = {}
  1367. eval_globals['__builtins__'] = {}
  1368. eval_globals.update(whitelisted_globals)
  1369. return eval(code, eval_globals, eval_locals)
  1370. def get_system_settings(key):
  1371. if key not in local.system_settings:
  1372. local.system_settings.update({key: db.get_single_value('System Settings', key)})
  1373. return local.system_settings.get(key)
  1374. def get_active_domains():
  1375. from frappe.core.doctype.domain_settings.domain_settings import get_active_domains
  1376. return get_active_domains()
  1377. def get_version(doctype, name, limit=None, head=False, raise_err=True):
  1378. '''
  1379. Returns a list of version information of a given DocType.
  1380. Note: Applicable only if DocType has changes tracked.
  1381. Example
  1382. >>> frappe.get_version('User', 'foobar@gmail.com')
  1383. >>>
  1384. [
  1385. {
  1386. "version": [version.data], # Refer Version DocType get_diff method and data attribute
  1387. "user": "admin@gmail.com", # User that created this version
  1388. "creation": <datetime.datetime> # Creation timestamp of that object.
  1389. }
  1390. ]
  1391. '''
  1392. meta = get_meta(doctype)
  1393. if meta.track_changes:
  1394. names = db.get_all('Version', filters={
  1395. 'ref_doctype': doctype,
  1396. 'docname': name,
  1397. 'order_by': 'creation' if head else None,
  1398. 'limit': limit
  1399. }, as_list=1)
  1400. from frappe.chat.util import squashify, dictify, safe_json_loads
  1401. versions = []
  1402. for name in names:
  1403. name = squashify(name)
  1404. doc = get_doc('Version', name)
  1405. data = doc.data
  1406. data = safe_json_loads(data)
  1407. data = dictify(dict(
  1408. version=data,
  1409. user=doc.owner,
  1410. creation=doc.creation
  1411. ))
  1412. versions.append(data)
  1413. return versions
  1414. else:
  1415. if raise_err:
  1416. raise ValueError(_('{0} has no versions tracked.').format(doctype))
  1417. @whitelist(allow_guest=True)
  1418. def ping():
  1419. return "pong"
  1420. def safe_encode(param, encoding='utf-8'):
  1421. try:
  1422. param = param.encode(encoding)
  1423. except Exception:
  1424. pass
  1425. return param
  1426. def safe_decode(param, encoding='utf-8'):
  1427. try:
  1428. param = param.decode(encoding)
  1429. except Exception:
  1430. pass
  1431. return param
  1432. def parse_json(val):
  1433. from frappe.utils import parse_json
  1434. return parse_json(val)
  1435. def mock(type, size=1, locale='en'):
  1436. results = []
  1437. fake = faker.Faker(locale)
  1438. if type not in dir(fake):
  1439. raise ValueError('Not a valid mock type.')
  1440. else:
  1441. for i in range(size):
  1442. data = getattr(fake, type)()
  1443. results.append(data)
  1444. from frappe.chat.util import squashify
  1445. return squashify(results)
  1446. def validate_and_sanitize_search_inputs(fn):
  1447. from frappe.desk.search import validate_and_sanitize_search_inputs as func
  1448. return func(fn)