espenmn
(Espen)
August 20, 2025, 3:35pm
1
How can I sync/add user portraits between two plone sites via restapi?
If I have
portrait = portal_membership.getPersonalPortrait(userid)
I can create a user on Plone site B (from Plone site A) with
payload = {
"email": email,
"fullname": fullname,
"sendPasswordReset": True
}
response = requests.post(users_endpoint, auth=auth, headers=headers, json=payload)
Can I 'add the portrait' the same way or do I need to first create the user and then use another endpoint to update it
Do I need to convert the image (data) to base64 ?
Or is it better to make 'my own endpoint' and call it with the url of the existing image?
For alternative 3, I assume I can set it (on site B) (with try:)
resp = requests.get(portrait_url) # portrait url should be /portal_memberdata/portraits/[user_id]
if resp.status_code == 200:
portrait_img = BytesIO(resp.content)
portrait_img.filename = "portrait.jpg"
if portrait_img:
portal_membership.changeMemberPortrait(portrait_img, user_id)
jensens
(Jens W. Klein)
August 21, 2025, 11:17am
2
You can pass the portrait as mapping with base64 encoded image on update (PATCH) of a user using the restapi. For some reason it is not possible on create.
espenmn
(Espen)
August 21, 2025, 11:57am
3
Thanks. Works.
I paste the code here for reference. (The only thing 'missing' is keeping the original file-name)
users_endpoint = f"{project_url}/@users"
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
for userid in user_list:
user = api.user.get(userid=userid)
if user is None:
print(f"⚠️ User '{userid}' not found")
continue
# Plone PAS user properties
username = user.getUserName()
fullname = user.getProperty("fullname")
email = user.getProperty("email")
payload = {
"email": email,
"fullname": fullname,
"username": username,
"sendPasswordReset": True,
# … etc
}
response = requests.post(users_endpoint, auth=auth, headers=headers, json=payload)
if response.status_code in (200, 201): # 201 = created
# print(f" User {email} created")
# Upload portrait if exists
portal_membership = api.portal.get_tool('portal_membership')
portrait = portal_membership.getPersonalPortrait(userid)
if portrait and hasattr(portrait, 'data'):
portrait_endpoint = response.json()['@id']
portrait_bytes = portrait.data or None # the binary image, None if it is the 'default image, then skip'
if portrait_bytes:
portrait_mime = getattr(portrait, "contentType", "image/jpeg")
# filename = portrait.__name__ or "portrait"
filename = "portrait"
portrait_b64 = base64.b64encode(portrait_bytes).decode("utf-8")
r = requests.patch(portrait_endpoint,
headers = headers,
json={'portrait': {'content-type': portrait_mime ,
'data': portrait_b64,
'encoding': "base64",
'filename': filename}},
auth=auth)
if r.status_code == 204:
print(f"✅ Portrait for '{userid}' uploaded successfully")
else:
print(f"⚠️ Failed to upload portrait for '{userid}'")
elif response.status_code == 409:
print(f"⚠️ User {email} already exists")
else:
print(f"❌ Error creating {email}: {response.status_code} {response.text}")
# TO do: Add messages, maybe, info / warning