diff --git a/.eslintrc b/.eslintrc index e79571f556..eef33ec8a0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,7 @@ "es6": true }, "parserOptions": { - "ecmaVersion": 8, + "ecmaVersion": 9, "sourceType": "module" }, "extends": "eslint:recommended", diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml deleted file mode 100644 index 26801ebbe8..0000000000 --- a/.github/workflows/backport.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Backport -on: - pull_request: - types: - - closed - - labeled - -jobs: - backport: - runs-on: ubuntu-18.04 - name: Backport - steps: - - name: Backport - uses: tibdex/backport@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CODEOWNERS b/CODEOWNERS index 2ff8752871..8e6f8eb5e9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,15 +3,16 @@ # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, -* @surajshetty3416, @netchampfaris +* @frappe/frappe-review-team website/ @scmmishra +web_form/ @scmmishra templates/ @scmmishra www/ @scmmishra integrations/ @Mangesh-Khairnar -patches/ @surajshetty3416 @sahil28297 +patches/ @sahil28297 dashboard/ @prssanna email/ @Thunderbottom event_streaming/ @ruchamahabal data_import* @netchampfaris core/ @surajshetty3416 -requirements.txt @gavindsouza \ No newline at end of file +requirements.txt @gavindsouza diff --git a/frappe/automation/desk_page/tools/tools.json b/frappe/automation/desk_page/tools/tools.json index 3cfaa0bd97..235498724d 100644 --- a/frappe/automation/desk_page/tools/tools.json +++ b/frappe/automation/desk_page/tools/tools.json @@ -1,22 +1,24 @@ { "cards": [ { - "icon": "octicon octicon-briefcase", - "links": "[\n {\n \"description\": \"Documents assigned to you and by you.\",\n \"label\": \"To Do\",\n \"name\": \"ToDo\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Event and other calendars.\",\n \"label\": \"Calendar\",\n \"link\": \"List/Event/Calendar\",\n \"name\": \"Event\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Private and public Notes.\",\n \"label\": \"Note\",\n \"name\": \"Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Files\",\n \"name\": \"File\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Activity log of all users.\",\n \"label\": \"Activity\",\n \"name\": \"activity\",\n \"type\": \"page\"\n }\n]", - "title": "Tools" + "hidden": 0, + "label": "Tools", + "links": "[\n {\n \"description\": \"Documents assigned to you and by you.\",\n \"label\": \"To Do\",\n \"name\": \"ToDo\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Event and other calendars.\",\n \"label\": \"Calendar\",\n \"link\": \"List/Event/Calendar\",\n \"name\": \"Event\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Private and public Notes.\",\n \"label\": \"Note\",\n \"name\": \"Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Files\",\n \"name\": \"File\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Activity log of all users.\",\n \"label\": \"Activity\",\n \"name\": \"activity\",\n \"type\": \"page\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Newsletters to contacts, leads.\",\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Email Group List\",\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n }\n]", - "title": "Email" + "hidden": 0, + "label": "Email", + "links": "[\n {\n \"description\": \"Newsletters to contacts, leads.\",\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Email Group List\",\n \"label\": \"Email Group\",\n \"name\": \"Email Group\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-cog", - "links": "[\n {\n \"type\": \"doctype\",\n \"name\": \"Assignment Rule\",\n \"description\": \"Set up rules for user assignments.\",\n \"label\": \"Assignment Rule\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Milestone\",\n \"description\": \"Tracks milestones on the lifecycle of a document if it undergoes multiple stages.\",\n \"label\": \"Milestone\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Auto Repeat\",\n \"description\": \"Automatically generates recurring documents.\",\n \"label\": \"Auto Repeat\"\n }\n]", - "title": "Automation" + "hidden": 0, + "label": "Automation", + "links": "[\n {\n \"type\": \"doctype\",\n \"name\": \"Assignment Rule\",\n \"description\": \"Set up rules for user assignments.\",\n \"label\": \"Assignment Rule\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Milestone\",\n \"description\": \"Tracks milestones on the lifecycle of a document if it undergoes multiple stages.\",\n \"label\": \"Milestone\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Auto Repeat\",\n \"description\": \"Automatically generates recurring documents.\",\n \"label\": \"Auto Repeat\"\n }\n]" }, { - "links": "[\n {\n \"type\": \"doctype\",\n \"name\": \"Event Producer\",\n \"description\": \"The site you want to subscribe to for consuming events.\",\n \"label\": \"Event Producer\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Consumer\",\n \"description\": \"The site which is consuming your events.\",\n \"label\": \"Event Consumer\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Update Log\",\n \"description\": \"Maintains a Log of all inserts, updates and deletions on Event Producer site for documents that have consumers.\",\n \"label\": \"Event Update Log\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Sync Log\",\n \"description\": \"Maintains a log of every event consumed along with the status of the sync and a Resync button in case sync fails.\",\n \"label\": \"Event Sync Log\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Document Type Mapping\",\n \"description\": \"The mapping configuration between two doctypes.\",\n \"label\": \"Document Type Mapping\"\n }\n]", - "title": "Event Streaming" + "hidden": 0, + "label": "Event Streaming", + "links": "[\n {\n \"type\": \"doctype\",\n \"name\": \"Event Producer\",\n \"description\": \"The site you want to subscribe to for consuming events.\",\n \"label\": \"Event Producer\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Consumer\",\n \"description\": \"The site which is consuming your events.\",\n \"label\": \"Event Consumer\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Update Log\",\n \"description\": \"Maintains a Log of all inserts, updates and deletions on Event Producer site for documents that have consumers.\",\n \"label\": \"Event Update Log\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Event Sync Log\",\n \"description\": \"Maintains a log of every event consumed along with the status of the sync and a Resync button in case sync fails.\",\n \"label\": \"Event Sync Log\"\n },\n {\n \"type\": \"doctype\",\n \"name\": \"Document Type Mapping\",\n \"description\": \"The mapping configuration between two doctypes.\",\n \"label\": \"Document Type Mapping\"\n }\n]" } ], "category": "Administration", @@ -30,7 +32,7 @@ "idx": 0, "is_standard": 1, "label": "Tools", - "modified": "2020-03-12 16:30:41.841895", + "modified": "2020-04-01 11:24:40.804346", "modified_by": "Administrator", "module": "Automation", "name": "Tools", @@ -39,27 +41,27 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "ToDo", "link_to": "ToDo", "type": "DocType" }, { - "is_query_report": 0, + "label": "Note", "link_to": "Note", "type": "DocType" }, { - "is_query_report": 0, + "label": "File", "link_to": "File", "type": "DocType" }, { - "is_query_report": 0, + "label": "Assignment Rule", "link_to": "Assignment Rule", "type": "DocType" }, { - "is_query_report": 0, + "label": "Auto Repeat", "link_to": "Auto Repeat", "type": "DocType" } diff --git a/frappe/contacts/doctype/contact/contact.json b/frappe/contacts/doctype/contact/contact.json index 7dd5aad4ce..2e2fb6df67 100644 --- a/frappe/contacts/doctype/contact/contact.json +++ b/frappe/contacts/doctype/contact/contact.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_events_in_timeline": 1, "allow_import": 1, "allow_rename": 1, @@ -115,6 +116,7 @@ "label": "Phone", "oldfieldname": "contact_no", "oldfieldtype": "Data", + "options": "Phone", "read_only": 1 }, { @@ -200,6 +202,7 @@ "fieldname": "mobile_no", "fieldtype": "Data", "label": "Mobile No", + "options": "Phone", "read_only": 1 }, { @@ -245,7 +248,8 @@ "icon": "fa fa-user", "idx": 1, "image_field": "image", - "modified": "2019-10-10 22:04:41.070479", + "links": [], + "modified": "2020-04-06 18:25:28.223693", "modified_by": "Administrator", "module": "Contacts", "name": "Contact", diff --git a/frappe/contacts/doctype/contact_phone/contact_phone.json b/frappe/contacts/doctype/contact_phone/contact_phone.json index 3fb203ed69..5412e4a1b7 100644 --- a/frappe/contacts/doctype/contact_phone/contact_phone.json +++ b/frappe/contacts/doctype/contact_phone/contact_phone.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-08-02 13:10:37.890214", "doctype": "DocType", "editable_grid": 1, @@ -14,6 +15,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Number", + "options": "Phone", "reqd": 1 }, { @@ -34,7 +36,8 @@ } ], "istable": 1, - "modified": "2019-09-24 17:47:50.375326", + "links": [], + "modified": "2020-04-06 18:28:10.486220", "modified_by": "Administrator", "module": "Contacts", "name": "Contact Phone", diff --git a/frappe/core/desk_page/settings/settings.json b/frappe/core/desk_page/settings/settings.json index 41d1765684..6569b2fb20 100644 --- a/frappe/core/desk_page/settings/settings.json +++ b/frappe/core/desk_page/settings/settings.json @@ -1,37 +1,37 @@ { "cards": [ { - "icon": "fa fa-th", - "links": "[\n {\n \"description\": \"Import Data from CSV / Excel files.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Import Data\",\n \"name\": \"Data Import\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Export Data in CSV / Excel format.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Export Data\",\n \"name\": \"Data Export\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update many values at one time.\",\n \"hide_count\": true,\n \"label\": \"Bulk Update\",\n \"name\": \"Bulk Update\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of backups available for download\",\n \"icon\": \"fa fa-download\",\n \"label\": \"Download Backups\",\n \"name\": \"backups\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Restore or permanently delete a document.\",\n \"label\": \"Deleted Documents\",\n \"name\": \"Deleted Document\",\n \"type\": \"doctype\"\n }\n]", - "title": "Data" + "hidden": 0, + "label": "Data", + "links": "[\n {\n \"description\": \"Import Data from CSV / Excel files.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Import Data\",\n \"name\": \"Data Import\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Export Data in CSV / Excel format.\",\n \"icon\": \"octicon octicon-cloud-upload\",\n \"label\": \"Export Data\",\n \"name\": \"Data Export\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update many values at one time.\",\n \"hide_count\": true,\n \"label\": \"Bulk Update\",\n \"name\": \"Bulk Update\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of backups available for download\",\n \"icon\": \"fa fa-download\",\n \"label\": \"Download Backups\",\n \"name\": \"backups\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Restore or permanently delete a document.\",\n \"label\": \"Deleted Documents\",\n \"name\": \"Deleted Document\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-envelope", - "links": "[\n {\n \"description\": \"Add / Manage Email Accounts.\",\n \"label\": \"Email Account\",\n \"name\": \"Email Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add / Manage Email Domains.\",\n \"label\": \"Email Domain\",\n \"name\": \"Email Domain\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup Notifications based on various criteria.\",\n \"label\": \"Notification\",\n \"name\": \"Notification\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Email Templates for common queries.\",\n \"label\": \"Email Template\",\n \"name\": \"Email Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup Reports to be emailed at regular intervals\",\n \"label\": \"Auto Email Report\",\n \"name\": \"Auto Email Report\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create and manage newsletter\",\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Configure notifications for mentions, assignments, energy points and more.\",\n \"label\": \"Notification Settings\",\n \"name\": \"Notification Settings\",\n \"route\": \"Form/Notification Settings/Administrator\",\n \"type\": \"doctype\"\n }\n]", - "title": "Email / Notifications" + "hidden": 0, + "label": "Email / Notifications", + "links": "[\n {\n \"description\": \"Add / Manage Email Accounts.\",\n \"label\": \"Email Account\",\n \"name\": \"Email Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add / Manage Email Domains.\",\n \"label\": \"Email Domain\",\n \"name\": \"Email Domain\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup Notifications based on various criteria.\",\n \"label\": \"Notification\",\n \"name\": \"Notification\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Email Templates for common queries.\",\n \"label\": \"Email Template\",\n \"name\": \"Email Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup Reports to be emailed at regular intervals\",\n \"label\": \"Auto Email Report\",\n \"name\": \"Auto Email Report\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Create and manage newsletter\",\n \"label\": \"Newsletter\",\n \"name\": \"Newsletter\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Configure notifications for mentions, assignments, energy points and more.\",\n \"label\": \"Notification Settings\",\n \"name\": \"Notification Settings\",\n \"route\": \"Form/Notification Settings/Administrator\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-globe", - "links": "[\n {\n \"description\": \"Setup of top navigation bar, footer and logo.\",\n \"label\": \"Website Settings\",\n \"name\": \"Website Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of themes for Website.\",\n \"label\": \"Website Theme\",\n \"name\": \"Website Theme\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Javascript to append to the head section of the page.\",\n \"label\": \"Website Script\",\n \"name\": \"Website Script\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for About Us Page.\",\n \"label\": \"About Us Settings\",\n \"name\": \"About Us Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for Contact Us Page.\",\n \"label\": \"Contact Us Settings\",\n \"name\": \"Contact Us Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Website" + "hidden": 0, + "label": "Website", + "links": "[\n {\n \"description\": \"Setup of top navigation bar, footer and logo.\",\n \"label\": \"Website Settings\",\n \"name\": \"Website Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of themes for Website.\",\n \"label\": \"Website Theme\",\n \"name\": \"Website Theme\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Javascript to append to the head section of the page.\",\n \"label\": \"Website Script\",\n \"name\": \"Website Script\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for About Us Page.\",\n \"label\": \"About Us Settings\",\n \"name\": \"About Us Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for Contact Us Page.\",\n \"label\": \"Contact Us Settings\",\n \"name\": \"Contact Us Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-wrench", - "links": "[\n {\n \"description\": \"Language, Date and Time settings\",\n \"hide_count\": true,\n \"label\": \"System Settings\",\n \"name\": \"System Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error on automated events (scheduler).\",\n \"label\": \"Error Log\",\n \"name\": \"Error Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error during requests.\",\n \"label\": \"Error Snapshot\",\n \"name\": \"Error Snapshot\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Enable / Disable Domains\",\n \"hide_count\": true,\n \"label\": \"Domain Settings\",\n \"name\": \"Domain Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Core" + "hidden": 0, + "label": "Core", + "links": "[\n {\n \"description\": \"Language, Date and Time settings\",\n \"hide_count\": true,\n \"label\": \"System Settings\",\n \"name\": \"System Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error on automated events (scheduler).\",\n \"label\": \"Error Log\",\n \"name\": \"Error Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error during requests.\",\n \"label\": \"Error Snapshot\",\n \"name\": \"Error Snapshot\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Enable / Disable Domains\",\n \"hide_count\": true,\n \"label\": \"Domain Settings\",\n \"name\": \"Domain Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-print", - "links": "[\n {\n \"description\": \"Drag and Drop tool to build and customize Print Formats.\",\n \"label\": \"Print Format Builder\",\n \"name\": \"print-format-builder\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Set default format, page size, print style etc.\",\n \"label\": \"Print Settings\",\n \"name\": \"Print Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customized HTML Templates for printing transactions.\",\n \"label\": \"Print Format\",\n \"name\": \"Print Format\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stylesheets for Print Formats\",\n \"label\": \"Print Style\",\n \"name\": \"Print Style\",\n \"type\": \"doctype\"\n }\n]", - "title": "Printing" + "hidden": 0, + "label": "Printing", + "links": "[\n {\n \"description\": \"Drag and Drop tool to build and customize Print Formats.\",\n \"label\": \"Print Format Builder\",\n \"name\": \"print-format-builder\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Set default format, page size, print style etc.\",\n \"label\": \"Print Settings\",\n \"name\": \"Print Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customized HTML Templates for printing transactions.\",\n \"label\": \"Print Format\",\n \"name\": \"Print Format\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stylesheets for Print Formats\",\n \"label\": \"Print Style\",\n \"name\": \"Print Style\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-random", - "links": "[\n {\n \"description\": \"Define workflows for forms.\",\n \"label\": \"Workflow\",\n \"name\": \"Workflow\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"States for workflow (e.g. Draft, Approved, Cancelled).\",\n \"label\": \"Workflow State\",\n \"name\": \"Workflow State\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Actions for workflow (e.g. Approve, Cancel).\",\n \"label\": \"Workflow Action\",\n \"name\": \"Workflow Action\",\n \"type\": \"doctype\"\n }\n]", - "title": "Workflow" + "hidden": 0, + "label": "Workflow", + "links": "[\n {\n \"description\": \"Define workflows for forms.\",\n \"label\": \"Workflow\",\n \"name\": \"Workflow\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"States for workflow (e.g. Draft, Approved, Cancelled).\",\n \"label\": \"Workflow State\",\n \"name\": \"Workflow State\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Actions for workflow (e.g. Approve, Cancel).\",\n \"label\": \"Workflow Action\",\n \"name\": \"Workflow Action\",\n \"type\": \"doctype\"\n }\n]" } ], - "category": "Administration", + "category": "Modules", "charts": [], "creation": "2020-03-02 15:09:40.527211", "developer_mode_only": 0, @@ -42,29 +42,29 @@ "idx": 0, "is_standard": 1, "label": "Settings", - "modified": "2020-03-12 16:30:43.510434", + "modified": "2020-04-01 11:24:40.636747", "modified_by": "Administrator", "module": "Core", "name": "Settings", "owner": "Administrator", - "pin_to_bottom": 0, - "pin_to_top": 1, + "pin_to_bottom": 1, + "pin_to_top": 0, "shortcuts": [ { "icon": "octicon octicon-settings", - "is_query_report": 0, + "label": "System Settings", "link_to": "System Settings", "type": "DocType" }, { "icon": "fa fa-print", - "is_query_report": 0, + "label": "Print Settings", "link_to": "Print Settings", "type": "DocType" }, { "icon": "fa fa-globe", - "is_query_report": 0, + "label": "Website Settings", "link_to": "Website Settings", "type": "DocType" } diff --git a/frappe/core/desk_page/users/users.json b/frappe/core/desk_page/users/users.json index dc9619314e..30455b86e6 100644 --- a/frappe/core/desk_page/users/users.json +++ b/frappe/core/desk_page/users/users.json @@ -1,19 +1,19 @@ { "cards": [ { - "icon": "fa fa-group", - "links": "[\n {\n \"description\": \"System and Website Users\",\n \"label\": \"User\",\n \"name\": \"User\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"User Roles\",\n \"label\": \"Role\",\n \"name\": \"Role\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Role Profile\",\n \"label\": \"Role Profile\",\n \"name\": \"Role Profile\",\n \"type\": \"doctype\"\n }\n]", - "title": "Users" + "hidden": 0, + "label": "Users", + "links": "[\n {\n \"description\": \"System and Website Users\",\n \"label\": \"User\",\n \"name\": \"User\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"User Roles\",\n \"label\": \"Role\",\n \"name\": \"Role\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Role Profile\",\n \"label\": \"Role Profile\",\n \"name\": \"Role Profile\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-group", - "links": "[\n {\n \"description\": \"Activity Log by \",\n \"label\": \"Activity Log\",\n \"name\": \"Activity Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"View Log of all print, download and export events\",\n \"label\": \"Access Log\",\n \"name\": \"Access Log\",\n \"type\": \"doctype\"\n }\n]", - "title": "Logs" + "hidden": 0, + "label": "Logs", + "links": "[\n {\n \"description\": \"Activity Log by \",\n \"label\": \"Activity Log\",\n \"name\": \"Activity Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"View Log of all print, download and export events\",\n \"label\": \"Access Log\",\n \"name\": \"Access Log\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-lock", - "links": "[\n {\n \"description\": \"Set Permissions on Document Types and Roles\",\n \"icon\": \"fa fa-lock\",\n \"label\": \"Role Permissions Manager\",\n \"name\": \"permission-manager\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Restrict user for specific document\",\n \"icon\": \"fa fa-lock\",\n \"label\": \"User Permissions\",\n \"name\": \"User Permission\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Set custom roles for page and report\",\n \"label\": \"Role Permission for Page and Report\",\n \"name\": \"Role Permission for Page and Report\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"User\"\n ],\n \"description\": \"Check which Documents are readable by a User\",\n \"doctype\": \"User\",\n \"icon\": \"fa fa-eye-open\",\n \"is_query_report\": true,\n \"label\": \"Permitted Documents For User\",\n \"name\": \"Permitted Documents For User\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"DocShare\"\n ],\n \"description\": \"Report of all document shares\",\n \"doctype\": \"DocShare\",\n \"icon\": \"fa fa-share\",\n \"label\": \"Document Share Report\",\n \"name\": \"Document Share Report\",\n \"type\": \"report\"\n }\n]", - "title": "Permissions" + "hidden": 0, + "label": "Permissions", + "links": "[\n {\n \"description\": \"Set Permissions on Document Types and Roles\",\n \"icon\": \"fa fa-lock\",\n \"label\": \"Role Permissions Manager\",\n \"name\": \"permission-manager\",\n \"type\": \"page\"\n },\n {\n \"description\": \"Restrict user for specific document\",\n \"icon\": \"fa fa-lock\",\n \"label\": \"User Permissions\",\n \"name\": \"User Permission\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Set custom roles for page and report\",\n \"label\": \"Role Permission for Page and Report\",\n \"name\": \"Role Permission for Page and Report\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"User\"\n ],\n \"description\": \"Check which Documents are readable by a User\",\n \"doctype\": \"User\",\n \"icon\": \"fa fa-eye-open\",\n \"is_query_report\": true,\n \"label\": \"Permitted Documents For User\",\n \"name\": \"Permitted Documents For User\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"DocShare\"\n ],\n \"description\": \"Report of all document shares\",\n \"doctype\": \"DocShare\",\n \"icon\": \"fa fa-share\",\n \"label\": \"Document Share Report\",\n \"name\": \"Document Share Report\",\n \"type\": \"report\"\n }\n]" } ], "category": "Administration", @@ -27,7 +27,7 @@ "idx": 0, "is_standard": 1, "label": "Users", - "modified": "2020-03-12 16:30:42.483376", + "modified": "2020-04-01 11:24:40.767676", "modified_by": "Administrator", "module": "Core", "name": "Users", @@ -36,22 +36,22 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "User", "link_to": "User", "type": "DocType" }, { - "is_query_report": 0, + "label": "Role", "link_to": "Role", "type": "DocType" }, { - "is_query_report": 0, + "label": "permission-manager", "link_to": "permission-manager", "type": "Page" }, { - "is_query_report": 0, + "label": "user-profile", "link_to": "user-profile", "type": "Page" } diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 5ebde7e7bd..7ed14e094c 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -238,12 +238,14 @@ { "fieldname": "phone", "fieldtype": "Data", - "label": "Phone" + "label": "Phone", + "options": "Phone" }, { "fieldname": "mobile_no", "fieldtype": "Data", "label": "Mobile No", + "options": "Phone", "unique": 1 }, { @@ -588,7 +590,7 @@ "image_field": "user_image", "links": [], "max_attachments": 5, - "modified": "2020-03-23 22:59:26.154985", + "modified": "2020-04-08 12:27:36.716490", "modified_by": "Administrator", "module": "Core", "name": "User", diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index ddad3a91fb..7837c90d2b 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -205,7 +205,7 @@ class User(Document): _update_password(user=self.name, pwd=new_password, logout_all_sessions=self.logout_all_sessions) - if not self.flags.no_welcome_mail and self.send_welcome_email: + if not self.flags.no_welcome_mail and cint(self.send_welcome_email): self.send_welcome_mail_to_user() self.flags.email_sent = 1 if frappe.session.user != 'Guest': @@ -577,7 +577,7 @@ def update_password(new_password, logout_all_sessions=0, key=None, old_password= return redirect_url if redirect_url else "/" @frappe.whitelist(allow_guest=True) -def test_password_strength(new_password, key=None, old_password=None, user_data=[]): +def test_password_strength(new_password, key=None, old_password=None, user_data=None): from frappe.utils.password_strength import test_password_strength as _test_password_strength password_policy = frappe.db.get_value("System Settings", None, diff --git a/frappe/core/doctype/user_email/user_email.json b/frappe/core/doctype/user_email/user_email.json index 16e6b5a24e..b106ed4a19 100644 --- a/frappe/core/doctype/user_email/user_email.json +++ b/frappe/core/doctype/user_email/user_email.json @@ -1,201 +1,63 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-03-30 10:04:25.828742", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-03-30 10:04:25.828742", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email_account", + "email_id", + "column_break_3", + "awaiting_password", + "enable_outgoing" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Email Account", - "length": 0, - "no_copy": 0, - "options": "Email Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Email Account", + "options": "Email Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "email_account.email_id", - "fieldname": "email_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email ID", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email ID", + "options": "Email", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "email_account.awaiting_password", - "fieldname": "awaiting_password", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Awaiting Password", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "email_account.awaiting_password", + "fieldname": "awaiting_password", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Awaiting Password", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "email_account.enable_outgoing", - "fieldname": "enable_outgoing", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enable Outgoing", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fetch_from": "email_account.enable_outgoing", + "fieldname": "enable_outgoing", + "fieldtype": "Check", + "label": "Enable Outgoing", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-25 22:43:34.045787", - "modified_by": "Administrator", - "module": "Core", - "name": "User Email", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-04-06 19:19:12.130246", + "modified_by": "Administrator", + "module": "Core", + "name": "User Email", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js index 88bfba9e84..ad65b05894 100644 --- a/frappe/core/page/dashboard/dashboard.js +++ b/frappe/core/page/dashboard/dashboard.js @@ -97,7 +97,13 @@ class Dashboard { container: this.container, type: "chart", columns: 2, - allow_sorting: false, + options: { + allow_sorting: false, + allow_create: false, + allow_delete: false, + allow_hiding: false, + allow_edit: false, + }, widgets: this.charts, }); }) diff --git a/frappe/custom/desk_page/customization/customization.json b/frappe/custom/desk_page/customization/customization.json index dedfcaeeec..29f4cb745f 100644 --- a/frappe/custom/desk_page/customization/customization.json +++ b/frappe/custom/desk_page/customization/customization.json @@ -1,17 +1,19 @@ { "cards": [ { - "links": "[\n {\n \"label\": \"Dashboard\",\n \"name\": \"Dashboard\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Dashboard Chart\",\n \"name\": \"Dashboard Chart\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Dashboard Chart Source\",\n \"name\": \"Dashboard Chart Source\",\n \"type\": \"doctype\"\n }\n]", - "title": "Dashboards" + "hidden": 0, + "label": "Dashboards", + "links": "[\n {\n \"label\": \"Dashboard\",\n \"name\": \"Dashboard\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Dashboard Chart\",\n \"name\": \"Dashboard Chart\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Dashboard Chart Source\",\n \"name\": \"Dashboard Chart Source\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-glass", - "links": "[\n {\n \"description\": \"Change field properties (hide, readonly, permission etc.)\",\n \"label\": \"Customize Form\",\n \"name\": \"Customize Form\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add fields to forms.\",\n \"label\": \"Custom Field\",\n \"name\": \"Custom Field\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add custom javascript to forms.\",\n \"label\": \"Custom Script\",\n \"name\": \"Custom Script\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add custom forms.\",\n \"label\": \"DocType\",\n \"name\": \"DocType\",\n \"type\": \"doctype\"\n }\n]", - "title": "Form Customization" + "hidden": 0, + "label": "Form Customization", + "links": "[\n {\n \"description\": \"Change field properties (hide, readonly, permission etc.)\",\n \"label\": \"Customize Form\",\n \"name\": \"Customize Form\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add fields to forms.\",\n \"label\": \"Custom Field\",\n \"name\": \"Custom Field\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add custom javascript to forms.\",\n \"label\": \"Custom Script\",\n \"name\": \"Custom Script\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add custom forms.\",\n \"label\": \"DocType\",\n \"name\": \"DocType\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Add your own translations\",\n \"label\": \"Custom Translations\",\n \"name\": \"Translation\",\n \"type\": \"doctype\"\n }\n]", - "title": "Other" + "hidden": 0, + "label": "Other", + "links": "[\n {\n \"description\": \"Add your own translations\",\n \"label\": \"Custom Translations\",\n \"name\": \"Translation\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Administration", @@ -25,7 +27,7 @@ "idx": 0, "is_standard": 1, "label": "Customization", - "modified": "2020-03-12 16:30:42.155206", + "modified": "2020-04-01 11:24:40.787109", "modified_by": "Administrator", "module": "Custom", "name": "Customization", @@ -34,17 +36,17 @@ "pin_to_top": 0, "shortcuts": [ { - "is_query_report": 0, + "label": "Customize Form", "link_to": "Customize Form", "type": "DocType" }, { - "is_query_report": 0, + "label": "Custom Role", "link_to": "Custom Role", "type": "DocType" }, { - "is_query_report": 0, + "label": "Custom Script", "link_to": "Custom Script", "type": "DocType" } diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index 57b4cec23b..34778a76e9 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -358,7 +358,7 @@ "default": "0", "fieldname": "allow_in_quick_entry", "fieldtype": "Check", - "label": " Allow in Quick Entry " + "label": "Allow in Quick Entry" }, { "fieldname": "property_depends_on_section", @@ -385,7 +385,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-16 14:53:40.619043", + "modified": "2020-04-07 14:53:40.619043", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form Field", diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 1cb03355c6..26bc8c96b3 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -4,24 +4,34 @@ from __future__ import unicode_literals import frappe -import json -from frappe import _, DoesNotExistError +from json import loads, dumps +from frappe import _, DoesNotExistError, ValidationError, _dict from frappe.boot import get_allowed_pages, get_allowed_reports from six import string_types -from frappe.cache_manager import build_domain_restriced_doctype_cache, build_domain_restriced_page_cache, build_table_count_cache +from frappe.cache_manager import ( + build_domain_restriced_doctype_cache, + build_domain_restriced_page_cache, + build_table_count_cache +) class Workspace: def __init__(self, page_name): self.page_name = page_name - - def build_cache(self): - self.doc = frappe.get_doc("Desk Page", self.page_name) - self.get_pages_to_extend() + self.extended_cards = [] + self.extended_charts = [] + self.extended_shortcuts = [] user = frappe.get_user() user.build_permissions() - self.user = user + user_doc = frappe.get_doc('User', frappe.session.user) + self.blocked_modules = user_doc.get_blocked_modules() + self.doc = self.get_page_for_user() + + if self.doc.module in self.blocked_modules: + raise frappe.PermissionError + + self.user = user self.allowed_pages = get_allowed_pages() self.allowed_reports = get_allowed_reports() @@ -29,16 +39,27 @@ class Workspace: self.restricted_doctypes = build_domain_restriced_doctype_cache() self.restricted_pages = build_domain_restriced_page_cache() + def get_page_for_user(self): + filters = { + 'extends': self.page_name, + 'for_user': frappe.session.user + } + pages = frappe.get_list("Desk Page", filters=filters) + if pages: + return frappe.get_doc("Desk Page", pages[0]) + + self.get_pages_to_extend() + return frappe.get_doc("Desk Page", self.page_name) + def get_pages_to_extend(self): pages = frappe.get_all("Desk Page", filters={ "extends": self.page_name, - 'restrict_to_domain': ['in', frappe.get_active_domains()] + 'restrict_to_domain': ['in', frappe.get_active_domains()], + 'for_user': '', + 'module': ['not in', self.blocked_modules] }) pages = [frappe.get_doc("Desk Page", page['name']) for page in pages] - self.extended_cards = [] - self.extended_charts = [] - self.extended_shortcuts = [] for page in pages: self.extended_cards = self.extended_cards + page.cards @@ -61,17 +82,17 @@ class Workspace: def build_workspace(self): self.cards = { - 'label': self.doc.cards_label, + 'label': _(self.doc.cards_label), 'items': self.get_cards() } self.charts = { - 'label': self.doc.charts_label, + 'label': _(self.doc.charts_label), 'items': self.get_charts() } self.shortcuts = { - 'label': self.doc.shortcuts_label, + 'label': _(self.doc.shortcuts_label), 'items': self.get_shortcuts() } @@ -105,18 +126,21 @@ class Workspace: item["count"] = count + # Translate label + item["label"] = _(item.label) if item.label else _(item.name) + return item new_data = [] for section in cards: new_items = [] if isinstance(section.links, string_types): - links = json.loads(section.links) + links = loads(section.links) else: links = section.links for item in links: - item = frappe._dict(item) + item = _dict(item) # Condition: based on country if item.country and item.country != default_country: @@ -125,15 +149,15 @@ class Workspace: # Check if user is allowed to view if self.is_item_allowed(item.name, item.type): prepared_item = _prepare_item(item) - new_items.append(item) + new_items.append(prepared_item) if new_items: - if isinstance(section, frappe._dict): + if isinstance(section, _dict): new_section = section.copy() else: new_section = section.as_dict().copy() new_section["links"] = new_items - new_section["label"] = section.title + new_section["label"] = _(new_section["label"]) new_data.append(new_section) return new_data @@ -147,7 +171,8 @@ class Workspace: for chart in charts: if frappe.has_permission('Dashboard Chart', doc=chart.chart_name): - chart.label = chart.label if chart.label else chart.chart_name + # Translate label + chart.label = _(chart.label) if chart.label else _(chart.chart_name) all_charts.append(chart) return all_charts @@ -167,21 +192,23 @@ class Workspace: for item in shortcuts: new_item = item.as_dict().copy() - new_item['name'] = _(item.link_to) if self.is_item_allowed(item.link_to, item.type) and _in_active_domains(item): - if item.type == "Page": - page = self.allowed_pages[item.link_to] - new_item['label'] = _(page.get("title", frappe.unscrub(item.link_to))) if item.type == "Report": report = self.allowed_reports.get(item.link_to, {}) if report.get("report_type") in ["Query Report", "Script Report"]: new_item['is_query_report'] = 1 + else: + new_item['ref_doctype'] = report.get('ref_doctype') + + # Translate label + new_item["label"] = _(item.label) if item.label else _(item.link_to) items.append(new_item) return items @frappe.whitelist() +@frappe.read_only() def get_desktop_page(page): """Applies permissions, customizations and returns the configruration for a page on desk. @@ -192,9 +219,8 @@ def get_desktop_page(page): Returns: dict: dictionary of cards, charts and shortcuts to be displayed on website """ - wspace = Workspace(page) try: - wspace.build_cache() + wspace = Workspace(page) wspace.build_workspace() return { 'charts': wspace.charts, @@ -213,9 +239,14 @@ def get_desk_sidebar_items(): """Get list of sidebar items for desk """ # don't get domain restricted pages + blocked_modules = frappe.get_doc('User', frappe.session.user).get_blocked_modules() + filters = { 'restrict_to_domain': ['in', frappe.get_active_domains()], - 'extends_another_page': False + 'extends_another_page': 0, + 'is_standard': 1, + 'for_user': '', + 'module': ['not in', blocked_modules] } if not frappe.local.conf.developer_mode: @@ -228,8 +259,10 @@ def get_desk_sidebar_items(): from collections import defaultdict sidebar_items = defaultdict(list) + # The order will be maintained while categorizing for page in pages: - # The order will be maintained while categorizing + # Translate label + page['label'] = _(page.get('name')) sidebar_items[page["category"]].append(page) return sidebar_items @@ -242,8 +275,8 @@ def get_table_with_counts(): def get_custom_reports_and_doctypes(module): return [ - frappe._dict({ - "title": "Custom", + _dict({ + "label": "Custom", "links": get_custom_doctype_list(module) + get_custom_report_list(module) }) ] @@ -280,104 +313,111 @@ def get_custom_report_list(module): return out -def make_them_pages(): - """Helper function to make pages +def get_custom_workspace_for_user(page): + """Get custom page from desk_page if exists or create one + + Args: + page (stirng): Page name + + Returns: + Object: Document object """ - pages = [ - ('Desk', 'frappe', 'octicon octicon-calendar'), - ('Settings', 'frappe', 'octicon octicon-settings'), - ('Users and Permissions', 'frappe', 'octicon octicon-settings'), - ('Customization', 'frappe', 'octicon octicon-settings'), - ('Integrations', 'frappe', 'octicon octicon-globe'), - ('Core', 'frappe', 'octicon octicon-circuit-board'), - ('Website', 'frappe', 'octicon octicon-globe'), - ('Getting Started', 'erpnext', 'fa fa-check-square-o'), - ('Accounts', 'erpnext', 'octicon octicon-repo'), - ('Selling', 'erpnext', 'octicon octicon-tag'), - ('Buying', 'erpnext', 'octicon octicon-briefcase'), - ('Stock', 'erpnext', 'octicon octicon-package'), - ('Assets', 'erpnext', 'octicon octicon-database'), - ('Projects', 'erpnext', 'octicon octicon-rocket'), - ('CRM', 'erpnext', 'octicon octicon-broadcast'), - ('Support', 'erpnext', 'fa fa-check-square-o'), - ('HR', 'erpnext', 'octicon octicon-organization'), - ('Quality Management', 'erpnext', 'fa fa-check-square-o'), - ('Manufacturing', 'erpnext', 'octicon octicon-tools'), - ('Retail', 'erpnext', 'octicon octicon-credit-card'), - ('Education', 'erpnext', 'octicon octicon-mortar-board'), - ('Healthcare', 'erpnext', 'fa fa-heartbeat'), - ('Agriculture', 'erpnext', 'octicon octicon-globe'), - ('Non Profit', 'erpnext', 'octicon octicon-heart'), - ('Help', 'erpnext', 'octicon octicon-device-camera-video') - ] - - for page in pages: - print("Processing Page: {0}".format(page[0])) - make_them_cards(page[0], page[2]) + filters = { + 'extends': page, + 'for_user': frappe.session.user + } + pages = frappe.get_list("Desk Page", filters=filters) + if pages: + return frappe.get_doc("Desk Page", pages[0]) + doc = frappe.new_doc("Desk Page") + doc.extends = page + doc.for_user = frappe.session.user + return doc -def make_them_cards(page_name, from_module=None, to_module=None, icon=None): - from frappe.desk.moduleview import get +@frappe.whitelist() +def save_customization(page, config): + """Save customizations as a separate doctype in Desk page per user - if not from_module: - from_module = page_name + Args: + page (string): Name of the page to be edited + config (dict): Dictionary config of al widgets - if not to_module: - to_module = page_name + Returns: + Boolean: Customization saving status + """ + original_page = frappe.get_doc("Desk Page", page) + page_doc = get_custom_workspace_for_user(page) + + # Update field values + page_doc.update({ + "charts_label": original_page.charts_label, + "cards_label": original_page.cards_label, + "shortcuts_label": original_page.shortcuts_label, + "icon": original_page.icon, + "module": original_page.module, + "developer_mode_only": original_page.developer_mode_only, + "category": original_page.category + }) + + config = _dict(loads(config)) + page_doc.charts = prepare_widget(config.charts, "Desk Chart", "charts") + page_doc.shortcuts = prepare_widget(config.shortcuts, "Desk Shortcut", "shortcuts") + page_doc.cards = prepare_widget(config.cards, "Desk Card", "cards") + + # Set label + page_doc.label = page + '-' + frappe.session.user try: - modules = get(from_module)['data'] - except: - return + if page_doc.is_new(): + page_doc.insert(ignore_permissions=True) + else: + page_doc.save(ignore_permissions=True) + except (ValidationError, TypeError) as e: + # Create a json string to log + json_config = dumps(config, sort_keys=True, indent=4) - # Find or make page doc - if frappe.db.exists("Desk Page", page_name): - page = frappe.get_doc("Desk Page", page_name) - print("--- Got Page: {0}".format(page.name)) - else: - page = frappe.new_doc("Desk Page") - page.label = page_name - page.cards = [] - page.icon = icon - print("--- New Page: {0}".format(page.name)) + # Error log body + log = \ + """ + page: {0} + config: {1} + exception: {2} + """.format(page, json_config, e) + frappe.log_error(log, _("Could not save customization")) + return False - # Guess Which Module - if not to_module and frappe.db.exists("Module Def", page_name): - page.module = page_name + return True - if to_module: - page.module = to_module - elif frappe.db.exists("Module Def", page_name): - page.module = page_name - for data in modules: - # Create a New Card Child Doc - card = frappe.new_doc("Desk Card") +def prepare_widget(config, doctype, parentfield): + """Create widget child table entries with parent details - # Data clean up - for item in data['items']: - try: - del item['count'] - del item['incomplete_dependencies'] - except KeyError: - pass + Args: + config (dict): Dictionary containing widget config + doctype (string): Doctype name of the child table + parentfield (string): Parent field for the child table - # Set Child doc values - card.title = data['label'] - card.icon = data.get('icon') - # Pretty dump JSON - card.links = json.dumps(data['items'], indent=4, sort_keys=True) + Returns: + TYPE: List of Document objects + """ + order = config.get('order') + widgets = config.get('widgets') + prepare_widget_list = [] + for idx, name in enumerate(order): + wid_config = widgets[name].copy() + # Some cleanup + wid_config.pop("name", None) - # Set Parent attributes - card.parent = page.name - card.parenttype = page.doctype - card.parentfield = "cards" + # New Doc + doc = frappe.new_doc(doctype) + doc.update(wid_config) - # Add cards to page doc - print("------- Adding Card: {0}".format(card.title)) - page.cards.append(card) + # Manually Set IDX + doc.idx = idx + 1 - # End it all - page.save() - frappe.db.commit() - return \ No newline at end of file + # Set Parent Field + doc.parentfield = parentfield + + prepare_widget_list.append(doc) + return prepare_widget_list diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json index 9652ae3945..676cdbe24a 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.json +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.json @@ -49,7 +49,8 @@ "fieldname": "chart_type", "fieldtype": "Select", "label": "Chart Type", - "options": "Count\nSum\nAverage\nGroup By\nCustom\nReport" + "options": "Count\nSum\nAverage\nGroup By\nCustom\nReport", + "set_only_once": 1 }, { "depends_on": "eval:doc.chart_type === 'Custom'", @@ -215,7 +216,7 @@ } ], "links": [], - "modified": "2020-03-31 16:00:01.987059", + "modified": "2020-04-08 18:54:36.739183", "modified_by": "Administrator", "module": "Desk", "name": "Dashboard Chart", diff --git a/frappe/desk/doctype/desk_card/desk_card.json b/frappe/desk/doctype/desk_card/desk_card.json index 4ccffd4f58..dbcb4b0d5c 100644 --- a/frappe/desk/doctype/desk_card/desk_card.json +++ b/frappe/desk/doctype/desk_card/desk_card.json @@ -2,11 +2,12 @@ "actions": [], "creation": "2020-01-29 14:45:54.383089", "doctype": "DocType", + "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "title", + "label", "column_break_2", - "icon", + "hidden", "section_break_3", "links" ], @@ -18,13 +19,6 @@ "options": "JSON", "reqd": 1 }, - { - "fieldname": "title", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Title", - "reqd": 1 - }, { "fieldname": "section_break_3", "fieldtype": "Section Break" @@ -34,14 +28,23 @@ "fieldtype": "Column Break" }, { - "fieldname": "icon", + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Hidden" + }, + { + "fieldname": "label", "fieldtype": "Data", - "label": "Icon" + "in_list_view": 1, + "label": "Label", + "reqd": 1 } ], "istable": 1, "links": [], - "modified": "2020-02-03 12:40:42.595122", + "modified": "2020-03-31 14:38:06.303847", "modified_by": "Administrator", "module": "Desk", "name": "Desk Card", diff --git a/frappe/desk/doctype/desk_chart/desk_chart.json b/frappe/desk/doctype/desk_chart/desk_chart.json index c3c9231353..09deefd59d 100644 --- a/frappe/desk/doctype/desk_chart/desk_chart.json +++ b/frappe/desk/doctype/desk_chart/desk_chart.json @@ -26,7 +26,7 @@ ], "istable": 1, "links": [], - "modified": "2020-03-20 10:04:13.992228", + "modified": "2020-03-31 13:33:13.128804", "modified_by": "Administrator", "module": "Desk", "name": "Desk Chart", diff --git a/frappe/desk/doctype/desk_page/desk_page.js b/frappe/desk/doctype/desk_page/desk_page.js index 7af3e0e98c..3087a5f5b8 100644 --- a/frappe/desk/doctype/desk_page/desk_page.js +++ b/frappe/desk/doctype/desk_page/desk_page.js @@ -2,17 +2,21 @@ // For license information, please see license.txt frappe.ui.form.on('Desk Page', { - refresh: function(frm) { + setup: function(frm) { frm.get_field("is_standard").toggle(frappe.boot.developer_mode); frm.get_field("extends_another_page").toggle(frappe.boot.developer_mode); - if (!frappe.boot.developer_mode) { - frm.set_read_only(); - frm.fields - .filter(field => field.has_input) - .forEach(field => { - frm.set_df_property(field.df.fieldname, "read_only", "1"); - }); - frm.disable_save(); + if (!frappe.boot.developer_mode || frm.doc.for_user) { + frm.trigger('disable_form'); } + }, + + disable_form: function(frm) { + frm.set_read_only(); + frm.fields + .filter(field => field.has_input) + .forEach(field => { + frm.set_df_property(field.df.fieldname, "read_only", "1"); + }); + frm.disable_save(); } -}); +}); \ No newline at end of file diff --git a/frappe/desk/doctype/desk_page/desk_page.json b/frappe/desk/doctype/desk_page/desk_page.json index 6bc33d1326..7e6baf221b 100644 --- a/frappe/desk/doctype/desk_page/desk_page.json +++ b/frappe/desk/doctype/desk_page/desk_page.json @@ -9,6 +9,7 @@ "field_order": [ "label", "extends", + "for_user", "module", "category", "restrict_to_domain", @@ -36,7 +37,6 @@ "fieldname": "label", "fieldtype": "Data", "label": "Name", - "length": 22, "unique": 1 }, { @@ -52,6 +52,7 @@ "options": "Desk Chart" }, { + "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", "fieldname": "shortcuts", "fieldtype": "Table", "label": "Shortcuts", @@ -136,16 +137,19 @@ "search_index": 1 }, { + "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", "fieldname": "charts_label", "fieldtype": "Data", "label": "Label" }, { + "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", "fieldname": "shortcuts_label", "fieldtype": "Data", "label": "Label" }, { + "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", "fieldname": "cards_label", "fieldtype": "Data", "label": "Label" @@ -166,24 +170,36 @@ "default": "0", "fieldname": "is_standard", "fieldtype": "Check", - "label": "Is Standard" + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Is Standard", + "search_index": 1 }, { "default": "0", "fieldname": "extends_another_page", "fieldtype": "Check", - "label": "Extends Another Page" + "label": "Extends Another Page", + "search_index": 1 }, { "depends_on": "eval:doc.extends_another_page == 1", "fieldname": "extends", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Extends", - "options": "Desk Page" + "options": "Desk Page", + "search_index": 1 + }, + { + "fieldname": "for_user", + "fieldtype": "Data", + "label": "For User", + "read_only": 1 } ], "links": [], - "modified": "2020-03-12 16:38:16.206732", + "modified": "2020-03-26 12:35:41.981432", "modified_by": "Administrator", "module": "Desk", "name": "Desk Page", diff --git a/frappe/desk/doctype/desk_shortcut/desk_shortcut.json b/frappe/desk/doctype/desk_shortcut/desk_shortcut.json index 6b57f250a9..9f8990732a 100644 --- a/frappe/desk/doctype/desk_shortcut/desk_shortcut.json +++ b/frappe/desk/doctype/desk_shortcut/desk_shortcut.json @@ -6,11 +6,11 @@ "engine": "InnoDB", "field_order": [ "type", - "icon", + "label", "column_break_4", "link_to", + "icon", "restrict_to_domain", - "is_query_report", "section_break_5", "stats_filter", "column_break_3", @@ -51,6 +51,7 @@ "label": "Format" }, { + "depends_on": "eval:doc.type == \"DocType\" && frappe.boot.developer_mode", "fieldname": "section_break_5", "fieldtype": "Section Break", "label": "Count Filter" @@ -61,13 +62,7 @@ "label": "Color" }, { - "default": "0", - "depends_on": "eval:doc.type === \"Report\"", - "fieldname": "is_query_report", - "fieldtype": "Check", - "label": "Is Query Report" - }, - { + "depends_on": "eval:frappe.boot.developer_mode", "fieldname": "icon", "fieldtype": "Data", "label": "Icon" @@ -77,15 +72,22 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:frappe.boot.developer_mode", "fieldname": "restrict_to_domain", "fieldtype": "Link", "label": "Restrict to Domain", "options": "Domain" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "label": "Label", + "reqd": 1 } ], "istable": 1, "links": [], - "modified": "2020-03-11 13:09:00.180528", + "modified": "2020-04-07 19:04:23.645198", "modified_by": "Administrator", "module": "Desk", "name": "Desk Shortcut", diff --git a/frappe/desk/doctype/notification_settings/notification_settings.js b/frappe/desk/doctype/notification_settings/notification_settings.js index d4e3b08def..b8b7f37a4f 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.js +++ b/frappe/desk/doctype/notification_settings/notification_settings.js @@ -8,5 +8,14 @@ frappe.ui.form.on('Notification Settings', { route: '#modules/Settings', type: 'Custom' }); + }, + + refresh: (frm) => { + if (frappe.user.has_role('System Manager')) { + frm.add_custom_button('Go to Notification Settings List', () => { + frappe.set_route('List', 'Notification Settings'); + }); + } } + }); diff --git a/frappe/desk/doctype/notification_settings/notification_settings.py b/frappe/desk/doctype/notification_settings/notification_settings.py index 233d15e3ce..6b5a13ee27 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.py +++ b/frappe/desk/doctype/notification_settings/notification_settings.py @@ -62,7 +62,14 @@ def get_subscribed_documents(): def get_permission_query_conditions(user): if not user: user = frappe.session.user - return '''(`tabNotification Settings`.user = '{user}')'''.format(user=user) + if user == 'Administrator': + return + + roles = frappe.get_roles(user) + if "System Manager" in roles: + return '''(`tabNotification Settings`.name != 'Administrator')''' + + return '''(`tabNotification Settings`.name = '{user}')'''.format(user=user) @frappe.whitelist() def set_seen_value(value, user): diff --git a/frappe/desk/doctype/todo/todo_calendar.js b/frappe/desk/doctype/todo/todo_calendar.js new file mode 100644 index 0000000000..4545846cf9 --- /dev/null +++ b/frappe/desk/doctype/todo/todo_calendar.js @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.views.calendar["ToDo"] = { + field_map: { + "start": "date", + "end": "date", + "id": "name", + "title": "description", + "allDay": "allDay", + "progress": "progress" + }, + gantt: true, + filters: [ + { + "fieldtype": "Link", + "fieldname": "reference_type", + "options": "Task", + "label": __("Task") + }, + { + "fieldtype": "Dynamic Link", + "fieldname": "reference_name", + "options": "reference_type", + "label": __("Task") + } + + ], + get_events_method: "frappe.desk.calendar.get_events" +}; + diff --git a/frappe/desk/page/setup_wizard/install_fixtures.py b/frappe/desk/page/setup_wizard/install_fixtures.py index e7e147fb7d..c857bd077f 100644 --- a/frappe/desk/page/setup_wizard/install_fixtures.py +++ b/frappe/desk/page/setup_wizard/install_fixtures.py @@ -8,16 +8,22 @@ from frappe import _ from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes def install(): - update_genders_and_salutations() + update_genders() + update_salutations() update_global_search_doctypes() setup_email_linking() @frappe.whitelist() -def update_genders_and_salutations(): - default_genders = [_("Male"), _("Female"), _("Other")] - default_salutations = [_("Mr"), _("Ms"), _('Mx'), _("Dr"), _("Mrs"), _("Madam"), _("Miss"), _("Master"), _("Prof")] +def update_genders(): + default_genders = [_("Male"), _("Female"), _("Other"),_("Transgender"), _("Genderqueer"), _("Non-Conforming"),_("Prefer not to say")] records = [{'doctype': 'Gender', 'gender': d} for d in default_genders] - records += [{'doctype': 'Salutation', 'salutation': d} for d in default_salutations] + for record in records: + frappe.get_doc(record).insert(ignore_permissions=True, ignore_if_duplicate=True) + +@frappe.whitelist() +def update_salutations(): + default_salutations = [_("Mr"), _("Ms"), _('Mx'), _("Dr"), _("Mrs"), _("Madam"), _("Miss"), _("Master"), _("Prof")] + records = [{'doctype': 'Salutation', 'salutation': d} for d in default_salutations] for record in records: doc = frappe.new_doc(record.get("doctype")) doc.update(record) diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json index e724102fdf..5c57a7f35d 100644 --- a/frappe/email/doctype/email_account/email_account.json +++ b/frappe/email/doctype/email_account/email_account.json @@ -66,6 +66,7 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Email Address", + "options": "Email", "reqd": 1 }, { @@ -410,7 +411,7 @@ ], "icon": "fa fa-inbox", "links": [], - "modified": "2019-12-18 15:56:39.744520", + "modified": "2020-04-06 19:20:50.491146", "modified_by": "Administrator", "module": "Email", "name": "Email Account", diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index 23495b2d70..b19a134713 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -68,8 +68,7 @@ def add_subscribers(name, email_list): email_list = email_list.replace(",", "\n").split("\n") template = frappe.db.get_value('Email Group', name, 'welcome_email_template') - if template: - welcome_email = frappe.get_doc("Email Template", template) + welcome_email = frappe.get_doc("Email Template", template) if template else None count = 0 for email in email_list: @@ -108,4 +107,4 @@ def send_welcome_email(welcome_email, email, email_group): ) message = frappe.render_template(welcome_email.response, args) - frappe.sendmail(email, subject=welcome_email.subject, message=message) \ No newline at end of file + frappe.sendmail(email, subject=welcome_email.subject, message=message) diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 2d40ffd800..2469569892 100755 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -51,9 +51,6 @@ class Newsletter(WebsiteGenerator): frappe.msgprint(_("Scheduled to send to {0} recipients").format(len(self.recipients))) - frappe.db.set(self, "email_sent", 1) - frappe.db.set(self, "schedule_send", now_datetime()) - frappe.db.set(self, 'scheduled_to_send', len(self.recipients)) else: frappe.msgprint(_("Newsletter should have atleast one recipient")) @@ -71,8 +68,8 @@ class Newsletter(WebsiteGenerator): attachments = [] if self.send_attachements: - files = frappe.get_all("File", fields = ["name"], filters = {"attached_to_doctype": "Newsletter", - "attached_to_name":self.name}, order_by="creation desc") + files = frappe.get_all("File", fields=["name"], filters={"attached_to_doctype": "Newsletter", + "attached_to_name": self.name}, order_by="creation desc") for file in files: try: @@ -82,17 +79,21 @@ class Newsletter(WebsiteGenerator): except IOError: frappe.throw(_("Unable to find attachment {0}").format(file.name)) - send(recipients = self.recipients, sender = sender, - subject = self.subject, message = self.message, - reference_doctype = self.doctype, reference_name = self.name, - add_unsubscribe_link = self.send_unsubscribe_link, attachments=attachments, - unsubscribe_method = "/unsubscribe", - unsubscribe_params = {"name": self.name}, - send_priority = 0, queue_separately=True) + send(recipients=self.recipients, sender=sender, + subject=self.subject, message=self.message, + reference_doctype=self.doctype, reference_name=self.name, + add_unsubscribe_link=self.send_unsubscribe_link, attachments=attachments, + unsubscribe_method="/unsubscribe", + unsubscribe_params={"name": self.name}, + send_priority=0, queue_separately=True) if not frappe.flags.in_test: frappe.db.auto_commit_on_many_writes = False + self.db_set("email_sent", 1) + self.db_set("schedule_send", now_datetime()) + self.db_set("scheduled_to_send", len(self.recipients)) + def get_recipients(self): """Get recipients from Email Group""" recipients_list = [] @@ -268,6 +269,6 @@ def send_scheduled_email(): scheduled_newsletter = frappe.get_all('Newsletter', filters = { 'schedule_send': ('<=', now_datetime()), 'email_sent': 0 - }, fields = ['name']) + }, fields = ['name'], ignore_ifnull=True) for newsletter in scheduled_newsletter: - send_newsletter(newsletter.name) \ No newline at end of file + send_newsletter(newsletter.name) diff --git a/frappe/integrations/desk_page/integrations/integrations.json b/frappe/integrations/desk_page/integrations/integrations.json index 6ea871cd90..9201e223f8 100644 --- a/frappe/integrations/desk_page/integrations/integrations.json +++ b/frappe/integrations/desk_page/integrations/integrations.json @@ -1,25 +1,29 @@ { "cards": [ { - "links": "[\n {\n \"description\": \"Dropbox backup settings\",\n \"label\": \"Dropbox Settings\",\n \"name\": \"Dropbox Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"S3 Backup Settings\",\n \"label\": \"S3 Backup Settings\",\n \"name\": \"S3 Backup Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Drive Backup.\",\n \"label\": \"Google Drive\",\n \"name\": \"Google Drive\",\n \"type\": \"doctype\"\n }\n]", - "title": "Backup" + "hidden": 0, + "label": "Backup", + "links": "[\n {\n \"description\": \"Dropbox backup settings\",\n \"label\": \"Dropbox Settings\",\n \"name\": \"Dropbox Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"S3 Backup Settings\",\n \"label\": \"S3 Backup Settings\",\n \"name\": \"S3 Backup Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Drive Backup.\",\n \"label\": \"Google Drive\",\n \"name\": \"Google Drive\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Google API Settings.\",\n \"label\": \"Google Settings\",\n \"name\": \"Google Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Contacts Integration.\",\n \"label\": \"Google Contacts\",\n \"name\": \"Google Contacts\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Calendar Integration.\",\n \"label\": \"Google Calendar\",\n \"name\": \"Google Calendar\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Drive Integration.\",\n \"label\": \"Google Drive\",\n \"name\": \"Google Drive\",\n \"type\": \"doctype\"\n }\n]", - "title": "Google Services" + "hidden": 0, + "label": "Google Services", + "links": "[\n {\n \"description\": \"Google API Settings.\",\n \"label\": \"Google Settings\",\n \"name\": \"Google Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Contacts Integration.\",\n \"label\": \"Google Contacts\",\n \"name\": \"Google Contacts\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Calendar Integration.\",\n \"label\": \"Google Calendar\",\n \"name\": \"Google Calendar\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Google Drive Integration.\",\n \"label\": \"Google Drive\",\n \"name\": \"Google Drive\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Webhooks calling API requests into web apps\",\n \"label\": \"Webhook\",\n \"name\": \"Webhook\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Slack Webhooks for internal integration\",\n \"label\": \"Slack Webhook URL\",\n \"name\": \"Slack Webhook URL\",\n \"type\": \"doctype\"\n }\n]", - "title": "Webhook" + "hidden": 0, + "label": "Webhook", + "links": "[\n {\n \"description\": \"Webhooks calling API requests into web apps\",\n \"label\": \"Webhook\",\n \"name\": \"Webhook\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Slack Webhooks for internal integration\",\n \"label\": \"Slack Webhook URL\",\n \"name\": \"Slack Webhook URL\",\n \"type\": \"doctype\"\n }\n]" }, { - "links": "[\n {\n \"description\": \"Enter keys to enable login via Facebook, Google, GitHub.\",\n \"label\": \"Social Login Key\",\n \"name\": \"Social Login Key\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Ldap settings\",\n \"label\": \"LDAP Settings\",\n \"name\": \"LDAP Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Register OAuth Client App\",\n \"label\": \"OAuth Client\",\n \"name\": \"OAuth Client\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for OAuth Provider\",\n \"label\": \"OAuth Provider Settings\",\n \"name\": \"OAuth Provider Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Authentication" + "hidden": 0, + "label": "Authentication", + "links": "[\n {\n \"description\": \"Enter keys to enable login via Facebook, Google, GitHub.\",\n \"label\": \"Social Login Key\",\n \"name\": \"Social Login Key\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Ldap settings\",\n \"label\": \"LDAP Settings\",\n \"name\": \"LDAP Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Register OAuth Client App\",\n \"label\": \"OAuth Client\",\n \"name\": \"OAuth Client\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Settings for OAuth Provider\",\n \"label\": \"OAuth Provider Settings\",\n \"name\": \"OAuth Provider Settings\",\n \"type\": \"doctype\"\n }\n]" }, { - "icon": "fa fa-star", - "links": "[\n {\n \"description\": \"Braintree payment gateway settings\",\n \"label\": \"Braintree Settings\",\n \"name\": \"Braintree Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"PayPal payment gateway settings\",\n \"label\": \"PayPal Settings\",\n \"name\": \"PayPal Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Razorpay Payment gateway settings\",\n \"label\": \"Razorpay Settings\",\n \"name\": \"Razorpay Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stripe payment gateway settings\",\n \"label\": \"Stripe Settings\",\n \"name\": \"Stripe Settings\",\n \"type\": \"doctype\"\n }\n]", - "title": "Payments" + "hidden": 0, + "label": "Payments", + "links": "[\n {\n \"description\": \"Braintree payment gateway settings\",\n \"label\": \"Braintree Settings\",\n \"name\": \"Braintree Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"PayPal payment gateway settings\",\n \"label\": \"PayPal Settings\",\n \"name\": \"PayPal Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Razorpay Payment gateway settings\",\n \"label\": \"Razorpay Settings\",\n \"name\": \"Razorpay Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Stripe payment gateway settings\",\n \"label\": \"Stripe Settings\",\n \"name\": \"Stripe Settings\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Administration", @@ -34,7 +38,7 @@ "idx": 0, "is_standard": 1, "label": "Integrations", - "modified": "2020-03-12 16:30:42.823316", + "modified": "2020-04-01 11:24:40.751651", "modified_by": "Administrator", "module": "Integrations", "name": "Integrations", diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json index 33d34e0210..858469647a 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.json @@ -1,487 +1,129 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-09-21 10:12:57.399174", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 1, + "creation": "2016-09-21 10:12:57.399174", + "doctype": "DocType", + "document_type": "System", + "editable_grid": 1, + "field_order": [ + "enabled", + "send_notifications_to", + "send_email_for_successful_backup", + "backup_frequency", + "limit_no_of_backups", + "no_of_backups", + "file_backup", + "app_access_key", + "app_secret_key", + "allow_dropbox_access", + "dropbox_access_key", + "dropbox_access_secret", + "dropbox_access_token" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "send_notifications_to", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Send Notifications To", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "send_notifications_to", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Send Notifications To", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "description": "Note: By default emails for failed backups are sent.", - "fieldname": "send_email_for_successful_backup", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Send Email for Successful Backup", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "description": "Note: By default emails for failed backups are sent.", + "fieldname": "send_email_for_successful_backup", + "fieldtype": "Check", + "label": "Send Email for Successful Backup" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "backup_frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Backup Frequency", - "length": 0, - "no_copy": 0, - "options": "\nDaily\nWeekly", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "backup_frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Backup Frequency", + "options": "\nDaily\nWeekly", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "limit_no_of_backups", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Limit Number of DB Backups", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "limit_no_of_backups", + "fieldtype": "Check", + "label": "Limit Number of DB Backups" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "5", - "depends_on": "eval:doc.limit_no_of_backups", - "fieldname": "no_of_backups", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Number of DB Backups", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "5", + "depends_on": "eval:doc.limit_no_of_backups", + "fieldname": "no_of_backups", + "fieldtype": "Int", + "label": "Number of DB Backups" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "file_backup", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "File Backup", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "file_backup", + "fieldtype": "Check", + "label": "File Backup" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "app_access_key", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "App Access Key", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "app_access_key", + "fieldtype": "Data", + "label": "App Access Key" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "app_secret_key", - "fieldtype": "Password", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "App Secret Key", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "app_secret_key", + "fieldtype": "Password", + "label": "App Secret Key" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_dropbox_access", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Dropbox Access", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "allow_dropbox_access", + "fieldtype": "Button", + "label": "Allow Dropbox Access" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dropbox_access_key", - "fieldtype": "Password", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dropbox Access Key", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "dropbox_access_key", + "fieldtype": "Password", + "hidden": 1, + "label": "Dropbox Access Key", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dropbox_access_secret", - "fieldtype": "Password", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dropbox Access Secret", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "dropbox_access_secret", + "fieldtype": "Password", + "hidden": 1, + "label": "Dropbox Access Secret", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "dropbox_access_token", - "fieldtype": "Password", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dropbox Access Token", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "dropbox_access_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Dropbox Access Token" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-01-03 05:44:40.520943", - "modified_by": "Administrator", - "module": "Integrations", - "name": "Dropbox Settings", - "name_case": "", - "owner": "Administrator", + ], + "in_create": 1, + "issingle": 1, + "modified": "2019-08-22 16:26:44.468391", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Dropbox Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py index dd4768f8b3..2a036f4838 100644 --- a/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py +++ b/frappe/integrations/doctype/dropbox_settings/dropbox_settings.py @@ -3,22 +3,25 @@ # For license information, please see license.txt from __future__ import unicode_literals +import dropbox +import json import frappe import os from frappe import _ from frappe.model.document import Document -import dropbox, json +from frappe.integrations.offsite_backup_utils import get_latest_backup_file, send_email, validate_file_size +from frappe.integrations.utils import make_post_request +from frappe.utils import (cint, get_request_site_address, + get_files_path, get_backups_path, get_url, encode) from frappe.utils.backups import new_backup from frappe.utils.background_jobs import enqueue from six.moves.urllib.parse import urlparse, parse_qs -from frappe.integrations.utils import make_post_request from rq.timeouts import JobTimeoutException -from frappe.utils import (cint, split_emails, get_request_site_address, - get_files_path, get_backups_path, get_url, encode) from six import text_type ignore_list = [".DS_Store"] + class DropboxSettings(Document): def onload(self): if not self.app_access_key and frappe.conf.dropbox_access_key: @@ -48,10 +51,12 @@ def take_backup_to_dropbox(retry_count=0, upload_db_backup=True): did_not_upload, error_log = [], [] try: if cint(frappe.db.get_value("Dropbox Settings", None, "enabled")): + validate_file_size() + did_not_upload, error_log = backup_to_dropbox(upload_db_backup) if did_not_upload: raise Exception - send_email(True, "Dropbox") + send_email(True, "Dropbox", "Dropbox Settings", "send_notifications_to") except JobTimeoutException: if retry_count < 2: args = { @@ -66,34 +71,8 @@ def take_backup_to_dropbox(retry_count=0, upload_db_backup=True): else: file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] error_message = ("\n".join(file_and_error) + "\n" + frappe.get_traceback()) - frappe.errprint(error_message) - send_email(False, "Dropbox", error_message) -def send_email(success, service_name, error_status=None): - if success: - if frappe.db.get_value("Dropbox Settings", None, "send_email_for_successful_backup") == '0': - return - - subject = "Backup Upload Successful" - message ="""

Backup Uploaded Successfully

Hi there, this is just to inform you - that your backup was successfully uploaded to your %s account. So relax!

- """ % service_name - - else: - subject = "[Warning] Backup Upload Failed" - message ="""

Backup Upload Failed

Oops, your automated backup to %s - failed.

-

Error message:
-

%s
-

-

Please contact your system manager for more information.

- """ % (service_name, error_status) - - if not frappe.db: - frappe.connect() - - recipients = split_emails(frappe.db.get_value("Dropbox Settings", None, "send_notifications_to")) - frappe.sendmail(recipients=recipients, subject=subject, message=message) + send_email(False, "Dropbox", "Dropbox Settings", "send_notifications_to", error_message) def backup_to_dropbox(upload_db_backup=True): if not frappe.db: @@ -114,8 +93,12 @@ def backup_to_dropbox(upload_db_backup=True): dropbox_client = dropbox.Dropbox(dropbox_settings['access_token']) if upload_db_backup: - backup = new_backup(ignore_files=True) - filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) + if frappe.flags.create_new_backup: + backup = new_backup(ignore_files=True) + filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) + else: + filename = get_latest_backup_file() + upload_file_to_dropbox(filename, "/database", dropbox_client) # delete older databases diff --git a/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py new file mode 100644 index 0000000000..539fc417f2 --- /dev/null +++ b/frappe/integrations/doctype/dropbox_settings/test_dropbox_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestDropboxSettings(unittest.TestCase): + pass diff --git a/frappe/integrations/doctype/google_drive/google_drive.py b/frappe/integrations/doctype/google_drive/google_drive.py index 8078c702c0..60ee173bbf 100644 --- a/frappe/integrations/doctype/google_drive/google_drive.py +++ b/frappe/integrations/doctype/google_drive/google_drive.py @@ -19,6 +19,7 @@ from apiclient.http import MediaFileUpload from frappe.utils import get_backups_path, get_bench_path from frappe.utils.backups import new_backup from frappe.integrations.doctype.google_settings.google_settings import get_auth_url +from frappe.integrations.offsite_backup_utils import get_latest_backup_file, send_email, validate_file_size SCOPES = "https://www.googleapis.com/auth/drive" @@ -183,13 +184,16 @@ def upload_system_backup_to_google_drive(): check_for_folder_in_google_drive() account.load_from_db() - progress(1, "Backing up Data.") - backup = new_backup() - - fileurl_backup = os.path.basename(backup.backup_path_db) - fileurl_public_files = os.path.basename(backup.backup_path_files) - fileurl_private_files = os.path.basename(backup.backup_path_private_files) + validate_file_size() + if frappe.flags.create_new_backup: + set_progress(1, "Backing up Data.") + backup = new_backup() + fileurl_backup = os.path.basename(backup.backup_path_db) + fileurl_public_files = os.path.basename(backup.backup_path_files) + fileurl_private_files = os.path.basename(backup.backup_path_private_files) + else: + fileurl_backup, fileurl_public_files, fileurl_private_files = get_latest_backup_file(with_files=True) for fileurl in [fileurl_backup, fileurl_public_files, fileurl_private_files]: file_metadata = { @@ -203,15 +207,14 @@ def upload_system_backup_to_google_drive(): frappe.throw(_("Google Drive - Could not locate locate - {0}").format(e)) try: - progress(2, "Uploading backup to Google Drive.") + set_progress(2, "Uploading backup to Google Drive.") google_drive.files().create(body=file_metadata, media_body=media, fields="id").execute() except HttpError as e: - send_email(success=False, error=e) - frappe.msgprint(_("Google Drive - Could not upload backup - Error {0}").format(e)) + send_email(False, "Google Drive", "Google Drive", "email", error_status=e) - progress(3, "Uploading successful.") + set_progress(3, "Uploading successful.") frappe.db.set_value("Google Drive", None, "last_backup_on", frappe.utils.now_datetime()) - send_email(success=True) + send_email(True, "Google Drive", "Google Drive", "email") return _("Google Drive Backup Successful.") def daily_backup(): @@ -226,30 +229,5 @@ def get_absolute_path(filename): file_path = os.path.join(get_backups_path()[2:], filename) return "{0}/sites/{1}".format(get_bench_path(), file_path) -def progress(progress, message): +def set_progress(progress, message): frappe.publish_realtime("upload_to_google_drive", dict(progress=progress, total=3, message=message), user=frappe.session.user) - -def send_email(success, error=None): - if success: - if not frappe.db.get_single_value("Google Drive", "send_email_for_successful_backup"): - return - - subject = "Backup Upload Successful" - message = """

Backup Uploaded Successfully

Hi there, this is just to inform you - that your backup was successfully uploaded to Google Drive.

- """ - else: - subject = "[Warning] Backup Upload Failed" - message = """

Backup Upload Failed

Oops, your automated backup to Google Drive - failed.

-

Error message:
-

{0}
-

-

Please contact your system manager for more information.

- """.format(error) - - frappe.sendmail( - recipients=frappe.db.get_single_value("Google Drive", "email"), - subject=subject, - message=message - ) \ No newline at end of file diff --git a/frappe/integrations/doctype/oauth_client/oauth_client.json b/frappe/integrations/doctype/oauth_client/oauth_client.json index 47ede6e280..d0d45c36ab 100644 --- a/frappe/integrations/doctype/oauth_client/oauth_client.json +++ b/frappe/integrations/doctype/oauth_client/oauth_client.json @@ -1,517 +1,517 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2016-08-24 14:07:21.955052", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "", + "beta": 0, + "creation": "2016-08-24 14:07:21.955052", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "client_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "App Client ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "client_id", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "App Client ID", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "app_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "App Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "app_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "App Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "user", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "User", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cb_1", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "client_secret", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "App Client Secret", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "client_secret", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "App Client Secret", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If checked, users will not see the Confirm Access dialog.", - "fieldname": "skip_authorization", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Skip Authorization", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "If checked, users will not see the Confirm Access dialog.", + "fieldname": "skip_authorization", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Skip Authorization", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "sb_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "", + "fieldname": "sb_1", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "all openid", - "description": "A list of resources which the Client App will have access to after the user allows it.
e.g. project", - "fieldname": "scopes", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Scopes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "all openid", + "description": "A list of resources which the Client App will have access to after the user allows it.
e.g. project", + "fieldname": "scopes", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Scopes", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cb_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "URIs for receiving authorization code once the user allows access, as well as failure responses. Typically a REST endpoint exposed by the Client App.\n
e.g. http://hostname//api/method/frappe.www.login.login_via_facebook", - "fieldname": "redirect_uris", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Redirect URIs", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "URIs for receiving authorization code once the user allows access, as well as failure responses. Typically a REST endpoint exposed by the Client App.\n
e.g. http://hostname//api/method/frappe.www.login.login_via_facebook", + "fieldname": "redirect_uris", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Redirect URIs", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_redirect_uri", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Redirect URI", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "default_redirect_uri", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Default Redirect URI", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "1", - "columns": 0, - "fieldname": "sb_advanced", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": " Advanced Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": "1", + "columns": 0, + "fieldname": "sb_advanced", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Advanced Settings", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "grant_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Grant Type", - "length": 0, - "no_copy": 0, - "options": "Authorization Code\nImplicit", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "grant_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Grant Type", + "length": 0, + "no_copy": 0, + "options": "Authorization Code\nImplicit", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cb_2", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Code", - "fieldname": "response_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Response Type", - "length": 0, - "no_copy": 0, - "options": "Code\nToken", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Code", + "fieldname": "response_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Response Type", + "length": 0, + "no_copy": 0, + "options": "Code\nToken", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-10-05 21:07:39.476360", - "modified_by": "Administrator", - "module": "Integrations", - "name": "OAuth Client", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2020-04-07 21:07:39.476360", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Client", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "app_name", - "track_changes": 1, + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "app_name", + "track_changes": 1, "track_seen": 0 } \ No newline at end of file diff --git a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.json b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.json index 93fff995f0..bbdbf74a67 100755 --- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.json +++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.json @@ -1,397 +1,110 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-09-04 20:57:20.129205", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2017-09-04 20:57:20.129205", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "notify_email", + "send_email_for_successful_backup", + "frequency", + "access_key_id", + "secret_access_key", + "region", + "endpoint_url", + "bucket", + "backup_limit" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enable Automatic Backup", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enable Automatic Backup" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "notify_email", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Send Notifications To", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "notify_email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Send Notifications To", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "description": "Note: By default emails for failed backups are sent.", - "fetch_if_empty": 0, - "fieldname": "send_email_for_successful_backup", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Send Email for Successful Backup", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "description": "Note: By default emails for failed backups are sent.", + "fieldname": "send_email_for_successful_backup", + "fieldtype": "Check", + "label": "Send Email for Successful Backup" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Backup Frequency", - "length": 0, - "no_copy": 0, - "options": "Daily\nWeekly\nMonthly\nNone", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Backup Frequency", + "options": "Daily\nWeekly\nMonthly\nNone", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "access_key_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Access Key ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "access_key_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Access Key ID", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "secret_access_key", - "fieldtype": "Password", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Secret Access Key", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "secret_access_key", + "fieldtype": "Password", + "in_list_view": 1, + "label": "Secret Access Key", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "us-east-1", - "description": "See https://docs.aws.amazon.com/de_de/general/latest/gr/rande.html#s3_region for details.", - "fetch_if_empty": 0, - "fieldname": "region", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Region", - "length": 0, - "no_copy": 0, - "options": "us-east-1\nus-east-2\nus-west-1\nus-west-2\nap-south-1\nap-southeast-1\nap-southeast-2\nap-northeast-1\nap-northeast-2\nap-northeast-3\nca-central-1\ncn-north-1\ncn-northwest-1\neu-central-1\neu-west-1\neu-west-2\neu-west-3\neu-north-1\nsa-east-1", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "us-east-1", + "description": "See https://docs.aws.amazon.com/de_de/general/latest/gr/rande.html#s3_region for details.", + "fieldname": "region", + "fieldtype": "Select", + "label": "Region", + "options": "us-east-1\nus-east-2\nus-west-1\nus-west-2\nap-south-1\nap-southeast-1\nap-southeast-2\nap-northeast-1\nap-northeast-2\nap-northeast-3\nca-central-1\ncn-north-1\ncn-northwest-1\neu-central-1\neu-west-1\neu-west-2\neu-west-3\neu-north-1\nsa-east-1" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "endpoint_url", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Endpoint URL", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "endpoint_url", + "fieldtype": "Data", + "label": "Endpoint URL" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "bucket", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bucket", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bucket", + "fieldtype": "Data", + "label": "Bucket", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "backup_limit", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Backup Limit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "backup_limit", + "fieldtype": "Int", + "label": "Backup Limit", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_toolbar": 1, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-04-10 03:56:55.632017", - "modified_by": "Administrator", - "module": "Integrations", - "name": "S3 Backup Settings", - "name_case": "", - "owner": "Administrator", + ], + "hide_toolbar": 1, + "issingle": 1, + "modified": "2019-08-22 16:26:04.774571", + "modified_by": "Administrator", + "module": "Integrations", + "name": "S3 Backup Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py index 55b9e63a4d..7e69da922c 100755 --- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py @@ -8,12 +8,14 @@ import os.path import frappe import boto3 from frappe import _ +from frappe.integrations.offsite_backup_utils import get_latest_backup_file, send_email, validate_file_size from frappe.model.document import Document -from frappe.utils import cint, split_emails +from frappe.utils import cint from frappe.utils.background_jobs import enqueue from rq.timeouts import JobTimeoutException from botocore.exceptions import ClientError + class S3BackupSettings(Document): def validate(self): @@ -49,7 +51,7 @@ class S3BackupSettings(Document): @frappe.whitelist() def take_backup(): - "Enqueue longjob for taking backup to s3" + """Enqueue longjob for taking backup to s3""" enqueue("frappe.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_s3", queue='long', timeout=1500) frappe.msgprint(_("Queued for backup. It may take a few minutes to an hour.")) @@ -65,22 +67,21 @@ def take_backups_weekly(): def take_backups_monthly(): take_backups_if("Monthly") - def take_backups_if(freq): if cint(frappe.db.get_value("S3 Backup Settings", None, "enabled")): if frappe.db.get_value("S3 Backup Settings", None, "frequency") == freq: take_backups_s3() - @frappe.whitelist() def take_backups_s3(retry_count=0): try: + validate_file_size() backup_to_s3() - send_email(True, "S3 Backup Settings") + send_email(True, "Amazon S3", "S3 Backup Settings", "notify_email") except JobTimeoutException: if retry_count < 2: args = { - "retry_count" :retry_count + 1 + "retry_count": retry_count + 1 } enqueue("frappe.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_s3", queue='long', timeout=1500, **args) @@ -89,31 +90,10 @@ def take_backups_s3(retry_count=0): except Exception: notify() + def notify(): error_message = frappe.get_traceback() - frappe.errprint(error_message) - send_email(False, "S3 Backup Settings", error_message) - -def send_email(success, service_name, error_status=None): - if success: - if frappe.db.get_value("S3 Backup Settings", None, "send_email_for_successful_backup") == '0': - return - - subject = "Backup Upload Successful" - message = """

Backup Uploaded Successfully!

Hi there, this is just to inform you - that your backup was successfully uploaded to your Amazon S3 bucket. So relax!

""" - - else: - subject = "[Warning] Backup Upload Failed" - message = """

Backup Upload Failed!

Oops, your automated backup to Amazon S3 failed. -

Error message: %s

Please contact your system manager - for more information.

""" % error_status - - if not frappe.db: - frappe.connect() - - recipients = split_emails(frappe.db.get_value("S3 Backup Settings", None, "notify_email")) - frappe.sendmail(recipients=recipients, subject=subject, message=message) + send_email(False, 'Amazon S3', "S3 Backup Settings", "notify_email", error_message) def backup_to_s3(): @@ -130,11 +110,15 @@ def backup_to_s3(): endpoint_url=doc.endpoint_url or 'https://s3.amazonaws.com' ) - backup = new_backup(ignore_files=False, backup_path_db=None, + if frappe.flags.create_new_backup: + backup = new_backup(ignore_files=False, backup_path_db=None, backup_path_files=None, backup_path_private_files=None, force=True) - db_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) - files_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_files)) - private_files = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_private_files)) + db_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) + files_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_files)) + private_files = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_private_files)) + else: + db_filename, files_filename, private_files = get_latest_backup_file(with_files=True) + folder = os.path.basename(db_filename)[:15] + '/' # for adding datetime to folder name @@ -143,8 +127,8 @@ def backup_to_s3(): upload_file_to_s3(files_filename, folder, conn, bucket) delete_old_backups(doc.backup_limit, bucket) -def upload_file_to_s3(filename, folder, conn, bucket): +def upload_file_to_s3(filename, folder, conn, bucket): destpath = os.path.join(folder, os.path.basename(filename)) try: print("Uploading file:", filename) @@ -156,7 +140,7 @@ def upload_file_to_s3(filename, folder, conn, bucket): def delete_old_backups(limit, bucket): - all_backups = list() + all_backups = [] doc = frappe.get_single("S3 Backup Settings") backup_limit = int(limit) diff --git a/frappe/integrations/offsite_backup_utils.py b/frappe/integrations/offsite_backup_utils.py new file mode 100644 index 0000000000..c280a1d9dd --- /dev/null +++ b/frappe/integrations/offsite_backup_utils.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import glob +import os +from frappe.utils import split_emails, get_backups_path + + +def send_email(success, service_name, doctype, email_field, error_status=None): + recipients = get_recipients(service_name, email_field) + if not recipients: + frappe.log_error("No Email Recipient found for {0}".format(service_name), + "{0}: Failed to send backup status email".format(service_name)) + return + + if success: + if not frappe.db.get_value(doctype, None, "send_email_for_successful_backup"): + return + + subject = "Backup Upload Successful" + message = """ +

Backup Uploaded Successfully!

+

Hi there, this is just to inform you that your backup was successfully uploaded to your {0} bucket. So relax!

""".format(service_name) + + else: + subject = "[Warning] Backup Upload Failed" + message = """ +

Backup Upload Failed!

+

Oops, your automated backup to {0} failed.

+

Error message: {1}

+

Please contact your system manager for more information.

""".format(service_name, error_status) + + frappe.sendmail(recipients=recipients, subject=subject, message=message) + + +def get_recipients(service_name, email_field): + if not frappe.db: + frappe.connect() + + return split_emails(frappe.db.get_value(service_name, None, email_field)) + + +def get_latest_backup_file(with_files=False): + + def get_latest(file_ext): + file_list = glob.glob(os.path.join(get_backups_path(), file_ext)) + return max(file_list, key=os.path.getctime) + + latest_file = get_latest('*.sql.gz') + + if with_files: + latest_public_file_bak = get_latest('*-files.tar') + latest_private_file_bak = get_latest('*-private-files.tar') + return latest_file, latest_public_file_bak, latest_private_file_bak + + return latest_file + + +def get_file_size(file_path, unit): + if not unit: + unit = 'MB' + + file_size = os.path.getsize(file_path) + + memory_size_unit_mapper = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4} + i = 0 + while i < memory_size_unit_mapper[unit]: + file_size = file_size / 1000.0 + i += 1 + + return file_size + + +def validate_file_size(): + frappe.flags.create_new_backup = True + latest_file = get_latest_backup_file() + file_size = get_file_size(latest_file, unit='GB') + + if file_size > 1: + frappe.flags.create_new_backup = False diff --git a/frappe/integrations/utils.py b/frappe/integrations/utils.py index 811b007131..808affe47a 100644 --- a/frappe/integrations/utils.py +++ b/frappe/integrations/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies and contributors +# Copyright (c) 2019, Frappe Technologies and contributors # For license information, please see license.txt from __future__ import unicode_literals diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 78d2c462e1..ffaf84e2b3 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -110,7 +110,11 @@ def make_autoname(key="", doctype="", doc=""): if "#" not in key: key = key + ".#####" elif "." not in key: - frappe.throw(_("Invalid naming series (. missing)") + (_(" for {0}").format(doctype) if doctype else "")) + error_message = _("Invalid naming series (. missing)") + if doctype: + error_message = _("Invalid naming series (. missing) for {0}").format(doctype) + + frappe.throw(error_message) parts = key.split('.') n = parse_naming_series(parts, doctype, doc) diff --git a/frappe/patches.txt b/frappe/patches.txt index fc4f3ae998..0e02423639 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -271,3 +271,4 @@ execute:frappe.delete_doc_if_exists('DocType', 'GSuite Templates') execute:frappe.delete_doc_if_exists('DocType', 'GCalendar Account') execute:frappe.delete_doc_if_exists('DocType', 'GCalendar Settings') frappe.patches.v12_0.remove_parent_and_parenttype_from_print_formats +execute:from frappe.desk.page.setup_wizard.install_fixtures import update_genders;update_genders() diff --git a/frappe/patches/v8_0/update_gender_and_salutation.py b/frappe/patches/v8_0/update_gender_and_salutation.py index c990e9c4aa..bcd9d4cbd7 100644 --- a/frappe/patches/v8_0/update_gender_and_salutation.py +++ b/frappe/patches/v8_0/update_gender_and_salutation.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe -from frappe.desk.page.setup_wizard.install_fixtures import update_genders_and_salutations +from frappe.desk.page.setup_wizard.install_fixtures import update_genders, update_salutations def execute(): frappe.db.set_value("DocType", "Contact", "module", "Contacts") @@ -11,4 +11,5 @@ def execute(): frappe.reload_doc('contacts', 'doctype', 'gender') frappe.reload_doc('contacts', 'doctype', 'salutation') - update_genders_and_salutations() \ No newline at end of file + update_genders() + update_salutations() \ No newline at end of file diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 0e36e671cc..31d62dc445 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -550,6 +550,7 @@ export default class GridRow { hide_form() { frappe.dom.unfreeze(); this.row.toggle(true); + frappe.utils.scroll_to(this.row, true, 15); this.refresh(); if(cur_frm) cur_frm.cur_grid = null; this.wrapper.removeClass("grid-row-open"); diff --git a/frappe/public/js/frappe/form/grid_row_form.js b/frappe/public/js/frappe/form/grid_row_form.js index 73f0856c08..f93640936f 100644 --- a/frappe/public/js/frappe/form/grid_row_form.js +++ b/frappe/public/js/frappe/form/grid_row_form.js @@ -9,6 +9,7 @@ export default class GridRowForm { var me = this; this.make_form(); this.form_area.empty(); + frappe.utils.scroll_to(0, false, 0, this.wrapper.find('.grid-form-body')); this.layout = new frappe.ui.form.Layout({ fields: this.row.docfields, diff --git a/frappe/public/js/frappe/form/workflow.js b/frappe/public/js/frappe/form/workflow.js index 4eb33a5f28..4c59e8219b 100644 --- a/frappe/public/js/frappe/form/workflow.js +++ b/frappe/public/js/frappe/form/workflow.js @@ -29,20 +29,18 @@ frappe.ui.form.States = Class.extend({ }); frappe.workflow.get_transitions(me.frm.doc).then((transitions) => { - var next_html = $.map(transitions, - function(d) { - return d.action.bold() + __(" by Role ") + d.allowed; - }).join(", ") || __("None: End of Workflow").bold(); + const next_actions = $.map(transitions, d => `${d.action.bold()} ${__("by Role")} ${d.allowed}`) + .join(", ") || __("None: End of Workflow").bold(); + + const document_editable_by = frappe.workflow.get_document_state(me.frm.doctype, state).allow_edit.bold(); + + $(d.body).html(` +

${__("Current status")}: ${state.bold()}

+

${__("Document is only editable by users with role")}: ${document_editable_by}

+

${__("Next actions")}: ${next_actions}

+

${__("{0}: Other permission rules may also apply", [__('Note').bold()])}

+ `).css({padding: '15px'}); - $(d.body).html("

"+__("Current status")+": " + state.bold() + "

" - + "

"+__("Document is only editable by users of role")+": " - + frappe.workflow.get_document_state(me.frm.doctype, - state).allow_edit.bold() + "

" - + "

"+__("Next actions")+": "+ next_html +"

" - + (me.frm.doc.__islocal ? ("
" - +__("Workflow will start after saving.")+"
") : "") - + "

"+__("Note: Other permission rules may also apply")+"

" - ).css({padding: '15px'}); d.show(); }); }, true); @@ -115,7 +113,7 @@ frappe.ui.form.States = Class.extend({ } else { this.setup_btn(added); } - + }); }, diff --git a/frappe/public/js/frappe/list/list_sidebar_group_by.js b/frappe/public/js/frappe/list/list_sidebar_group_by.js index bd37b71ae4..7aa62dcb5f 100644 --- a/frappe/public/js/frappe/list/list_sidebar_group_by.js +++ b/frappe/public/js/frappe/list/list_sidebar_group_by.js @@ -53,15 +53,18 @@ frappe.views.ListGroupBy = class ListGroupBy { render_group_by_items() { let get_item_html = (fieldname) => { - let label; - let fieldtype; + let label, fieldtype; if (fieldname === 'assigned_to') { label = __('Assigned To'); } else if (fieldname === 'owner') { label = __('Created By'); } else { label = frappe.meta.get_label(this.doctype, fieldname); - fieldtype = frappe.meta.get_docfield(this.doctype, fieldname).fieldtype; + let docfield = frappe.meta.get_docfield(this.doctype, fieldname); + if (!docfield) { + return; + } + fieldtype = docfield.fieldtype; } return `