Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6916953
implement share_reindex for none and false reindexed resources
mkovalua Mar 31, 2026
2e5fda0
added ability to add/remove files of an archived registration under o…
ihorsokhanexoft Apr 1, 2026
4b51e36
remove registration files by their guid, not project files guids
ihorsokhanexoft Apr 2, 2026
c9dc568
Merge pull request #11679 from CenterForOpenScience/develop
adlius Apr 3, 2026
b8409f8
[ENG-10683] Fixed typo in registration schemas permission (#11660)
ihorsokhanexoft Apr 7, 2026
4779c89
[ENG-10615] Have test runner check for uncreated migrations (#11643)
antkryt Apr 7, 2026
2e0046d
[ENG-10575] - Update command, add tests (#11641)
Vlad0n20 Apr 7, 2026
f67d8e6
make confirm ham a celery task (#11666)
antkryt Apr 7, 2026
51fa36f
[ENG-9810] Notification Settings Missing from Provider Settings Tab (…
Ostap-Zherebetskyi Apr 7, 2026
98b15aa
[ENG-10338] add embargo report to admin (#11637)
antkryt Apr 7, 2026
8212ede
[ENG-9907] Analytics are increasing (unique views) when a contributor…
antkryt Apr 7, 2026
8612887
[ENG-10042] Fix/eng 10042 (#11551)
Vlad0n20 Feb 4, 2026
c705963
[ENG-10042] Fix/eng 10042 (#11593)
Vlad0n20 Feb 18, 2026
0805fef
fixed non-consistent COI
ihorsokhanexoft Mar 12, 2026
23f0a83
fixed permission in a template
ihorsokhanexoft Mar 12, 2026
75d571e
resolve CR
mkovalua Apr 8, 2026
7ffcf81
ci: bump github actions versions
afuetterer Apr 9, 2026
910231d
fixed missing validation and added new tests
ihorsokhanexoft Apr 9, 2026
4d396af
Merge pull request #11690 from CenterForOpenScience/develop
adlius Apr 10, 2026
8c5c641
Merge pull request #11689 from afuetterer/ci
adlius Apr 10, 2026
dcb6bd4
Merge pull request #11671 from mkovalua/fix/ENG-10028-pbs-26-6
adlius Apr 13, 2026
a404bcd
Merge pull request #11633 from ihorsokhanexoft/fix/ENG-10328
adlius Apr 13, 2026
1448490
remove rejected preprint version (#11693)
mkovalua Apr 13, 2026
caf7b21
added error handling when file is not attached to a registration
ihorsokhanexoft Apr 14, 2026
60b11cd
simplify if statement
ihorsokhanexoft Apr 14, 2026
a1aa899
Merge pull request #11674 from ihorsokhanexoft/feature/ENG-10283
adlius Apr 15, 2026
f5bd996
fix logging and emails
antkryt Apr 15, 2026
ae9adba
fix tests
antkryt Apr 15, 2026
4dfb03f
Merge pull request #11696 from antkryt/hotfix/ENG-10771
adlius Apr 21, 2026
31b69f9
Bump version no. Add CHANGELOG
adlius Apr 22, 2026
ce5cd38
Merge branch 'hotfix/26.7.1'
adlius Apr 22, 2026
7b9333a
Merge tag '26.7.1' into develop
adlius Apr 22, 2026
6330798
Merge develop into pbs-26-6 (#11704)
brianjgeiger Apr 23, 2026
0d7a1af
Merge pull request #11709 from CenterForOpenScience/feature/pbs-26-6
adlius Apr 23, 2026
a64c9ce
Bump version no. Add CHANGELOG
adlius Apr 23, 2026
3174ec4
Merge branch 'release/26.8.0'
adlius Apr 23, 2026
3f36856
Merge tag '26.8.0' into develop
adlius Apr 23, 2026
a4b06c7
Merge remote-tracking branch 'upstream/develop' into osf4i-in-progres…
Ostap-Zherebetskyi Apr 27, 2026
271e1cb
Add merge migration
Ostap-Zherebetskyi Apr 27, 2026
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
4 changes: 2 additions & 2 deletions .github/actions/gen-report/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ runs:
using: "composite"
steps:
- name: Archive code coverage results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{github.job}} HTML REPORT
path: report.html
- name: Generate Report
uses: dorny/test-reporter@v2
uses: dorny/test-reporter@v3
if: success() || failure() # run this step even if previous step failed
with:
name: ${{github.job}} REPORT # Name of the check run which will be created
Expand Down
4 changes: 2 additions & 2 deletions .github/actions/start-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ runs:
using: "composite"
steps:
- id: cache-objects
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.cache
key: reqs_${{ hashFiles('poetry.lock') }}
Expand All @@ -16,7 +16,7 @@ runs:
with:
ELASTICSEARCH6_ARCHIVE: ${{ env.ELASTICSEARCH6_ARCHIVE }}
- name: Set up Python 3.12
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install lxml
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:
build-cache:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- id: cache-objects
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.cache
key: reqs_${{ hashFiles('poetry.lock') }}
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
# Maps tcp port 5432 on service container to the host
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-addons --junit
Expand All @@ -77,7 +77,7 @@ jobs:
# Maps tcp port 5432 on service container to the host
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-website --junit
Expand All @@ -104,7 +104,7 @@ jobs:
# Maps tcp port 5432 on service container to the host
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: NVM & yarn install
run: poetry run python3 -m invoke assets --dev
Expand Down Expand Up @@ -133,7 +133,7 @@ jobs:
# Maps tcp port 5432 on service container to the host
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-api2 --junit
Expand Down Expand Up @@ -161,7 +161,7 @@ jobs:
# Maps tcp port 5432 on service container to the host
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-api3-and-osf --junit
Expand Down Expand Up @@ -194,7 +194,7 @@ jobs:
- 1025:1025
- 8025:8025
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-mailhog -n 1 --junit
Expand Down Expand Up @@ -222,7 +222,7 @@ jobs:
# Maps tcp port 5432 on service container to the host
- 5432:5432
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-scripts -n 1 --junit
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

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

26.8.0 (2026-04-23)
===================

- Miscellaneous improvements and bug fixes

26.7.1 (2026-04-22)
===================

- Hotfix to improve notifications and logging for failed registrations

26.7.0 (2026-04-08)
===================

Expand Down
1 change: 1 addition & 0 deletions admin/base/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
re_path(r'^cedar_metadata_templates/', include('admin.cedar.urls', namespace='cedar_metadata_templates')),
re_path(r'^draft_registrations/', include('admin.draft_registrations.urls', namespace='draft_registrations')),
re_path(r'^files/', include('admin.files.urls', namespace='files')),
re_path(r'^share_reindex/', include('admin.share_reindex.urls', namespace='share_reindex')),
]),
),
]
Expand Down
4 changes: 4 additions & 0 deletions admin/nodes/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
re_path(r'^flagged_spam$', views.NodeFlaggedSpamList.as_view(), name='flagged-spam'),
re_path(r'^known_spam$', views.NodeKnownSpamList.as_view(), name='known-spam'),
re_path(r'^known_ham$', views.NodeKnownHamList.as_view(), name='known-ham'),
re_path(r'^embargo_report/$', views.EmbargoReportView.as_view(), name='embargo-report'),
re_path(r'^doi_backlog_list/$', views.DoiBacklogListView.as_view(), name='doi-backlog-list'),
re_path(r'^approval_backlog_list/$', views.ApprovalBacklogListView.as_view(), name='approval-backlog-list'),
re_path(r'^confirm_approve_backlog_list/$', views.ConfirmApproveBacklogView.as_view(), name='confirm-approve-backlog-list'),
Expand Down Expand Up @@ -51,4 +52,7 @@
re_path(r'^(?P<guid>[a-z0-9]+)/system_tags/(?P<tag_id>[a-z0-9]+)/remove/$', views.NodeRemoveSystemTag.as_view(), name='remove-system-tag'),
re_path(r'^(?P<guid>[a-z0-9]+)/update_permissions/$', views.NodeUpdatePermissionsView.as_view(), name='update-permissions'),
re_path(r'^(?P<guid>[a-z0-9]+)/remove_file/$', views.NodeRemoveFileView.as_view(), name='remove-file'),
re_path(r'^(?P<guid>[a-z0-9]+)/add_osfstorage_file/$', views.NodeAddOsfStorageFileView.as_view(), name='add-osfstorage-file'),
re_path(r'^(?P<guid>[a-z0-9]+)/remove_osfstorage_file/$', views.NodeRemoveOsfStorageFileView.as_view(), name='remove-osfstorage-file'),

]
119 changes: 119 additions & 0 deletions admin/nodes/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
View,
FormView,
ListView,
TemplateView
)
from django.core.paginator import Paginator, InvalidPage

from admin.base.forms import GuidForm
from admin.base.utils import change_embargo_date
from admin.base.views import GuidView
from admin.nodes.forms import AddSystemTagForm, RegistrationDateForm
from admin.notifications.views import delete_selected_notifications
from addons.osfstorage.models import OsfStorageFolder
from api.caching.tasks import update_storage_usage_cache
from api.share.utils import update_share
from framework import status
Expand All @@ -39,6 +42,7 @@
SpamStatus,
TrashedFile
)
from osf.models.sanctions import Embargo
from osf.models.admin_log_entry import (
update_admin_log,
NODE_REMOVED,
Expand All @@ -50,6 +54,7 @@
REINDEX_SHARE,
REINDEX_ELASTIC,
)
from osf.models.files import File
from osf.utils.permissions import ADMIN, API_CONTRIBUTOR_PERMISSIONS
from scripts.approve_registrations import approve_past_pendings
from website import settings, search
Expand Down Expand Up @@ -474,6 +479,54 @@ def get_context_data(self, **kwargs):
}


class EmbargoReportView(PermissionRequiredMixin, TemplateView):
"""Report view for inspecting current and overdue embargoed registrations.

Shows:
- pending embargoes that should have been activated
- active embargoes that are past their end date
- upcoming active embargoes
"""
template_name = 'nodes/embargo_report.html'
permission_required = 'osf.view_registration'
raise_exception = True

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
pending_embargoes = Embargo.objects.pending_embargoes().select_related('initiated_by')
active_embargoes = Embargo.objects.active_embargoes().select_related('initiated_by')

pending_overdue_embargoes = [
embargo for embargo in pending_embargoes
if embargo.should_be_embargoed
]

overdue_embargoes = [
embargo for embargo in active_embargoes
if embargo.should_be_completed
]

upcoming_queryset = active_embargoes.filter(
end_date__gte=timezone.now(),
).order_by('end_date')

page_number = self.request.GET.get('page') or 1
paginator = Paginator(upcoming_queryset, 10)
try:
upcoming_page = paginator.page(page_number)
except InvalidPage:
upcoming_page = paginator.page(1)

context.update({
'now': timezone.now(),
'pending_overdue_embargoes': pending_overdue_embargoes,
'overdue_embargoes': overdue_embargoes,
'upcoming_embargoes': upcoming_page.object_list,
'upcoming_page': upcoming_page,
})
return context


class ConfirmApproveBacklogView(RegistrationListView):
template_name = 'nodes/registration_approval_list.html'
permission_required = 'osf.view_registrationapproval'
Expand Down Expand Up @@ -846,6 +899,72 @@ def _remove_file_from_schema_response_blocks(registration, removed_file_id):
return redirect(self.get_success_url())


class NodeAddOsfStorageFileView(NodeMixin, View):
""" Allows an authorized user to add a file to osfstorage of an archived node.
"""
permission_required = 'osf.change_node'

def post(self, request, *args, **kwargs):
registration = self.get_object()
guid_id = request.POST.get('file-guid', '').strip()
guid = Guid.load(guid_id)
if not guid:
messages.error(request, 'No file found with the provided guid.')
return redirect(self.get_success_url())

file = guid.referent
if not isinstance(file, File):
messages.error(request, 'The guid provided does not correspond to a file.')
return redirect(self.get_success_url())

parent_node = registration.registered_from
if not parent_node:
messages.error(request, 'The registration does not have the parent node.')
return redirect(self.get_success_url())

if not parent_node.files.filter(id=file.id).exists():
messages.error(request, 'The file with the provided guid is not part of the parent node.')
return redirect(self.get_success_url())

osfstorage = registration.get_addon('osfstorage')
# copy file to Archive of OSF Storage folder
archive_folder = OsfStorageFolder.objects.filter(
parent=osfstorage.get_root(),
name=osfstorage.archive_folder_name
).first()
file.copy_under(archive_folder)
messages.success(request, 'The file was successfully added.')
return redirect(self.get_success_url())


class NodeRemoveOsfStorageFileView(NodeMixin, View):
""" Allows an authorized user to remove a file from osfstorage of an archived node.
"""
permission_required = 'osf.change_node'

def post(self, request, *args, **kwargs):
registration = self.get_object()
guid_id = request.POST.get('file-guid', '').strip()
guid = Guid.load(guid_id)
if not guid:
messages.error(request, 'No file found with the provided guid.')
return redirect(self.get_success_url())

file = guid.referent
if not isinstance(file, File):
messages.error(request, 'The guid provided does not correspond to a file.')
return redirect(self.get_success_url())

registration_file = registration.files.filter(id=file.id)
if not registration_file.exists():
messages.error(request, 'The file with the provided guid is not part of the registration.')
return redirect(self.get_success_url())

registration_file.delete()
messages.success(request, 'The file was successfully removed.')
return redirect(self.get_success_url())


class RemoveStuckRegistrationsView(NodeMixin, View):
""" Allows an authorized user to remove a registrations if it's stuck in the archiving process.
"""
Expand Down
1 change: 1 addition & 0 deletions admin/preprints/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
re_path(r'^(?P<guid>\w+)/remove_user/(?P<user_id>[a-z0-9]+)/$', views.PreprintRemoveContributorView.as_view(), name='remove-user'),
re_path(r'^(?P<guid>\w+)/make_private/$', views.PreprintMakePrivate.as_view(), name='make-private'),
re_path(r'^(?P<guid>\w+)/fix_editing/$', views.PreprintFixEditing.as_view(), name='fix-editing'),
re_path(r'^(?P<guid>\w+)/fix_coi/$', views.PreprintFixCOI.as_view(), name='fix-coi'),
re_path(r'^(?P<guid>\w+)/make_public/$', views.PreprintMakePublic.as_view(), name='make-public'),
re_path(r'^(?P<guid>\w+)/remove/$', views.PreprintDeleteView.as_view(), name='remove'),
re_path(r'^(?P<guid>\w+)/restore/$', views.PreprintDeleteView.as_view(), name='restore'),
Expand Down
16 changes: 16 additions & 0 deletions admin/preprints/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,22 @@ def post(self, request, *args, **kwargs):
return redirect(self.get_success_url())


class PreprintFixCOI(PreprintMixin, View):
""" Allows an authorized user to manually fix conflict of interest field.
"""
permission_required = 'osf.change_preprint'

def post(self, request, *args, **kwargs):
preprint = self.get_object()
if preprint.conflict_of_interest_statement and not preprint.has_coi:
preprint.update_has_coi(auth=request, has_coi=True, ignore_permission=True)
messages.success(request, 'The COI was successfully fixed.')
else:
messages.error(request, 'The COI is either already fixed or the preprint does not have conflict of interest set.')

return redirect(self.get_success_url())


class PreprintMakePublic(PreprintMixin, View):
""" Allows an authorized user to manually make a private preprint public.
"""
Expand Down
4 changes: 2 additions & 2 deletions admin/registration_schemas/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class RegistrationSchemaDetailView(FormView, PermissionRequiredMixin, TemplateVi
Allows authorized users to view and edit some attributes of a Registration Schema.
"""
template_name = 'registration_schemas/registration_schema.html'
permission_required = 'osf.view_registration_schema'
permission_required = 'osf.view_registrationschema'
raise_exception = True
form_class = RegistrationSchemaEditForm

Expand Down Expand Up @@ -143,7 +143,7 @@ class RegistrationSchemaListView(PermissionRequiredMixin, ListView):
Allows authorized users to view all Registration Schema.
"""
template_name = 'registration_schemas/registration_schema_list.html'
permission_required = 'osf.view_registration_schema'
permission_required = 'osf.view_registrationschema'
raise_exception = True

def get_queryset(self):
Expand Down
Empty file added admin/share_reindex/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions admin/share_reindex/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.urls import re_path
from . import views

app_name = 'admin'

urlpatterns = [
re_path(r'^$', views.FailedShareIndexedGuidList.as_view(), name='list'),
re_path(r'^(?P<resource_type>[^/]+)/$', views.FailedShareIndexedGuidReindex.as_view(), name='reindex-share-resource'),
]
Loading