Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

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

25.18.2 (2025-12-19)
====================

- Remove users from sitemap
- Fix error when making registrations public
- Fix PR templates

25.18.1 (2025-12-10)
====================

Expand Down
12 changes: 6 additions & 6 deletions PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@

## QA Notes

Please make verification statements inspired by your code and what your code touches.
- Verify
- Verify

What are the areas of risk?

Any concerns/considerations/questions that development raised?
<!-- Please make verification statements inspired by your code and what your code touches.
- Verify
- Verify
What are the areas of risk?
Any concerns/considerations/questions that development raised?
-->

## Documentation

Expand Down
3 changes: 3 additions & 0 deletions osf/models/admin_log_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
PREPRINT_REMOVED = 70
PREPRINT_RESTORED = 71

DOI_CREATION_FAILED = 80
DOI_UPDATE_FAILED = 81

def update_admin_log(user_id, object_id, object_repr, message, action_flag=UNKNOWN):
AdminLogEntry.objects.log_action(
user_id=user_id,
Expand Down
46 changes: 41 additions & 5 deletions osf/models/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,30 @@ def set_privacy(self, permissions, auth=None, log=True, save=True, meeting_creat
# Embargoed registrations can be made public early
self.request_embargo_termination(auth.user)
return False

if not self.get_identifier_value('doi'):
try:
doi = self.request_identifier('doi')['doi']
self.set_identifier_value('doi', doi)
except Exception as e:
from osf.models.admin_log_entry import update_admin_log, DOI_CREATION_FAILED
logger.exception(
f'Failed to create DOI for registration {self._id} during set_privacy. '
f'Registration cannot be made public without a DOI.'
)
if auth and auth.user:
update_admin_log(
user_id=auth.user.id,
object_id=self._id,
object_repr=f'Registration {self.title}',
message=f'DOI creation failed during make public: {str(e)}. DataCite may be unavailable.',
action_flag=DOI_CREATION_FAILED
)
raise NodeStateError(
'Unable to make registration public: DOI creation failed. '
'This may be due to a temporary DataCite service outage. '
'Please try again later or contact support if the issue persists.'
)
self.is_public = True
elif permissions == 'private' and self.is_public:
if self.is_registration and not self.is_pending_embargo and not force:
Expand All @@ -1244,12 +1268,24 @@ def set_privacy(self, permissions, auth=None, log=True, save=True, meeting_creat
if message:
status.push_status_message(message, kind='info', trust=False)

# Update existing identifiers
# Update existing identifiers metadata
if self.get_identifier_value('doi'):
update_doi_metadata_on_change(self._id)
elif self.is_registration:
doi = self.request_identifier('doi')['doi']
self.set_identifier_value('doi', doi)
try:
update_doi_metadata_on_change(self._id)
except Exception as e:
from osf.models.admin_log_entry import update_admin_log, DOI_UPDATE_FAILED
logger.exception(
f'Failed to update DOI metadata for {self._id} during set_privacy. '
)
# Log DOI metadata update failures for tracking
if auth and auth.user and self.is_registration:
update_admin_log(
user_id=auth.user.id,
object_id=self._id,
object_repr=f'Registration {self.title}',
message=f'DOI metadata update failed: {str(e)}. DataCite may be unavailable.',
action_flag=DOI_UPDATE_FAILED
)

if log:
action = NodeLog.MADE_PUBLIC if permissions == 'public' else NodeLog.MADE_PRIVATE
Expand Down
4 changes: 2 additions & 2 deletions osf_tests/test_generate_sitemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ def all_included_links(self, user_admin_project_public, user_admin_project_priva
# Return urls of all fixtures
urls_to_include = [item['loc'] for item in settings.SITEMAP_STATIC_URLS]
urls_to_include.extend([
user_admin_project_public.url,
user_admin_project_private.url,
# user_admin_project_public.url,
# user_admin_project_private.url,
project_registration_public.url + 'overview',
project_preprint_osf.url + 'overview',
project_preprint_other.url + 'overview',
Expand Down
41 changes: 41 additions & 0 deletions osf_tests/test_registrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.utils import timezone
from framework.auth.core import Auth
from framework.exceptions import PermissionsError
from osf.exceptions import NodeStateError
from osf.models import Node, Registration, Sanction, RegistrationSchema, NodeLog, GuidMetadataRecord
from addons.wiki.models import WikiPage
from osf.utils.permissions import ADMIN
Expand Down Expand Up @@ -373,6 +374,46 @@ def test_legacy_private_registrations_can_be_made_public(self, registration, aut
registration.set_privacy(Node.PUBLIC, auth=auth)
assert registration.is_public

def test_registration_cannot_become_public_when_doi_creation_fails(self, registration, auth):
registration.is_public = False
existing_doi = registration.get_identifier('doi')
if existing_doi:
existing_doi.delete()
registration.save()

assert registration.get_identifier_value('doi') is None

with mock.patch.object(registration, 'get_doi_client') as mock_get_client:
mock_client = mock.Mock()
mock_client.create_identifier.side_effect = Exception('DataCite API unavailable')
mock_get_client.return_value = mock_client

with pytest.raises(NodeStateError) as exc_info:
registration.set_privacy(Node.PUBLIC, auth=auth, log=False)

assert 'Unable to make registration public: DOI creation failed' in str(exc_info.value)
assert registration.is_public is False

mock_client.create_identifier.assert_called_once()

@mock.patch('osf.models.node.update_doi_metadata_on_change')
def test_registration_becomes_public_even_when_doi_metadata_update_fails(self, mock_update_doi, registration, auth):

registration.is_public = False
registration.set_identifier_value('doi', '10.1234/test.doi')
registration.save()

assert registration.get_identifier_value('doi') == '10.1234/test.doi'

mock_update_doi.side_effect = Exception('DataCite metadata update failed')

result = registration.set_privacy(Node.PUBLIC, auth=auth, log=False)

assert registration.is_public is True
assert result is True

mock_update_doi.assert_called_once_with(registration._id)


class TestRegisterNodeContributors:

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "OSF",
"version": "25.18.1",
"version": "25.18.2",
"description": "Facilitating Open Science",
"repository": "https://github.com/CenterForOpenScience/osf.io",
"author": "Center for Open Science",
Expand Down
24 changes: 12 additions & 12 deletions scripts/generate_sitemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,18 @@ def generate(self):
progress.increment()
progress.stop()

# User urls
objs = OSFUser.objects.filter(is_active=True).exclude(date_confirmed__isnull=True).values_list('guids___id', flat=True)
progress.start(objs.count(), 'USER: ')
for obj in objs:
try:
config = settings.SITEMAP_USER_CONFIG
config['loc'] = urljoin(settings.DOMAIN, f'/{obj}/')
self.add_url(config)
except Exception as e:
self.log_errors('USER', obj, e)
progress.increment()
progress.stop()
# # User urls
# objs = OSFUser.objects.filter(is_active=True).exclude(date_confirmed__isnull=True).values_list('guids___id', flat=True)
# progress.start(objs.count(), 'USER: ')
# for obj in objs:
# try:
# config = settings.SITEMAP_USER_CONFIG
# config['loc'] = urljoin(settings.DOMAIN, f'/{obj}/')
# self.add_url(config)
# except Exception as e:
# self.log_errors('USER', obj, e)
# progress.increment()
# progress.stop()

# AbstractNode urls (Nodes and Registrations, no Collections)
objs = (AbstractNode.objects
Expand Down
Loading