Skip to content

Commit faad331

Browse files
committed
Merge branch 'hotfix/25.17.1'
2 parents 484adc0 + 9c9ea7b commit faad331

6 files changed

Lines changed: 59 additions & 48 deletions

File tree

CHANGELOG

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO.
44

5+
25.17.1 (2025-10-09)
6+
====================
7+
8+
- Misc. fixes for Angular migration
9+
510
25.17.0 (2025-10-03)
611
====================
712

api/nodes/serializers.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,7 @@ class NodeContributorsCreateSerializer(NodeContributorsSerializer):
12281228
full_name = ser.CharField(required=False)
12291229
email = ser.EmailField(required=False, source='user.email', write_only=True)
12301230
index = ser.IntegerField(required=False, source='_order')
1231+
child_nodes = ser.ListField(required=False, write_only=True)
12311232

12321233
users = RelationshipField(
12331234
related_view='users:user-detail',
@@ -1241,13 +1242,15 @@ class NodeContributorsCreateSerializer(NodeContributorsSerializer):
12411242
def get_proposed_permissions(self, validated_data):
12421243
return validated_data.get('permission') or osf_permissions.DEFAULT_CONTRIBUTOR_PERMISSIONS
12431244

1244-
def validate_data(self, node, user_id=None, full_name=None, email=None, index=None):
1245+
def validate_data(self, node, user_id=None, full_name=None, email=None, index=None, child_nodes=None):
12451246
if not user_id and not full_name:
12461247
raise exceptions.ValidationError(detail='A user ID or full name must be provided to add a contributor.')
12471248
if user_id and email:
12481249
raise exceptions.ValidationError(detail='Do not provide an email when providing this user_id.')
12491250
if index is not None and index > len(node.contributors):
12501251
raise exceptions.ValidationError(detail=f'{index} is not a valid contributor index for node with id {node._id}')
1252+
if child_nodes and (not_children := set(child_nodes) - set(node.node_ids)):
1253+
raise exceptions.ValidationError(detail=f'Nodes {', '.join(not_children)} are not children of node with id {node._id}')
12511254

12521255
def create(self, validated_data):
12531256
id = validated_data.get('_id')
@@ -1260,21 +1263,23 @@ def create(self, validated_data):
12601263
full_name = validated_data.get('full_name')
12611264
bibliographic = validated_data.get('bibliographic')
12621265
send_email = self.context['request'].GET.get('send_email') or self.context['default_email']
1266+
child_nodes = validated_data.get('child_nodes')
12631267
permissions = self.get_proposed_permissions(validated_data)
12641268

1265-
self.validate_data(node, user_id=id, full_name=full_name, email=email, index=index)
1269+
self.validate_data(node, user_id=id, full_name=full_name, email=email, index=index, child_nodes=child_nodes)
12661270

12671271
if send_email not in self.email_preferences:
12681272
raise exceptions.ValidationError(detail=f'{send_email} is not a valid email preference.')
12691273

12701274
try:
12711275
contributor_dict = {
12721276
'auth': auth, 'user_id': id, 'email': email, 'full_name': full_name, 'send_email': send_email,
1273-
'bibliographic': bibliographic, 'index': index, 'save': True,
1277+
'bibliographic': bibliographic, 'index': index, 'save': True, 'permissions': permissions,
12741278
}
1275-
1276-
contributor_dict['permissions'] = permissions
12771279
contributor_obj = node.add_contributor_registered_or_not(**contributor_dict)
1280+
if child_nodes:
1281+
for child in AbstractNode.objects.filter(guids___id__in=child_nodes):
1282+
child.add_contributor_registered_or_not(**contributor_dict)
12781283
except ValidationError as e:
12791284
raise exceptions.ValidationError(detail=e.messages[0])
12801285
except ValueError as e:

framework/status/__init__.py

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,37 @@ def push_status_message(message, kind='warning', dismissible=True, trust=True, j
2828
:param jumbotron: Should this be in a jumbotron element rather than an alert
2929
"""
3030
# TODO: Change the default to trust=False once conversion to markupsafe rendering is complete
31-
try:
32-
current_session = get_session()
33-
statuses = current_session.get('status', None)
34-
except RuntimeError as e:
35-
exception_message = str(e)
36-
if 'Working outside of request context.' in exception_message:
37-
# Working outside of request context, so should be a DRF issue. Status messages are not appropriate there.
38-
# If it's any kind of notification, then it doesn't make sense to send back to the API routes.
39-
if kind == 'error':
40-
# If it's an error, then the call should fail with the error message. I do not know of any cases where
41-
# this branch will be hit, but I'd like to avoid a silent failure.
42-
from rest_framework.exceptions import ValidationError
43-
raise ValidationError(message)
44-
return
45-
else:
46-
raise
47-
if not statuses:
48-
statuses = []
49-
if not extra:
50-
extra = {}
51-
css_class = TYPE_MAP.get(kind, 'warning')
52-
statuses.append(Status(message=message,
53-
jumbotron=jumbotron,
54-
css_class=css_class,
55-
dismissible=dismissible,
56-
id=id,
57-
extra=extra,
58-
trust=trust))
59-
current_session['status'] = statuses
60-
current_session.save()
31+
# try:
32+
# current_session = get_session()
33+
# statuses = current_session.get('status', None)
34+
# except RuntimeError as e:
35+
# exception_message = str(e)
36+
# if 'Working outside of request context.' in exception_message:
37+
# # Working outside of request context, so should be a DRF issue. Status messages are not appropriate there.
38+
# # If it's any kind of notification, then it doesn't make sense to send back to the API routes.
39+
# if kind == 'error':
40+
# # If it's an error, then the call should fail with the error message. I do not know of any cases where
41+
# # this branch will be hit, but I'd like to avoid a silent failure.
42+
# from rest_framework.exceptions import ValidationError
43+
# raise ValidationError(message)
44+
# return
45+
# else:
46+
# raise
47+
# if not statuses:
48+
# statuses = []
49+
# if not extra:
50+
# extra = {}
51+
# css_class = TYPE_MAP.get(kind, 'warning')
52+
# statuses.append(Status(message=message,
53+
# jumbotron=jumbotron,
54+
# css_class=css_class,
55+
# dismissible=dismissible,
56+
# id=id,
57+
# extra=extra,
58+
# trust=trust))
59+
# current_session['status'] = statuses
60+
# current_session.save()
61+
pass
6162

6263

6364
def pop_status_messages(level=0):

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "OSF",
3-
"version": "25.17.0",
3+
"version": "25.17.1",
44
"description": "Facilitating Open Science",
55
"repository": "https://github.com/CenterForOpenScience/osf.io",
66
"author": "Center for Open Science",

tests/test_auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def test_confirm_email(self):
108108
assert res.status_code == 302
109109
assert '/' == urlparse(res.location).path
110110
assert len(self.mock_send_grid.call_args_list) == 0
111-
assert len(get_session()['status']) == 1
111+
# assert len(get_session()['status']) == 1
112112

113113
def test_get_user_by_id(self):
114114
user = UserFactory()

tests/test_webtests.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ def test_can_receive_resend_confirmation_email(self):
747747
assert self.mock_send_grid.called
748748
assert res.status_code == 200
749749
assert res.request.path == self.post_url
750-
assert_in_html('If there is an OSF account', res.text)
750+
# assert_in_html('If there is an OSF account', res.text)
751751

752752
# test that confirmed user cannot receive resend confirmation email
753753
def test_cannot_receive_resend_confirmation_email_1(self):
@@ -761,7 +761,7 @@ def test_cannot_receive_resend_confirmation_email_1(self):
761761
assert not self.mock_send_grid.called
762762
assert res.status_code == 200
763763
assert res.request.path == self.post_url
764-
assert_in_html('has already been confirmed', res.text)
764+
# assert_in_html('has already been confirmed', res.text)
765765

766766
# test that non-existing user cannot receive resend confirmation email
767767
def test_cannot_receive_resend_confirmation_email_2(self):
@@ -775,7 +775,7 @@ def test_cannot_receive_resend_confirmation_email_2(self):
775775
assert not self.mock_send_grid.called
776776
assert res.status_code == 200
777777
assert res.request.path == self.post_url
778-
assert_in_html('If there is an OSF account', res.text)
778+
# assert_in_html('If there is an OSF account', res.text)
779779

780780
# test that user cannot submit resend confirmation request too quickly
781781
def test_cannot_resend_confirmation_twice_quickly(self):
@@ -788,7 +788,7 @@ def test_cannot_resend_confirmation_twice_quickly(self):
788788

789789
# check request and response
790790
assert res.status_code == 200
791-
assert_in_html('Please wait', res.text)
791+
# assert_in_html('Please wait', res.text)
792792

793793

794794
@mock.patch('website.mails.settings.USE_EMAIL', True)
@@ -839,8 +839,8 @@ def test_can_receive_reset_password_email(self):
839839
# check request URL is /forgotpassword
840840
assert res.request.path == self.post_url
841841
# check push notification
842-
assert_in_html('If there is an OSF account', res.text)
843-
assert_not_in_html('Please wait', res.text)
842+
# assert_in_html('If there is an OSF account', res.text)
843+
# assert_not_in_html('Please wait', res.text)
844844

845845
# check verification_key_v2 is set
846846
self.user.reload()
@@ -861,7 +861,7 @@ def test_cannot_receive_reset_password_email(self):
861861
# check request URL is /forgotpassword
862862
assert res.request.path == self.post_url
863863
# check push notification
864-
assert_in_html('If there is an OSF account', res.text)
864+
# assert_in_html('If there is an OSF account', res.text)
865865
assert_not_in_html('Please wait', res.text)
866866

867867
# check verification_key_v2 is not set
@@ -886,7 +886,7 @@ def test_not_active_user_no_reset_password_email(self):
886886
# check request URL is /forgotpassword
887887
assert res.request.path == self.post_url
888888
# check push notification
889-
assert_in_html('If there is an OSF account', res.text)
889+
# assert_in_html('If there is an OSF account', res.text)
890890
assert_not_in_html('Please wait', res.text)
891891

892892
# check verification_key_v2 is not set
@@ -905,7 +905,7 @@ def test_cannot_reset_password_twice_quickly(self):
905905
# check http 200 response
906906
assert res.status_code == 200
907907
# check push notification
908-
assert_in_html('Please wait', res.text)
908+
# assert_in_html('Please wait', res.text)
909909
assert_not_in_html('If there is an OSF account', res.text)
910910

911911

@@ -955,8 +955,8 @@ def test_can_receive_reset_password_email(self):
955955
# check request URL is /forgotpassword
956956
assert res.request.path == self.post_url
957957
# check push notification
958-
assert_in_html('If there is an OSF account', res.text)
959-
assert_not_in_html('Please wait', res.text)
958+
# assert_in_html('If there is an OSF account', res.text)
959+
# assert_not_in_html('Please wait', res.text)
960960

961961
# check verification_key_v2 is set
962962
self.user.reload()

0 commit comments

Comments
 (0)