Hey all,
Using this link about how to use hook and python code to set permission on file, I am struggling to make my code work.
Use case. I have a DocType called meeting with an associated child DocType called Meeting Attendee. A user could have “read” access to a Meeting instance if she/he is part of the attendee list.
Here is what I have but it doesn’t work as now, no one can have access to any meeting. Any pointer on what I might be doing wrong?
Thanks in advance for help, pointers, support
On hook.py
has_permission = {
“Meeting”: “ifitwala_ed.school_settings.doctype.meeting.meeting.meeting_has_permission”,
}
On the Meeting DocType:
def meeting_has_permission(doc, user=None, permission_type=None):
attendees=frappe.get_all("Meeting Attendee", filters = {"parent": doc}, fields = ["attendee"])
attendee_list = [d.attendee for d in attendees]
if permission_type == "read" and user in attendee_list:
return True
return False
system does not support using child field as where condition in get_permission_query_conditions hook. the following changes will make it possible
frappe.model.db_query.py
def get_permission_query_conditions(self):
valid_operators = ("=", "!=", ">", "<", ">=", "<=", "like", "not like", "in", "not in", "is",
"between")
condition_methods = frappe.get_hooks("permission_query_conditions", {}).get(self.doctype, [])
if condition_methods:
conditions = []
for method in condition_methods:
c = frappe.call(frappe.get_attr(method), self.user)
if c:
conditions.append(c)
# in case child talbe field used, add child table to tables which will auto inner join
token = re.split(' and | or ',c, flags=re.IGNORECASE)
for t in token:
field = re.split('|'.join(valid_operators),t)
if len(field)>1:
f = {field[0].replace('(','').replace(' ','').replace('`',''):''}
try:
f = get_filter(self.doctype, f)
tname = ('`tab' + f.doctype + '`')
if not tname in self.tables:
self.append_table(tname)
except:
pass
return " and ".join(conditions) if conditions else None
what I have done
-
hooks.py
permission_query_conditions = {
"Meeting": "ibp.integrated_budget_and_planning.doctype.meeting.meeting.get_permission_query_conditions",
}
has_permission = {
"Meeting": "ibp.integrated_budget_and_planning.doctype.meeting.meeting.has_permission",
}
-
meetings.py
from future import unicode_literals
import frappe
from frappe.model.document import Document
class Meeting(Document):
pass
def get_permission_query_conditions(user):
if not user: user = frappe.session.user
return """(`attendee`=%(user)s)""" % {
"user": frappe.db.escape(user),
}
def has_permission(doc, user):
if user in [d.attendee for d in doc.attendees]:
return True
return False
-
in Meetings doctype, assigned a custom role
-
in user master assign custom role to user.
5 Likes
Oh woohaaaa. thank you so very much @szufisher for your detailed help.
Let me try all of that and I’ll get back to you.
I am a bit confused with the sql query:
return """(`attendee`=%(user)s)""" % {
"user": frappe.db.escape(user),
}
attendee is the field name in meeting attendance child table(field name attendees) of meeting doctype, this is simply where condition which used the child table field, the change made to db_query will implicitly add the inner join between master(meeting) and child table(meeting attendance).
1 Like
Yes, Thanks @szufisher .
The sql query threw me an error.
The has_permission function worked as intended.
The issue is now that user have no permission to create any new meeting. How can one go around that.
The idea is that everyone can create a meeting and invite anyone. But anyone can only see the meetings that they have been invited to (or are being part of).
Best,
please provide screen shot of meeting and meeting attendance doctype definition(field name list).
change from return “”“(attendee
=%(user)s)”“” % {
“user”: frappe.db.escape(user),
}
to
return “”“(attendee=%(user)s)”“” % {
“user”: frappe.db.escape(user),
}
the quote sign around field name attendee is necessary.
also make sure you changed the db_query. py as above
Thanks, @szufisher.
I think your two codes are the same, but I got it with the quote sign around attendee.
How do you solve the issue to create new meetings. The has_permission code allow for users to only see meetings they have been invited to, but how can anyone create a meeting. It seems then now, no one can create meetings anymore; although, In the doctype meeting, I did give create permission to a whole bunch of roles.
I am a bit wary to change the code straight in the frappe app. This will create issue later on when updating Frappe. (will need to either manually update with git pull, then migrate, then rebuild, etc.) or stag changes.
Have you considered putting in a pull request for it?
I can see so many cases where we need permission on child’s tables. So many documents in HR would benefit from it. And we would then be able to solve the student’s view issue (students should only be seen by instructors who teach them. At the moment, instructors see all students)
Again, I am very thankful for your time and explanations. Much appreciated.
if user added himself/herself in the attendee list, then he/she can successfully create new meeting.
anyway, I changed the code as following, then user as owner is always has the permission to create/display the meeting.
def get_permission_query_conditions(user):
if not user: user = frappe.session.user
return """(attendee=%(user)s or tabMeeting.owner=%(user)s)""" % {
"user": frappe.db.escape(user),
}
def has_permission(doc, user):
if doc.is_new():
return True
if doc.owner==user or user in [d.attendee for d in doc.attendees]:
return True
return False
I tried to overwrite get_permission_query_conditions in custom app hooks, seems it does not work. so only way is to apply PR to merge into core.
I will try, but per my experience in the past, it is not so easy to be accepted into core.
this is the tested working version which do not need change to the core
3 Likes
@szufisher, trying it all now.
Man you’ve been such a huge help. Thank you so much. I’ll get back to you.
It all works!!!
Thanks so much
Please mark my answer as solution , otherwise other user will be confused .
@szufisher
Will this solution work for reports created using report builder ?
I am facing a similar issue.
In my use case, I have provided user permission to Sales Person doctype to the users who are the Sales Person.
The sales person is added to the sales_team child table of the Sales Invoice. The users are able to see all the Sales Invoice in the List View (though not able to open) but in the report view they can see the details of all the sales invoices.