From 7959ca14dc100b4a294fcf6add01ea311246717b Mon Sep 17 00:00:00 2001 From: Ephraim Date: Mon, 26 Aug 2024 15:52:59 +0300 Subject: [PATCH 1/8] fix(User profile): Change what fields get added to the response of the profile GET API when `basic=True` --- sefaria/model/user_profile.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sefaria/model/user_profile.py b/sefaria/model/user_profile.py index bb58dc1fd6..645cbf692b 100644 --- a/sefaria/model/user_profile.py +++ b/sefaria/model/user_profile.py @@ -686,25 +686,24 @@ def to_api_dict(self, basic=False): "slug": self.slug, "profile_pic_url": self.profile_pic_url, "full_name": self.full_name, + "followers": self.followers.uids, + "followees": self.followees.uids, + "profile_pic_url": self.profile_pic_url, + "jewish_education": self.jewish_education, + "bio": self.bio, + "website": self.website, + "location": self.location, + "public_email": self.public_email, + "facebook": self.facebook, + "twitter": self.twitter, + "linkedin": self.linkedin, + "youtube": self.youtube, "position": self.position, "organization": self.organization } if basic: return dictionary other_info = { - "full_name": self.full_name, - "followers": self.followers.uids, - "followees": self.followees.uids, - "profile_pic_url": self.profile_pic_url, - "jewish_education": self.jewish_education, - "bio": self.bio, - "website": self.website, - "location": self.location, - "public_email": self.public_email, - "facebook": self.facebook, - "twitter": self.twitter, - "linkedin": self.linkedin, - "youtube": self.youtube, "pinned_sheets": self.pinned_sheets, "show_editor_toggle": self.show_editor_toggle, "uses_new_editor": self.uses_new_editor, From 4e37ea5c10a62f2260615bdbf83e6715e8eae7a2 Mon Sep 17 00:00:00 2001 From: Ephraim Date: Mon, 26 Aug 2024 15:53:46 +0300 Subject: [PATCH 2/8] fix(User profile): If the profile being requested doesnt matched the auth'd user - only send basic information --- reader/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 4062f2fdbe..210de3ec42 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3589,7 +3589,8 @@ def account_user_update(request): def profile_get_api(request, slug): if request.method == "GET": profile = UserProfile(slug=slug) - return jsonResponse(profile.to_api_dict()) + owner_of_profile = request.user.is_authenticated and request.user.id == profile.id + return jsonResponse(profile.to_api_dict(basic = not owner_of_profile)) return jsonResponse({"error": "Unsupported HTTP method."}) From 09a4d9e4e249cb7b4bc813d963a913df71bdfe13 Mon Sep 17 00:00:00 2001 From: Ephraim Date: Mon, 26 Aug 2024 15:56:42 +0300 Subject: [PATCH 3/8] fix(User profile): If the profile being requested doesnt matched the auth'd user - only send basic information --- reader/views.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/reader/views.py b/reader/views.py index 210de3ec42..1d63cf77a1 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3499,10 +3499,12 @@ def user_profile(request, username): if not requested_profile.user.is_active: raise Http404('Profile is inactive.') + owner_of_profile = request.user.is_authenticated and request.user.id == requested_profile.id + tab = request.GET.get("tab", "sheets") props = { "initialMenu": "profile", - "initialProfile": requested_profile.to_api_dict(), + "initialProfile": requested_profile.to_api_dict(basic=not owner_of_profile), "initialTab": tab, } title = _("%(full_name)s on Sefaria") % {"full_name": requested_profile.full_name} @@ -3585,15 +3587,6 @@ def account_user_update(request): return jsonResponse({"error": "Unsupported HTTP method."}) -@catch_error_as_json -def profile_get_api(request, slug): - if request.method == "GET": - profile = UserProfile(slug=slug) - owner_of_profile = request.user.is_authenticated and request.user.id == profile.id - return jsonResponse(profile.to_api_dict(basic = not owner_of_profile)) - return jsonResponse({"error": "Unsupported HTTP method."}) - - @catch_error_as_json def profile_follow_api(request, ftype, slug): if request.method == "GET": From a8186b6e3bf170fd0b4f144c4adc7ed7ccd98f4d Mon Sep 17 00:00:00 2001 From: Ephraim Date: Mon, 26 Aug 2024 15:57:07 +0300 Subject: [PATCH 4/8] refactor(User profile): Move profile GET API view closer to the profile view --- reader/views.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/reader/views.py b/reader/views.py index 1d63cf77a1..5be5604bdd 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3515,6 +3515,15 @@ def user_profile(request, username): }) +@catch_error_as_json +def profile_get_api(request, slug): + if request.method == "GET": + profile = UserProfile(slug=slug) + owner_of_profile = request.user.is_authenticated and request.user.id == profile.id + return jsonResponse(profile.to_api_dict(basic = not owner_of_profile)) + return jsonResponse({"error": "Unsupported HTTP method."}) + + @catch_error_as_json def profile_api(request): """ From e380595d1149ba4156d10d463748593a2e924403 Mon Sep 17 00:00:00 2001 From: Ephraim Date: Mon, 26 Aug 2024 16:31:18 +0300 Subject: [PATCH 5/8] refactor(User profile): Consolidate Profile API views --- reader/views.py | 19 ++++++++----------- sefaria/urls.py | 4 ++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/reader/views.py b/reader/views.py index 5be5604bdd..6ee88fd127 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3516,23 +3516,19 @@ def user_profile(request, username): @catch_error_as_json -def profile_get_api(request, slug): +def profile_api(request, slug=None): + """ + API for user profiles. + """ if request.method == "GET": profile = UserProfile(slug=slug) owner_of_profile = request.user.is_authenticated and request.user.id == profile.id return jsonResponse(profile.to_api_dict(basic = not owner_of_profile)) - return jsonResponse({"error": "Unsupported HTTP method."}) + elif request.method == "POST": # The POST only works for the logged in user, which is not very API'ish but thats for another day + if not request.user.is_authenticated: + return jsonResponse({"error": _("You must be logged in to update your profile.")}) -@catch_error_as_json -def profile_api(request): - """ - API for user profiles. - """ - if not request.user.is_authenticated: - return jsonResponse({"error": _("You must be logged in to update your profile.")}) - - if request.method == "POST": profileJSON = request.POST.get("json") if not profileJSON: return jsonResponse({"error": "No post JSON."}) @@ -3548,6 +3544,7 @@ def profile_api(request): else: profile.save() return jsonResponse(profile.to_mongo_dict()) + return jsonResponse({"error": "Unsupported HTTP method."}) diff --git a/sefaria/urls.py b/sefaria/urls.py index cd467b760e..928606f4e0 100644 --- a/sefaria/urls.py +++ b/sefaria/urls.py @@ -84,13 +84,13 @@ url(r'^profile/(?P[^/]+)/?$', reader_views.user_profile), url(r'^settings/account?$', reader_views.account_settings), url(r'^settings/profile?$', reader_views.edit_profile), + url(r'^settings/account/user$', reader_views.account_user_update), url(r'^interface/(?Penglish|hebrew)$', reader_views.interface_language_redirect), url(r'^api/profile/user_history$', reader_views.user_history_api), url(r'^api/profile/sync$', reader_views.profile_sync_api), url(r'^api/profile/upload-photo$', reader_views.profile_upload_photo), url(r'^api/profile$', reader_views.profile_api), - url(r'^settings/account/user$', reader_views.account_user_update), - url(r'^api/profile/(?P[^/]+)$', reader_views.profile_get_api), + url(r'^api/profile/(?P[^/]+)$', reader_views.profile_api), url(r'^api/profile/(?P[^/]+)/(?Pfollowers|following)$', reader_views.profile_follow_api), url(r'^api/user_history/saved$', reader_views.saved_history_for_ref), ] From 9cb677152e26ea7be29f8c66ffc1ab9a679dd332 Mon Sep 17 00:00:00 2001 From: Ephraim Date: Tue, 27 Aug 2024 11:10:03 +0300 Subject: [PATCH 6/8] docs(User profile): Consolidate Profile API views - update inline comment --- reader/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/reader/views.py b/reader/views.py index 6ee88fd127..08f1c921bf 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3525,7 +3525,10 @@ def profile_api(request, slug=None): owner_of_profile = request.user.is_authenticated and request.user.id == profile.id return jsonResponse(profile.to_api_dict(basic = not owner_of_profile)) - elif request.method == "POST": # The POST only works for the logged in user, which is not very API'ish but thats for another day + elif request.method == "POST": + # The POST only works for the logged in user, which is more common for a website view rather than API. + # If the API were to be consistent, we might need to add ability to post updates for any user, + # and of course limit authorization on who can do that if not request.user.is_authenticated: return jsonResponse({"error": _("You must be logged in to update your profile.")}) From 45ae3c0f2bf4e527ba15b8dcbd7bbb735fe08c41 Mon Sep 17 00:00:00 2001 From: Ephraim Date: Tue, 27 Aug 2024 11:33:32 +0300 Subject: [PATCH 7/8] fix(User profile): Adds an approproate HTTP response for a null user param --- reader/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reader/views.py b/reader/views.py index 08f1c921bf..0119b20b3d 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3521,6 +3521,8 @@ def profile_api(request, slug=None): API for user profiles. """ if request.method == "GET": + if not slug: + raise Http404("Please Supply a valid user identification") profile = UserProfile(slug=slug) owner_of_profile = request.user.is_authenticated and request.user.id == profile.id return jsonResponse(profile.to_api_dict(basic = not owner_of_profile)) From 483b41fa685b13115933fd9727a77f33f50a2104 Mon Sep 17 00:00:00 2001 From: Ephraim Date: Tue, 27 Aug 2024 12:00:45 +0300 Subject: [PATCH 8/8] fix(User profile): Adds an approproate HTTP response for a null user param --- reader/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reader/views.py b/reader/views.py index 0119b20b3d..6a8ead48fc 100644 --- a/reader/views.py +++ b/reader/views.py @@ -3521,9 +3521,9 @@ def profile_api(request, slug=None): API for user profiles. """ if request.method == "GET": - if not slug: - raise Http404("Please Supply a valid user identification") profile = UserProfile(slug=slug) + if not slug or profile.id is None: + raise Http404("Please Supply a valid user identification") owner_of_profile = request.user.is_authenticated and request.user.id == profile.id return jsonResponse(profile.to_api_dict(basic = not owner_of_profile))