From 4c688b8a97e83aaa7e64c7e8b95e5c0469ba743d Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 15 Mar 2017 09:56:41 +0530 Subject: [PATCH 1/4] [kanban] fix kanban board not found --- frappe/public/js/frappe/views/kanban/kanban_view.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js index 4391202dfd..0dfd318410 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_view.js +++ b/frappe/public/js/frappe/views/kanban/kanban_view.js @@ -41,7 +41,13 @@ frappe.views.KanbanView = frappe.views.ListRenderer.extend({ var kb = this.meta.__kanban_boards.find( board => board.name === board_name ); - frappe.kanban_filters[board_name] = JSON.parse(kb && kb.filters || "[]"); + frappe.kanban_filters[board_name] = JSON.parse(kb && kb.filters || '[]'); + } + if(typeof frappe.kanban_filters[board_name] === 'string') { + frappe.kanban_filters[board_name] = + JSON.parse( + frappe.kanban_filters[board_name] || '[]' + ) } var filters = frappe.kanban_filters[board_name]; return filters; @@ -49,14 +55,11 @@ frappe.views.KanbanView = frappe.views.ListRenderer.extend({ set_defaults: function() { this._super(); this.no_realtime = true; + this.show_no_result = false; this.page_title = __(this.get_board_name()); }, get_board_name: function() { var route = frappe.get_route(); - if(!route[3] || !this.meta.__kanban_boards.find(b => b.name === route[3])) { - frappe.throw(__(`Kanban Board ${route[3] || ''} not found`)); - return; - } return route[3]; }, get_header_html: function() { From 0fa716c56d7ad92a0b57bef8b166830ff2a47412 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 15 Mar 2017 09:57:51 +0530 Subject: [PATCH 2/4] [kanban] private kanban board for Note, create new project for Task --- .../doctype/kanban_board/kanban_board.json | 51 ++++++++++++++++-- .../desk/doctype/kanban_board/kanban_board.py | 31 +++++++++++ frappe/desk/form/meta.py | 6 +-- frappe/hooks.py | 2 + frappe/public/js/frappe/list/list_sidebar.js | 53 +++++++++++++------ 5 files changed, 121 insertions(+), 22 deletions(-) diff --git a/frappe/desk/doctype/kanban_board/kanban_board.json b/frappe/desk/doctype/kanban_board/kanban_board.json index 5f2191bc64..cb8f4ba809 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.json +++ b/frappe/desk/doctype/kanban_board/kanban_board.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 1, "autoname": "field:kanban_board_name", @@ -22,6 +23,8 @@ "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": "Kanban Board Name", @@ -49,6 +52,8 @@ "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Reference DocType", @@ -77,6 +82,8 @@ "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": "Field Name", @@ -104,6 +111,8 @@ "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, @@ -130,6 +139,8 @@ "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": "Columns", @@ -155,9 +166,11 @@ "columns": 0, "fieldname": "filters", "fieldtype": "Text", - "hidden": 0, + "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": "Filters", @@ -174,20 +187,49 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "private", + "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": "Private", + "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 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-09 12:23:30.059564", - "modified_by": "Administrator", + "modified": "2017-03-14 23:02:13.267243", + "modified_by": "faris@erpnext.com", "module": "Desk", "name": "Kanban Board", "name_case": "", @@ -217,6 +259,7 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "track_changes": 0, diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index ae77d6fbac..c282bbd126 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -11,10 +11,29 @@ from frappe.model.document import Document class KanbanBoard(Document): def validate(self): + self.validate_column_name() + + def validate_column_name(self): for column in self.columns: if not column.column_name: frappe.msgprint(frappe._("Column Name cannot be empty"), raise_exception=True) +def get_permission_query_conditions(user): + if not user: user = frappe.session.user + + if user == "Administrator": + return "" + + return """(`tabKanban Board`.private=0 or `tabKanban Board`.owner="{user}")""".format(user=user) + +def has_permission(doc, ptype, user): + if doc.private == 0 or user == "Administrator": + return True + + if user == doc.owner: + return True + + return False @frappe.whitelist() def add_column(board_name, column_title): @@ -118,6 +137,18 @@ def quick_kanban_board(doctype, board_name, field_name): doc.kanban_board_name = board_name doc.reference_doctype = doctype doc.field_name = field_name + + if doctype == 'Task': + project = frappe.new_doc('Project') + project.project_name = board_name + project.status = 'Open' + project.save() + + doc.filters = '[["Task","project","=","{0}"]]'.format(board_name) + + if doctype == 'Note': + doc.private = 1 + doc.save() return doc diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index 39b5c670cc..0f81582182 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -176,12 +176,12 @@ class FormMeta(Meta): self.load_kanban_column_fields() def load_kanban_boards(self): - kanban_boards = frappe.get_all( - 'Kanban Board', fields=['name', 'filters', 'reference_doctype'], filters={'reference_doctype': self.name}) + kanban_boards = frappe.get_list( + 'Kanban Board', fields=['name', 'filters', 'reference_doctype', 'private'], filters={'reference_doctype': self.name}) self.set("__kanban_boards", kanban_boards, as_value=True) def load_kanban_column_fields(self): - values = frappe.get_all( + values = frappe.get_list( 'Kanban Board', fields=['field_name'], filters={'reference_doctype': self.name}) diff --git a/frappe/hooks.py b/frappe/hooks.py index 0f5cbb27bf..4013240ea8 100755 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -78,6 +78,7 @@ permission_query_conditions = { "ToDo": "frappe.desk.doctype.todo.todo.get_permission_query_conditions", "User": "frappe.core.doctype.user.user.get_permission_query_conditions", "Note": "frappe.desk.doctype.note.note.get_permission_query_conditions", + "Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions", "Contact": "frappe.geo.address_and_contact.get_permission_query_conditions_for_contact", "Address": "frappe.geo.address_and_contact.get_permission_query_conditions_for_address" } @@ -87,6 +88,7 @@ has_permission = { "ToDo": "frappe.desk.doctype.todo.todo.has_permission", "User": "frappe.core.doctype.user.user.has_permission", "Note": "frappe.desk.doctype.note.note.has_permission", + "Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.has_permission", "Contact": "frappe.geo.address_and_contact.has_permission", "Address": "frappe.geo.address_and_contact.has_permission", "Communication": "frappe.core.doctype.communication.communication.has_permission", diff --git a/frappe/public/js/frappe/list/list_sidebar.js b/frappe/public/js/frappe/list/list_sidebar.js index 4618f56210..55641c4f7f 100644 --- a/frappe/public/js/frappe/list/list_sidebar.js +++ b/frappe/public/js/frappe/list/list_sidebar.js @@ -130,16 +130,18 @@ frappe.views.ListSidebar = Class.extend({ $('').appendTo($dropdown); divider = true; } - $(`
  • ${__(board.name)}
  • `).appendTo($dropdown); + $(`
  • + ${__(board.name)} + ${board.private ? '' : ''} +
  • `).appendTo($dropdown); }); $dropdown.find('.new-kanban-board').click(function() { // frappe.new_doc('Kanban Board', {reference_doctype: me.doctype}); var select_fields = frappe.get_meta(me.doctype) .fields.filter(function(df) { - return df.fieldtype === 'Select'; - }).map(function(df) { - return df.fieldname; + return df.fieldtype === 'Select' && + df.fieldname !== 'kanban_column'; }); var fields = [ @@ -149,45 +151,61 @@ frappe.views.ListSidebar = Class.extend({ label: __('Kanban Board Name'), reqd: 1 } - ] + ]; if(select_fields.length > 0) { fields = fields.concat([{ fieldtype: 'Select', fieldname: 'field_name', label: __('Columns based on'), - options: select_fields.join('\n'), + options: select_fields.map(df => df.label).join('\n'), default: select_fields[0] }, { fieldtype: 'Check', fieldname: 'custom_column', - label: __('Add Custom Column Field'), + label: __('Custom Column'), default: 0, onchange: function(e) { var checked = d.get_value('custom_column'); if(checked) { - d.get_input('field_name').prop('disabled', true); + $(d.body).find('.frappe-control[data-fieldname="field_name"]').hide(); } else { - d.get_input('field_name').prop('disabled', null); + $(d.body).find('.frappe-control[data-fieldname="field_name"]').show(); } } }]); } + if(me.doctype === 'Task') { + fields[0].description = __('A new Project with this name will be created'); + } + + if(me.doctype === 'Note') { + fields[0].description = __('This Kanban Board will be private'); + } + var d = new frappe.ui.Dialog({ title: __('New Kanban Board'), fields: fields, - primary_action: function() { - var values = d.get_values(); + primary_action_label: __('Save'), + primary_action: function(values) { + var custom_column = values.custom_column !== undefined ? values.custom_column : 1; + + if(custom_column) { + var field_name = 'kanban_column'; + } else { + var field_name = + select_fields + .find(df => df.label === values.field_name) + .fieldname; + } me.add_custom_column_field(custom_column) .then(function(custom_column) { - var f = custom_column ? - 'kanban_column' : values.field_name; - return me.make_kanban_board(values.board_name, f) + return me.make_kanban_board(values.board_name, field_name) }) .then(function() { d.hide(); @@ -231,11 +249,16 @@ frappe.views.ListSidebar = Class.extend({ field_name: field_name }, callback: function(r) { + var kb = r.message; + if(kb.filters) { + frappe.provide('frappe.kanban_filters'); + frappe.kanban_filters[kb.kanban_board_name] = kb.filters; + } frappe.set_route( 'List', me.doctype, 'Kanban', - r.message.kanban_board_name + kb.kanban_board_name ); } }); From 20b09eb6f6070ae5d77199636fb3683fce67aed7 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 15 Mar 2017 09:59:11 +0530 Subject: [PATCH 3/4] [minor] readonly checkbox, fix multiple column add --- frappe/public/js/frappe/form/formatters.js | 2 +- frappe/public/js/frappe/ui/dialog.js | 7 ++++++- frappe/public/js/frappe/views/kanban/kanban_board.js | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js index 166fb6ae39..00300c3c96 100644 --- a/frappe/public/js/frappe/form/formatters.js +++ b/frappe/public/js/frappe/form/formatters.js @@ -59,7 +59,7 @@ frappe.form.formatters = { if(value) { return ''; } else { - return ''; + return ''; } }, Link: function(value, docfield, options, doc) { diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index c9b0a3289f..0ef3046bf1 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -79,7 +79,12 @@ frappe.ui.Dialog = frappe.ui.FieldGroup.extend({ .html(label) .click(function() { me.primary_action_fulfilled = true; - click.apply(me); + // get values and send it + // as first parameter to click callback + // if no values then return + var values = me.get_values(); + if(!values) return; + click.apply(me, [values]); }); }, make_head: function() { diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.js index 11143501c2..a630df601e 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.js @@ -913,7 +913,8 @@ frappe.provide("frappe.views"); if (df.fieldname === board.field_name && df.fieldtype === "Select") { if (action === "add") { //add column_name to Select field's option field - df.options += "\n" + title; + if(!df.options.includes(title)) + df.options += "\n" + title; } else if (action === "delete") { var options = df.options.split("\n"); var index = options.indexOf(title); From e8444438e98a036ef1b95a7152978d4e7b738592 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 15 Mar 2017 10:01:21 +0530 Subject: [PATCH 4/4] [kanban] overflow auto --- frappe/public/css/kanban.css | 2 +- frappe/public/less/kanban.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/css/kanban.css b/frappe/public/css/kanban.css index 4b32a3a47d..cae0ff1275 100644 --- a/frappe/public/css/kanban.css +++ b/frappe/public/css/kanban.css @@ -2,7 +2,7 @@ min-height: calc(100vh - 252px); background-color: #fafbfc; display: flex; - overflow: scroll; + overflow: auto; } .kanban .kanban-column { flex: 0 0 230px; diff --git a/frappe/public/less/kanban.less b/frappe/public/less/kanban.less index f1b33b4927..d10a606553 100644 --- a/frappe/public/less/kanban.less +++ b/frappe/public/less/kanban.less @@ -4,7 +4,7 @@ min-height: ~"calc(100vh - 252px)"; background-color: @light-bg; display: flex; - overflow: scroll; + overflow: auto; .kanban-column { flex: 0 0 230px;