From 11e290cea893156a13d488a5a707cf501edd5198 Mon Sep 17 00:00:00 2001 From: Jorge Date: Thu, 29 Aug 2024 21:49:58 -0400 Subject: [PATCH] Implement moving/renaming operation for folders, files and link files --- OLD_DEVELOP.md | 0 action.yml | 0 chris_backend/core/models.py | 183 +++++++++++++++---- chris_backend/core/storage/plain_fs.py | 46 +++++ chris_backend/core/storage/storagemanager.py | 22 ++- chris_backend/core/storage/swiftmanager.py | 18 ++ chris_backend/filebrowser/serializers.py | 124 +++++++------ chris_backend/filebrowser/views.py | 9 +- chris_backend/userfiles/serializers.py | 2 + docker-compose_just.yml | 0 githubActions/cleanup.js | 0 githubActions/main.js | 0 githubActions/setup.js | 0 justfile | 0 make.sh | 8 +- 15 files changed, 302 insertions(+), 110 deletions(-) mode change 100644 => 100755 OLD_DEVELOP.md mode change 100644 => 100755 action.yml mode change 100644 => 100755 docker-compose_just.yml mode change 100644 => 100755 githubActions/cleanup.js mode change 100644 => 100755 githubActions/main.js mode change 100644 => 100755 githubActions/setup.js mode change 100644 => 100755 justfile diff --git a/OLD_DEVELOP.md b/OLD_DEVELOP.md old mode 100644 new mode 100755 diff --git a/action.yml b/action.yml old mode 100644 new mode 100755 diff --git a/chris_backend/core/models.py b/chris_backend/core/models.py index d3a9e272..b9ca4fd2 100755 --- a/chris_backend/core/models.py +++ b/chris_backend/core/models.py @@ -94,8 +94,10 @@ def save(self, *args, **kwargs): to the DB. """ if self.path: - parent_path = os.path.dirname(self.path) + if self.path.startswith('/') or self.path.endswith('/'): + raise ValueError('Paths starting or ending with slashes are not allowed.') + parent_path = os.path.dirname(self.path) try: parent = ChrisFolder.objects.get(path=parent_path) except ChrisFolder.DoesNotExist: @@ -108,14 +110,48 @@ def save(self, *args, **kwargs): self.owner = User.objects.get(username='chris') super(ChrisFolder, self).save(*args, **kwargs) + def move(self, new_path): + """ + Custom method to move the folder's tree to a new path. + """ + new_path = new_path.strip('/') + path = str(self.path) + + storage_manager = connect_storage(settings) + storage_manager.move_path(path, new_path) + + prefix = path + '/' # avoid sibling folders with paths that start with path + + folders = [self] + list(ChrisFolder.objects.filter(path__startswith=prefix)) + for folder in folders: + folder.path = folder.path.replace(path, new_path, 1) + ChrisFolder.objects.bulk_update(folders, ['path']) + + files = list(ChrisFile.objects.filter(fname__startswith=prefix)) + for f in files: + f.fname.name = f.fname.name.replace(path, new_path, 1) + ChrisFile.objects.bulk_update(files, ['fname']) + + link_files = list(ChrisLinkFile.objects.filter(fname__startswith=prefix)) + for lf in link_files: + lf.fname.name = lf.fname.name.replace(path, new_path, 1) + ChrisLinkFile.objects.bulk_update(link_files, ['fname']) + + new_parent_path = os.path.dirname(new_path) + + if new_parent_path != os.path.dirname(path): + # parent folder has changed + (parent_folder, _) = ChrisFolder.objects.get_or_create(path=new_parent_path, + owner=self.owner) + self.parent_folder = parent_folder + self.save() + def get_descendants(self): """ Custom method to return all the folders that are a descendant of this folder (including itself). """ path = str(self.path) - if path.endswith('/'): - return list(ChrisFolder.objects.filter(path__startswith=path)) return [self] + list(ChrisFolder.objects.filter(path__startswith=path + '/')) def has_group_permission(self, group, permission=''): @@ -200,7 +236,7 @@ def get_shared_link(self): Custom method to get the link file in the SHARED folder pointing to this folder if it exists. """ - path = self.path.rstrip('/') + path = str(self.path) str_source_trace_dir = path.replace('/', '_') fname = 'SHARED/' + str_source_trace_dir + '.chrislink' @@ -215,7 +251,7 @@ def create_shared_link(self): Custom method to create a link file in the SHARED folder pointing to this folder. """ - path = self.path.rstrip('/') + path = str(self.path) str_source_trace_dir = path.replace('/', '_') fname = 'SHARED/' + str_source_trace_dir + '.chrislink' @@ -232,7 +268,8 @@ def remove_shared_link(self): Custom method to remove a link file in the SHARED folder pointing to this folder if it exists. """ - fname = 'SHARED/' + self.path.rstrip('/').replace('/', '_') + '.chrislink' + path = str(self.path) + fname = 'SHARED/' + path.replace('/', '_') + '.chrislink' try: lf = ChrisLinkFile.objects.get(fname=fname) except ChrisLinkFile.DoesNotExist: @@ -245,7 +282,7 @@ def create_public_link(self): Custom method to create a public link file in the PUBLIC folder pointing to this folder. """ - path = self.path.rstrip('/') + path = str(self.path) str_source_trace_dir = path.replace('/', '_') fname = 'PUBLIC/' + str_source_trace_dir + '.chrislink' @@ -262,7 +299,9 @@ def remove_public_link(self): Custom method to remove a public link file in the PUBLIC folder pointing to this folder if it exists. """ - fname = 'PUBLIC/' + self.path.rstrip('/').replace('/', '_') + '.chrislink' + path = str(self.path) + fname = 'PUBLIC/' + path.replace('/', '_') + '.chrislink' + try: lf = ChrisLinkFile.objects.get(fname=fname) except ChrisLinkFile.DoesNotExist: @@ -276,22 +315,19 @@ def _update_public_access(self, public_tf): folders, link files and files. """ path = str(self.path) + prefix = path + '/' # avoid sibling folders with paths that start with path - if path.endswith('/'): - folders = list(ChrisFolder.objects.filter(path__startswith=path)) - else: - folders = [self] + list(ChrisFolder.objects.filter(path__startswith=path + '/')) - + folders = [self] + list(ChrisFolder.objects.filter(path__startswith=prefix)) for folder in folders: folder.public = public_tf ChrisFolder.objects.bulk_update(folders, ['public']) - files = list(ChrisFile.objects.filter(fname__startswith=path)) + files = list(ChrisFile.objects.filter(fname__startswith=prefix)) for f in files: f.public = public_tf ChrisFile.objects.bulk_update(files, ['public']) - link_files = list(ChrisLinkFile.objects.filter(fname__startswith=path)) + link_files = list(ChrisLinkFile.objects.filter(fname__startswith=prefix)) for lf in link_files: lf.public = public_tf ChrisLinkFile.objects.bulk_update(link_files, ['public']) @@ -325,9 +361,10 @@ def save(self, *args, **kwargs): group = self.group permission = self.permission - path = self.folder.path.rstrip('/') + '/' + path = str(self.folder.path) + prefix = path + '/' # avoid sibling folders with paths that start with path - folders = ChrisFolder.objects.filter(path__startswith=path) + folders = ChrisFolder.objects.filter(path__startswith=prefix) objs = [] for folder in folders: perm = FolderGroupPermission(folder=folder, group=group, @@ -337,7 +374,7 @@ def save(self, *args, **kwargs): update_fields=['permission'], unique_fields=['folder_id', 'group_id']) - files = ChrisFile.objects.filter(fname__startswith=path) + files = ChrisFile.objects.filter(fname__startswith=prefix) objs = [] for f in files: perm = FileGroupPermission(file=f, group=group, permission=permission) @@ -346,7 +383,7 @@ def save(self, *args, **kwargs): update_fields=['permission'], unique_fields=['file_id', 'group_id']) - link_files = ChrisLinkFile.objects.filter(fname__startswith=path) + link_files = ChrisLinkFile.objects.filter(fname__startswith=prefix) objs = [] for lf in link_files: perm = LinkFileGroupPermission(link_file=lf, group=group, @@ -366,15 +403,16 @@ def delete(self, *args, **kwargs): group = self.group permission = self.permission - path = self.folder.path.rstrip('/') + '/' + path = str(self.folder.path) + prefix = path + '/' # avoid sibling folders with paths that start with path - FolderGroupPermission.objects.filter(folder__path__startswith=path, group=group, + FolderGroupPermission.objects.filter(folder__path__startswith=prefix, group=group, permission=permission).delete() - FileGroupPermission.objects.filter(file__fname__startswith=path, group=group, + FileGroupPermission.objects.filter(file__fname__startswith=prefix, group=group, permission=permission).delete() - LinkFileGroupPermission.objects.filter(link_file__fname__startswith=path, + LinkFileGroupPermission.objects.filter(link_file__fname__startswith=prefix, group=group, permission=permission).delete() @@ -407,9 +445,10 @@ def save(self, *args, **kwargs): user = self.user permission = self.permission - path = self.folder.path.rstrip('/') + '/' + path = str(self.folder.path) + prefix = path + '/' # avoid sibling folders with paths that start with path - folders = ChrisFolder.objects.filter(path__startswith=path) + folders = ChrisFolder.objects.filter(path__startswith=prefix) objs = [] for folder in folders: perm = FolderUserPermission(folder=folder, user=user, permission=permission) @@ -418,7 +457,7 @@ def save(self, *args, **kwargs): update_fields=['permission'], unique_fields=['folder_id', 'user_id']) - files = ChrisFile.objects.filter(fname__startswith=path) + files = ChrisFile.objects.filter(fname__startswith=prefix) objs = [] for f in files: perm = FileUserPermission(file=f, user=user, permission=permission) @@ -427,7 +466,7 @@ def save(self, *args, **kwargs): update_fields=['permission'], unique_fields=['file_id', 'user_id']) - link_files = ChrisLinkFile.objects.filter(fname__startswith=path) + link_files = ChrisLinkFile.objects.filter(fname__startswith=prefix) objs = [] for lf in link_files: perm = LinkFileUserPermission(link_file=lf, user=user, permission=permission) @@ -446,15 +485,16 @@ def delete(self, *args, **kwargs): user = self.user permission = self.permission - path = self.folder.path.rstrip('/') + '/' + path = str(self.folder.path) + prefix = path + '/' # avoid sibling folders with paths that start with path - FolderUserPermission.objects.filter(folder__path__startswith=path, user=user, + FolderUserPermission.objects.filter(folder__path__startswith=prefix, user=user, permission=permission).delete() - FileUserPermission.objects.filter(file__fname__startswith=path, user=user, + FileUserPermission.objects.filter(file__fname__startswith=prefix, user=user, permission=permission).delete() - LinkFileUserPermission.objects.filter(link_file__fname__startswith=path, + LinkFileUserPermission.objects.filter(link_file__fname__startswith=prefix, user=user, permission=permission).delete() @@ -484,6 +524,40 @@ class Meta: def __str__(self): return self.fname.name + def save(self, *args, **kwargs): + """ + Overriden to ensure file paths never start or end with slashes. + """ + path = self.fname.name + if path.startswith('/') or path.endswith('/'): + raise ValueError('Paths starting or ending with slashes are not allowed.') + super(ChrisFile, self).save(*args, **kwargs) + + def move(self, new_path): + """ + Custom method to move the file to a new path. + """ + new_path = new_path.strip('/') + + storage_manager = connect_storage(settings) + if storage_manager.obj_exists(new_path): + storage_manager.delete_obj(new_path) + + old_path = self.fname.name + storage_manager.copy_obj(old_path, new_path) + storage_manager.delete_obj(old_path) + + old_folder_path = os.path.dirname(old_path) + new_folder_path = os.path.dirname(new_path) + + if new_folder_path != old_folder_path: # parent folder has changed + (parent_folder, _) = ChrisFolder.objects.get_or_create(path=new_folder_path, + owner=self.owner) + self.parent_folder = parent_folder + + self.fname.name = new_path + self.save() + def has_group_permission(self, group, permission=''): """ Custom method to determine whether a group has been granted a permission to @@ -572,8 +646,7 @@ def get_shared_link(self): Custom method to get the link file in the SHARED folder pointing to this file if it exists. """ - path = self.fname.name - str_source_trace_dir = path.replace('/', '_') + str_source_trace_dir = self.fname.name.replace('/', '_') fname = 'SHARED/' + str_source_trace_dir + '.chrislink' try: @@ -738,6 +811,35 @@ def save(self, *args, **kwargs): self.fname.name = link_file_path super(ChrisLinkFile, self).save(*args, **kwargs) + def move(self, new_path): + """ + Custom method to move the link file to a new path. + """ + new_path = new_path.strip('/') + if not new_path.endswith('.chrislink'): + raise ValueError("The new path must end with '.chrislink' sufix.") + + storage_manager = connect_storage(settings) + if storage_manager.obj_exists(new_path): + storage_manager.delete_obj(new_path) + + old_path = self.fname.name + storage_manager.copy_obj(old_path, new_path) + storage_manager.delete_obj(old_path) + + old_folder_path = os.path.dirname(old_path) + new_folder_path = os.path.dirname(new_path) + + if new_folder_path != old_folder_path: # parent folder has changed + (parent_folder, _) = ChrisFolder.objects.get_or_create(path=new_folder_path, + owner=self.owner) + self.parent_folder = parent_folder + + self.fname.name = new_path + + link_name = os.path.basename(new_path).rsplit('.chrislink', 1)[0] + self.save(name=link_name) + def has_group_permission(self, group, permission=''): """ Custom method to determine whether a group has been granted a permission to @@ -811,25 +913,28 @@ def remove_user_permission(self, user, permission): def grant_public_access(self): """ - Custom method to grant public access to the file. + Custom method to grant public access to the link file. """ self.public = True - self.save() + path = self.fname.name + link_name = os.path.basename(path).rsplit('.chrislink', 1)[0] + self.save(name=link_name) def remove_public_access(self): """ - Custom method to remove public access to the file. + Custom method to remove public access to the link file. """ self.public = False - self.save() + path = self.fname.name + link_name = os.path.basename(path).rsplit('.chrislink', 1)[0] + self.save(name=link_name) def get_shared_link(self): """ Custom method to get the link file in the SHARED folder pointing to this file if it exists. """ - path = self.fname.name - str_source_trace_dir = path.replace('/', '_') + str_source_trace_dir = self.fname.name.replace('/', '_') fname = 'SHARED/' + str_source_trace_dir + '.chrislink' try: diff --git a/chris_backend/core/storage/plain_fs.py b/chris_backend/core/storage/plain_fs.py index a9f4beb8..c369b367 100755 --- a/chris_backend/core/storage/plain_fs.py +++ b/chris_backend/core/storage/plain_fs.py @@ -1,4 +1,6 @@ + from pathlib import Path +import shutil from typing import Union, List, AnyStr, Optional from core.storage.storagemanager import StorageManager @@ -60,3 +62,47 @@ def copy_obj(self, src: str, dst: str) -> None: def delete_obj(self, file_path: str) -> None: (self.__base / file_path).unlink() + + def copy_path(self, src: str, dst: str) -> None: + src_path = self.__base / src + dst_path = self.__base / dst + + if not src_path.exists(): + raise FileNotFoundError( + f"The source path '{src_path}' does not exist.") + + if src_path.is_dir(): + dst_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copytree(src_path, dst_path) + else: + self.copy_obj(src, dst) + + def move_path(self, src: str, dst: str) -> None: + src_path = self.__base / src + dst_path = self.__base / dst + + if not src_path.exists(): + raise FileNotFoundError( + f"The source path '{src_path}' does not exist.") + + if src_path.is_dir(): + if src_path.parent == dst_path.parent: # just a renaming + src_path.rename(dst_path) + else: + dst_path.parent.mkdir(parents=True, exist_ok=True) + shutil.move(src_path, dst_path) + else: + dst_path.parent.mkdir(parents=True, exist_ok=True) + src_path.rename(dst_path) + + def delete_path(self, path: str) -> None: + p = self.__base / path + + if not p.exists(): + raise FileNotFoundError( + f"The source path '{p}' does not exist.") + + if p.is_dir(): + shutil.rmtree(p) + else: + self.delete_obj(path) diff --git a/chris_backend/core/storage/storagemanager.py b/chris_backend/core/storage/storagemanager.py index b6bc4b14..f63c9362 100755 --- a/chris_backend/core/storage/storagemanager.py +++ b/chris_backend/core/storage/storagemanager.py @@ -57,7 +57,7 @@ def download_obj(self, file_path: str) -> AnyStr: def copy_obj(self, src: str, dst: str) -> None: """ - Copy data to a new path. + Copy file data to a new path. Instead of a copy, implementations may create links or shallow copies for efficiency. """ @@ -65,6 +65,24 @@ def copy_obj(self, src: str, dst: str) -> None: def delete_obj(self, file_path: str) -> None: """ - Delete data from the given path. + Delete file data from the given path. + """ + ... + + def copy_path(self, src: str, dst: str) -> None: + """ + Copy all the data under a src path to a new dst path. + """ + ... + + def move_path(self, src: str, dst: str) -> None: + """ + Move all the data under a src path to a new dst path. + """ + ... + + def delete_path(self, path: str) -> None: + """ + Delete all the data under a path. """ ... diff --git a/chris_backend/core/storage/swiftmanager.py b/chris_backend/core/storage/swiftmanager.py index 3ad362e6..8e1ac86e 100755 --- a/chris_backend/core/storage/swiftmanager.py +++ b/chris_backend/core/storage/swiftmanager.py @@ -175,3 +175,21 @@ def delete_obj(self, obj_path): time.sleep(0.4) else: break + + def copy_path(self, src: str, dst: str) -> None: + l_ls = self.ls(src) + for obj_path in l_ls: + new_obj_path = obj_path.replace(src, dst, 1) + self.copy_obj(obj_path, new_obj_path) + + def move_path(self, src: str, dst: str) -> None: + l_ls = self.ls(src) + for obj_path in l_ls: + new_obj_path = obj_path.replace(src, dst, 1) + self.copy_obj(obj_path, new_obj_path) + self.delete_obj(obj_path) + + def delete_path(self, path: str) -> None: + l_ls = self.ls(path) + for obj_path in l_ls: + self.delete_obj(obj_path) diff --git a/chris_backend/filebrowser/serializers.py b/chris_backend/filebrowser/serializers.py index d5f52cbc..d1abc203 100755 --- a/chris_backend/filebrowser/serializers.py +++ b/chris_backend/filebrowser/serializers.py @@ -52,17 +52,32 @@ def update(self, instance, validated_data): """ Overriden to grant or remove public access to the folder and all its descendant folders, link files and files depending on the new public status of - the folder. + the folder. Also move the folder's tree to a new path and recreate the public + link to the folder if required. """ - if 'public' in validated_data: - if instance.public and not validated_data['public']: + public = instance.public + + if public and 'public' in validated_data and not validated_data['public']: + instance.remove_public_link() + instance.remove_public_access() + + new_path = validated_data.get('path') + + if new_path: + if public and ('public' not in validated_data or validated_data['public']): instance.remove_public_link() - instance.remove_public_access() - elif not instance.public and validated_data['public']: - instance.grant_public_access() - instance.create_public_link() - return super(FileBrowserFolderSerializer, self).update(instance, validated_data) + # folder will be stored at: SWIFT_CONTAINER_NAME/ + # where must start with home/ + instance.move(new_path) + + if public and ('public' not in validated_data or validated_data['public']): + instance.create_public_link() # recreate public link + + if not public and 'public' in validated_data and validated_data['public']: + instance.grant_public_access() + instance.create_public_link() + return instance def validate_path(self, path): """ @@ -122,8 +137,10 @@ def validate(self, data): being moved. """ if self.instance: # on update - if 'public' not in data: - raise serializers.ValidationError({'public': ['This field is required.']}) + if 'public' not in data and 'path' not in data: + raise serializers.ValidationError( + {'non_field_errors': ["At least one of the fields 'public' " + "or 'path' must be provided."]}) username = self.context['request'].user.username @@ -139,6 +156,8 @@ def validate(self, data): else: if 'path' not in data: # on create raise serializers.ValidationError({'path': ['This field is required.']}) + + data.pop('public', None) # can only be set to public on update return data @@ -294,35 +313,31 @@ class Meta: def update(self, instance, validated_data): """ - Overriden to set the file's saving path and parent folder and delete the old - path from storage. + Overriden to grant or remove public access to the file and/or move it to a new + path. """ - if 'public' in validated_data: - instance.public = validated_data['public'] + public = instance.public + + if public and 'public' in validated_data and not validated_data['public']: + instance.remove_public_link() + instance.remove_public_access() new_file_path = validated_data.pop('new_file_path', None) if new_file_path: + if public and ('public' not in validated_data or validated_data['public']): + instance.remove_public_link() + # user file will be stored at: SWIFT_CONTAINER_NAME/ # where must start with home/ + instance.move(new_file_path) - old_storage_path = instance.fname.name + if public and ('public' not in validated_data or validated_data['public']): + instance.create_public_link() # recreate public link - storage_manager = connect_storage(settings) - if storage_manager.obj_exists(new_file_path): - storage_manager.delete_obj(new_file_path) - - storage_manager.copy_obj(old_storage_path, new_file_path) - storage_manager.delete_obj(old_storage_path) - - folder_path = os.path.dirname(new_file_path) - owner = instance.owner - (parent_folder, _) = ChrisFolder.objects.get_or_create(path=folder_path, - owner=owner) - instance.parent_folder = parent_folder - instance.fname.name = new_file_path - - instance.save() + if not public and 'public' in validated_data and validated_data['public']: + instance.grant_public_access() + instance.create_public_link() return instance def get_file_link(self, obj): @@ -337,7 +352,7 @@ def validate_new_file_path(self, new_file_path): for which the user has write permission. """ # remove leading and trailing slashes - new_file_path = new_file_path.strip(' ').strip('/') + new_file_path = new_file_path.strip().strip('/') if new_file_path.endswith('.chrislink'): raise serializers.ValidationError(["Invalid path. This is not a ChRIS link " @@ -546,36 +561,31 @@ class Meta: def update(self, instance, validated_data): """ - Overriden to set the link file's saving path and parent folder and delete - the old path from storage. + Overriden to grant or remove public access to the file and/or move it to a new + path. """ - if 'public' in validated_data: - instance.public = validated_data['public'] + public = instance.public + + if public and 'public' in validated_data and not validated_data['public']: + instance.remove_public_link() + instance.remove_public_access() new_link_file_path = validated_data.pop('new_link_file_path', None) if new_link_file_path: - # user file will be stored at: SWIFT_CONTAINER_NAME/ - # where must start with home/ - - old_storage_path = instance.fname.name - - storage_manager = connect_storage(settings) - if storage_manager.obj_exists(new_link_file_path): - storage_manager.delete_obj(new_link_file_path) + if public and ('public' not in validated_data or validated_data['public']): + instance.remove_public_link() - storage_manager.copy_obj(old_storage_path, new_link_file_path) - storage_manager.delete_obj(old_storage_path) + # link file will be stored at: SWIFT_CONTAINER_NAME/ + # where must start with home/ + instance.move(new_link_file_path) - folder_path = os.path.dirname(new_link_file_path) - owner = instance.owner - (parent_folder, _) = ChrisFolder.objects.get_or_create(path=folder_path, - owner=owner) - instance.parent_folder = parent_folder - instance.fname.name = new_link_file_path + if public and ('public' not in validated_data or validated_data['public']): + instance.create_public_link() # recreate public link - link_name = os.path.basename(instance.fname.name).rsplit('.chrislink', 1)[0] - instance.save(name=link_name) + if not public and 'public' in validated_data and validated_data['public']: + instance.grant_public_access() + instance.create_public_link() return instance def get_file_link(self, obj): @@ -627,11 +637,11 @@ def validate_new_link_file_path(self, new_link_file_path): for which the user has write permission. """ # remove leading and trailing slashes - new_link_file_path = new_link_file_path.strip(' ').strip('/') + new_link_file_path = new_link_file_path.strip().strip('/') - if new_link_file_path.endswith('.chrislink'): - raise serializers.ValidationError(["Invalid path. This is not a ChRIS link " - "file."]) + if not new_link_file_path.endswith('.chrislink'): + raise serializers.ValidationError(["Invalid path. The new path must end with" + " '.chrislink' sufix."]) if not new_link_file_path.startswith('home/'): raise serializers.ValidationError(["Invalid path. Path must start with " "'home/'."]) diff --git a/chris_backend/filebrowser/views.py b/chris_backend/filebrowser/views.py index 95e23ccd..6702dacc 100755 --- a/chris_backend/filebrowser/views.py +++ b/chris_backend/filebrowser/views.py @@ -130,16 +130,9 @@ def retrieve(self, request, *args, **kwargs): Overriden to retrieve a file browser folder and append a collection+json template. """ response = super(FileBrowserFolderDetail, self).retrieve(request, *args, **kwargs) - template_data = {"public": ""} + template_data = {"public": "", "path": ""} return services.append_collection_template(response, template_data) - def update(self, request, *args, **kwargs): - """ - Overriden to remove path if provided by the user before serializer validation. - """ - request.data.pop('path', None) # change path is not implemented yet - return super(FileBrowserFolderDetail, self).update(request, *args, **kwargs) - def destroy(self, request, *args, **kwargs): """ Overriden to verify that the user's home or feeds folder is not being deleted. diff --git a/chris_backend/userfiles/serializers.py b/chris_backend/userfiles/serializers.py index 0989160a..d6ed9f02 100755 --- a/chris_backend/userfiles/serializers.py +++ b/chris_backend/userfiles/serializers.py @@ -136,4 +136,6 @@ def validate(self, data): if 'fname' not in data: raise serializers.ValidationError( {'fname': ["This field is required."]}) + + data.pop('public', None) # can only be set to public on update return data diff --git a/docker-compose_just.yml b/docker-compose_just.yml old mode 100644 new mode 100755 diff --git a/githubActions/cleanup.js b/githubActions/cleanup.js old mode 100644 new mode 100755 diff --git a/githubActions/main.js b/githubActions/main.js old mode 100644 new mode 100755 diff --git a/githubActions/setup.js b/githubActions/setup.js old mode 100644 new mode 100755 diff --git a/justfile b/justfile old mode 100644 new mode 100755 diff --git a/make.sh b/make.sh index 24ad7abf..4a01e124 100755 --- a/make.sh +++ b/make.sh @@ -350,7 +350,7 @@ rm -f dc.out ; title -d 1 "Setting global exports" windowBottom rm -f dc.out ; title -d 1 "Pulling non-'local/' core containers where needed" \ - "and creating appropriate .env for docker-compose" + "and creating appropriate .env for docker compose" printf "${LightCyan}%13s${Green}%-67s${Yellow}\n" \ "$ docker pull" " library/postgres" | ./boxes.sh @@ -366,7 +366,7 @@ rm -f dc.out ; title -d 1 "Pulling non-'local/' core containers where needed" if (( ! b_skipIntro )) ; then echo "# Variables declared here are available to" > .env - echo "# docker-compose on execution" >>.env + echo "# docker compose on execution" >>.env for CORE in ${A_CONTAINER[@]} ; do cparse $CORE "REPO" "CONTAINER" "MMN" "ENV" echo "${ENV}=${REPO}" >>.env @@ -643,10 +643,10 @@ fi # set compute resource 'host' to operate in-network (workaround until chrisomatic supports it) if (( b_pfconInNetwork )) ; then if [[ $STORAGE_ENV == 'swift' ]]; then - docker-compose -f docker-compose_dev.yml exec chris_dev /bin/bash -c \ + docker compose -f docker-compose_dev.yml exec chris_dev /bin/bash -c \ 'python manage.py shell -c "from plugins.models import ComputeResource; cr = ComputeResource.objects.get(name=\"host\"); cr.compute_innetwork = True; cr.save()"' elif [[ $STORAGE_ENV =~ ^(fslink|filesystem)$ ]]; then - docker-compose -f docker-compose_noswift.yml exec chris_dev /bin/bash -c \ + docker compose -f docker-compose_noswift.yml exec chris_dev /bin/bash -c \ 'python manage.py shell -c "from plugins.models import ComputeResource; cr = ComputeResource.objects.get(name=\"host\"); cr.compute_innetwork = True; cr.save()"' fi