Merge branch 'develop' into schema-breadcrumb

This commit is contained in:
Suraj Shetty 2020-04-10 18:49:54 +05:30 committed by GitHub
commit 07bdf24e2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1832 additions and 988 deletions

View file

@ -5,7 +5,7 @@
"es6": true
},
"parserOptions": {
"ecmaVersion": 8,
"ecmaVersion": 9,
"sourceType": "module"
},
"extends": "eslint:recommended",

View file

@ -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 }}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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"
}

View file

@ -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,

View file

@ -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,
});
})

View file

@ -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"
}

View file

@ -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",

View file

@ -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
# Set Parent Field
doc.parentfield = parentfield
prepare_widget_list.append(doc)
return prepare_widget_list

View file

@ -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",

View file

@ -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",

View file

@ -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();
}
});
});

View file

@ -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",

View file

@ -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",

View file

@ -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)
send_newsletter(newsletter.name)

View file

@ -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",

View file

@ -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.<br> 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.<br> 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<br>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<br>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
}

View file

@ -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)

View file

@ -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");

View file

@ -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,

View file

@ -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(`
<p>${__("Current status")}: ${state.bold()}</p>
<p>${__("Document is only editable by users with role")}: ${document_editable_by}</p>
<p>${__("Next actions")}: ${next_actions}</p>
<p>${__("{0}: Other permission rules may also apply", [__('Note').bold()])}</p>
`).css({padding: '15px'});
$(d.body).html("<p>"+__("Current status")+": " + state.bold() + "</p>"
+ "<p>"+__("Document is only editable by users of role")+": "
+ frappe.workflow.get_document_state(me.frm.doctype,
state).allow_edit.bold() + "</p>"
+ "<p>"+__("Next actions")+": "+ next_html +"</p>"
+ (me.frm.doc.__islocal ? ("<div class='alert alert-info'>"
+__("Workflow will start after saving.")+"</div>") : "")
+ "<p class='help'>"+__("Note: Other permission rules may also apply")+"</p>"
).css({padding: '15px'});
d.show();
});
}, true);
@ -115,7 +113,7 @@ frappe.ui.form.States = Class.extend({
} else {
this.setup_btn(added);
}
});
},

View file

@ -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 `<li class="group-by-field list-link">

View file

@ -125,11 +125,14 @@ frappe.ui.FieldGroup = frappe.ui.form.Layout.extend({
return this.set_value(key, val);
},
set_values: function(dict) {
let promises = [];
for(var key in dict) {
if(this.fields_dict[key]) {
this.set_value(key, dict[key]);
promises.push(this.set_value(key, dict[key]));
}
}
return Promise.all(promises);
},
clear: function() {
for(var key in this.fields_dict) {

View file

@ -203,6 +203,7 @@ $.extend(frappe.ui.toolbar, {
fullwidth = !fullwidth;
localStorage.container_fullwidth = fullwidth;
frappe.ui.toolbar.set_fullwidth_if_enabled();
$(document.body).trigger('toggleFullWidth');
},
set_fullwidth_if_enabled() {
let fullwidth = JSON.parse(localStorage.container_fullwidth || 'false');

View file

@ -122,9 +122,11 @@ Object.assign(frappe.utils, {
</a></p>');
return content.html();
},
scroll_to: function(element, animate, additional_offset) {
scroll_to: function(element, animate, additional_offset, element_to_be_scrolled) {
element_to_be_scrolled = element_to_be_scrolled || $("html, body");
var y = 0;
if(element && typeof element==='number') {
if (element && typeof element==="number") {
y = element;
} else if(element) {
var header_offset = $(".navbar").height() + $(".page-head").height();
@ -136,14 +138,14 @@ Object.assign(frappe.utils, {
}
// already there
if(y==$('html, body').scrollTop()) {
if (y == element_to_be_scrolled.scrollTop()) {
return;
}
if (animate!==false) {
$("html, body").animate({ scrollTop: y });
if (animate !== false) {
element_to_be_scrolled.animate({ scrollTop: y });
} else {
$(window).scrollTop(y);
element_to_be_scrolled.scrollTop(y);
}
},

View file

@ -1,7 +1,6 @@
export default class Desktop {
constructor({ wrapper }) {
this.wrapper = wrapper;
window.desk = this;
this.pages = {};
this.sidebar_items = {};
this.sidebar_categories = [
@ -45,12 +44,12 @@ export default class Desktop {
this.desktop_settings = response.message;
} else {
frappe.throw({
title: "Couldn't Load Desk",
title: __("Couldn't Load Desk"),
message:
"Something went wrong while loading Desk. <b>Please relaod the page</b>. If the problem persists, contact the Administrator",
__("Something went wrong while loading Desk. <b>Please relaod the page</b>. If the problem persists, contact the Administrator"),
indicator: "red",
primary_action: {
label: "Reload",
label: __("Reload"),
action: () => location.reload()
}
});
@ -64,7 +63,7 @@ export default class Desktop {
item.name}" class="sidebar-item ${
item.selected ? "selected" : ""
}">
<span>${item.name}</span>
<span>${item.label || item.name}</span>
</div>`);
};
@ -79,8 +78,10 @@ export default class Desktop {
};
const make_category_title = name => {
// DO NOT REMOVE: Comment to load translation
// __("Modules") __("Domains") __("Places") __("Administration")
let $title = $(
`<div class="sidebar-group-title h6 uppercase">${name}</div>`
`<div class="sidebar-group-title h6 uppercase">${__(name)}</div>`
);
$title.appendTo(this.sidebar);
};
@ -106,8 +107,6 @@ export default class Desktop {
}
this.current_page = page;
localStorage.current_desk_page = page;
frappe.set_route("workspace", page);
this.pages[page] ? this.pages[page].show() : this.make_page(page);
}
@ -131,20 +130,20 @@ export default class Desktop {
this.pages[page] = $page;
return $page;
}
setup_events() {}
}
class DesktopPage {
constructor({ container, page_name }) {
frappe.desk_page = this;
this.container = container;
this.page_name = page_name;
this.sections = {};
this.allow_customization = false;
this.make();
this.reload();
}
show() {
frappe.desk_page = this;
this.page.show();
}
@ -152,8 +151,34 @@ class DesktopPage {
this.page.hide();
}
reload() {
this.in_customize_mode = false;
this.page && this.page.remove();
this.make();
this.setup_events();
}
make_customization_link() {
this.customize_link = $(`<div class="small customize-options" style="cursor: pointer;">${__('Customize Workspace')}</div>`);
this.customize_link.appendTo(this.page);
this.customize_link.on('click', () => {
this.customize();
});
this.save_or_discard_link = $(`<div class="small customize-options small-bounce">
<span class="save-customization">${__('Save')}</span> / <span class="discard-customization">${__('Discard')}</span>
</div>`).hide();
this.save_or_discard_link.appendTo(this.page);
this.save_or_discard_link.find(".save-customization").on("click", () => this.save_customization());
this.save_or_discard_link.find(".discard-customization").on("click", () => this.reload());
this.page.addClass('allow-customization');
}
make() {
this.make_page();
this.page = $(`<div class="desk-page" data-page-name=${this.page_name}></div>`);
this.page.appendTo(this.container);
this.get_data().then(res => {
this.data = res.message;
// this.make_onboarding();
@ -163,28 +188,32 @@ class DesktopPage {
return;
}
this.allow_customization = this.data.allow_customization || false;
let create_shortcuts_and_cards = () => {
this.data.shortcuts.items.length && this.make_shortcuts();
this.data.cards.items.length && this.make_cards();
};
if (!this.sections["onboarding"] && this.data.charts.items.length) {
this.make_charts().then(() => {
create_shortcuts_and_cards();
});
} else {
create_shortcuts_and_cards();
}
this.refresh();
});
}
make_page() {
this.page = $(
`<div class="desk-page" data-page-name=${this.page_name}></div>`
);
this.page.appendTo(this.container);
refresh() {
this.page.empty();
this.allow_customization = this.data.allow_customization || false;
this.allow_customization && this.make_customization_link();
let create_shortcuts_and_cards = () => {
this.data.shortcuts.items.length && this.make_shortcuts();
this.data.cards.items.length && this.make_cards();
if (this.allow_customization) {
// Move the widget group up to align with labels if customization is allowed
$('.desk-page .widget-group:visible:first').css('margin-top', '-25px');
}
};
if (!this.sections["onboarding"] && this.data.charts.items.length) {
this.make_charts().then(() => {
create_shortcuts_and_cards();
});
} else {
create_shortcuts_and_cards();
}
}
get_data() {
@ -193,40 +222,50 @@ class DesktopPage {
});
}
make_onboarding() {
this.sections["onboarding"] = new frappe.widget.WidgetGroup({
title: `Getting Started`,
container: this.page,
type: "onboarding",
columns: 1,
widgets: [
{
label: "Unlock Great Customer Experience",
subtitle: "Just a few steps, and youre good to go.",
steps: [
{
label: "Configure Lead Sources",
completed: true
},
{
label: "Add Your Leads",
completed: false
},
{
label: "Create Your First Opportunity",
completed: false
},
{
label: "Onboard your Sales Team",
completed: false
},
{
label: "Assign Territories",
completed: false
}
]
}
]
setup_events() {
$(document.body).on('toggleFullWidth', () => this.refresh());
}
customize() {
if (this.in_customize_mode) {
return;
}
// It may be possible the chart area is hidden since it has no widgets
// So the margin-top: -25px would be applied to the shortcut group
// We need to remove this as the chart group will be visible during customization
$('.desk-page .widget-group:visible:first').css('margin-top', '0px');
this.customize_link.hide();
this.save_or_discard_link.show();
Object.keys(this.sections).forEach(section => {
this.sections[section].customize();
});
this.in_customize_mode = true;
// Move the widget group up to align with labels if customization is allowed
$('.desk-page .widget-group:visible:first').css('margin-top', '-25px');
}
save_customization() {
const config = {};
if (this.sections.charts) config.charts = this.sections.charts.get_widget_config();
if (this.sections.shortcuts) config.shortcuts = this.sections.shortcuts.get_widget_config();
if (this.sections.cards) config.cards = this.sections.cards.get_widget_config();
frappe.call('frappe.desk.desktop.save_customization', {
page: this.page_name,
config: config
}).then(res => {
if (res.message) {
frappe.msgprint({ message: __("Customizations Saved Successfully"), title: __("Success")});
this.reload();
} else {
frappe.throw({message: __("Something went wrong while saving customizations"), title: __("Failed")});
this.reload();
}
});
}
@ -240,11 +279,18 @@ class DesktopPage {
}
this.sections["charts"] = new frappe.widget.WidgetGroup({
title: this.data.charts.label || `${this.page_name} Dashboard`,
title: this.data.charts.label || __('{} Dashboard', [__(this.page_name)]),
container: this.page,
type: "chart",
columns: 1,
allow_sorting: false,
options: {
allow_sorting: this.allow_customization && !frappe.is_mobile(),
allow_create: this.allow_customization,
allow_delete: this.allow_customization,
allow_hiding: false,
allow_edit: true,
max_widget_count: 2,
},
widgets: this.data.charts.items
});
});
@ -252,22 +298,34 @@ class DesktopPage {
make_shortcuts() {
this.sections["shortcuts"] = new frappe.widget.WidgetGroup({
title: this.data.shortcuts.label || `Your Shortcuts`,
title: this.data.shortcuts.label || __(`Your Shortcuts`),
container: this.page,
type: "bookmark",
type: "shortcut",
columns: 3,
allow_sorting: this.allow_customization && frappe.is_mobile(),
options: {
allow_sorting: this.allow_customization && !frappe.is_mobile(),
allow_create: this.allow_customization,
allow_delete: this.allow_customization,
allow_hiding: false,
allow_edit: true,
},
widgets: this.data.shortcuts.items
});
}
make_cards() {
let cards = new frappe.widget.WidgetGroup({
title: this.data.cards.label || `Reports & Masters`,
title: this.data.cards.label || __(`Reports & Masters`),
container: this.page,
type: "links",
columns: 3,
allow_sorting: this.allow_customization && frappe.is_mobile(),
options: {
allow_sorting: this.allow_customization && !frappe.is_mobile(),
allow_create: false,
allow_delete: false,
allow_hiding: this.allow_customization,
allow_edit: false,
},
widgets: this.data.cards.items
});

View file

@ -1,3 +1,5 @@
import get_dialog_constructor from './widget_dialog.js';
export default class Widget {
constructor(opts) {
Object.assign(this, opts);
@ -8,22 +10,71 @@ export default class Widget {
this.set_title();
this.set_actions();
this.set_body();
this.setup_events();
}
customize() {
get_config() {
return {
name: this.name,
label: this.label
};
}
customize(options) {
this.in_customize_mode = true;
this.action_area.empty();
options.allow_delete &&
this.add_custom_button(
'<i class="fa fa-trash" aria-hidden="true"></i>',
() => this.delete()
);
options.allow_sorting &&
this.add_custom_button(
'<i class="fa fa-arrows" aria-hidden="true"></i>',
null,
"drag-handle"
);
if (options.allow_hiding) {
if (this.hidden) {
this.widget.removeClass("hidden");
this.body.css("opacity", 0.5);
this.title_field.css("opacity", 0.5);
this.footer.css("opacity", 0.5);
}
const classname = this.hidden ? 'fa fa-eye' : 'fa fa-eye-slash';
this.add_custom_button(
`<i class="${classname}" aria-hidden="true"></i>`,
() => this.hide_or_show(),
"show-or-hide-button"
);
this.show_or_hide_button = this.action_area.find(
".show-or-hide-button"
);
}
options.allow_edit &&
this.add_custom_button(
'<i class="fa fa-pencil" aria-hidden="true"></i>',
() => this.edit()
);
}
make() {
this.make_widget();
this.widget.appendTo(this.container);
this.setup_events();
}
make_widget() {
this.widget = $(`<div class="widget">
this.widget = $(`<div class="widget ${
this.hidden ? "hidden" : ""
}" data-widget-name=${this.name ? this.name : ''}>
<div class="widget-head">
<div class="widget-title"></div>
<div class="widget-title ellipsis"></div>
<div class="widget-control"></div>
</div>
<div class="widget-body">
@ -37,13 +88,74 @@ export default class Widget {
this.action_area = this.widget.find(".widget-control");
this.head = this.widget.find(".widget-head");
this.footer = this.widget.find(".widget-footer");
this.set_title();
this.set_actions();
this.set_body();
this.refresh();
}
set_title() {
this.title_field[0].innerHTML = this.label || this.name;
this.title_field[0].innerHTML = this.label;
}
add_custom_button(html, action, class_name = "") {
let button = $(
`<button class="btn btn-default btn-xs ${class_name}">${html}</button>`
);
button.click(event => {
event.stopPropagation();
action && action();
});
button.appendTo(this.action_area);
}
delete() {
this.widget.addClass("zoomOutDelete");
// wait for animation
setTimeout(() => {
this.widget.remove();
this.options.on_delete && this.options.on_delete(this.name);
}, 300);
}
edit() {
const dialog_class = get_dialog_constructor(this.widget_type);
this.edit_dialog = new dialog_class({
label: this.label,
type: this.widget_type,
values: this.get_config(),
primary_action: (data) => {
Object.assign(this, data);
data.name = this.name;
this.refresh();
},
primary_action_label: __("Save")
});
this.edit_dialog.make();
}
hide_or_show() {
if (!this.hidden) {
this.body.css("opacity", 0.5);
this.title_field.css("opacity", 0.5);
this.footer.css("opacity", 0.5);
this.hidden = true;
} else {
this.body.css("opacity", 1);
this.title_field.css("opacity", 1);
this.footer.css("opacity", 1);
this.hidden = false;
}
this.show_or_hide_button.empty();
const classname = this.hidden ? 'fa fa-eye' : 'fa fa-eye-slash';
$(`<i class="${classname}" aria-hidden="true"></i>`).appendTo(
this.show_or_hide_button
);
}
setup_events() {
//
}
set_actions() {
@ -53,8 +165,4 @@ export default class Widget {
set_body() {
//
}
setup_events() {
//
}
}
}

View file

@ -9,12 +9,19 @@ export default class ChartWidget extends Widget {
this.height = 240;
}
refresh() {
this.make_chart();
get_config() {
return {
name: this.name,
chart_name: this.chart_name,
label: this.label,
};
}
customize() {
this.setup_customize_actions();
refresh() {
delete this.dashboard_chart;
this.set_title();
this.set_body();
this.make_chart();
}
set_body() {
@ -67,28 +74,22 @@ export default class ChartWidget extends Widget {
}
this.setup_container();
this.prepare_chart_object();
this.action_area.empty();
this.prepare_chart_actions();
this.setup_filter_button();
if (!this.in_customize_mode) {
this.action_area.empty();
this.prepare_chart_actions();
this.setup_filter_button();
if (
this.chart_doc.timeseries &&
this.chart_doc.chart_type !== "Custom"
) {
this.render_time_series_filters();
if (
this.chart_doc.timeseries &&
this.chart_doc.chart_type !== "Custom"
) {
this.render_time_series_filters();
}
}
this.fetch_and_update_chart();
});
}
setup_customize_actions() {
this.action_area.empty();
const buttons = $(`<button type="button" class="btn btn-xs btn-secondary btn-default selected">Resize</button>
<button class="btn btn-secondary btn-light btn-danger btn-xs"><i class="fa fa-trash" aria-hidden="true"></i></button>`);
buttons.appendTo(this.action_area);
}
render_time_series_filters() {
let filters = [
{
@ -428,9 +429,7 @@ export default class ChartWidget extends Widget {
}
fetch(filters, refresh = false, args) {
let method = this.settings
? this.settings.method
: "frappe.desk.doctype.dashboard_chart.dashboard_chart.get";
let method = this.settings.method;
if (this.chart_doc.chart_type == "Report") {
args = {
@ -558,6 +557,9 @@ export default class ChartWidget extends Widget {
};
return Promise.resolve();
} else {
this.settings = {
method: "frappe.desk.doctype.dashboard_chart.dashboard_chart.get"
};
return Promise.resolve();
}
});

View file

@ -6,8 +6,13 @@ export default class LinksWidget extends Widget {
super(opts);
}
refresh() {
//
get_config() {
return {
name: this.name,
links: JSON.stringify(this.links),
label: this.label,
hidden: this.hidden,
};
}
set_body() {
@ -75,21 +80,22 @@ export default class LinksWidget extends Widget {
const popover = link.find(".module-link-popover");
link_label.mouseover(() => {
if (this.in_customize_mode) return;
popover.show();
});
link_label.mouseout(() => popover.hide());
} else {
if (link_label.hasClass("help-video-link")) {
link_label.click(event => {
link_label.click(event => {
if (this.in_customize_mode) return;
if (link_label.hasClass("help-video-link")) {
let yt_id = event.target.dataset.youtubeid;
frappe.help.show_video(yt_id);
});
} else {
link_label.click(event => {
} else {
let route = event.target.dataset.route;
frappe.set_route(route);
});
}
}
});
}
});
}

View file

@ -0,0 +1,52 @@
import get_dialog_constructor from "./widget_dialog.js";
export default class NewWidget {
constructor(opts) {
Object.assign(this, opts);
this.make();
}
customize() {
return;
}
make() {
this.make_widget();
this.widget.appendTo(this.container);
this.setup_events();
}
get_title() {
// DO NOT REMOVE: Comment to load translation
// __("New Chart") __("New Shortcut")
return __(`New ${frappe.utils.to_title_case(this.type)}`);
}
make_widget() {
this.widget = $(`<div class="widget new-widget">
+ ${this.get_title()}
</div>`);
this.body = this.widget;
}
setup_events() {
this.widget.on("click", () => this.open_dialog());
}
open_dialog() {
const dialog_class = get_dialog_constructor(this.type);
this.dialog = new dialog_class({
label: this.label,
type: this.type,
values: false,
primary_action: this.on_create,
});
this.dialog.make();
}
delete() {
this.widget.remove();
}
}

View file

@ -3,7 +3,6 @@ import Widget from "./base_widget.js";
export default class OnboardingWidget extends Widget {
constructor(opts) {
super(opts);
window.onb = this;
}
refresh() { }

View file

@ -1,48 +1,62 @@
import Widget from "./base_widget.js";
import { generate_route } from "./utils";
// import { get_luminosity, shadeColor } from "./utils";
String.prototype.format = function () {
var i = 0, args = arguments;
return this.replace(/{}/g, function () {
return typeof args[i] != 'undefined' ? args[i++] : '';
});
};
export default class ShortcutWidget extends Widget {
constructor(opts) {
super(opts);
}
refresh() {
//
get_config() {
return {
name: this.name,
icon: this.icon,
label: this.label,
format: this.format,
link_to: this.link_to,
color: this.color,
restrict_to_domain: this.restrict_to_domain,
stats_filter: this.stats_filter,
type: this.type,
};
}
setup_events() {
this.widget.click(() => {
let route = generate_route(this)
frappe.set_route(route)
})
if (this.in_customize_mode) return;
let route = generate_route({
route: this.route,
name: this.link_to,
type: this.type,
is_query_report: this.is_query_report,
doctype: this.ref_doctype
});
frappe.set_route(route);
});
}
set_actions() {
this.widget.addClass('shortcut-widget-box');
const get_filter = new Function(`return ${this.stats_filter}`)
if (this.in_customize_mode) return;
this.widget.addClass("shortcut-widget-box");
const get_filter = new Function(`return ${this.stats_filter}`);
if (this.type == "DocType" && this.stats_filter) {
frappe.db.count(this.link_to, {
filters: get_filter()
}).then(count => this.set_count(count))
frappe.db
.count(this.link_to, {
filters: get_filter(),
})
.then((count) => this.set_count(count));
}
}
set_title() {
if (this.icon) {
this.title_field[0].innerHTML = `<div>
<i class="${this.icon}" style="color: rgb(141, 153, 166); font-size: 18px; margin-right: 6px;"></i>
<i class="${this.icon}" style=""></i>
${this.label || this.name}
</div>`
}
else {
</div>`;
} else {
super.set_title();
}
}
@ -50,19 +64,22 @@ export default class ShortcutWidget extends Widget {
set_count(count) {
const get_label = () => {
if (this.format) {
return this.format.format(count);
return this.format.replace(/{}/g, count);
}
return count
}
return count;
};
this.action_area.empty();
const label = get_label();
const buttons = $(`<div class="small pill">${label}</div>`);
if(this.color) {
buttons.css('background-color', this.color);
buttons.css('color', frappe.ui.color.get_contrast_color(this.color))
if (this.color) {
buttons.css("background-color", this.color);
buttons.css(
"color",
frappe.ui.color.get_contrast_color(this.color)
);
}
buttons.appendTo(this.action_area);
}
}
}

View file

@ -0,0 +1,266 @@
class WidgetDialog {
constructor(opts) {
Object.assign(this, opts);
this.editing = Boolean(this.values && Object.keys(this.values).length);
}
make() {
this.make_dialog();
this.setup_dialog_events();
this.dialog.show();
this.editing && this.set_default_values();
}
make_dialog() {
this.dialog = new frappe.ui.Dialog({
title: this.get_title(),
fields: this.get_fields(),
primary_action: (data) => {
data = this.process_data(data);
if (!this.editing) {
data.name = `${this.type}-${this.label}-${frappe.utils.get_random(20)}`;
}
this.dialog.hide();
this.primary_action(data);
},
primary_action_label: this.primary_action_label || __("Add"),
});
}
get_title() {
// DO NOT REMOVE: Comment to load translation
// __("New Chart") __("New Shortcut") __("Edit Chart") __("Edit Shortcut")
let action = this.editing ? "Edit" : "Add";
return __(`${action} ${frappe.utils.to_title_case(this.type)}`);
}
get_fields() {
//
}
set_default_values() {
return this.dialog.set_values(this.values);
}
process_data(data) {
return data;
}
setup_dialog_events() {
//
}
hide_field(fieldname) {
this.dialog.set_df_property(fieldname, "hidden", true);
}
show_field(fieldname) {
this.dialog.set_df_property(fieldname, "hidden", false);
}
}
class ChartDialog extends WidgetDialog {
constructor(opts) {
super(opts);
}
get_fields() {
return [
{
fieldtype: "Link",
fieldname: "chart_name",
label: "Chart Name",
options: "Dashboard Chart",
reqd: 1,
},
{
fieldtype: "Data",
fieldname: "label",
label: "Label",
},
];
}
process_data(data) {
data.label = data.label ? data.label : data.chart_name;
return data;
}
}
class ShortcutDialog extends WidgetDialog {
constructor(opts) {
super(opts);
}
hide_filters() {
this.hide_field("count_section_break");
this.hide_field("filters_section_break");
}
show_filters() {
this.show_field("count_section_break");
this.show_field("filters_section_break");
}
get_fields() {
return [
{
fieldtype: "Select",
fieldname: "type",
label: "Type",
reqd: 1,
options: "DocType\nReport\nPage",
onchange: () => {
if (this.dialog.get_value("type") == "DocType") {
this.dialog.fields_dict.link_to.get_query = () => {
return { filters: { istable: false } };
};
} else {
this.dialog.fields_dict.link_to.get_query = null;
}
},
},
{
fieldtype: "Data",
fieldname: "label",
label: "Label",
},
{
fieldtype: "Column Break",
fieldname: "column_break_4",
},
{
fieldtype: "Dynamic Link",
fieldname: "link_to",
label: "Link To",
reqd: 1,
options: "type",
onchange: () => {
if (this.dialog.get_value("type") == "DocType") {
let doctype = this.dialog.get_value("link_to");
doctype &&
frappe.db
.get_value("DocType", doctype, "issingle")
.then((res) => {
if (res.message && res.message.issingle) {
this.hide_filters();
} else {
this.setup_filter(doctype);
this.show_filters();
}
});
} else {
this.hide_filters();
}
},
},
{
fieldtype: "Section Break",
fieldname: "filters_section_break",
label: "Count Filter",
hidden: 1,
},
{
fieldtype: "HTML",
fieldname: "filter_area_loading",
},
{
fieldtype: "HTML",
fieldname: "filter_area",
hidden: 1,
},
{
fieldtype: "Section Break",
fieldname: "count_section_break",
label: "Count Customizations",
hidden: 1,
},
{
fieldtype: "Color",
fieldname: "color",
label: "Color",
},
{
fieldtype: "Column Break",
fieldname: "column_break_3",
},
{
fieldtype: "Data",
fieldname: "format",
label: "Format",
description: "For Example: {} Open",
},
];
}
set_default_values() {
super.set_default_values().then(() => {
this.dialog.fields_dict.link_to.df.onchange();
});
}
process_data(data) {
let stats_filter = {};
if (this.dialog.get_value("type") == "DocType" && this.filter_group) {
let filters = this.filter_group.get_filters();
filters.forEach((arr) => {
stats_filter[arr[1]] = [arr[2], arr[3]];
});
data.stats_filter = JSON.stringify(stats_filter);
}
data.label = data.label
? data.label
: frappe.model.unscrub(data.link_to);
return data;
}
setup_filter(doctype) {
if (this.filter_group) {
this.filter_group.wrapper.empty();
delete this.filter_group;
}
let $loading = this.dialog.get_field("filter_area_loading").$wrapper;
$(`<span class="text-muted">Loading Filters...</span>`).appendTo($loading);
this.filters = [];
if (this.values && this.values.stats_filter) {
const filters_json = JSON.parse(this.values.stats_filter);
this.filters = Object.keys(filters_json).map((filter) => {
let val = filters_json[filter];
return [this.values.link_to, filter, val[0], val[1], false];
});
}
this.filter_group = new frappe.ui.FilterGroup({
parent: this.dialog.get_field("filter_area").$wrapper,
doctype: doctype,
on_change: () => {},
});
frappe.model.with_doctype(doctype, () => {
this.filter_group.add_filters_to_filter_group(this.filters);
this.hide_field("filter_area_loading");
this.show_field("filter_area");
});
}
}
export default function get_dialog_constructor(type) {
const widget_map = {
chart: ChartDialog,
shortcut: ShortcutDialog,
};
return widget_map[type] || WidgetDialog;
}

View file

@ -3,46 +3,31 @@ import BaseWidget from "../widgets/base_widget";
import ShortcutWidget from "../widgets/shortcut_widget";
import LinksWidget from "../widgets/links_widget";
import OnboardingWidget from "../widgets/onboarding_widget";
import NewWidget from "../widgets/new_widget";
frappe.provide('frappe.widget')
frappe.provide("frappe.widget");
const widget_factory = {
chart: ChartWidget,
base: BaseWidget,
bookmark: ShortcutWidget,
shortcut: ShortcutWidget,
links: LinksWidget,
onboarding: OnboardingWidget
onboarding: OnboardingWidget,
};
export default class WidgetGroup {
constructor(opts) {
Object.assign(this, opts);
// opts = {
// title: "CRM Dashboard",
// container: $(''),
// widgets: [
// {type: "dashboard", width: "Full", options: {}}.
// {type: "dashboard", width: "Full", options: {}}
// ],
// allow_delete: true,
// allow_create: true,
// allow_rearrange: true,
// hide_edit_option: false,
// collapsible: false
// }
window.wid_area = this;
this.widgets_list = [];
this.widgets_dict = {};
this.widget_order = [];
this.make();
}
make() {
this.make_container();
this.refresh();
}
refresh() {
this.title && this.set_title(this.title);
this.title && this.set_title();
this.widgets && this.make_widgets();
this.allow_sorting && this.setup_sortable();
}
make_container() {
@ -58,22 +43,94 @@ export default class WidgetGroup {
this.title_area = widget_area.find(".widget-group-title");
this.control_area = widget_area.find(".widget-group-control");
this.body = widget_area.find(".widget-group-body");
!this.widgets.length && this.widget_area.hide();
widget_area.appendTo(this.container);
}
set_title(title) {
set_title() {
this.title_area[0].innerText = this.title;
}
make_widgets() {
this.body.empty()
this.body.empty();
this.widgets.forEach((widget) => {
this.add_widget(widget);
});
}
add_widget(widget) {
const widget_class = widget_factory[this.type];
this.widgets.forEach(widget => {
new widget_class({
...widget,
container: this.body
})
let widget_object = new widget_class({
...widget,
widget_type: this.type,
container: this.body,
options: {
...this.options,
on_delete: (name) => this.on_delete(name),
},
});
this.widgets_list.push(widget_object);
this.widgets_dict[widget.name] = widget_object;
return widget_object;
}
customize() {
this.widget_area.show();
this.widgets_list.forEach((wid) => {
wid.customize(this.options);
});
this.options.allow_create && this.setup_new_widget();
this.options.allow_sorting && this.setup_sortable();
}
setup_new_widget() {
const max = this.options
? this.options.max_widget_count || Number.POSITIVE_INFINITY
: Number.POSITIVE_INFINITY;
if (this.widgets_list.length < max) {
this.new_widget = new NewWidget({
container: this.body,
type: this.type,
on_create: (config) => {
// Remove new widget
this.new_widget.delete();
delete this.new_widget;
config.in_customize_mode = 1;
// Add new widget and customize it
let wid = this.add_widget(config);
wid.customize(this.options);
// Put back the new widget if required
if (this.widgets_list.length < max) {
this.setup_new_widget();
}
},
});
}
}
on_delete(name) {
this.widgets_list = this.widgets_list.filter((wid) => name != wid.name);
delete this.widgets_dict[name];
this.update_widget_order();
if (!this.new_widget) this.setup_new_widget();
}
update_widget_order() {
this.widget_order = [];
this.body.children().each((index, element) => {
let name = element.dataset.widgetName;
if (name) {
this.widget_order.push(name);
}
});
}
@ -81,13 +138,26 @@ export default class WidgetGroup {
const container = this.body[0];
this.sortable = new Sortable(container, {
animation: 150,
onEnd: () => {
console.log("Sorting")
},
// onChoose: (evt) => this.sortable_config.on_choose(evt, container),
// onStart: (evt) => this.sortable_config.on_start(evt, container)
handle: ".drag-handle",
onEnd: () => this.update_widget_order(),
});
}
get_widget_config() {
this.update_widget_order();
let prepared_dict = {};
this.widgets_list.forEach((wid) => {
let config = wid.get_config();
let name = config.docname ? config.docname : config.name;
prepared_dict[name] = config;
});
return {
order: this.widget_order,
widgets: prepared_dict,
};
}
}
frappe.widget.WidgetGroup = WidgetGroup;
frappe.widget.WidgetGroup = WidgetGroup;

View file

@ -76,6 +76,24 @@
position: relative;
min-height: 1px;
padding-right: 15px;
.desk-page.allow-customization {
.customize-options {
text-align: right;
margin-top: 7px;
color: @text-muted;
z-index: 99;
.save-customization {
cursor: pointer;
color: @text-color;
}
.discard-customization {
cursor: pointer;
}
}
}
}
@media (max-width: 768px) {
@ -90,6 +108,9 @@
.widget-group {
margin-bottom: 25px;
// -webkit-animation-name: slideInUp;
// animation-name: slideInUp;
// animation-duration: 0.4s;
.widget-group-head {
display: flex;
@ -201,6 +222,17 @@
margin-left: 5px;
}
.drag-handle {
cursor: all-scroll;
cursor: -webkit-grabbing;
&:active {
cursor: grabbing;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
}
}
.dashboard-date-field {
.clearfix,
.help-box {
@ -220,6 +252,16 @@
border-color: @disabled-background
}
&.new-widget {
min-height: 65px;
background-color: @disabled-background;
color: @text-muted;
display: flex;
align-content: center;
justify-content: center;
cursor: pointer;
}
// Overrides for each widgets
&.dashboard-widget-box {
padding: 10px 15px !important;
@ -300,6 +342,14 @@
.widget-head {
margin-top: 5px;
margin-bottom: 5px;
.widget-title {
i {
color: @text-muted;
font-size: 18px;
margin-right: 6px;
}
}
}
}
@ -357,23 +407,153 @@
border-radius: 10px;
}
.pill-green {
background: #71b92c;
// color: #000;
@-webkit-keyframes smallBounce {
from,
20%,
53%,
80%,
to {
-webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
40%,
43% {
-webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-webkit-transform: translate3d(0, -12px, 0);
transform: translate3d(0, -12px, 0);
}
70% {
-webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-webkit-transform: translate3d(0, -6px, 0);
transform: translate3d(0, -6px, 0);
}
90% {
-webkit-transform: translate3d(0, -4px, 0);
transform: translate3d(0, -4px, 0);
}
}
.pill-red {
background: @red;
@keyframes smallBounce {
from,
20%,
53%,
80%,
to {
-webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
40%,
43% {
-webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-webkit-transform: translate3d(0, -12px, 0);
transform: translate3d(0, -12px, 0);
}
70% {
-webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-webkit-transform: translate3d(0, -6px, 0);
transform: translate3d(0, -6px, 0);
}
90% {
-webkit-transform: translate3d(0, -4px, 0);
transform: translate3d(0, -4px, 0);
}
}
.pill-blue {
background: @blue;
.small-bounce {
-webkit-animation-name: smallBounce;
animation-name: smallBounce;
-webkit-transform-origin: center bottom;
transform-origin: center bottom;
animation-duration: 1s;
}
.pill-yellow {
background: @yellow;
@-webkit-keyframes slideInUp {
from {
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
visibility: visible;
}
to {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
.pill-orange {
background: @orange;
@keyframes slideInUp {
from {
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
visibility: visible;
}
to {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}
.slide-in-up {
-webkit-animation-name: slideInUp;
animation-name: slideInUp;
animation-duration: 1s;
}
@-webkit-keyframes pulse {
from {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
50% {
-webkit-transform: scale3d(1.05, 1.05, 1.05);
transform: scale3d(1.05, 1.05, 1.05);
}
to {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
@keyframes pulse {
from {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
50% {
-webkit-transform: scale3d(1.05, 1.05, 1.05);
transform: scale3d(1.05, 1.05, 1.05);
}
to {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1);
}
}
.zoomOutDelete {
// -webkit-animation-name: zoomOut;
// animation-name: zoomOut;
// animation-duration: 1s;
transition: opacity 0.2s, visibility 0.2s, transform 0.2s;
transform: scale3d(0.5, 0.5, 0.5);
opacity: 0;
visibility: hidden;
}

View file

@ -262,6 +262,11 @@
border-bottom: 1px solid @border-color;
}
.grid-form-body {
max-height: 75vh;
overflow-y: auto;
}
.grid-header-toolbar {
display: flow-root;
}

View file

@ -7,5 +7,6 @@
<a href="{{ link }}" rel="nofollow" class="btn btn-primary btn-sm primary-action" style="padding: 8px 20px;">{{ _("Confirm Request") }}</a>
</p>
<p style="font-size: 85%;">
{{_("You can also copy-paste this ")}} <a href="{{ link }}">{{_("Verification Link")}}</a>{{_(" to your browser")}}
{% set verification_link = '<a href="{{ link }}">{{ _("Verification Link") }}</a>' %}
{{_("You can also copy-paste this {0} to your browser").format(verification_link) }}
</p>

View file

@ -181,7 +181,7 @@ class TestDocument(unittest.TestCase):
# css attributes
xss = '<div style="something: doesn\'t work; color: red;">Test</div>'
escaped_xss = '<div style="color: red;">Test</div>'
escaped_xss = '<div style="">Test</div>'
d.subject += xss
d.save()
d.reload()

View file

@ -54,8 +54,8 @@ def get_datetime(datetime_str=None):
elif isinstance(datetime_str, datetime.date):
return datetime.datetime.combine(datetime_str, datetime.time())
# dateutil parser does not agree with dates like 0001-01-01
if not datetime_str or (datetime_str or "").startswith("0001-01-01"):
# dateutil parser does not agree with dates like "0001-01-01" or "0000-00-00"
if not datetime_str or (datetime_str or "").startswith(("0001-01-01", "0000-00-00")):
return None
try:

View file

@ -1,26 +1,29 @@
{
"cards": [
{
"icon": "fa fa-cog",
"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": "Setup"
"hidden": 0,
"label": "Setup",
"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]"
},
{
"links": "[\n {\n \"description\": \"Single Post (article).\",\n \"label\": \"Blog Post\",\n \"name\": \"Blog Post\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"A user who posts blogs.\",\n \"label\": \"Blogger\",\n \"name\": \"Blogger\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Categorize blog posts.\",\n \"label\": \"Blog Category\",\n \"name\": \"Blog Category\",\n \"type\": \"doctype\"\n }\n]",
"title": "Blog"
"hidden": 0,
"label": "Blog",
"links": "[\n {\n \"description\": \"Single Post (article).\",\n \"label\": \"Blog Post\",\n \"name\": \"Blog Post\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"A user who posts blogs.\",\n \"label\": \"Blogger\",\n \"name\": \"Blogger\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Categorize blog posts.\",\n \"label\": \"Blog Category\",\n \"name\": \"Blog Category\",\n \"type\": \"doctype\"\n }\n]"
},
{
"icon": "fa fa-star",
"links": "[\n {\n \"description\": \"Content web page.\",\n \"label\": \"Web Page\",\n \"name\": \"Web Page\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"User editable form on Website.\",\n \"label\": \"Web Form\",\n \"name\": \"Web Form\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Website Sidebar\",\n \"name\": \"Website Sidebar\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Embed image slideshows in website pages.\",\n \"label\": \"Website Slideshow\",\n \"name\": \"Website Slideshow\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add meta tags to your web pages\",\n \"label\": \"Website Route Meta\",\n \"name\": \"Website Route Meta\",\n \"type\": \"doctype\"\n }\n]",
"title": "Web Site"
"hidden": 0,
"label": "Web Site",
"links": "[\n {\n \"description\": \"Content web page.\",\n \"label\": \"Web Page\",\n \"name\": \"Web Page\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"User editable form on Website.\",\n \"label\": \"Web Form\",\n \"name\": \"Web Form\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Website Sidebar\",\n \"name\": \"Website Sidebar\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Embed image slideshows in website pages.\",\n \"label\": \"Website Slideshow\",\n \"name\": \"Website Slideshow\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Add meta tags to your web pages\",\n \"label\": \"Website Route Meta\",\n \"name\": \"Website Route Meta\",\n \"type\": \"doctype\"\n }\n]"
},
{
"links": "[\n {\n \"label\": \"Portal Settings\",\n \"name\": \"Portal Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]",
"title": "Portal"
"hidden": 0,
"label": "Portal",
"links": "[\n {\n \"label\": \"Portal Settings\",\n \"name\": \"Portal Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]"
},
{
"links": "[\n {\n \"label\": \"Help Category\",\n \"name\": \"Help Category\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Help Article\",\n \"name\": \"Help Article\",\n \"type\": \"doctype\"\n }\n]",
"title": "Knowledge Base"
"hidden": 0,
"label": "Knowledge Base",
"links": "[\n {\n \"label\": \"Help Category\",\n \"name\": \"Help Category\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Help Article\",\n \"name\": \"Help Article\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Modules",
@ -34,7 +37,7 @@
"idx": 0,
"is_standard": 1,
"label": "Website",
"modified": "2020-03-12 16:30:43.092622",
"modified": "2020-04-01 11:24:40.726934",
"modified_by": "Administrator",
"module": "Website",
"name": "Website",
@ -45,30 +48,30 @@
{
"color": "",
"format": "{} Published",
"is_query_report": 0,
"label": "Blog Post",
"link_to": "Blog Post",
"stats_filter": "{\"published\":\"1\"}",
"type": "DocType"
},
{
"format": "{} Active",
"is_query_report": 0,
"label": "Blogger",
"link_to": "Blogger",
"stats_filter": "{\"disabled\": 0}",
"type": "DocType"
},
{
"is_query_report": 0,
"label": "Web Page",
"link_to": "Web Page",
"type": "DocType"
},
{
"is_query_report": 0,
"label": "Web Form",
"link_to": "Web Form",
"type": "DocType"
},
{
"is_query_report": 0,
"label": "Website Settings",
"link_to": "Website Settings",
"type": "DocType"
}

View file

@ -1,7 +1,7 @@
Babel==2.6.0
beautifulsoup4==4.8.2
bleach-whitelist==0.0.10
bleach==3.1.2
bleach==3.1.4
boto3==1.10.18
braintree==3.57.1
chardet==3.0.4