diff --git a/app/assets/javascripts/projects/index.js b/app/assets/javascripts/projects/index.js index 28d3388b6..5452a34c1 100644 --- a/app/assets/javascripts/projects/index.js +++ b/app/assets/javascripts/projects/index.js @@ -407,6 +407,7 @@ initManageUsersModal(); initExportProjectsModal(); initExportProjects(); + $('.new-project-folder-btn').initSubmitModal('#new-project-folder-modal', 'project_folder'); initEditProjectButton($('.panel-project')); initArchiveRestoreButton($('.panel-project')); diff --git a/app/assets/javascripts/repositories/edit.js b/app/assets/javascripts/repositories/edit.js index 27dd8542f..e5fe3e4b1 100644 --- a/app/assets/javascripts/repositories/edit.js +++ b/app/assets/javascripts/repositories/edit.js @@ -1,8 +1,8 @@ (function() { 'use strict'; - $('.delete-repo-option').initializeModal('#delete-repo-modal'); - $('.rename-repo-option').initializeModal('#rename-repo-modal'); - $('.share-repo-option').initializeModal('.share-repo-modal'); - $('.copy-repo-option').initializeModal('#copy-repo-modal'); + $('.delete-repo-option').initSubmitModal('#delete-repo-modal', 'repository'); + $('.rename-repo-option').initSubmitModal('#rename-repo-modal', 'repository'); + $('.share-repo-option').initSubmitModal('.share-repo-modal', 'repository'); + $('.copy-repo-option').initSubmitModal('#copy-repo-modal', 'repository'); })(); diff --git a/app/assets/javascripts/repositories/index.js b/app/assets/javascripts/repositories/index.js index a9465e6b4..65615cd2b 100644 --- a/app/assets/javascripts/repositories/index.js +++ b/app/assets/javascripts/repositories/index.js @@ -100,10 +100,10 @@ DataTableHelpers.initSearchField(dataTableWrapper, I18n.t('repositories.index.filter_inventory')); $('.content-body .toolbar').html($('#repositoriesListButtons').html()); dataTableWrapper.find('.main-actions, .pagination-row').removeClass('hidden'); - $('#createRepoBtn').initializeModal('#create-repo-modal'); - $('#deleteRepoBtn').initializeModal('#delete-repo-modal'); - $('#renameRepoBtn').initializeModal('#rename-repo-modal'); - $('#copyRepoBtn').initializeModal('#copy-repo-modal'); + $('#createRepoBtn').initSubmitModal('#create-repo-modal', 'repository'); + $('#deleteRepoBtn').initSubmitModal('#delete-repo-modal', 'repository'); + $('#renameRepoBtn').initSubmitModal('#rename-repo-modal', 'repository'); + $('#copyRepoBtn').initSubmitModal('#copy-repo-modal', 'repository'); }, drawCallback: function() { if (CHECKBOX_SELECTOR) CHECKBOX_SELECTOR.checkSelectAllStatus(); @@ -128,7 +128,7 @@ archived: $('.repositories-index').hasClass('archived') }, function(data) { slidePanel.html(data.html); - $('.create-new-repository').initializeModal('#create-repo-modal'); + $('.create-new-repository').initSubmitModal('#create-repo-modal', 'repository'); }); } @@ -187,7 +187,7 @@ } }); - $('.create-new-repository').initializeModal('#create-repo-modal'); + $('.create-new-repository').initSubmitModal('#create-repo-modal', 'repository'); if (notTurbolinksPreview()) { initRepositoriesDataTable('#repositoriesList', $('.repositories-index').hasClass('archived')); } diff --git a/app/assets/javascripts/repositories/show.js b/app/assets/javascripts/repositories/show.js index 092ae77df..76ac28dd1 100644 --- a/app/assets/javascripts/repositories/show.js +++ b/app/assets/javascripts/repositories/show.js @@ -172,7 +172,7 @@ initShareModal(); }); - $('.create-new-repository').initializeModal('#create-repo-modal'); + $('.create-new-repository').initSubmitModal('#create-repo-modal', 'repository'); function initArchivingActionsInDropdown() { $('.archive-repository-option').on('click', function(event) { diff --git a/app/assets/javascripts/sitewide/utils.js b/app/assets/javascripts/sitewide/utils.js index c74607e38..5d0c142be 100644 --- a/app/assets/javascripts/sitewide/utils.js +++ b/app/assets/javascripts/sitewide/utils.js @@ -88,10 +88,11 @@ $.fn.checkboxTreeLogic = function(dependencies, checkAll) { * submit gets JSON response, displays errors if any or either refreshes the * page or redirects it (if 'url' parameter is specified in JSON response). * @param {string} modalID Modal ID + * @param {string} modelName Modal Name * @param {object} $fn Link objects for opening the modal (can have more * links for same modal) */ -$.fn.initializeModal = function(modalID) { +$.fn.initSubmitModal = function(modalID, modelName) { /** * Popup modal validator * @param {object} $modal Modal object @@ -107,7 +108,7 @@ $.fn.initializeModal = function(modalID) { } }) .on('ajax:error', function(e, data) { - $(this).renderFormErrors('repository', data.responseJSON); + $(this).renderFormErrors(modelName, data.responseJSON); }) .animateSpinner(true); } diff --git a/app/controllers/project_folders_controller.rb b/app/controllers/project_folders_controller.rb new file mode 100644 index 000000000..c180f7be2 --- /dev/null +++ b/app/controllers/project_folders_controller.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +class ProjectFoldersController < ApplicationController + before_action :load_current_folder, only: %i(new) + before_action :check_create_permissions, only: %i(new create) + + def new + @project_folder = ProjectFolder.new + respond_to do |format| + format.json do + render json: { + html: render_to_string( + partial: 'projects/index/modals/new_project_folder.html.erb' + ) + } + end + end + end + + def create + @project_folder = ProjectFolder.new(team: current_team) + @project_folder.assign_attributes(project_folders_params) + + respond_to do |format| + format.json do + if @project_folder.save + # log_activity() + flash[:success] = t('projects.index.modal_new_project_folder.success_flash', + name: @project_folder.name) + render json: { url: project_folder_path(@project_folder) }, + status: :ok + else + render json: @project_folder.errors, + status: :unprocessable_entity + end + end + end + end + + private + + def load_current_folder + if current_team && params[:project_folder_id].present? + @current_folder = current_team.project_folders.find_by(id: params[:project_folder_id]) + end + end + + def project_folders_params + params.require(:project_folder).permit(:name, :parent_folder_id) + end + + def check_create_permissions + render_403 unless can_create_project_folders?(current_team) + end +end diff --git a/app/permissions/team.rb b/app/permissions/team.rb index 34114c665..ce1d2fbe8 100644 --- a/app/permissions/team.rb +++ b/app/permissions/team.rb @@ -21,6 +21,11 @@ Canaid::Permissions.register_for(Team) do true end + # project_folder: create + can :create_project_folders do |user, team| + user.is_admin_of_team?(team) + end + # project: create can :create_projects do |user, team| user.is_normal_user_or_admin_of_team?(team) diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index 277a318dd..03f9c46fa 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -8,8 +8,8 @@ <%= render partial: "shared/secondary_navigation" %> <span style="display: none;" data-hook="projects-index-html"></span> - <%= render partial: 'projects/index/modals/new' %> - <%= render partial: 'projects/index/modals/edit' %> + <%= render partial: 'projects/index/modals/new_project' %> + <%= render partial: 'projects/index/modals/edit_project' %> <%= render partial: 'projects/index/modals/manage_users' %> <%= render partial: 'projects/index/modals/export_projects' %> diff --git a/app/views/projects/index/_toolbar.html.erb b/app/views/projects/index/_toolbar.html.erb index 45639b2e1..0cf8c9740 100644 --- a/app/views/projects/index/_toolbar.html.erb +++ b/app/views/projects/index/_toolbar.html.erb @@ -5,14 +5,18 @@ <div class="form-group"> <div class="new-project-actions pull-left"> - <% if current_team && can_create_projects?(current_team) %> <!-- new project button --> + <% if current_team && can_create_projects?(current_team) %> <a href="#" class="btn btn-primary new-project-btn"> <span class="fas fa-plus" aria-hidden="true"></span> <span class="hidden-xs"><%= t('projects.index.new') %></span> </a> - <!-- new project folder button --> - <a href="#" class="btn btn-secondary new-project-folder-btn"> + <% end %> + <!-- new project folder button --> + <% if current_team && can_create_project_folders?(current_team) %> + <a href="<%= new_project_folder_path(project_folder_id: @current_folder) %>" + data-remote="true" + class="btn btn-secondary new-project-folder-btn"> <span class="fas fa-folder" aria-hidden="true"></span> <span class="hidden-xs"><%= t('projects.index.new_folder') %></span> </a> diff --git a/app/views/projects/index/modals/_edit.html.erb b/app/views/projects/index/modals/_edit_project.html.erb similarity index 100% rename from app/views/projects/index/modals/_edit.html.erb rename to app/views/projects/index/modals/_edit_project.html.erb diff --git a/app/views/projects/index/modals/_new.html.erb b/app/views/projects/index/modals/_new_project.html.erb similarity index 100% rename from app/views/projects/index/modals/_new.html.erb rename to app/views/projects/index/modals/_new_project.html.erb diff --git a/app/views/projects/index/modals/_new_project_folder.html.erb b/app/views/projects/index/modals/_new_project_folder.html.erb new file mode 100644 index 000000000..92b0bdcca --- /dev/null +++ b/app/views/projects/index/modals/_new_project_folder.html.erb @@ -0,0 +1,28 @@ +<div class="modal" id="new-project-folder-modal" tabindex="-1" role="dialog" aria-labelledby="new-project-folder-modal-label"> + <%= bootstrap_form_for [@project_folder], remote: true do |f| %> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> + <h4 class="modal-title" id="new-project-folder-modal-label"> + <%= t("projects.index.modal_new_project_folder.modal_title") %> + </h4> + </div> + <div class="modal-body"> + <p><%= t("projects.index.modal_new_project_folder.description") %></p> + <div class="form-group sci-input-container"> + <%= hidden_field :project_folder, :parent_folder_id, value: @current_folder.id if @current_folder %> + <%= f.text_field :name, + autofocus: true, + label: t("projects.index.modal_new_project_folder.name"), + placeholder: t("projects.index.modal_new_project_folder.name_placeholder") %> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal"><%=t "general.cancel" %></button> + <%= f.submit t("projects.index.modal_new_project_folder.create"), class: "btn btn-primary" %> + </div> + </div> + </div> + <% end %> +</div> diff --git a/config/locales/en.yml b/config/locales/en.yml index 70e6637c8..f5c2c1945 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -342,6 +342,13 @@ en: name: "Name" folder: description: "%{projects_count} projects | %{folders_count} folders" + modal_new_project_folder: + description: "Give your folder a distinct name to recognize it faster." + modal_title: "Create folder" + name: "New folder name" + name_placeholder: "Name your folder" + create: "Create folder" + success_flash: "Project folder <strong>%{name}</strong> successfully created." modal_new_project: modal_title: "Create new project" create: "Create" diff --git a/config/routes.rb b/config/routes.rb index d5162165e..2d341caa9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -302,10 +302,10 @@ Rails.application.routes.draw do end end - resources :project_folders, only: [] do - get '/', to: 'projects#index' + resources :project_folders, only: %i(new create) do get 'cards', to: 'projects#cards' end + get 'project_folders/:project_folder_id', to: 'projects#index', as: :project_folder resources :experiments do member do