mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-09-06 13:14:29 +08:00
Merge pull request #649 from mz3944/mz-SCI-1269
Global repositories screen - rename, delete (just core)
This commit is contained in:
commit
876fcfc979
13 changed files with 296 additions and 15 deletions
|
@ -90,8 +90,7 @@ function animateLoading(start){
|
|||
* Optional parameter options for spin.js options.
|
||||
*/
|
||||
function animateSpinner(el, start, options) {
|
||||
// If overlaying the whole page,
|
||||
// put the spinner in the middle of the page
|
||||
// If overlaying the whole page, put the spinner in the middle of the page
|
||||
var overlayPage = false;
|
||||
if (_.isUndefined(el) || el === null) {
|
||||
overlayPage = true;
|
||||
|
@ -123,6 +122,30 @@ function animateSpinner(el, start, options) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatic handling of show/hide spinner.
|
||||
* @param {boolean} redirection Whether page is refreshed/redirected on success
|
||||
* @param {boolean} onElement Whether spinner is fixed on the center of fn
|
||||
* element or it's positions on the center of whole page
|
||||
*/
|
||||
$.fn.animateSpinner = function(redirection, onElement) {
|
||||
redirection = _.isUndefined(redirection) ? false : redirection;
|
||||
onElement = _.isUndefined(onElement) ? false : onElement;
|
||||
|
||||
$(this)
|
||||
.on('ajax:beforeSend', function() {
|
||||
onElement ? animateSpinner($(this)) : animateSpinner();
|
||||
})
|
||||
.on('ajax:error', function(e, data) {
|
||||
animateSpinner(null, false);
|
||||
})
|
||||
.on('ajax:success', function(e, data) {
|
||||
if (!redirection) {
|
||||
animateSpinner(null, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents user from accidentally leaving page when server is busy
|
||||
* and notifies him with a message.
|
||||
|
|
6
app/assets/javascripts/repositories/index.js
Normal file
6
app/assets/javascripts/repositories/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
$('.delete-repo-option').initializeModal('#delete-repo-modal');
|
||||
$('.rename-repo-option').initializeModal('#rename-repo-modal');
|
||||
})();
|
|
@ -60,6 +60,7 @@ var renderFormError = function(ev, input, errMsgs, clearErr, errAttributes) {
|
|||
// Don't submit form
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
ev.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -67,11 +68,11 @@ var renderFormError = function(ev, input, errMsgs, clearErr, errAttributes) {
|
|||
* Render errors specified in JSON format for many form elements.
|
||||
*/
|
||||
$.fn.renderFormErrors = function(modelName, errors, clear, ev) {
|
||||
clear = ((typeof clear) === 'undefined') ? true : clear;
|
||||
if (clear || _.isUndefined(clear)) {
|
||||
clear = _.isUndefined(clear) ? true : clear;
|
||||
if (clear) {
|
||||
this.clearFormErrors();
|
||||
}
|
||||
var form = $(this);
|
||||
var $form = $(this);
|
||||
$.each(errors, function(field, messages) {
|
||||
// Special exception for file uploads in steps and results
|
||||
if (field === 'assets.file') {
|
||||
|
@ -80,7 +81,7 @@ $.fn.renderFormErrors = function(modelName, errors, clear, ev) {
|
|||
field = 'asset_attribute';
|
||||
}
|
||||
var types = 'input, file, select, textarea';
|
||||
var $input = $(_.filter(form.find(types), function(el) {
|
||||
var $input = $(_.filter($form.find(types), function(el) {
|
||||
var name = $(el).attr('name');
|
||||
if (name) {
|
||||
return name.match(new RegExp(modelName + '\\[' + field + '\\(?'));
|
||||
|
|
|
@ -239,3 +239,57 @@ $.fn.checkboxTreeLogic = function(dependencies, checkAll) {
|
|||
});
|
||||
}).trigger('change');
|
||||
};
|
||||
|
||||
/**
|
||||
* Show modal on link click and handle its' submition and validation.
|
||||
*
|
||||
* On link click it gets HTTP reponse with modal partial, shows it, and then on
|
||||
* 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 {object} $fn Link objects for opening the modal (can have more
|
||||
* links for same modal)
|
||||
*/
|
||||
$.fn.initializeModal = function(modalID) {
|
||||
/**
|
||||
* Popup modal validator
|
||||
* @param {object} $modal Modal object
|
||||
*/
|
||||
function modalResponse($modal) {
|
||||
var $modalForm = $modal.find('form');
|
||||
$modalForm
|
||||
.on('ajax:success', function(ev, data) {
|
||||
if (_.isUndefined(data)) {
|
||||
location.reload();
|
||||
} else {
|
||||
$(location).attr('href', data.url);
|
||||
}
|
||||
})
|
||||
.on('ajax:error', function(e, data) {
|
||||
$(this).renderFormErrors('repository', data.responseJSON);
|
||||
})
|
||||
.animateSpinner(true);
|
||||
}
|
||||
|
||||
var $linksToModal = $(this);
|
||||
$linksToModal
|
||||
.on('ajax:success', function(e, data) {
|
||||
// Add and show modal
|
||||
$('body').append($.parseHTML(data.html));
|
||||
$(modalID).modal('show', {
|
||||
backdrop: true,
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
modalResponse($(modalID));
|
||||
|
||||
// Remove modal when it gets closed
|
||||
$(modalID).on('hidden.bs.modal', function() {
|
||||
$(modalID).remove();
|
||||
});
|
||||
})
|
||||
.on('ajax:error', function() {
|
||||
// TODO
|
||||
})
|
||||
.animateSpinner();
|
||||
};
|
||||
|
|
|
@ -1,11 +1,67 @@
|
|||
class RepositoriesController < ApplicationController
|
||||
before_action :load_vars
|
||||
before_action :check_view_all_permissions, only: :index
|
||||
before_action :check_edit_and_destroy_permissions, only:
|
||||
%(destroy destroy_modal rename_modal update)
|
||||
|
||||
def index
|
||||
render('repositories/index')
|
||||
end
|
||||
|
||||
def destroy_modal
|
||||
@repository = Repository.find(params[:repository_id])
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'delete_repository_modal.html.erb'
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@repository = Repository.find(params[:id])
|
||||
flash[:success] = t('repositories.index.delete_flash',
|
||||
name: @repository.name)
|
||||
@repository.destroy
|
||||
redirect_to team_repositories_path
|
||||
end
|
||||
|
||||
def rename_modal
|
||||
@repository = Repository.find(params[:repository_id])
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: render_to_string(
|
||||
partial: 'rename_repository_modal.html.erb'
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@repository = Repository.find(params[:id])
|
||||
old_name = @repository.name
|
||||
@repository.update_attributes(repository_params)
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
if @repository.save
|
||||
flash[:success] = t('repositories.index.rename_flash',
|
||||
old_name: old_name, new_name: @repository.name)
|
||||
render json: {
|
||||
url: team_repositories_path(repository: @repository)
|
||||
}, status: :ok
|
||||
else
|
||||
render json: @repository.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_vars
|
||||
|
@ -17,4 +73,12 @@ class RepositoriesController < ApplicationController
|
|||
def check_view_all_permissions
|
||||
render_403 unless can_view_team_repositories(@team)
|
||||
end
|
||||
|
||||
def check_edit_and_destroy_permissions
|
||||
render_403 unless can_edit_and_destroy_repository(@repository)
|
||||
end
|
||||
|
||||
def repository_params
|
||||
params.require(:repository).permit(:name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1060,4 +1060,8 @@ module PermissionHelper
|
|||
def can_view_repository(repository)
|
||||
is_normal_user_or_admin_of_team(repository.team)
|
||||
end
|
||||
|
||||
def can_edit_and_destroy_repository(repository)
|
||||
is_admin_of_team(repository.team)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class Repository < ActiveRecord::Base
|
|||
auto_strip_attributes :name, nullify: false
|
||||
validates :name,
|
||||
presence: true,
|
||||
uniqueness: { scope: :team },
|
||||
uniqueness: { scope: :team, case_sensitive: false },
|
||||
length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
validates :team, presence: true
|
||||
validates :created_by, presence: true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<ol class="breadcrumb breadcrumb-protocols-manager">
|
||||
<li><%= link_to current_team.name, projects_path(team: current_team) %></li>
|
||||
<li class="active"><%= t("repositories.nav.breadcrumbs.repositories") %></li>
|
||||
</ol>
|
||||
</ol>
|
||||
|
|
33
app/views/repositories/_delete_repository_modal.html.erb
Normal file
33
app/views/repositories/_delete_repository_modal.html.erb
Normal file
|
@ -0,0 +1,33 @@
|
|||
<div class="modal fade" id="delete-repo-modal" tabindex="-1" role="dialog">
|
||||
<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"><%= t("repositories.index.modal_delete.title_html", name: @repository.name) %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><%= t("repositories.index.modal_delete.message_html", name: @repository.name) %></p>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
|
||||
<%= t("repositories.index.modal_delete.alert_heading") %>
|
||||
<ul>
|
||||
<li><%= t("repositories.index.modal_delete.alert_line_1") %></li>
|
||||
<li><%= t("repositories.index.modal_delete.alert_line_2") %></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%= link_to t('repositories.index.modal_delete.delete'),
|
||||
team_repository_path(id: @repository),
|
||||
id: "confirm-repo-delete",
|
||||
method: :delete,
|
||||
type: 'button',
|
||||
class: 'btn btn-primary' %>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%= t("general.cancel")%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
32
app/views/repositories/_rename_repository_modal.html.erb
Normal file
32
app/views/repositories/_rename_repository_modal.html.erb
Normal file
|
@ -0,0 +1,32 @@
|
|||
<div class="modal fade" id="rename-repo-modal" tabindex="-1" role="dialog">
|
||||
<%= bootstrap_form_for @repository,
|
||||
url: team_repository_path(id: @repository, format: :json),
|
||||
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"><%= t("repositories.index.modal_rename.title_html", name: @repository.name ) %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||
<div class="form-group">
|
||||
<%= f.text_field :name,
|
||||
label: t("repositories.index.modal_rename.name"),
|
||||
autofocus: true,
|
||||
placeholder: t("repositories.index.modal_rename.name_placeholder") %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%= f.submit t("repositories.index.modal_rename.rename"), class: "btn btn-primary" %>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><%=t "general.cancel" %></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -1,13 +1,17 @@
|
|||
<% provide(:head_title, t("repositories.index.head_title")) %>
|
||||
|
||||
<% if current_team %>
|
||||
<%= render partial: "repositories/breadcrumbs.html.erb", locals: { teams: @teams, current_team: current_team, type: @type } %>
|
||||
<%= render partial: "repositories/breadcrumbs.html.erb",
|
||||
locals: { teams: @teams, current_team: current_team, type: @type } %>
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<% if @repositories.present? %>
|
||||
<% active_repo = @repositories.find_by_id(params[:repository]) %>
|
||||
<% active_repo = @repositories.first if !active_repo %>
|
||||
|
||||
<ul class="nav nav-tabs nav-settings" role="tablist" id="tabs">
|
||||
<% @repositories.each.with_index do |repo, i| %>
|
||||
<li role="presentation" class="<%= 'active' if i == 0 %>">
|
||||
<% @repositories.each do |repo| %>
|
||||
<li role="presentation" class="<%= 'active' if repo == active_repo %>">
|
||||
<a class="repository-nav-tab"
|
||||
href="#custom_repo_<%= repo.id %>"
|
||||
data-toggle="tab"
|
||||
|
@ -18,9 +22,44 @@
|
|||
</ul>
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
<% @repositories.each.with_index do |repo, i| %>
|
||||
<div class="tab-pane tab-pane-settings <%= 'active' if i == 0 %>" id="custom_repo_<%= repo.id %>">
|
||||
<% @repositories.each do |repo| %>
|
||||
<div class="tab-pane tab-pane-settings <%= 'active' if repo == active_repo %>" id="custom_repo_<%= repo.id %>">
|
||||
<!-- Tab Content -->
|
||||
|
||||
<div id="repository-toolbar">
|
||||
<div class="dropdown text-right">
|
||||
<div class="btn btn-default btn-xs"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
<%= "disabled='disabled'" if !can_edit_and_destroy_repository repo %>>
|
||||
<span class="glyphicon glyphicon-cog"></span>
|
||||
<span class="caret"></span>
|
||||
</div>
|
||||
<% if can_edit_and_destroy_repository repo %>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li class="dropdown-header">
|
||||
<%= t("repositories.index.options_dropdown.header") %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to t('repositories.index.options_dropdown.rename'),
|
||||
team_repository_rename_modal_path(repository_id: repo),
|
||||
class: "rename-repo-option",
|
||||
remote: true %>
|
||||
</li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<%= link_to t('repositories.index.modal_delete.delete'),
|
||||
team_repository_destroy_modal_path(repository_id: repo),
|
||||
class: "delete-repo-option",
|
||||
remote: true %>
|
||||
</li>
|
||||
</ul>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
@ -35,3 +74,5 @@
|
|||
<p><%=t 'repositories.index.no_teams.text' %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= javascript_include_tag "repositories/index", "data-turbolinks-track" => true %>
|
||||
|
|
|
@ -153,9 +153,27 @@ en:
|
|||
head_title: "Repositories"
|
||||
title: "Repositories"
|
||||
no_repositories: "No repositories"
|
||||
delete_flash: "\"%{name}\" repository was successfully deleted!"
|
||||
rename_flash: "\"%{old_name}\" repository was successfully renamed to \"%{new_name}\"!"
|
||||
no_teams:
|
||||
title: "Your dashboard is empty!"
|
||||
text: "It seems you're not a member of any team. See team management to sort it out."
|
||||
options_dropdown:
|
||||
header: "Edit repository"
|
||||
rename: "Rename"
|
||||
delete: "Delete"
|
||||
modal_delete:
|
||||
title_html: "Delete repository <em>%{name}</em>"
|
||||
message_html: "Are you sure you want to delete repository <em>%{name}</em>? This action is irreversible."
|
||||
alert_heading: "Deleting repository has following consequences:"
|
||||
alert_line_1: "all data inside the repository will be lost;"
|
||||
alert_line_2: "all references to repository items will be rendered as invalid."
|
||||
delete: "Delete repository"
|
||||
modal_rename:
|
||||
title_html: "Rename repository <em>%{name}</em>"
|
||||
name: "New repository name"
|
||||
name_placeholder: "My repository"
|
||||
rename: "Rename repository"
|
||||
nav:
|
||||
breadcrumbs:
|
||||
repositories: "Repositories"
|
||||
|
@ -895,7 +913,7 @@ en:
|
|||
modal_delete_custom_field:
|
||||
title: "Delete a column"
|
||||
message: "Are you sure you wish to permanently delete selected column %{cf}? This action is irreversible."
|
||||
alert_heading: "Deleting a column has following consequences:"
|
||||
alert_heading: "Deleting column has following consequences:"
|
||||
alert_line_1: "you will lose information in this column for %{nr} samples;"
|
||||
alert_line_2: "the column will be deleted for all team members."
|
||||
delete: "Delete column"
|
||||
|
|
|
@ -123,7 +123,12 @@ Rails.application.routes.draw do
|
|||
as: 'file_expired'
|
||||
|
||||
resources :teams do
|
||||
resources :repositories, only: [:index]
|
||||
resources :repositories, only: %i(index destroy update) do
|
||||
get 'destroy_modal', to: 'repositories#destroy_modal',
|
||||
defaults: { format: 'json' }
|
||||
get 'rename_modal', to: 'repositories#rename_modal',
|
||||
defaults: { format: 'json' }
|
||||
end
|
||||
resources :samples, only: [:new, :create]
|
||||
resources :sample_types, except: [:show, :new] do
|
||||
get 'sample_type_element', to: 'sample_types#sample_type_element'
|
||||
|
|
Loading…
Add table
Reference in a new issue