diff --git a/app/assets/javascripts/projects/index.js b/app/assets/javascripts/projects/index.js index 722074c03..44b67d2cd 100644 --- a/app/assets/javascripts/projects/index.js +++ b/app/assets/javascripts/projects/index.js @@ -7,10 +7,10 @@ // - refresh project users tab after manage user modal is closed // - refactor view handling using library, ex. backbone.js -/* global animateSpinner HelperModule dropdownSelector Sidebar Turbolinks filterDropdown */ +/* global HelperModule dropdownSelector Sidebar Turbolinks filterDropdown */ (function() { - const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable']; + const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable', 'deletable']; var projectsWrapper = '#projectsWrapper'; var toolbarWrapper = '#toolbarWrapper'; var cardsWrapper = '#cardsWrapper'; @@ -97,6 +97,42 @@ }); } + // init delete project folders + function initDeleteFoldersToolbarButton() { + $(projectsWrapper) + .on('ajax:before', '.delete-folders-btn', function() { + let buttonForm = $(this); + buttonForm.find('input[name="project_folders_ids[]"]').remove(); + selectedProjectFolders.forEach(function(id) { + $('').attr({ + type: 'hidden', + name: 'project_folders_ids[]', + value: id + }).appendTo(buttonForm); + }); + }) + .on('ajax:success', '.delete-folders-btn', function(ev, data) { + // Add and show modal + let deleteModal = $(data.html); + $(projectsWrapper).append(deleteModal); + deleteModal.modal('show'); + // Remove modal when it gets closed + deleteModal.on('hidden.bs.modal', function() { + $(this).remove(); + }); + }); + + $(projectsWrapper) + .on('ajax:success', '.delete-folders-form', function(ev, data) { + $('.modal-project-folder-delete').modal('hide'); + HelperModule.flashAlertMsg(data.message, 'success'); + refreshCurrentView(); + }) + .on('ajax:error', '.delete-folders-form', function(ev, data) { + HelperModule.flashAlertMsg(data.responseJSON.message, 'danger'); + }); + } + // init project toolbar archive/restore functions function initArchiveRestoreToolbarButtons() { $(projectsWrapper) @@ -322,6 +358,8 @@ projectsToolbar.find('.single-object-action, .multiple-object-action').removeClass('hidden'); if (selectedProjectFolders.length === 1) { projectsToolbar.find('.project-only-action').addClass('hidden'); + } else { + projectsToolbar.find('.folders-only-action').addClass('hidden'); } } else { projectsToolbar.find('.single-object-action').addClass('hidden'); @@ -329,6 +367,9 @@ if (selectedProjectFolders.length > 0) { projectsToolbar.find('.project-only-action').addClass('hidden'); } + if (selectedProjects.length > 0) { + projectsToolbar.find('.folder-only-action').addClass('hidden'); + } } PERMISSIONS.forEach((permission) => { if (!checkActionPermission(permission)) { @@ -637,6 +678,7 @@ initManageUsersModal(); initExportProjectsModal(); initExportProjects(); + initDeleteFoldersToolbarButton(); initArchiveRestoreToolbarButtons(); initViewProjectUsersLink(); initManageProjectUsersLink(); diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss index 075926096..55c573893 100644 --- a/app/assets/stylesheets/projects.scss +++ b/app/assets/stylesheets/projects.scss @@ -475,6 +475,11 @@ li.module-hover { } } + .delete-folders-form, + .delete-folders-btn { + display: inline-block; + } + .filter-container { .projects-filters { .select-block { diff --git a/app/controllers/project_folders_controller.rb b/app/controllers/project_folders_controller.rb index 3c75b56d1..a8712bb92 100644 --- a/app/controllers/project_folders_controller.rb +++ b/app/controllers/project_folders_controller.rb @@ -96,6 +96,35 @@ class ProjectFoldersController < ApplicationController end end + def destroy_modal + render json: { + html: render_to_string(partial: 'projects/index/modals/project_folder_delete.html.erb', + locals: { project_folders_ids: params[:project_folders_ids] }) + } + end + + def destroy + project_folders = current_team.project_folders.where(id: params[:project_folders_ids]) + counter = 0 + project_folders.each do |folder| + next if folder.projects.exists? || folder.project_folders.exists? || !can_update_team?(current_team) + + folder.transaction do + log_activity(:delete_project_folder, folder, project_folder: folder.id) + folder.destroy! + counter += 1 + rescue StandardError => e + Rails.logger.error e.message + raise ActiveRecord::Rollback + end + end + if counter.positive? + render json: { message: t('projects.delete_folders.success_flash', number: counter) } + else + render json: { message: t('projects.delete_folders.error_flash') }, status: :unprocessable_entity + end + end + private def load_project_folder diff --git a/app/permissions/project.rb b/app/permissions/project.rb index 92347ff64..b72e7eed5 100644 --- a/app/permissions/project.rb +++ b/app/permissions/project.rb @@ -97,3 +97,10 @@ Canaid::Permissions.register_for(ProjectComment) do user.is_owner_of_project?(project_comment.project)) end end + +Canaid::Permissions.register_for(ProjectFolder) do + # ProjectFolder: delete + can :delete_project_folder do |_, project_folder| + !project_folder.projects.exists? && !project_folder.project_folders.exists? + end +end diff --git a/app/views/projects/index/_folder_card.html.erb b/app/views/projects/index/_folder_card.html.erb index daa2263a2..4cc977bbb 100644 --- a/app/views/projects/index/_folder_card.html.erb +++ b/app/views/projects/index/_folder_card.html.erb @@ -4,7 +4,8 @@ data-editable="<%= can_update_team?(current_team) %>" data-moveable="<%= can_update_team?(current_team) %>" data-archivable="false" - data-restorable="false"> + data-restorable="false" + data-deletable="<%= can_delete_project_folder?(folder) && can_update_team?(current_team) %>">
diff --git a/app/views/projects/index/_toolbar.html.erb b/app/views/projects/index/_toolbar.html.erb index c6b01b94d..b6b5f37b3 100644 --- a/app/views/projects/index/_toolbar.html.erb +++ b/app/views/projects/index/_toolbar.html.erb @@ -46,6 +46,15 @@ <% end %> + <%= button_to destroy_modal_project_folders_url, + class: 'btn btn-light multiple-object-action folders-only-action hidden', + form_class: 'delete-folders-btn', + data: { for: :deletable }, + remote: true, + method: :post do %> + + + <% end %>