|
16 | 16 | View, |
17 | 17 | FormView, |
18 | 18 | ListView, |
| 19 | + TemplateView |
19 | 20 | ) |
| 21 | +from django.core.paginator import Paginator, InvalidPage |
20 | 22 |
|
21 | 23 | from admin.base.forms import GuidForm |
22 | 24 | from admin.base.utils import change_embargo_date |
23 | 25 | from admin.base.views import GuidView |
24 | 26 | from admin.nodes.forms import AddSystemTagForm, RegistrationDateForm |
25 | 27 | from admin.notifications.views import delete_selected_notifications |
| 28 | +from addons.osfstorage.models import OsfStorageFolder |
26 | 29 | from api.caching.tasks import update_storage_usage_cache |
27 | 30 | from api.share.utils import update_share |
28 | 31 | from framework import status |
|
39 | 42 | SpamStatus, |
40 | 43 | TrashedFile |
41 | 44 | ) |
| 45 | +from osf.models.sanctions import Embargo |
42 | 46 | from osf.models.admin_log_entry import ( |
43 | 47 | update_admin_log, |
44 | 48 | NODE_REMOVED, |
|
50 | 54 | REINDEX_SHARE, |
51 | 55 | REINDEX_ELASTIC, |
52 | 56 | ) |
| 57 | +from osf.models.files import File |
53 | 58 | from osf.utils.permissions import ADMIN, API_CONTRIBUTOR_PERMISSIONS |
54 | 59 | from scripts.approve_registrations import approve_past_pendings |
55 | 60 | from website import settings, search |
@@ -474,6 +479,54 @@ def get_context_data(self, **kwargs): |
474 | 479 | } |
475 | 480 |
|
476 | 481 |
|
| 482 | +class EmbargoReportView(PermissionRequiredMixin, TemplateView): |
| 483 | + """Report view for inspecting current and overdue embargoed registrations. |
| 484 | +
|
| 485 | + Shows: |
| 486 | + - pending embargoes that should have been activated |
| 487 | + - active embargoes that are past their end date |
| 488 | + - upcoming active embargoes |
| 489 | + """ |
| 490 | + template_name = 'nodes/embargo_report.html' |
| 491 | + permission_required = 'osf.view_registration' |
| 492 | + raise_exception = True |
| 493 | + |
| 494 | + def get_context_data(self, **kwargs): |
| 495 | + context = super().get_context_data(**kwargs) |
| 496 | + pending_embargoes = Embargo.objects.pending_embargoes().select_related('initiated_by') |
| 497 | + active_embargoes = Embargo.objects.active_embargoes().select_related('initiated_by') |
| 498 | + |
| 499 | + pending_overdue_embargoes = [ |
| 500 | + embargo for embargo in pending_embargoes |
| 501 | + if embargo.should_be_embargoed |
| 502 | + ] |
| 503 | + |
| 504 | + overdue_embargoes = [ |
| 505 | + embargo for embargo in active_embargoes |
| 506 | + if embargo.should_be_completed |
| 507 | + ] |
| 508 | + |
| 509 | + upcoming_queryset = active_embargoes.filter( |
| 510 | + end_date__gte=timezone.now(), |
| 511 | + ).order_by('end_date') |
| 512 | + |
| 513 | + page_number = self.request.GET.get('page') or 1 |
| 514 | + paginator = Paginator(upcoming_queryset, 10) |
| 515 | + try: |
| 516 | + upcoming_page = paginator.page(page_number) |
| 517 | + except InvalidPage: |
| 518 | + upcoming_page = paginator.page(1) |
| 519 | + |
| 520 | + context.update({ |
| 521 | + 'now': timezone.now(), |
| 522 | + 'pending_overdue_embargoes': pending_overdue_embargoes, |
| 523 | + 'overdue_embargoes': overdue_embargoes, |
| 524 | + 'upcoming_embargoes': upcoming_page.object_list, |
| 525 | + 'upcoming_page': upcoming_page, |
| 526 | + }) |
| 527 | + return context |
| 528 | + |
| 529 | + |
477 | 530 | class ConfirmApproveBacklogView(RegistrationListView): |
478 | 531 | template_name = 'nodes/registration_approval_list.html' |
479 | 532 | permission_required = 'osf.view_registrationapproval' |
@@ -846,6 +899,72 @@ def _remove_file_from_schema_response_blocks(registration, removed_file_id): |
846 | 899 | return redirect(self.get_success_url()) |
847 | 900 |
|
848 | 901 |
|
| 902 | +class NodeAddOsfStorageFileView(NodeMixin, View): |
| 903 | + """ Allows an authorized user to add a file to osfstorage of an archived node. |
| 904 | + """ |
| 905 | + permission_required = 'osf.change_node' |
| 906 | + |
| 907 | + def post(self, request, *args, **kwargs): |
| 908 | + registration = self.get_object() |
| 909 | + guid_id = request.POST.get('file-guid', '').strip() |
| 910 | + guid = Guid.load(guid_id) |
| 911 | + if not guid: |
| 912 | + messages.error(request, 'No file found with the provided guid.') |
| 913 | + return redirect(self.get_success_url()) |
| 914 | + |
| 915 | + file = guid.referent |
| 916 | + if not isinstance(file, File): |
| 917 | + messages.error(request, 'The guid provided does not correspond to a file.') |
| 918 | + return redirect(self.get_success_url()) |
| 919 | + |
| 920 | + parent_node = registration.registered_from |
| 921 | + if not parent_node: |
| 922 | + messages.error(request, 'The registration does not have the parent node.') |
| 923 | + return redirect(self.get_success_url()) |
| 924 | + |
| 925 | + if not parent_node.files.filter(id=file.id).exists(): |
| 926 | + messages.error(request, 'The file with the provided guid is not part of the parent node.') |
| 927 | + return redirect(self.get_success_url()) |
| 928 | + |
| 929 | + osfstorage = registration.get_addon('osfstorage') |
| 930 | + # copy file to Archive of OSF Storage folder |
| 931 | + archive_folder = OsfStorageFolder.objects.filter( |
| 932 | + parent=osfstorage.get_root(), |
| 933 | + name=osfstorage.archive_folder_name |
| 934 | + ).first() |
| 935 | + file.copy_under(archive_folder) |
| 936 | + messages.success(request, 'The file was successfully added.') |
| 937 | + return redirect(self.get_success_url()) |
| 938 | + |
| 939 | + |
| 940 | +class NodeRemoveOsfStorageFileView(NodeMixin, View): |
| 941 | + """ Allows an authorized user to remove a file from osfstorage of an archived node. |
| 942 | + """ |
| 943 | + permission_required = 'osf.change_node' |
| 944 | + |
| 945 | + def post(self, request, *args, **kwargs): |
| 946 | + registration = self.get_object() |
| 947 | + guid_id = request.POST.get('file-guid', '').strip() |
| 948 | + guid = Guid.load(guid_id) |
| 949 | + if not guid: |
| 950 | + messages.error(request, 'No file found with the provided guid.') |
| 951 | + return redirect(self.get_success_url()) |
| 952 | + |
| 953 | + file = guid.referent |
| 954 | + if not isinstance(file, File): |
| 955 | + messages.error(request, 'The guid provided does not correspond to a file.') |
| 956 | + return redirect(self.get_success_url()) |
| 957 | + |
| 958 | + registration_file = registration.files.filter(id=file.id) |
| 959 | + if not registration_file.exists(): |
| 960 | + messages.error(request, 'The file with the provided guid is not part of the registration.') |
| 961 | + return redirect(self.get_success_url()) |
| 962 | + |
| 963 | + registration_file.delete() |
| 964 | + messages.success(request, 'The file was successfully removed.') |
| 965 | + return redirect(self.get_success_url()) |
| 966 | + |
| 967 | + |
849 | 968 | class RemoveStuckRegistrationsView(NodeMixin, View): |
850 | 969 | """ Allows an authorized user to remove a registrations if it's stuck in the archiving process. |
851 | 970 | """ |
|
0 commit comments