Skip to content

Commit df76b18

Browse files
committed
Merge branch 'release/25.14.0'
2 parents 14529a2 + 1b3ed1b commit df76b18

File tree

78 files changed

+1767
-482
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1767
-482
lines changed

addons/base/views.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,13 +1006,17 @@ def persistent_file_download(auth, **kwargs):
10061006
file = BaseFileNode.active.filter(_id=id_or_guid).first()
10071007
if not file:
10081008
guid = Guid.load(id_or_guid)
1009-
if guid:
1010-
file = guid.referent
1011-
else:
1009+
if not guid:
10121010
raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={
10131011
'message_short': 'File Not Found',
10141012
'message_long': 'The requested file could not be found.'
10151013
})
1014+
1015+
file = guid.referent
1016+
if type(file) is Preprint:
1017+
referent, _ = Guid.load_referent(id_or_guid)
1018+
file = referent.primary_file
1019+
10161020
if not file.is_file:
10171021
raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={
10181022
'message_long': 'Downloading folders is not permitted.'

admin/institutions/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
re_path(r'^import/$', views.ImportInstitution.as_view(), name='import'),
1010
re_path(r'^(?P<institution_id>[0-9]+)/$', views.InstitutionDetail.as_view(), name='detail'),
1111
re_path(r'^(?P<institution_id>[0-9]+)/export/$', views.InstitutionExport.as_view(), name='export'),
12+
re_path(r'^(?P<institution_id>[0-9]+)/monthly_report/$', views.InstitutionMonthlyReporterDo.as_view(), name='monthly_report'),
1213
re_path(r'^(?P<institution_id>[0-9]+)/delete/$', views.DeleteInstitution.as_view(), name='delete'),
1314
re_path(r'^(?P<institution_id>[0-9]+)/deactivate/$', views.DeactivateInstitution.as_view(), name='deactivate'),
1415
re_path(r'^(?P<institution_id>[0-9]+)/reactivate/$', views.ReactivateInstitution.as_view(), name='reactivate'),

admin/institutions/views.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
from dateutil.parser import isoparse
23

34
from django.contrib import messages
45
from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -15,6 +16,9 @@
1516
from admin.base.forms import ImportFileForm
1617
from admin.institutions.forms import InstitutionForm, InstitutionalMetricsAdminRegisterForm
1718
from osf.models import Institution, Node, OSFUser
19+
from osf.metrics.utils import YearMonth
20+
from osf.metrics.reporters import AllMonthlyReporters
21+
from osf.management.commands.monthly_reporters_go import monthly_reporter_do
1822

1923

2024
class InstitutionList(PermissionRequiredMixin, ListView):
@@ -129,6 +133,38 @@ def get(self, request, *args, **kwargs):
129133
return response
130134

131135

136+
class InstitutionMonthlyReporterDo(PermissionRequiredMixin, View):
137+
permission_required = 'osf.view_institution'
138+
raise_exception = True
139+
140+
def post(self, request, *args, **kwargs):
141+
institution_id = self.kwargs.get('institution_id')
142+
try:
143+
institution = Institution.objects.get_all_institutions().get(id=institution_id)
144+
except Institution.DoesNotExist:
145+
raise Http404(f"Institution with id {institution_id} is not found or deactivated.")
146+
147+
monthly_report_date = request.POST.get('monthly_report_date', None)
148+
if monthly_report_date:
149+
try:
150+
monthly_report_date = isoparse(monthly_report_date).date()
151+
except ValueError as exc:
152+
messages.error(request, str(exc))
153+
return redirect('institutions:detail', institution_id=institution.id)
154+
else:
155+
messages.error(request, 'Report date cannot be none.')
156+
return redirect('institutions:detail', institution_id=institution.id)
157+
158+
monthly_reporter_do.apply_async(kwargs={
159+
'yearmonth': str(YearMonth.from_date(monthly_report_date)),
160+
'reporter_key': request.POST.get('monthly_reporter', None),
161+
'report_kwargs': {'institution_pk': institution.id},
162+
})
163+
164+
messages.success(request, 'Monthly reporter successfully went.')
165+
return redirect('institutions:detail', institution_id=institution.id)
166+
167+
132168
class CreateInstitution(PermissionRequiredMixin, CreateView):
133169
permission_required = 'osf.change_institution'
134170
raise_exception = True
@@ -141,6 +177,18 @@ def get_context_data(self, *args, **kwargs):
141177
kwargs['import_form'] = ImportFileForm()
142178
return super().get_context_data(*args, **kwargs)
143179

180+
def form_valid(self, form):
181+
response = super().form_valid(form)
182+
183+
# Make a report after Institution is created
184+
monthly_reporter_do.apply_async(kwargs={
185+
'yearmonth': str(YearMonth.from_date(self.object.created)),
186+
'reporter_key': AllMonthlyReporters.INSTITUTIONAL_SUMMARY.name,
187+
'report_kwargs': {'institution_pk': self.object.id},
188+
})
189+
190+
return response
191+
144192

145193
class InstitutionNodeList(PermissionRequiredMixin, ListView):
146194
template_name = 'institutions/node_list.html'

admin/maintenance/views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from admin.maintenance.forms import MaintenanceForm
77

88
from django.shortcuts import redirect
9+
from django.urls import reverse_lazy
910
from django.forms.models import model_to_dict
1011
from django.views.generic import DeleteView, TemplateView
1112
from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -15,11 +16,13 @@ class DeleteMaintenance(PermissionRequiredMixin, DeleteView):
1516
permission_required = 'osf.delete_maintenancestate'
1617
raise_exception = True
1718
template_name = 'maintenance/delete_maintenance.html'
19+
success_url = reverse_lazy('maintenance:display')
1820

1921
def get_object(self, queryset=None):
2022
return MaintenanceState.objects.first()
2123

22-
def delete(self, request, *args, **kwargs):
24+
def post(self, request, *args, **kwargs):
25+
super().post(request, *args, **kwargs)
2326
maintenance.unset_maintenance()
2427
return redirect('maintenance:display')
2528

admin/management/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def post(self, request, *args, **kwargs):
130130
if report_date is not None
131131
else ''
132132
),
133+
reporter_key=request.POST.get('monthly_reporter', '')
133134
)
134135

135136
if errors:

admin/nodes/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@
4545
re_path(r'^(?P<guid>[a-z0-9]+)/remove_notifications/$', views.NodeRemoveNotificationView.as_view(), name='node-remove-notifications'),
4646
re_path(r'^(?P<guid>[a-z0-9]+)/update_moderation_state/$', views.NodeUpdateModerationStateView.as_view(), name='node-update-mod-state'),
4747
re_path(r'^(?P<guid>[a-z0-9]+)/resync_datacite/$', views.NodeResyncDataCiteView.as_view(), name='resync-datacite'),
48+
re_path(r'^(?P<guid>[a-z0-9]+)/revert/$', views.NodeRevertToDraft.as_view(), name='revert-to-draft'),
4849
]

admin/nodes/views.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
View,
1515
FormView,
1616
ListView,
17-
TemplateView,
1817
)
1918
from django.shortcuts import redirect, reverse, get_object_or_404
2019
from django.urls import reverse_lazy
@@ -102,11 +101,16 @@ def get_context_data(self, **kwargs):
102101
node = self.get_object()
103102

104103
detailed_duplicates = detect_duplicate_notifications(node_id=node.id)
105-
104+
children = node.get_nodes(is_node_link=False)
105+
# Annotate guid because django templates prohibit accessing attributes that start with underscores
106+
children = AbstractNode.objects.filter(
107+
id__in=[child.id for child in children]
108+
).prefetch_related('guids').annotate(guid=F('guids___id'))
106109
context.update({
107110
'SPAM_STATUS': SpamStatus,
108111
'STORAGE_LIMITS': settings.StorageLimits,
109112
'node': node,
113+
'children': children,
110114
'duplicates': detailed_duplicates
111115
})
112116

@@ -193,10 +197,9 @@ def add_contributor_removed_log(self, node, user):
193197
).save()
194198

195199

196-
class NodeDeleteView(NodeMixin, TemplateView):
200+
class NodeDeleteView(NodeMixin, View):
197201
""" Allows authorized users to mark nodes as deleted.
198202
"""
199-
template_name = 'nodes/remove_node.html'
200203
permission_required = ('osf.view_node', 'osf.delete_node')
201204
raise_exception = True
202205

@@ -761,7 +764,7 @@ def post(self, request, *args, **kwargs):
761764

762765
allow_unconfigured = force_archive_params.get('allow_unconfigured', False)
763766

764-
addons = set(force_archive_params.getlist('addons', []))
767+
addons = set(registration.registered_from.get_addon_names())
765768
addons.update(DEFAULT_PERMISSIBLE_ADDONS)
766769

767770
try:
@@ -780,8 +783,8 @@ def post(self, request, *args, **kwargs):
780783
registration,
781784
permissible_addons=addons,
782785
allow_unconfigured=allow_unconfigured,
783-
skip_collision=skip_collision,
784-
delete_collision=delete_collision,
786+
skip_collisions=skip_collision,
787+
delete_collisions=delete_collision,
785788
)
786789
messages.success(request, 'Registration archive process has finished.')
787790
except Exception as exc:
@@ -800,3 +803,12 @@ def post(self, request, *args, **kwargs):
800803
registration = self.get_object()
801804
registration.request_identifier_update('doi', create=True)
802805
return redirect(self.get_success_url())
806+
807+
808+
class NodeRevertToDraft(NodeMixin, View):
809+
permission_required = 'osf.change_node'
810+
811+
def post(self, request, *args, **kwargs):
812+
registration = self.get_object()
813+
registration.to_draft()
814+
return redirect(self.get_success_url())

admin/preprint_providers/forms.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ def __init__(self, *args, provider_groups=None, **kwargs):
116116
super().__init__(*args, **kwargs)
117117

118118
provider_groups = provider_groups or Group.objects.none()
119-
self.fields['group_perms'] = forms.ModelMultipleChoiceField(
119+
self.fields['group_perms'] = forms.ModelChoiceField(
120120
queryset=provider_groups,
121121
required=False,
122-
widget=forms.CheckboxSelectMultiple
122+
widget=forms.RadioSelect
123123
)
124124

125125
user_id = forms.CharField(required=True, max_length=5, min_length=5)

admin/preprint_providers/views.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,10 +481,14 @@ def form_valid(self, form):
481481
if not osf_user:
482482
raise Http404(f'OSF user with id "{user_id}" not found. Please double check.')
483483

484-
for group in form.cleaned_data.get('group_perms'):
485-
self.target_provider.add_to_group(osf_user, group)
484+
if osf_user.has_groups(self.target_provider.group_names):
485+
messages.error(self.request, f'User with guid: {user_id} is already a moderator or admin')
486+
return super().form_invalid(form)
486487

488+
group = form.cleaned_data.get('group_perms')
489+
self.target_provider.add_to_group(osf_user, group)
487490
osf_user.save()
491+
488492
messages.success(self.request, f'Permissions update successful for OSF User {osf_user.username}!')
489493
return super().form_valid(form)
490494

admin/providers/views.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ def post(self, request, *args, **kwargs):
2929
messages.error(request, f'User for guid: {data["add-moderators-form"][0]} could not be found')
3030
return redirect(f'{self.url_namespace}:add_admin_or_moderator', provider_id=provider.id)
3131

32-
groups = [provider.format_group(name) for name in provider.groups.keys()]
33-
if target_user.has_groups(groups):
32+
if target_user.has_groups(provider.group_names):
3433
messages.error(request, f'User with guid: {data["add-moderators-form"][0]} is already a moderator or admin')
3534
return redirect(f'{self.url_namespace}:add_admin_or_moderator', provider_id=provider.id)
3635

0 commit comments

Comments
 (0)