Add export project modal

Closes [SCI-2645]
This commit is contained in:
Jure Grabnar 2018-10-11 22:55:35 +02:00
parent b9bb0db62d
commit 26df1e5a98
12 changed files with 204 additions and 31 deletions

View file

@ -27,6 +27,10 @@
var projectActionsModalBody = null;
var projectActionsModalFooter = null;
var exportProjectsModal = null;
var exportProjectsModalHeader = null;
var exportProjectsModalBody = null;
var projectsViewMode = 'cards';
var projectsViewFilter = $('.projects-view-filter.active').data('filter');
var projectsViewFilterChanged = false;
@ -210,6 +214,44 @@
});
}
/**
* Initialize the JS for export projects modal to work.
*/
function initExportProjectsModal() {
$exportProjectsBtn = $('#export-projects-button')
$exportProjectsBtn.click(function() {
// Load HTML to refresh users list
$.ajax({
url: $exportProjectsBtn.data('export-projects-url'),
type: 'POST',
dataType: 'json',
data: {
project_ids: selectedProjects
},
success: function(data) {
// Update modal title
exportProjectsModalHeader.html(data.title);
// Set modal body
exportProjectsModalBody.html(data.html);
// Show the modal
exportProjectsModal.modal('show');
},
error: function() {
// TODO
}
});
});
// Remove modal content when modal window is closed.
exportProjectsModal.on('hidden.bs.modal', function() {
exportProjectsModalHeader.html('');
exportProjectsModalBody.html('');
});
}
// Initialize reloading manage user modal content after posting new
// user.
@ -295,10 +337,15 @@
projectActionsModalBody = projectActionsModal.find('.modal-body');
projectActionsModalFooter = projectActionsModal.find('.modal-footer');
exportProjectsModal = $('#export-projects-modal');
exportProjectsModalHeader = exportProjectsModal.find('.modal-title');
exportProjectsModalBody = exportProjectsModal.find('.modal-body');
updateSelectedCards();
initNewProjectModal();
initEditProjectModal();
initManageUsersModal();
initExportProjectsModal();
Comments.initCommentOptions('ul.content-comments', true);
Comments.initEditComments('.panel-project .tab-content');
Comments.initDeleteComments('.panel-project .tab-content');

View file

@ -1,11 +1,11 @@
class TeamsController < ApplicationController
before_action :load_vars, only: %i(:parse_sheet :import_samples
:export_samples :export_all)
before_action :load_vars, only: %i(parse_sheet import_samples
export_samples export_projects)
before_action :check_create_samples_permissions, only: %i(parse_sheet
import_samples)
before_action :check_view_samples_permission, only: [:export_samples]
before_action :check_export_all_permission, only: [:export_all]
before_action :check_export_projects_permissions, only: [:export_projects]
def parse_sheet
session[:return_to] ||= request.referer
@ -225,9 +225,36 @@ class TeamsController < ApplicationController
redirect_back(fallback_location: root_path)
end
def export_all
if export_params[:project_ids]
TeamZipExporter.generate_zip(export_params, @team, current_user)
def export_projects
if export_projects_params[:project_ids]
# Check if user has enough requests for the day
limit = ENV['EXPORT_ALL_LIMIT_24_HOURS'].to_i || 3
if current_user.export_vars['num_of_export_all_last_24_hours'] >= limit
render json: {
html: render_to_string(
partial: 'projects/export/error.html.erb',
locals: { limit: limit }
),
title: t('projects.export_projects.modal_title_error')
}
else
current_user.export_vars['num_of_export_all_last_24_hours'] += 1
current_user.save
ids = generate_export_projects_zip
curr_num = current_user.export_vars['num_of_export_all_last_24_hours']
render json: {
html: render_to_string(
partial: 'projects/export/success.html.erb',
locals: { num_projects: ids.length,
limit: limit,
num_of_requests_left: limit - curr_num }
),
title: t('projects.export_projects.modal_title_success')
}
end
return
else
flash[:alert] = t('zip_export.export_error')
end
@ -270,6 +297,10 @@ class TeamsController < ApplicationController
params.permit(sample_ids: [], header_ids: []).to_h
end
def export_projects_params
params.permit(:id, project_ids: []).to_h
end
def check_create_samples_permissions
render_403 unless can_create_samples?(@team)
end
@ -280,13 +311,13 @@ class TeamsController < ApplicationController
end
end
def check_export_all_permission
def check_export_projects_permissions
render_403 unless can_read_team?(@team)
if export_params[:project_ids]
projects = Project.where(id: export_params[:project_ids])
if export_projects_params[:project_ids]
projects = Project.where(id: export_projects_params[:project_ids])
projects.each do |project|
render_403 unless can_read_project(current_user, project)
render_403 unless can_read_project?(current_user, project)
end
end
end
@ -302,4 +333,20 @@ class TeamsController < ApplicationController
:samples
)
end
def generate_export_projects_zip
ids = Project.where(id: export_projects_params[:project_ids],
team_id: @team)
.index_by(&:id)
options = { team: @team }
zip = TeamZipExport.create(user: current_user)
zip.generate_exportable_zip(
current_user,
ids,
:teams,
options
)
ids
end
end

View file

@ -0,0 +1,22 @@
module VariablesModel
extend ActiveSupport::Concern
@@default_variables = HashWithIndifferentAccess.new
included do
serialize :variables, JsonbHashSerializer
after_initialize :init_default_variables, if: :new_record?
end
class_methods do
def default_variables(dfs)
@@default_variables.merge!(dfs)
end
end
protected
def init_default_variables
self.variables = @@default_variables
end
end

View file

@ -1,5 +1,5 @@
class User < ApplicationRecord
include SearchableModel, SettingsModel
include SearchableModel, SettingsModel, VariablesModel
include User::TeamRoles, User::ProjectRoles
acts_as_token_authenticatable
@ -47,6 +47,14 @@ class User < ApplicationRecord
}
)
store_accessor :variables, :export_vars
default_variables(
export_vars: {
num_of_export_all_last_24_hours: 0
}
)
# Relations
has_many :user_identities, inverse_of: :user
has_many :user_teams, inverse_of: :user

View file

@ -1,19 +0,0 @@
module TeamZipExporter
def self.generate_zip(params, team, current_user)
Rails.logger.info('Exporting team zip')
project_ids = params[:project_ids]
ids = Project.where(id: project_ids,
team_id: team)
.index_by(&:id)
options = { team: team }
zip = TeamZipExport.create(user: current_user)
zip.generate_exportable_zip(
current_user,
ids,
:team,
options
)
end
end

View file

@ -0,0 +1,6 @@
<p>
It looks like you have exceeded your daily export limit. The number of exports is <strong>limited to <%= limit %> requests per day </strong>- you currently have 0 export requests left.
</p>
<p>Please repeat the desired action <strong>tomorrow</strong>, when your daily limit will rise back to <%= limit %> export requests.
</p>

View file

@ -0,0 +1,12 @@
<p>Your export request for <strong><%= num_projects %> projects in <%= @team.name %> </strong> team is being processed and will soon be exported in a .zip file format. It may take anywhere from 5 minutes up to several hours, depending on the file size. Please also note, that any new data in the projects from this point will not be included in the exported file.
</p>
<p>
When it's ready, you will receive a confirmation e-mail and the <strong> link </strong> to your file will appear in your <strong>SciNote notifications</strong>. For security reasons, the link will <strong>expire in 7 days</strong>.
</p>
<p>
<i>
Please note that the number of exports is limited to <%= limit %> requests per day - you currently have <%= num_of_requests_left %> more.
</i>
</p>

View file

@ -77,6 +77,23 @@
</div>
</div>
<!-- Export projects modal -->
<div class="modal" id="export-projects-modal" tabindex="-1" role="dialog" aria-labelledby="export-projects-modal-label">
<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">&times;</span></button>
<h4 class="modal-title" id="export-projects-modal-label"></h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%=t "general.cancel" %></button>
</div>
</div>
</div>
</div>
<div id="projects-toolbar">
<form class="form-inline" action="<%= projects_path %>">
@ -115,6 +132,14 @@
</ul>
</div>
<!-- export projects button -->
<button type="button" class="btn btn-default pull-right"
id="export-projects-button"
data-export-projects-url="<%= export_projects_team_path(current_team) %>">
<span class="fas fa-file-export"></span>
<span class="hidden-xs-custom"><%= t("projects.export_projects.export_button") %></span>
</button>
</div>
</form>
</div>

View file

@ -283,6 +283,10 @@ en:
invite_users_link: "Invite users"
invite_users_details: "to team %{team}."
contact_admins: "To invite additional users to team %{team}, contact its administrator/s."
export_projects:
export_button: "Export projects..."
modal_title_success: 'Your export is being prepared'
modal_title_error: 'Your export is denied'
table:
status: "Status"
name: "Project name"

View file

@ -178,6 +178,7 @@ Rails.application.routes.draw do
# post 'import_samples'
# post 'export_samples'
post 'export_repository', to: 'repositories#export_repository'
post 'export_projects'
# Used for atwho (smart annotations)
get 'atwho_users', to: 'at_who#users'
get 'atwho_repositories', to: 'at_who#repositories'

View file

@ -0,0 +1,19 @@
class AddVariablesToUsers < ActiveRecord::Migration[5.1]
def up
add_column :users, :variables, :jsonb, default: {}, null: false
User.find_each do |user|
variables = {
export_vars: {
num_of_export_all_last_24_hours: 0
}
}
user.update(variables: variables)
end
end
def down
remove_column :users, :variables, :jsonb
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180524091143) do
ActiveRecord::Schema.define(version: 20180930205254) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -786,6 +786,7 @@ ActiveRecord::Schema.define(version: 20180524091143) do
t.integer "current_team_id"
t.string "authentication_token", limit: 30
t.jsonb "settings", default: {}, null: false
t.jsonb "variables", default: {}, null: false
t.index "trim_html_tags((full_name)::text) gin_trgm_ops", name: "index_users_on_full_name", using: :gin
t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true