Skip to content

Commit 21a161e

Browse files
authored
Merge pull request #11751 from CenterForOpenScience/feature/pbs-26-9
[ENG-11560] PBS-26-9 Feature Release PR
2 parents 74a7ad3 + 8c28cc6 commit 21a161e

46 files changed

Lines changed: 1520 additions & 144 deletions

Some content is hidden

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

admin/base/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
re_path(r'^draft_registrations/', include('admin.draft_registrations.urls', namespace='draft_registrations')),
3939
re_path(r'^files/', include('admin.files.urls', namespace='files')),
4040
re_path(r'^share_reindex/', include('admin.share_reindex.urls', namespace='share_reindex')),
41+
re_path(r'^notifications/', include('admin.notifications.urls', namespace='notifications')),
4142
]),
4243
),
4344
]

admin/maintenance/views.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytz
22
import datetime
33

4-
from osf.models import MaintenanceState
4+
from osf.models import MaintenanceState, MaintenanceMode
55
import website.maintenance as maintenance
66
from admin.maintenance.forms import MaintenanceForm
77

@@ -36,15 +36,17 @@ def get_context_data(self, **kwargs):
3636
maintenance = MaintenanceState.objects.first()
3737
kwargs['form'] = MaintenanceForm()
3838
kwargs['current_alert'] = model_to_dict(maintenance) if maintenance else None
39+
kwargs['maintenance_mode'] = MaintenanceMode.is_under_maintenance()
3940
return super().get_context_data(**kwargs)
4041

4142
def post(self, request, *args, **kwargs):
4243
data = request.POST
43-
44-
start = convert_eastern_to_utc(data['start']).isoformat() if data.get('start') else None
45-
end = convert_eastern_to_utc(data['end']).isoformat() if data.get('end') else None
46-
47-
maintenance.set_maintenance(data.get('message', ''), data['level'], start, end)
44+
if maintenance_mode := data.get('maintenance_mode'):
45+
MaintenanceMode(maintenance_mode=False if maintenance_mode == 'True' else True).save()
46+
else:
47+
start = convert_eastern_to_utc(data['start']).isoformat() if data.get('start') else None
48+
end = convert_eastern_to_utc(data['end']).isoformat() if data.get('end') else None
49+
maintenance.set_maintenance(data.get('message', ''), data['level'], start, end)
4850
return redirect('maintenance:display')
4951

5052

admin/management/views.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,17 @@ def post(self, request):
194194
class SyncNotificationTemplates(ManagementCommandPermissionView):
195195

196196
def post(self, request):
197-
populate_notification_types()
197+
run_type = request.POST.get('run_type')
198+
if run_type == 'restore_one':
199+
template_name = request.POST.get('template_name')
200+
if not template_name:
201+
messages.error(request, 'A template name must be specified when restoring one template. Check your inputs and try again')
202+
return redirect(reverse('management:commands'))
203+
populate_notification_types(restore_one=template_name)
204+
elif run_type == 'restore_all':
205+
populate_notification_types(restore_all=True)
206+
else:
207+
populate_notification_types()
198208
messages.success(request, 'Notification templates have been successfully synced.')
199209
return redirect(reverse('management:commands'))
200210

admin/nodes/views.py

Lines changed: 128 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,16 @@ def post(self, request, *args, **kwargs):
110110
node.created = new_date
111111
node.save()
112112

113+
params = dict(node.log_params)
114+
params.update({
115+
'last_date': str(last_date),
116+
'new_date': str(new_date),
117+
})
113118
node.add_log(
114119
action=NodeLog.REGISTRATION_DATE_UPDATED,
115-
auth=request,
116-
params={
117-
'last_date': str(last_date),
118-
'new_date': str(new_date)
119-
},
120+
auth=None,
121+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
122+
params=params,
120123
log_date=timezone.now(),
121124
should_hide=False,
122125
)
@@ -225,22 +228,18 @@ def post(self, request, *args, **kwargs):
225228
message=f'User {user.pk} removed from {node.__class__.__name__.lower()} {node.pk}.',
226229
action_flag=CONTRIBUTOR_REMOVED
227230
)
228-
# Log invisibly on the OSF.
229-
self.add_contributor_removed_log(node, user)
230-
return redirect(self.get_success_url())
231+
params = dict(node.log_params)
232+
params['contributors'] = [user._id]
233+
node.add_log(
234+
action=NodeLog.CONTRIB_REMOVED,
235+
auth=None,
236+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
237+
params=params,
238+
log_date=timezone.now(),
239+
should_hide=False,
240+
)
231241

232-
def add_contributor_removed_log(self, node, user):
233-
NodeLog(
234-
action=NodeLog.CONTRIB_REMOVED,
235-
user=None,
236-
params={
237-
'project': node.parent_id,
238-
'node': node.pk,
239-
'contributors': user.pk
240-
},
241-
date=timezone.now(),
242-
should_hide=True,
243-
).save()
242+
return redirect(self.get_success_url())
244243

245244

246245
class NodeUpdatePermissionsView(NodeMixin, View):
@@ -266,6 +265,7 @@ def post(self, request, *args, **kwargs):
266265
new_permissions_to_add = data.get('new-permissions', [])
267266

268267
new_permission_indexes_to_remove = []
268+
added_contributor_ids = []
269269
for email, permission in zip(new_emails_to_add, new_permissions_to_add):
270270
contributor_user = OSFUser.objects.filter(emails__address=email.lower()).first()
271271
if not contributor_user:
@@ -281,8 +281,10 @@ def post(self, request, *args, **kwargs):
281281
auth=request,
282282
user_id=contributor_user._id,
283283
permissions=permission,
284-
notification_type=None
284+
notification_type=None,
285+
log=False,
285286
)
287+
added_contributor_ids.append(contributor_user._id)
286288
messages.success(self.request, f'User with email {email} was successfully added.')
287289

288290
# should remove permissions of invalid emails because
@@ -291,13 +293,27 @@ def post(self, request, *args, **kwargs):
291293
for permission_index in new_permission_indexes_to_remove:
292294
new_permissions_to_add.pop(permission_index)
293295

296+
# Log support-added contributors, if any
297+
if added_contributor_ids:
298+
params = resource.log_params
299+
params['contributors'] = added_contributor_ids
300+
resource.add_log(
301+
action=NodeLog.CONTRIB_ADDED,
302+
auth=None,
303+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
304+
params=params,
305+
log_date=timezone.now(),
306+
should_hide=False,
307+
)
308+
294309
updated_permissions = data.get('updated-permissions', [])
295310
all_permissions = updated_permissions + new_permissions_to_add
296311
has_admin = list(filter(lambda permission: ADMIN in permission, all_permissions))
297312
if not has_admin:
298313
messages.error(self.request, 'Must be at least one admin on this node.')
299314
return redirect(self.get_success_url())
300315

316+
permissions_changed = {}
301317
for contributor_permission in updated_permissions:
302318
guid, permission = contributor_permission.split('-')
303319
user = OSFUser.load(guid)
@@ -307,7 +323,21 @@ def post(self, request, *args, **kwargs):
307323
resource.get_visible(user),
308324
request,
309325
save=True,
310-
skip_permission=True
326+
skip_permission=True,
327+
log=False,
328+
)
329+
permissions_changed[user._id] = permission
330+
331+
if permissions_changed:
332+
params = resource.log_params
333+
params['contributors'] = permissions_changed
334+
resource.add_log(
335+
action=NodeLog.PERMISSIONS_UPDATED,
336+
auth=None,
337+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
338+
params=params,
339+
log_date=timezone.now(),
340+
should_hide=False,
311341
)
312342

313343
return redirect(self.get_success_url())
@@ -332,15 +362,14 @@ def post(self, request, *args, **kwargs):
332362
message=f'Node {node.pk} restored.',
333363
action_flag=NODE_RESTORED
334364
)
335-
NodeLog(
365+
node.add_log(
336366
action=NodeLog.NODE_CREATED,
337-
user=None,
338-
params={
339-
'project': node.parent_id,
340-
},
341-
date=timezone.now(),
342-
should_hide=True,
343-
).save()
367+
auth=None,
368+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
369+
params=dict(node.log_params),
370+
log_date=timezone.now(),
371+
should_hide=False,
372+
)
344373
else:
345374
node.is_deleted = True
346375
node.deleted = timezone.now()
@@ -352,15 +381,14 @@ def post(self, request, *args, **kwargs):
352381
message=f'Node {node.pk} removed.',
353382
action_flag=NODE_REMOVED
354383
)
355-
NodeLog(
384+
node.add_log(
356385
action=NodeLog.NODE_REMOVED,
357-
user=None,
358-
params={
359-
'project': node.parent_id,
360-
},
361-
date=timezone.now(),
362-
should_hide=True,
363-
).save()
386+
auth=None,
387+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
388+
params=dict(node.log_params),
389+
log_date=timezone.now(),
390+
should_hide=False,
391+
)
364392
node.save()
365393

366394
return redirect(self.get_success_url())
@@ -860,6 +888,15 @@ def post(self, request, *args, **kwargs):
860888

861889
node.save()
862890

891+
node.add_log(
892+
action=NodeLog.MADE_PRIVATE,
893+
auth=None,
894+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
895+
params=dict(node.log_params),
896+
log_date=timezone.now(),
897+
should_hide=False,
898+
)
899+
863900
return redirect(self.get_success_url())
864901

865902

@@ -871,9 +908,18 @@ class NodeMakePublic(NodeMixin, View):
871908
def post(self, request, *args, **kwargs):
872909
node = self.get_object()
873910
try:
874-
node.set_privacy('public')
911+
node.set_privacy('public', auth=None, log=False)
875912
except NodeStateError as e:
876913
messages.error(request, str(e))
914+
else:
915+
node.add_log(
916+
action=NodeLog.MADE_PUBLIC,
917+
auth=None,
918+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
919+
params=dict(node.log_params),
920+
log_date=timezone.now(),
921+
should_hide=False,
922+
)
877923
return redirect(self.get_success_url())
878924

879925

@@ -900,10 +946,26 @@ def _remove_file_from_schema_response_blocks(registration, removed_file_id):
900946

901947
# delete file from registration and metadata and keep it for original project
902948
if guid and (file := guid.referent) and (file.target == node) and not isinstance(file, TrashedFile):
949+
file_id = file._id
950+
file_path = getattr(file, 'materialized_path', None) or getattr(file, 'path', None) or ''
951+
copied_from_id = getattr(file, 'copied_from_id', None) or getattr(getattr(file, 'copied_from', None), '_id', None)
903952
with transaction.atomic():
904953
file.delete()
905954
_update_schema_meta(file.target)
906-
_remove_file_from_schema_response_blocks(node, [file._id, file.copied_from._id])
955+
_remove_file_from_schema_response_blocks(node, [file_id, copied_from_id])
956+
params = dict(node.log_params)
957+
params.update({
958+
'pathType': 'file',
959+
'path': file_path,
960+
})
961+
node.add_log(
962+
action=NodeLog.FILE_REMOVED,
963+
auth=None,
964+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
965+
params=params,
966+
log_date=timezone.now(),
967+
should_hide=False,
968+
)
907969
return redirect(self.get_success_url())
908970

909971

@@ -940,7 +1002,18 @@ def post(self, request, *args, **kwargs):
9401002
parent=osfstorage.get_root(),
9411003
name=osfstorage.archive_folder_name
9421004
).first()
943-
file.copy_under(archive_folder)
1005+
copied = file.copy_under(archive_folder)
1006+
copied_path = getattr(copied, 'materialized_path', None) or getattr(copied, 'path', None) or ''
1007+
params = dict(registration.log_params)
1008+
params['path'] = copied_path
1009+
registration.add_log(
1010+
action=NodeLog.FILE_ADDED,
1011+
auth=None,
1012+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
1013+
params=params,
1014+
log_date=timezone.now(),
1015+
should_hide=False,
1016+
)
9441017
messages.success(request, 'The file was successfully added.')
9451018
return redirect(self.get_success_url())
9461019

@@ -967,8 +1040,21 @@ def post(self, request, *args, **kwargs):
9671040
if not registration_file.exists():
9681041
messages.error(request, 'The file with the provided guid is not part of the registration.')
9691042
return redirect(self.get_success_url())
970-
1043+
file_path = getattr(file, 'materialized_path', None) or getattr(file, 'path', None) or ''
9711044
registration_file.delete()
1045+
params = dict(registration.log_params)
1046+
params.update({
1047+
'pathType': 'file',
1048+
'path': file_path,
1049+
})
1050+
registration.add_log(
1051+
action=NodeLog.FILE_REMOVED,
1052+
auth=None,
1053+
foreign_user=NodeLog.SUPPORT_USER_LABEL,
1054+
params=params,
1055+
log_date=timezone.now(),
1056+
should_hide=False,
1057+
)
9721058
messages.success(request, 'The file was successfully removed.')
9731059
return redirect(self.get_success_url())
9741060

admin/notifications/forms.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from django import forms
2+
from osf.models import NotificationType
3+
4+
5+
class NotificationTypeForm(forms.ModelForm):
6+
class Meta:
7+
model = NotificationType
8+
fields = '__all__'

admin/notifications/urls.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from django.urls import re_path
2+
from . import views
3+
4+
app_name = 'admin'
5+
6+
urlpatterns = [
7+
re_path(r'$', views.NotificationsList.as_view(), name='list'),
8+
re_path(r'types/$', views.NotificationTypeList.as_view(), name='types_list'),
9+
re_path(r'type_display/(?P<pk>\d+)/$', views.NotificationTypeDisplay.as_view(), name='type_display'),
10+
re_path(r'type_detail/(?P<pk>\d+)/$', views.NotificationTypeDetail.as_view(), name='type_detail'),
11+
re_path(r'types_preview/(?P<pk>\d+)/$', views.NotificationTypePreview.as_view(), name='types_preview'),
12+
re_path(r'subscriptions/$', views.NotificationSubscriptionsList.as_view(), name='subscriptions_list'),
13+
re_path(r'email_tasks/$', views.EmailTasksList.as_view(), name='email_tasks_list'),
14+
]

0 commit comments

Comments
 (0)