Skip to content

Commit ce84844

Browse files
mkovaluaantkrytJohnetordoffihorsokhanexoftVlad0n20
committed
[ENG-8740] Ability to delete a file from a registration via admin (CenterForOpenScience#11285)
* [ENG-8514] Remove CSRF protection from reset password api v2 POST (CenterForOpenScience#11246) * remove csrf protection from reset password endpoint * update test * add background color prop to Brand (CenterForOpenScience#11254) * fix flaky test_serialized_metadata * fix throttle test * reset throttle cache * don't ignore components when create a view-only link * fixed updating subscribe_osf_general_email subscription * [ENG-7277] Update doc to include version as relationship (CenterForOpenScience#11262) * added tests * [ENG-8691] Wrong server on reset password email (CenterForOpenScience#11271) * use same logic for admin and web password reset * fix tests * enable filtering for linked-nodes endpoint (CenterForOpenScience#11273) * remove deleted users from institutional dashboard (CenterForOpenScience#11261) * fixed tests * [ENG-7803] Update text on VOL modal for registration (CenterForOpenScience#11279) * add a file removal feature for a node (registration) using admin panel * delete file only for archived registration when it is not possible from user side to avoid inconsistency * remove file from scheme response blocks * delete file from registration and metadata and keep it for original project --------- Co-authored-by: antkryt <ant.krytskyi@gmail.com> Co-authored-by: John Tordoff <Johnetordoff@users.noreply.github.com> Co-authored-by: Ihor Sokhan <isokhan@exoft.net> Co-authored-by: Vlad0n20 <137097005+Vlad0n20@users.noreply.github.com>
1 parent e7571b2 commit ce84844

5 files changed

Lines changed: 81 additions & 6 deletions

File tree

admin/nodes/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@
5050
re_path(r'^(?P<guid>[a-z0-9]+)/system_tags/add/$', views.NodeAddSystemTag.as_view(), name='add-system-tag'),
5151
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'),
5252
re_path(r'^(?P<guid>[a-z0-9]+)/update_permissions/$', views.NodeUpdatePermissionsView.as_view(), name='update-permissions'),
53+
re_path(r'^(?P<guid>[a-z0-9]+)/remove_file/$', views.NodeRemoveFileView.as_view(), name='remove-file'),
5354
]

admin/nodes/views.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.utils import timezone
77
from django.core.exceptions import PermissionDenied, ValidationError
88
from django.urls import NoReverseMatch
9+
from django.db import transaction
910
from django.db.models import F, Case, When, IntegerField
1011
from django.contrib import messages
1112
from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -28,14 +29,17 @@
2829
from api.caching.tasks import update_storage_usage_cache
2930

3031
from osf.exceptions import NodeStateError, RegistrationStuckError
32+
from osf.management.commands.change_node_region import _update_schema_meta
3133
from osf.models import (
34+
Guid,
3235
OSFUser,
3336
NodeLog,
3437
AbstractNode,
3538
Registration,
3639
RegistrationProvider,
3740
RegistrationApproval,
38-
SpamStatus
41+
SpamStatus,
42+
TrashedFile
3943
)
4044
from osf.models.admin_log_entry import (
4145
update_admin_log,
@@ -813,6 +817,36 @@ def post(self, request, *args, **kwargs):
813817
return redirect(self.get_success_url())
814818

815819

820+
class NodeRemoveFileView(NodeMixin, View):
821+
""" Allows an authorized user to remove file from node.
822+
"""
823+
permission_required = 'osf.change_node'
824+
825+
def post(self, request, *args, **kwargs):
826+
def _remove_file_from_schema_response_blocks(registration, removed_file_id):
827+
file_input_keys = registration.registration_schema.schema_blocks.filter(
828+
block_type='file-input'
829+
).values_list('registration_response_key', flat=True)
830+
for schema_response in registration.schema_responses.all():
831+
for block in schema_response.response_blocks.filter(schema_key__in=file_input_keys):
832+
if not block.response:
833+
continue
834+
block.response = [entry for entry in block.response if entry.get('file_id') not in removed_file_id]
835+
block.save()
836+
837+
node = self.get_object()
838+
guid_id = request.POST.get('remove-file-guid', '').strip()
839+
guid = Guid.load(guid_id)
840+
841+
# delete file from registration and metadata and keep it for original project
842+
if guid and (file := guid.referent) and (file.target == node) and not isinstance(file, TrashedFile):
843+
with transaction.atomic():
844+
file.delete()
845+
_update_schema_meta(file.target)
846+
_remove_file_from_schema_response_blocks(node, [file._id, file.copied_from._id])
847+
return redirect(self.get_success_url())
848+
849+
816850
class RemoveStuckRegistrationsView(NodeMixin, View):
817851
""" Allows an authorized user to remove a registrations if it's stuck in the archiving process.
818852
"""

admin/templates/nodes/node.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<a href="{% url 'nodes:search' %}" class="btn btn-primary"> <i class="fa fa-search"></i></a>
1818
<a href="{% url 'nodes:node-logs' guid=node.guid %}" class="btn btn-primary">View Logs</a>
1919
{% include "nodes/remove_node.html" with node=node %}
20+
{% include "nodes/remove_file.html" with node=node %}
2021
{% include "nodes/registration_force_archive.html" with node=node %}
2122
{% include "nodes/make_private.html" with node=node %}
2223
{% include "nodes/make_public.html" with node=node %}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{% if node.is_registration and node.archived %}
2+
<a data-toggle="modal" data-target="#confirmDeleteFileModal" class="btn btn-danger">
3+
Delete File
4+
</a>
5+
<div id="confirmDeleteFileModal" class="modal fade well" tabindex="-1" role="dialog">
6+
<div class="modal-dialog" role="document">
7+
<div class="modal-content">
8+
<form class="well" method="post" action="{% url 'nodes:remove-file' guid=node.guid %}">
9+
<div class="modal-header">
10+
<button type="button" class="close" data-dismiss="modal">x</button>
11+
<h3>Enter file to delete</h3>
12+
</div>
13+
{% csrf_token %}
14+
15+
<div class="modal-body">
16+
<div style="display:flex; align-items:center; gap:12px;">
17+
<label for="file-guid" style="margin:0; white-space:nowrap;">File guid:</label>
18+
<input id="file-guid"
19+
type="text"
20+
name="remove-file-guid"
21+
class="form-control"
22+
required
23+
style="flex:1; min-width:0;">
24+
</div>
25+
</div>
26+
<div class="modal-footer">
27+
<button class="btn btn-danger" name="action" value="ham" type="submit">Confirm</button>
28+
<button type="button" class="btn btn-default" data-dismiss="modal">
29+
Cancel
30+
</button>
31+
</div>
32+
</form>
33+
</div>
34+
</div>
35+
</div>
36+
{% endif %}

osf/management/commands/change_node_region.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ def _update_blocks(file_block_map, original_id, cloned_id):
3939
block.save()
4040

4141
def _update_schema_meta(node):
42-
logger.info('Updating legacy schema information...')
43-
node.registration_responses = node.schema_responses.latest('-created').all_responses
44-
node.registered_meta[node.registration_schema._id] = node.expand_registration_responses()
45-
node.save()
46-
logger.info('Updated legacy schema information.')
42+
try:
43+
logger.info('Updating legacy schema information...')
44+
node.registration_responses = node.schema_responses.latest('-created').all_responses
45+
node.registered_meta[node.registration_schema._id] = node.expand_registration_responses()
46+
node.save()
47+
logger.info('Updated legacy schema information.')
48+
except Exception:
49+
logger.error('There is no data in schema responses to update legacy schema information.')
4750

4851
def _copy_and_clone_versions(original_file, cloned_file, src_bucket, dest_bucket, dest_bucket_name, dest_region):
4952
for v in original_file.versions.order_by('identifier').all():

0 commit comments

Comments
 (0)