From bedf04a5f429b58a095be3314bc0384abe6629fc Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 18 Sep 2015 18:01:49 +0530 Subject: [PATCH] [fixes] file manager, attachments go to 'Attachments' folder and permission + cleanups --- frappe/config/desktop.py | 2 +- frappe/core/doctype/file/file.js | 36 ++--- frappe/core/doctype/file/file.json | 140 ++++++++++++++++--- frappe/core/doctype/file/file.py | 71 +++------- frappe/core/doctype/file/file_list.js | 11 +- frappe/patches/v6_1/rename_file_data.py | 2 +- frappe/permissions.py | 54 ++++--- frappe/public/js/frappe/model/perm.js | 19 ++- frappe/public/js/frappe/views/breadcrumbs.js | 11 +- 9 files changed, 229 insertions(+), 117 deletions(-) diff --git a/frappe/config/desktop.py b/frappe/config/desktop.py index fb655a2c3b..fa17ffa449 100644 --- a/frappe/config/desktop.py +++ b/frappe/config/desktop.py @@ -46,7 +46,7 @@ def get_data(): "type": "list" }, "File Manager": { - "color": "#95a5a6", + "color": "#905df5", "doctype": "File", "icon": "icon-folder-close", "icon": "octicon octicon-file-directory", diff --git a/frappe/core/doctype/file/file.js b/frappe/core/doctype/file/file.js index b5f0133048..7f05e2ff9a 100644 --- a/frappe/core/doctype/file/file.js +++ b/frappe/core/doctype/file/file.js @@ -1,19 +1,21 @@ -frappe.ui.form.on("File", "onload", function(frm) { - if(frappe.utils.is_image_file(frm.doc.file_url)){ - frm.doc.preview = '
\ - \ -
'; - frm.refresh_field("preview"); - } -}) - frappe.ui.form.on("File", "refresh", function(frm) { - frm.add_custom_button(__('Download'), function(){ - window.open(frm.doc.file_url); - }, "icon-download"); -}) + if(!frm.doc.is_folder) { + frm.add_custom_button(__('Download'), function() { + window.open(frm.doc.file_url); + }, "icon-download"); + } + + var wrapper = frm.get_field("preview_html").$wrapper; + var is_viewable = frappe.utils.is_image_file(frm.doc.file_url); + + frm.toggle_display("preview", is_viewable); + frm.toggle_display("preview_html", is_viewable); -// frappe.ui.form.on("File", "download", function(frm) { -// window.open(frm.doc.file_url); -// }) + if(is_viewable){ + wrapper.html('
\ + \ +
'); + } else { + wrapper.empty(); + } +}); diff --git a/frappe/core/doctype/file/file.json b/frappe/core/doctype/file/file.json index 61d8930b4b..c9ff215cb9 100644 --- a/frappe/core/doctype/file/file.json +++ b/frappe/core/doctype/file/file.json @@ -1,7 +1,7 @@ { "allow_copy": 0, "allow_import": 1, - "allow_rename": 1, + "allow_rename": 0, "autoname": "hash", "creation": "2012-12-12 11:19:22", "custom": 0, @@ -24,6 +24,28 @@ "oldfieldtype": "Data", "permlevel": 0, "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "preview", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Preview", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, "read_only": 0, "report_hide": 0, "reqd": 0, @@ -35,13 +57,34 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "is_folder", - "fieldtype": "Check", + "fieldname": "preview_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Preview HTML", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "section_break_5", + "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Is Folder", "no_copy": 0, "permlevel": 0, "precision": "", @@ -80,14 +123,35 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "depends_on": "eval:!doc.is_folder", - "fieldname": "file_url", - "fieldtype": "Data", - "hidden": 0, + "fieldname": "is_attachments_folder", + "fieldtype": "Check", + "hidden": 1, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "File URL", + "label": "Is Attachments Folder", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "file_size", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "File Size", "no_copy": 0, "permlevel": 0, "print_hide": 0, @@ -119,13 +183,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "eval:!doc.is_folder", + "fieldname": "file_url", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "File URL", + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, "collapsible": 0, "fieldname": "folder", "fieldtype": "Link", - "hidden": 0, + "hidden": 1, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, @@ -135,7 +221,7 @@ "permlevel": 0, "precision": "", "print_hide": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -146,15 +232,16 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "file_size", - "fieldtype": "Int", + "fieldname": "is_folder", + "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, - "in_list_view": 1, - "label": "File Size", + "in_list_view": 0, + "label": "Is Folder", "no_copy": 0, "permlevel": 0, + "precision": "", "print_hide": 0, "read_only": 1, "report_hide": 0, @@ -167,6 +254,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "depends_on": "eval:!doc.is_folder", "fieldname": "section_break_8", "fieldtype": "Section Break", "hidden": 0, @@ -345,7 +433,7 @@ "is_submittable": 0, "issingle": 0, "istable": 0, - "modified": "2015-09-08 15:57:09.103049", + "modified": "2015-09-18 06:22:10.902847", "modified_by": "Administrator", "module": "Core", "name": "File", @@ -370,6 +458,26 @@ "share": 1, "submit": 0, "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 } ], "read_only": 0, diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index d5480598fd..944b0891c3 100644 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -30,7 +30,7 @@ class File(NestedSet): path = get_breadcrumbs(self.folder) folder_name = frappe.get_value("File", self.folder, "file_name") return "/".join([d.file_name for d in path] + [folder_name, self.file_name]) - + def set_name(self): """Set name for folder""" if self.is_folder: @@ -44,14 +44,14 @@ class File(NestedSet): def after_insert(self): self.update_parent_folder_size() - + def after_rename(self, olddn, newdn, merge=False): for successor in self.get_successor(): setup_folder_path(successor, self.name) - + def get_successor(self): return frappe.db.sql_list("select name from tabFile where folder='%s'"%self.name) or [] - + def validate(self): self.validate_duplicate_entry() self.validate_folder() @@ -83,52 +83,7 @@ class File(NestedSet): def set_folder_name(self): """Make parent folders if not exists based on reference doctype and name""" if self.attached_to_doctype and not self.folder: - self.folder = self.get_parent_folder_name() - - def get_parent_folder_name(self): - """Returns parent folder name. If not exists, then make""" - doctype_folder_name = self.get_doctype_folder_name() - parent_folder_name = frappe.db.get_value("File", {"file_name": self.attached_to_name, - "is_folder": 1, "folder": doctype_folder_name}) - - return self.make_folder(parent_folder_name, doctype_folder_name, - self.attached_to_name) - - def get_doctype_folder_name(self): - """Returns doctype folder name. If not exists, then make""" - module_folder_name = self.get_module_folder_name() - doctype_folder_name = frappe.db.get_value("File", {"file_name": self.attached_to_doctype, - "is_folder": 1, "folder": module_folder_name}) - - return self.make_folder(doctype_folder_name, module_folder_name, - _(self.attached_to_doctype, frappe.db.get_default("lang"))) - - def get_module_folder_name(self): - """Returns module folder name. If not exists, then make""" - if self.attached_to_doctype: - module = frappe.db.get_value("DocType", self.attached_to_doctype, "module") - - home_folder_name = frappe.db.get_value("File", {"is_home_folder": 1}) - - module_folder_name = frappe.db.get_value("File", {"file_name": module, - "is_folder": 1, "folder": home_folder_name}) - - return self.make_folder(module_folder_name, home_folder_name, _(module, - frappe.db.get_default("lang"))) - - def make_folder(self, name, folder, file_name): - if not name: - # parent folder - file = frappe.get_doc({ - "doctype": "File", - "is_folder": 1, - "file_name": file_name, - "folder": folder - }).insert() - - name = file.name - - return name + self.folder = frappe.db.get_value("File", {"is_attachments_folder": 1}) def validate_folder(self): if not self.is_home_folder and not self.folder and \ @@ -188,13 +143,21 @@ def on_doctype_update(): frappe.db.add_index("File", ["attached_to_doctype", "attached_to_name"]) def make_home_folder(): - frappe.get_doc({ + home = frappe.get_doc({ "doctype": "File", "is_folder": 1, "is_home_folder": 1, "file_name": _("Home") }).insert() + frappe.get_doc({ + "doctype": "File", + "folder": home.name, + "is_folder": 1, + "is_attachments_folder": 1, + "file_name": _("Attachments") + }).insert() + @frappe.whitelist() def get_breadcrumbs(folder): """returns name, file_name of parent folder""" @@ -221,11 +184,11 @@ def move_file(file_list, new_parent, old_parent): frappe.get_doc("File", new_parent).save() return "File(s) has been moved successfully!!" - + def setup_folder_path(filename, new_parent): file = frappe.get_doc("File", filename) file.folder = new_parent file.save() - + if file.is_folder: - frappe.rename_doc("File", file.name, file.get_name_based_on_parent_folder()) \ No newline at end of file + frappe.rename_doc("File", file.name, file.get_name_based_on_parent_folder()) diff --git a/frappe/core/doctype/file/file_list.js b/frappe/core/doctype/file/file_list.js index b84f6f221a..20b860fa7d 100644 --- a/frappe/core/doctype/file/file_list.js +++ b/frappe/core/doctype/file/file_list.js @@ -32,7 +32,7 @@ frappe.listview_settings['File'] = { doclist.breadcrumb = $('') .insertBefore(doclist.filter_area); - doclist.listview.settings.setup_new_folder(doclist); + doclist.listview.settings.setup_menu(doclist); doclist.listview.settings.setup_dragdrop(doclist); doclist.$page.on("click", ".list-delete", function(event) { @@ -52,7 +52,7 @@ frappe.listview_settings['File'] = { }); }); }, - setup_new_folder: function(doclist) { + setup_menu: function(doclist) { doclist.page.add_menu_item(__("New Folder"), function() { var d = frappe.prompt(__("Name"), function(values) { if((values.value.indexOf("/") > -1)){ @@ -70,6 +70,10 @@ frappe.listview_settings['File'] = { }) }, __('Enter folder name'), __("Create")); }); + + doclist.page.add_menu_item(__("Edit Folder"), function() { + frappe.set_route("Form", "File", doclist.current_folder); + }); }, setup_dragdrop: function(doclist) { $(doclist.$page).on('dragenter dragover', false) @@ -153,7 +157,9 @@ frappe.listview_settings['File'] = { doclist.filter_list.add_filter("File", "folder", "=", doclist.current_folder, true); doclist.dirty = true; + doclist.fresh = false; + doclist.page.set_title(doclist.current_folder_name); frappe.utils.set_title(doclist.current_folder_name); }, set_primary_action:function(doclist){ @@ -165,7 +171,6 @@ frappe.listview_settings['File'] = { "from_form": 1 }, callback: function() { - console.log('here') doclist.refresh(); } }); diff --git a/frappe/patches/v6_1/rename_file_data.py b/frappe/patches/v6_1/rename_file_data.py index 6e7c163182..9c2a8edc3b 100644 --- a/frappe/patches/v6_1/rename_file_data.py +++ b/frappe/patches/v6_1/rename_file_data.py @@ -7,7 +7,7 @@ def execute(): frappe.rename_doc("DocType", "File Data", "File") frappe.reload_doctype("File") - # DELETE THIS + # TODO: DELETE THIS frappe.db.sql("""delete from tabFile where is_folder=1""") frappe.db.sql("""update tabFile set folder=null""") diff --git a/frappe/permissions.py b/frappe/permissions.py index 0122fbde98..c534cd15c3 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -52,6 +52,7 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None): if doc_name in shared: if verbose: print "Shared" if ptype in ("read", "write", "share") or meta.permissions[0].get(ptype): + if verbose: print "Is shared" return True elif shared: @@ -60,6 +61,7 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None): if verbose: print "Has a shared document" return True + if verbose: print "Not Shared" return False role_permissions = get_role_permissions(meta, user=user, verbose=verbose) @@ -67,28 +69,40 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None): if not role_permissions.get(ptype): return false_if_not_shared() + perm = True + if doc: if isinstance(doc, basestring): doc = frappe.get_doc(meta.name, doc) - # if owner match, then return True - if doc.owner == frappe.session.user and role_permissions["if_owner"].get(ptype) and ptype!="create": - return True + owner_perm = user_perm = controller_perm = None + + if role_permissions["if_owner"].get(ptype) and ptype!="create": + owner_perm = doc.owner == frappe.session.user + if verbose: print "Owner permission: {0}".format(owner_perm) # check if user permission - if role_permissions["apply_user_permissions"].get(ptype): - if not user_has_permission(doc, verbose=verbose, user=user, - user_permission_doctypes=role_permissions.get("user_permission_doctypes", {}).get(ptype) or []): - if verbose: print "No user permission" - return false_if_not_shared() - - if not has_controller_permissions(doc, ptype, user=user): - if verbose: print "No controller permission" - return false_if_not_shared() - - if verbose: - print "Has Role" - return True + if not owner_perm and role_permissions["apply_user_permissions"].get(ptype): + user_perm = user_has_permission(doc, verbose=verbose, user=user, + user_permission_doctypes=role_permissions.get("user_permission_doctypes", {}).get(ptype) or []) + + if verbose: print "User permission: {0}".format(user_perm) + + if not owner_perm and not user_perm: + controller_perm = has_controller_permissions(doc, ptype, user=user) + + if verbose: print "Controller permission: {0}".format(controller_perm) + + # permission true if any one condition is explicitly True or all permissions are undefined (None) + perm = any([owner_perm, user_perm, controller_perm]) or \ + all([owner_perm==None, user_perm==None, controller_perm==None]) + + if not perm: + perm = false_if_not_shared() + + if verbose: print "Final Permission: {0}".format(perm) + + return perm def get_doc_permissions(doc, verbose=False, user=None): """Returns a dict of evaluated permissions for given `doc` like `{"read":1, "write":1}`""" @@ -271,9 +285,15 @@ def user_has_permission(doc, verbose=True, user=None, user_permission_doctypes=N return _user_has_permission def has_controller_permissions(doc, ptype, user=None): + """Returns controller permissions if defined. None if not defined""" if not user: user = frappe.session.user - for method in frappe.get_hooks("has_permission").get(doc.doctype, []): + methods = frappe.get_hooks("has_permission").get(doc.doctype, []) + + if not methods: + return None + + for method in methods: if not frappe.call(frappe.get_attr(method), doc=doc, ptype=ptype, user=user): return False diff --git a/frappe/public/js/frappe/model/perm.js b/frappe/public/js/frappe/model/perm.js index 028d4c586f..a76bf01658 100644 --- a/frappe/public/js/frappe/model/perm.js +++ b/frappe/public/js/frappe/model/perm.js @@ -60,6 +60,20 @@ $.extend(frappe.perm, { }); } + // if owner + if(!$.isEmptyObject(perm[0].if_owner)) { + if(doc.owner===user) { + $.extend(perm[0], perm[0].if_owner); + } else { + // not owner, remove permissions + $.each(perm[0].if_owner, function(ptype, value) { + if(perm[0].if_owner[ptype]) { + perm[0][ptype] = 0 + } + }) + } + } + // apply permissions from shared if(docinfo.shared) { for(var i=0; i%(label)s', - {doctype: breadcrumbs.doctype, label: __(breadcrumbs.doctype)})) + route = (cur_frm && cur_frm.list_route) || ("List/" + breadcrumbs.doctype) + $(repl('
  • %(label)s
  • ', + {route: route, label: __(breadcrumbs.doctype)})) .appendTo($breadcrumbs); } }