def data_store_file_or_folder_move_or_rename(request, res_id=None):
    """
    Move or rename a file or folder in hydroshareZone or any federated zone used for HydroShare
    resource backend store. It is invoked by an AJAX call and returns json object that has the
    relative path of the target file or folder being moved to if succeeds, and return empty string
    if fails. The AJAX request must be a POST request with input data passed in for res_id,
    source_path, and target_path where source_path and target_path are the relative paths for the
    source and target file or folder under res_id collection/directory.
    """
    res_id = request.POST.get('res_id', res_id)
    if res_id is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    res_id = str(res_id).strip()
    try:
        resource, _, user = authorize(
            request,
            res_id,
            needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found',
                            status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied',
                            status=status.HTTP_401_UNAUTHORIZED)

    src_path = resolve_request(request).get('source_path', None)
    tgt_path = resolve_request(request).get('target_path', None)
    if src_path is None or tgt_path is None:
        return HttpResponse(
            'Bad request - src_path or tgt_path is not included',
            status=status.HTTP_400_BAD_REQUEST)
    src_path = str(src_path).strip()
    tgt_path = str(tgt_path).strip()
    if not src_path or not tgt_path:
        return HttpResponse(
            'Bad request - src_path or tgt_path cannot be empty',
            status=status.HTTP_400_BAD_REQUEST)

    try:
        move_or_rename_file_or_folder(user, res_id, src_path, tgt_path)
    except SessionException as ex:
        return HttpResponse(ex.stderr,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'target_rel_path': tgt_path}

    return HttpResponse(json.dumps(return_object),
                        content_type='application/json')
def data_store_file_or_folder_move_or_rename(request, res_id=None):
    """
    Move or rename a file or folder in hydroshareZone or any federated zone used for HydroShare
    resource backend store. It is invoked by an AJAX call and returns json object that has the
    relative path of the target file or folder being moved to if succeeds, and return empty string
    if fails. The AJAX request must be a POST request with input data passed in for res_id,
    source_path, and target_path where source_path and target_path are the relative paths
    (relative to path res_id/data/contents) for the source and target file or folder under
    res_id collection/directory.
    """
    res_id = request.POST.get('res_id', res_id)
    if res_id is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    res_id = str(res_id).strip()
    try:
        resource, _, user = authorize(request, res_id,
                                      needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found', status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied', status=status.HTTP_401_UNAUTHORIZED)

    src_path = resolve_request(request).get('source_path', None)
    tgt_path = resolve_request(request).get('target_path', None)
    try:
        src_path = _validate_path(src_path, 'src_path')
        tgt_path = _validate_path(tgt_path, 'tgt_path')
    except ValidationError as ex:
        return HttpResponse(ex.message, status=status.HTTP_400_BAD_REQUEST)

    try:
        move_or_rename_file_or_folder(user, res_id, src_path, tgt_path)
    except SessionException as ex:
        return HttpResponse(ex.stderr, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'target_rel_path': tgt_path}

    return HttpResponse(
        json.dumps(return_object),
        content_type='application/json'
    )
def data_store_rename_file_or_folder(request, pk=None):
    """
    Rename one file or folder in a resource file hierarchy.  It is invoked by an AJAX call

    :param request: a REST request
    :param pk: the short_id of a resource to modify, from REST URL.

    This is invoked by an AJAX call in the UI. It returns a json object that has the
    relative path of the target file or folder that has been renamed. The AJAX request
    must be a POST request with input data for source_path and target_path, where source_path
    and target_path are the relative paths for the source and target file or folder.

    This routine is **specifically** targeted at validating requests from the UI.
    Thus it is much more limiting than a general purpose REST responder.
    """
    pk = request.POST.get('res_id', pk)
    if pk is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    pk = str(pk).strip()
    try:
        resource, _, user = authorize(
            request, pk, needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found',
                            status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied',
                            status=status.HTTP_401_UNAUTHORIZED)

    src_path = resolve_request(request).get('source_path', None)
    tgt_path = resolve_request(request).get('target_path', None)
    if src_path is None or tgt_path is None:
        return HttpResponse('Source or target name is not specified',
                            status=status.HTTP_400_BAD_REQUEST)

    if not src_path or not tgt_path:
        return HttpResponse('Source or target name is empty',
                            status=status.HTTP_400_BAD_REQUEST)

    src_path = str(src_path).strip()
    tgt_path = str(tgt_path).strip()
    src_folder, src_base = os.path.split(src_path)
    tgt_folder, tgt_base = os.path.split(tgt_path)

    if src_folder != tgt_folder:
        return HttpResponse(
            'Rename: Source and target names must be in same folder',
            status=status.HTTP_400_BAD_REQUEST)

    if not src_path.startswith('data/contents/'):
        return HttpResponse(
            'Rename: Source path must start with data/contents/',
            status=status.HTTP_400_BAD_REQUEST)

    if src_path.find('/../') >= 0 or src_path.endswith('/..'):
        return HttpResponse('Rename: Source path cannot contain /../',
                            status=status.HTTP_400_BAD_REQUEST)

    if not tgt_path.startswith('data/contents/'):
        return HttpResponse(
            'Rename: Target path must start with data/contents/',
            status=status.HTTP_400_BAD_REQUEST)

    if tgt_path.find('/../') >= 0 or tgt_path.endswith('/..'):
        return HttpResponse('Rename: Target path cannot contain /../',
                            status=status.HTTP_400_BAD_REQUEST)

    istorage = resource.get_irods_storage()

    # protect against stale data botches: source files should exist
    src_storage_path = os.path.join(resource.root_path, src_path)
    try:
        folder, base = ResourceFile.resource_path_is_acceptable(
            resource, src_storage_path, test_exists=True)
    except ValidationError:
        return HttpResponse('Object to be renamed does not exist',
                            status=status.HTTP_400_BAD_REQUEST)

    if not irods_path_is_directory(istorage, src_storage_path):
        try:  # Django record should exist for each file
            ResourceFile.get(resource, base, folder=folder)
        except ResourceFile.DoesNotExist:
            return HttpResponse('Object to be renamed does not exist',
                                status=status.HTTP_400_BAD_REQUEST)

    # check that the target doesn't exist
    tgt_storage_path = os.path.join(resource.root_path, tgt_path)
    tgt_short_path = tgt_path[len('data/contents/'):]
    if istorage.exists(tgt_storage_path):
        return HttpResponse('Desired name is already in use',
                            status=status.HTTP_400_BAD_REQUEST)
    try:
        folder, base = ResourceFile.resource_path_is_acceptable(
            resource, tgt_storage_path, test_exists=False)
    except ValidationError:
        return HttpResponse(
            'Poorly structured desired name {}'.format(tgt_short_path),
            status=status.HTTP_400_BAD_REQUEST)
    try:
        ResourceFile.get(resource, base, folder=tgt_short_path)
        return HttpResponse(
            'Desired name {} is already in use'.format(tgt_short_path),
            status=status.HTTP_400_BAD_REQUEST)
    except ResourceFile.DoesNotExist:
        pass  # correct response

    try:
        rename_file_or_folder(user, pk, src_path, tgt_path)
    except SessionException as ex:
        return HttpResponse(ex.stderr,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'target_rel_path': tgt_path}

    return HttpResponse(json.dumps(return_object),
                        content_type='application/json')
def data_store_move_to_folder(request, pk=None):
    """
    Move a list of files and/or folders to another folder in a resource file hierarchy.

    :param request: a REST request
    :param pk: the short_id of a resource to modify, from REST URL.

    It is invoked by an AJAX call and returns a json object that has the relative paths of
    the target files or folders to which files have been moved. The AJAX request must be a POST
    request with input data passed in for source_paths and target_path where source_paths
    and target_path are the relative paths for the source and target file or folder in the
    res_id file directory.

    This routine is **specifically** targeted at validating requests from the UI.
    Thus it is much more limiting than a general purpose REST responder.
    """
    pk = request.POST.get('res_id', pk)
    if pk is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)

    # whether to treat request as atomic: skip overwrites for valid request
    atomic = request.POST.get('atomic', 'false') == 'true'  # False by default

    pk = str(pk).strip()
    try:
        resource, _, user = authorize(
            request, pk, needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found',
                            status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied',
                            status=status.HTTP_401_UNAUTHORIZED)

    tgt_path = resolve_request(request).get('target_path', None)
    src_paths = resolve_request(request).get('source_paths', None)
    if src_paths is None or tgt_path is None:
        return HttpResponse(
            'Bad request - src_paths or tgt_path is not included',
            status=status.HTTP_400_BAD_REQUEST)

    tgt_path = str(tgt_path).strip()
    if not tgt_path:
        return HttpResponse('Target directory not specified',
                            status=status.HTTP_400_BAD_REQUEST)

    # protect against common hacking attacks
    if not tgt_path.startswith('data/contents/'):
        return HttpResponse(
            'Target directory path must start with data/contents/',
            status=status.HTTP_400_BAD_REQUEST)
    if tgt_path.find('/../') >= 0 or tgt_path.endswith('/..'):
        return HttpResponse('Bad request - tgt_path cannot contain /../',
                            status=status.HTTP_400_BAD_REQUEST)

    istorage = resource.get_irods_storage()

    # strip trailing slashes (if any)
    tgt_path = tgt_path.rstrip('/')
    tgt_short_path = tgt_path[len('data/contents/'):]
    tgt_storage_path = os.path.join(resource.root_path, tgt_path)

    if not irods_path_is_directory(istorage, tgt_storage_path):
        return HttpResponse('Target of move is not an existing folder',
                            status=status.HTTP_400_BAD_REQUEST)

    src_paths = json.loads(src_paths)

    for i in range(len(src_paths)):
        src_paths[i] = str(src_paths[i]).strip().rstrip('/')

    # protect against common hacking attacks
    for src_path in src_paths:

        if not src_path.startswith('data/contents/'):
            return HttpResponse(
                'Paths to be moved must start with data/contents/',
                status=status.HTTP_400_BAD_REQUEST)

        if src_path.find('/../') >= 0 or src_path.endswith('/..'):
            return HttpResponse('Paths to be moved cannot contain /../',
                                status=status.HTTP_400_BAD_REQUEST)

    valid_src_paths = []
    skipped_tgt_paths = []

    for src_path in src_paths:
        src_storage_path = os.path.join(resource.root_path, src_path)
        src_short_path = src_path[len('data/contents/'):]

        # protect against stale data botches: source files should exist
        try:
            folder, file = ResourceFile.resource_path_is_acceptable(
                resource, src_storage_path, test_exists=True)
        except ValidationError:
            return HttpResponse(
                'Source file {} does not exist'.format(src_short_path),
                status=status.HTTP_400_BAD_REQUEST)

        if not irods_path_is_directory(
                istorage, src_storage_path):  # there is django record
            try:
                ResourceFile.get(resource, file, folder=folder)
            except ResourceFile.DoesNotExist:
                return HttpResponse(
                    'Source file {} does not exist'.format(src_short_path),
                    status=status.HTTP_400_BAD_REQUEST)

        # protect against inadvertent overwrite
        base = os.path.basename(src_storage_path)
        tgt_overwrite = os.path.join(tgt_storage_path, base)
        if not istorage.exists(tgt_overwrite):
            valid_src_paths.append(
                src_path)  # partly qualified path for operation
        else:  # skip pre-existing objects
            skipped_tgt_paths.append(os.path.join(tgt_short_path, base))

    if skipped_tgt_paths:
        if atomic:
            message = 'move would overwrite {}'.format(
                ', '.join(skipped_tgt_paths))
            return HttpResponse(message, status=status.HTTP_400_BAD_REQUEST)

    # if not atomic, then try to move the files that don't have conflicts
    # stop immediately on error.

    try:
        move_to_folder(user, pk, valid_src_paths, tgt_path)
    except SessionException as ex:
        return HttpResponse(ex.stderr,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'target_rel_path': tgt_path}

    if skipped_tgt_paths:  # add information on skipped steps
        message = '[Warn] skipped move to existing {}'.format(
            ', '.join(skipped_tgt_paths))
        return_object['additional_status'] = message

    return HttpResponse(json.dumps(return_object),
                        content_type='application/json')
def data_store_folder_zip(request, res_id=None):
    """
    Zip requested files and folders into a zip file in hydroshareZone or any federated zone
    used for CommonsShare resource backend store. It is invoked by an AJAX call and returns
    json object that holds the created zip file name if it succeeds, and an empty string
    if it fails. The AJAX request must be a POST request with input data passed in for
    res_id, input_coll_path, output_zip_file_name, and remove_original_after_zip where
    input_coll_path is the relative sub-collection path under res_id collection to be zipped,
    output_zip_file_name is the file name only with no path of the generated zip file name,
    and remove_original_after_zip has a value of "true" or "false" (default is "true") indicating
    whether original files will be deleted after zipping.
    """
    res_id = request.POST.get('res_id', res_id)
    if res_id is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    res_id = str(res_id).strip()
    try:
        resource, _, user = authorize(
            request,
            res_id,
            needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found',
                            status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied',
                            status=status.HTTP_401_UNAUTHORIZED)

    input_coll_path = resolve_request(request).get('input_coll_path', None)
    if input_coll_path is None:
        return HttpResponse('Bad request - input_coll_path is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    input_coll_path = str(input_coll_path).strip()
    if not input_coll_path:
        return HttpResponse('Bad request - input_coll_path cannot be empty',
                            status=status.HTTP_400_BAD_REQUEST)

    if not input_coll_path.startswith('data/contents/'):
        return HttpResponse(
            'Bad request - input_coll_path must start with data/contents/',
            status=status.HTTP_400_BAD_REQUEST)

    if input_coll_path.find('/../') >= 0 or input_coll_path.endswith('/..'):
        return HttpResponse(
            'Bad request - input_coll_path must not contain /../',
            status=status.HTTP_400_BAD_REQUEST)

    output_zip_fname = resolve_request(request).get('output_zip_file_name',
                                                    None)
    if output_zip_fname is None:
        return HttpResponse('Bad request - output_zip_fname is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    output_zip_fname = str(output_zip_fname).strip()
    if not output_zip_fname:
        return HttpResponse('Bad request - output_zip_fname cannot be empty',
                            status=status.HTTP_400_BAD_REQUEST)

    if output_zip_fname.find('/') >= 0:
        return HttpResponse('Bad request - output_zip_fname cannot contain /',
                            status=status.HTTP_400_BAD_REQUEST)

    remove_original = resolve_request(request).get('remove_original_after_zip',
                                                   None)
    bool_remove_original = True
    if remove_original:
        remove_original = str(remove_original).strip().lower()
        if remove_original == 'false':
            bool_remove_original = False

    try:
        output_zip_fname, size = \
            zip_folder(user, res_id, input_coll_path, output_zip_fname, bool_remove_original)
    except SessionException as ex:
        return HttpResponse(ex.stderr,
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'name': output_zip_fname, 'size': size, 'type': 'zip'}

    return HttpResponse(json.dumps(return_object),
                        content_type="application/json")
def data_store_rename_file_or_folder(request, pk=None):
    """
    Rename one file or folder in a resource file hierarchy.  It is invoked by an AJAX call

    :param request: a REST request
    :param pk: the short_id of a resource to modify, from REST URL.

    This is invoked by an AJAX call in the UI. It returns a json object that has the
    relative path of the target file or folder that has been renamed. The AJAX request
    must be a POST request with input data for source_path and target_path, where source_path
    and target_path are the relative paths (relative to path res_id/data/contents) for the
    source and target file or folder.

    This routine is **specifically** targeted at validating requests from the UI.
    Thus it is much more limiting than a general purpose REST responder.
    """
    pk = request.POST.get('res_id', pk)
    if pk is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    pk = str(pk).strip()
    try:
        resource, _, user = authorize(request, pk,
                                      needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found', status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied', status=status.HTTP_401_UNAUTHORIZED)

    src_path = resolve_request(request).get('source_path', None)
    tgt_path = resolve_request(request).get('target_path', None)
    try:
        src_path = _validate_path(src_path, 'src_path')
        tgt_path = _validate_path(tgt_path, 'tgt_path')
    except ValidationError as ex:
        return HttpResponse(ex.message, status=status.HTTP_400_BAD_REQUEST)

    src_folder, src_base = os.path.split(src_path)
    tgt_folder, tgt_base = os.path.split(tgt_path)

    if src_folder != tgt_folder:
        return HttpResponse('Rename: Source and target names must be in same folder',
                            status=status.HTTP_400_BAD_REQUEST)

    istorage = resource.get_irods_storage()

    # protect against stale data botches: source files should exist
    src_storage_path = os.path.join(resource.root_path, src_path)
    try:
        folder, base = ResourceFile.resource_path_is_acceptable(resource,
                                                                src_storage_path,
                                                                test_exists=True)
    except ValidationError:
        return HttpResponse('Object to be renamed does not exist',
                            status=status.HTTP_400_BAD_REQUEST)

    if not irods_path_is_directory(istorage, src_storage_path):
        try:  # Django record should exist for each file
            ResourceFile.get(resource, base, folder=folder)
        except ResourceFile.DoesNotExist:
            return HttpResponse('Object to be renamed does not exist',
                                status=status.HTTP_400_BAD_REQUEST)

    # check that the target doesn't exist
    tgt_storage_path = os.path.join(resource.root_path, tgt_path)
    tgt_short_path = tgt_path[len('data/contents/'):]
    if istorage.exists(tgt_storage_path):
        return HttpResponse('Desired name is already in use',
                            status=status.HTTP_400_BAD_REQUEST)
    try:
        folder, base = ResourceFile.resource_path_is_acceptable(resource,
                                                                tgt_storage_path,
                                                                test_exists=False)
    except ValidationError:
        return HttpResponse('Poorly structured desired name {}'
                            .format(tgt_short_path),
                            status=status.HTTP_400_BAD_REQUEST)
    try:
        ResourceFile.get(resource, base, folder=tgt_short_path)
        return HttpResponse('Desired name {} is already in use'
                            .format(tgt_short_path),
                            status=status.HTTP_400_BAD_REQUEST)
    except ResourceFile.DoesNotExist:
        pass  # correct response

    try:
        rename_file_or_folder(user, pk, src_path, tgt_path)
    except SessionException as ex:
        return HttpResponse(ex.stderr, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'target_rel_path': tgt_path}

    return HttpResponse(
        json.dumps(return_object),
        content_type='application/json'
    )
def data_store_move_to_folder(request, pk=None):
    """
    Move a list of files and/or folders to another folder in a resource file hierarchy.

    :param request: a REST request
    :param pk: the short_id of a resource to modify, from REST URL.

    It is invoked by an AJAX call and returns a json object that has the relative paths of
    the target files or folders to which files have been moved. The AJAX request must be a POST
    request with input data passed in for source_paths and target_path where source_paths
    and target_path are the relative paths (relative to path res_id/data/contents) for the source
    and target file or folder in the res_id file directory.

    This routine is **specifically** targeted at validating requests from the UI.
    Thus it is much more limiting than a general purpose REST responder.
    """
    pk = request.POST.get('res_id', pk)
    if pk is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)

    # whether to treat request as atomic: skip overwrites for valid request
    atomic = request.POST.get('atomic', 'false') == 'true'  # False by default

    pk = str(pk).strip()
    try:
        resource, _, user = authorize(request, pk,
                                      needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found', status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied', status=status.HTTP_401_UNAUTHORIZED)

    tgt_path = resolve_request(request).get('target_path', None)
    src_paths = resolve_request(request).get('source_paths', None)

    try:
        tgt_path = _validate_path(tgt_path, 'tgt_path', check_path_empty=False)
    except ValidationError as ex:
        return HttpResponse(ex.message, status=status.HTTP_400_BAD_REQUEST)

    istorage = resource.get_irods_storage()

    tgt_short_path = tgt_path[len('data/contents/'):]
    tgt_storage_path = os.path.join(resource.root_path, tgt_path)

    if not irods_path_is_directory(istorage, tgt_storage_path):
        return HttpResponse('Target of move is not an existing folder',
                            status=status.HTTP_400_BAD_REQUEST)

    src_paths = json.loads(src_paths)

    # protect against common hacking attacks
    for index, src_path in enumerate(src_paths):
        try:
            src_paths[index] = _validate_path(src_path, 'src_paths')
        except ValidationError as ex:
            return HttpResponse(ex.message, status=status.HTTP_400_BAD_REQUEST)

    valid_src_paths = []
    skipped_tgt_paths = []

    for src_path in src_paths:
        src_storage_path = os.path.join(resource.root_path, src_path)
        src_short_path = src_path[len('data/contents/'):]

        # protect against stale data botches: source files should exist
        try:
            folder, file = ResourceFile.resource_path_is_acceptable(resource,
                                                                    src_storage_path,
                                                                    test_exists=True)
        except ValidationError:
            return HttpResponse('Source file {} does not exist'.format(src_short_path),
                                status=status.HTTP_400_BAD_REQUEST)

        if not irods_path_is_directory(istorage, src_storage_path):  # there is django record
            try:
                ResourceFile.get(resource, file, folder=folder)
            except ResourceFile.DoesNotExist:
                return HttpResponse('Source file {} does not exist'.format(src_short_path),
                                    status=status.HTTP_400_BAD_REQUEST)

        # protect against inadvertent overwrite
        base = os.path.basename(src_storage_path)
        tgt_overwrite = os.path.join(tgt_storage_path, base)
        if not istorage.exists(tgt_overwrite):
            valid_src_paths.append(src_path)  # partly qualified path for operation
        else:  # skip pre-existing objects
            skipped_tgt_paths.append(os.path.join(tgt_short_path, base))

    if skipped_tgt_paths:
        if atomic:
            message = 'move would overwrite {}'.format(', '.join(skipped_tgt_paths))
            return HttpResponse(message, status=status.HTTP_400_BAD_REQUEST)

    # if not atomic, then try to move the files that don't have conflicts
    # stop immediately on error.

    try:
        move_to_folder(user, pk, valid_src_paths, tgt_path)
    except SessionException as ex:
        return HttpResponse(ex.stderr, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'target_rel_path': tgt_path}

    if skipped_tgt_paths:  # add information on skipped steps
        message = '[Warn] skipped move to existing {}'.format(', '.join(skipped_tgt_paths))
        return_object['additional_status'] = message

    return HttpResponse(
        json.dumps(return_object),
        content_type='application/json'
    )
def data_store_folder_zip(request, res_id=None):
    """
    Zip requested files and folders into a zip file in hydroshareZone or any federated zone
    used for HydroShare resource backend store. It is invoked by an AJAX call and returns
    json object that holds the created zip file name if it succeeds, and an empty string
    if it fails. The AJAX request must be a POST request with input data passed in for
    res_id, input_coll_path, output_zip_file_name, and remove_original_after_zip where
    input_coll_path is the relative path under res_id/data/contents to be zipped,
    output_zip_file_name is the file name only with no path of the generated zip file name,
    and remove_original_after_zip has a value of "true" or "false" (default is "true") indicating
    whether original files will be deleted after zipping.
    """
    res_id = request.POST.get('res_id', res_id)
    if res_id is None:
        return HttpResponse('Bad request - resource id is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    res_id = str(res_id).strip()
    try:
        resource, _, user = authorize(request, res_id,
                                      needed_permission=ACTION_TO_AUTHORIZE.EDIT_RESOURCE)
    except NotFound:
        return HttpResponse('Bad request - resource not found', status=status.HTTP_400_BAD_REQUEST)
    except PermissionDenied:
        return HttpResponse('Permission denied', status=status.HTTP_401_UNAUTHORIZED)

    input_coll_path = resolve_request(request).get('input_coll_path', None)

    try:
        input_coll_path = _validate_path(input_coll_path, 'input_coll_path')
    except ValidationError as ex:
        return HttpResponse(ex.message, status=status.HTTP_400_BAD_REQUEST)

    output_zip_fname = resolve_request(request).get('output_zip_file_name', None)
    if output_zip_fname is None:
        return HttpResponse('Bad request - output_zip_fname is not included',
                            status=status.HTTP_400_BAD_REQUEST)
    output_zip_fname = str(output_zip_fname).strip()
    if not output_zip_fname:
        return HttpResponse('Bad request - output_zip_fname cannot be empty',
                            status=status.HTTP_400_BAD_REQUEST)

    if output_zip_fname.find('/') >= 0:
        return HttpResponse('Bad request - output_zip_fname cannot contain /',
                            status=status.HTTP_400_BAD_REQUEST)

    remove_original = resolve_request(request).get('remove_original_after_zip', None)
    bool_remove_original = True
    if remove_original:
        remove_original = str(remove_original).strip().lower()
        if remove_original == 'false':
            bool_remove_original = False

    try:
        output_zip_fname, size = \
            zip_folder(user, res_id, input_coll_path, output_zip_fname, bool_remove_original)
    except SessionException as ex:
        return HttpResponse(ex.stderr, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    except DRF_ValidationError as ex:
        return HttpResponse(ex.detail, status=status.HTTP_400_BAD_REQUEST)

    return_object = {'name': output_zip_fname,
                     'size': size,
                     'type': 'zip'}

    return HttpResponse(
        json.dumps(return_object),
        content_type="application/json"
    )