Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions addons/osfstorage/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,22 @@ def test_delete_root_node(self):
assert BaseFileNode.objects.get(_id=folder._id).type == 'osf.trashedfolder'
assert BaseFileNode.objects.get(_id=file._id).type == 'osf.trashedfile'

def test_restore_deleted_file_without_deleted_field(self):
assert models.TrashedFileNode.objects.exists() is False

child = self.node_settings.get_root().append_file('Test')
child.delete()

trashed_file = models.TrashedFileNode.objects.first()
restored_file = trashed_file.restore()

assert restored_file.deleted is None
# None because we do not set deleted_by when delete the child
assert restored_file.deleted_by is None
assert restored_file.deleted_on is not None

assert models.TrashedFileNode.objects.exists() is False

def test_delete_file(self):
child = self.node_settings.get_root().append_file('Test')
field_names = [f.name for f in child._meta.get_fields() if not f.is_relation and f.name not in ['id', 'content_type_pk']]
Expand Down
2 changes: 2 additions & 0 deletions admin/management/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@
name='remove_orcid_from_user_social'),
re_path(r'^migrate_funder_names_to_ror', views.MigrateFunderNamesToRor.as_view(),
name='migrate_funder_names_to_ror'),
re_path(r'^fix_restored_trashed_files', views.FixRestoredTrashedFiles.as_view(),
name='fix_restored_trashed_files'),
]
8 changes: 8 additions & 0 deletions admin/management/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,11 @@ def post(self, request):
for _line in _out_io.getvalue().split('\n'):
messages.info(request, _line)
return redirect(reverse('management:commands'))


class FixRestoredTrashedFiles(ManagementCommandPermissionView):

def post(self, request):
call_command('fix_restored_trashed_files')
messages.success(request, 'Restored trashed files have been successfully fixed.')
return redirect(reverse('management:commands'))
13 changes: 13 additions & 0 deletions admin/templates/management/commands.html
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ <h4><u>Update ROR funder names to be consistent and proper.</u></h4>
</nav>
</form>
</section>
<section>
<h4><u>Fix restored files</u></h4>
<p>
Use this management command to fix restored files that were previously trashed.
</p>
<form method="post"
action="{% url 'management:fix_restored_trashed_files'%}">
{% csrf_token %}
<nav>
<input class="btn btn-success" type="submit" value="Run" />
</nav>
</form>
</section>
</div>
</section>
{% endblock %}
41 changes: 41 additions & 0 deletions osf/management/commands/fix_restored_trashed_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""
Clears deleted field value for all restored OsfStorageFileNode objects
as restore() method did not remove it and such files after restore from TrashedFileNode are not shown on UI
"""
import logging

from django.db import transaction
from django.core.management.base import BaseCommand

from addons.osfstorage.models import OsfStorageFileNode


logger = logging.getLogger(__name__)

class Command(BaseCommand):

def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Run without making changes',
)

def handle(self, *args, **options):
files_to_fix = OsfStorageFileNode.objects.filter(deleted__isnull=False)
file_ids = list(files_to_fix.values_list('_id', flat=True))
dry_run = options.get('dry_run', False)

if dry_run:
logger.info(f'Running in dry-run mode, the following files would be fixed: {file_ids}')
self.stdout.write(f'Running in dry-run mode, the following files would be fixed: {file_ids}')
return

with transaction.atomic():
for file in files_to_fix:
file.deleted = None

OsfStorageFileNode.objects.bulk_update(files_to_fix, ['deleted'], batch_size=1000)

logger.info(f'The following files have been fixed: {file_ids}')
self.stdout.write(f'The following files have been fixed: {file_ids}')
2 changes: 2 additions & 0 deletions osf/models/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,8 @@ def restore(self, recursive=True, parent=None, save=True, deleted_on=None, clien

type_cls = File if self.is_file else Folder

self.deleted = None

self.recast(self._resolve_class(type_cls)._typedmodels_type)

if save:
Expand Down
Loading