The Doctype User has level 0 permission completely removed for the role Employee. However, a user of such role (solely Employee) can still change profile picture clicking on the user’s icon on the top right corner and chosing the option “My Profile>Edit Profile”.
Solution: Prevent Employees from Changing Profile Pictures (Server + Client Side)
The reason simple permission changes often fail is that the “My Profile” page in Frappe is a dedicated UI page, not a standard DocType Form. To block this completely, you need a two-layered approach: a Server Script for security and a Client Script for the UI.
1. The “Safety Lock” (Server Script)
This prevents any change to the user_image field in the database, even if the user tries to bypass the UI via API or Developer Tools.
- Search for:
Server Script List→ Add Server Script - Script Name: Prevent Employee Profile Picture Change
- Server Script Type:
DocType Event - Reference DocType:
User - DocType Event:
Before Save - Script:
# Check if the user has the 'Employee' role and is NOT a 'System Manager'
if "Employee" in [d.role for d in frappe.get_roles(doc.name)] and not "System Manager" in frappe.get_roles():
# Fetch the existing data from the database
old_doc = frappe.get_doc("User", doc.name)
# Compare the new image with the old one
if doc.user_image != old_doc.user_image:
frappe.throw("Sorry, employees are not allowed to change their profile picture. Please contact HR.")
2. The “UI Mask” (Client Script)
This hides the “Edit” buttons and the camera icon to provide a better user experience, preventing users from trying to upload a photo in the first place.
- Search for:
Client Script List→ Add Client Script - DocType:
User - Script:
frappe.ui.form.on('User', {
refresh: function(frm) {
// Standard Form: Make the image field read-only for employees
if (frappe.user.has_role('Employee') && !frappe.user.has_role('System Manager')) {
frm.set_df_property('user_image', 'read_only', 1);
}
}
});
// My Profile Page: Hide the edit buttons using jQuery
$(document).on('app_ready', function() {
if (frappe.user.has_role('Employee') && !frappe.user.has_role('System Manager')) {
// Use a small interval to ensure the buttons are caught when the profile modal opens
setInterval(() => {
$('.edit-profile-btn, .avatar-action-btn, .btn-edit-details').hide();
}, 500);
}
});
Why this works:
- Security: The Server Script is the final authority. Even if a tech-savvy user tries to send a
PUTrequest via the API, the server will reject it. - User Experience: The Client Script ensures that the “My Profile” page (which is a custom Vue/JS component) doesn’t show the editing options to unauthorized roles.
Note: After applying these scripts, remember to run Clear Cache or Reload (Ctrl+Shift+R) for the changes to take effect in the browser.