diff --git a/app/assets/javascripts/repositories/import/records_importer.js b/app/assets/javascripts/repositories/import/records_importer.js index d4d42a142..8ccbab01e 100644 --- a/app/assets/javascripts/repositories/import/records_importer.js +++ b/app/assets/javascripts/repositories/import/records_importer.js @@ -47,6 +47,7 @@ // Populate the errors container $('#import-errors-container').html(data.responseJSON.html); + animateSpinner(null, false); } }); } diff --git a/app/assets/javascripts/repositories/repository_datatable.js b/app/assets/javascripts/repositories/repository_datatable.js index 2b43035e9..e9202369f 100644 --- a/app/assets/javascripts/repositories/repository_datatable.js +++ b/app/assets/javascripts/repositories/repository_datatable.js @@ -148,6 +148,7 @@ function dataTableInit() { data = myData; } $.ajax({ + async: false, url: '/repositories/' + repositoryId + '/state_save', data: {state: data}, dataType: 'json', diff --git a/app/assets/javascripts/results/result_assets.js b/app/assets/javascripts/results/result_assets.js index ebf838234..4cd7b846c 100644 --- a/app/assets/javascripts/results/result_assets.js +++ b/app/assets/javascripts/results/result_assets.js @@ -76,7 +76,14 @@ Comments.initialize(); initNewResultAsset(); }).on('ajax:error', function(e, xhr) { - $form.renderFormErrors('result', xhr.responseJSON, true, e); + var errors = xhr.responseJSON.errors; + var formInput = $form.find('#result_asset_attributes_file'); + $('[data-status="error"]').remove(); + $.each(errors, function(key, value) { + var message = ''; + message += value + ''; + formInput.after(message); + }) animateSpinner(null, false); }); } diff --git a/app/assets/javascripts/samples/samples_importer.js b/app/assets/javascripts/samples/samples_importer.js index 2f6afa3c8..80d1ee921 100644 --- a/app/assets/javascripts/samples/samples_importer.js +++ b/app/assets/javascripts/samples/samples_importer.js @@ -46,5 +46,6 @@ $('form#form-import') // Populate the errors container $('#import-errors-container').html(data.responseJSON.html); + animateSpinner(null, false); } }); diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 728a6c8b6..e5a41a988 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -583,6 +583,19 @@ a[data-toggle="tooltip"] { background-color: $color-emperor; opacity: 0.8; } + + } + + .repositories-team { + padding: 10px 15px; + + &.active { + color: $color-theme-primary; + } + } + + .repository-search { + padding-left: 15px; } } diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 80ad95773..ca41b2ed4 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -226,18 +226,33 @@ class RepositoriesController < ApplicationController end def import_records - import_records = repostiory_import_actions - status = import_records.import! respond_to do |format| format.json do - if status[:status] == :ok - flash[:success] = t('repositories.import_records.success_flash', - number_of_rows: status[:nr_of_added]) - render json: {}, status: :ok + # Check if there exist mapping for repository record (it's mandatory) + if params[:mappings].value?('-1') + import_records = repostiory_import_actions + status = import_records.import! + + if status[:status] == :ok + flash[:success] = t('repositories.import_records.success_flash', + number_of_rows: status[:nr_of_added]) + render json: {}, status: :ok + else + flash[:alert] = t('repositories.import_records.error_flash', + message: status[:errors]) + render json: {}, status: :unprocessable_entity + end else - flash[:alert] = t('repositories.import_records.error_flash', - message: status[:errors]) - render json: {}, status: :unprocessable_entity + render json: { + html: render_to_string( + partial: 'shared/flash_errors.html.erb', + locals: { error_title: t('repositories.import_records'\ + '.error_message.errors_list_title'), + error: t('repositories.import_records.error_message'\ + '.no_repository_name') } + ) + }, + status: :unprocessable_entity end end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index fb473801c..de98b6347 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -17,6 +17,9 @@ class SearchController < ApplicationController search_steps if @search_category == :steps search_checklists if @search_category == :checklists search_samples if @search_category == :samples + if @search_category == :repositories && params[:repository] + search_repository + end search_assets if @search_category == :assets search_tables if @search_category == :tables search_comments if @search_category == :comments @@ -106,6 +109,39 @@ class SearchController < ApplicationController whole_phrase: @search_whole_phrase).size end + def count_by_repository + count_total = 0 + search_results = Repository.search(current_user, + true, + @search_query, + Constants::SEARCH_NO_LIMIT, + nil, + match_case: @search_case, + whole_word: @search_whole_word, + whole_phrase: @search_whole_phrase) + @repository_search_count = {} + current_user.teams.includes(:repositories).each do |team| + team_results = {} + team_results[:count] = 0 + team_results[:repositories] = {} + team.repositories.each do |repository| + repository_results = {} + repository_results[:id] = repository.id + repository_results[:count] = 0 + search_results.each do |result| + if repository.id == result.id + count_total += result.counter + repository_results[:count] += result.counter + end + end + team_results[:repositories][repository.name] = repository_results + team_results[:count] += repository_results[:count] + end + @repository_search_count[team.name] = team_results + end + count_total + end + def count_search_results @project_search_count = count_by_name Project @experiment_search_count = count_by_name Experiment @@ -118,6 +154,7 @@ class SearchController < ApplicationController @step_search_count = count_by_name Step @checklist_search_count = count_by_name Checklist @sample_search_count = count_by_name Sample + @repository_search_count_total = count_by_repository @asset_search_count = count_by_name Asset @table_search_count = count_by_name Table @comment_search_count = count_by_name Comment @@ -133,6 +170,7 @@ class SearchController < ApplicationController @search_results_count += @step_search_count @search_results_count += @checklist_search_count @search_results_count += @sample_search_count + @search_results_count += @repository_search_count_total @search_results_count += @asset_search_count @search_results_count += @table_search_count @search_results_count += @comment_search_count @@ -210,6 +248,20 @@ class SearchController < ApplicationController @search_count = @sample_search_count end + def search_repository + @repository = Repository.find_by_id(params[:repository]) + render_403 unless can_view_repository(@repository) + @repository_results = [] + if @repository_search_count_total > 0 + @repository_results = + RepositoryRow.search(@repository, @search_query, @search_page, + match_case: @search_case, + whole_word: @search_whole_word, + whole_phrase: @search_whole_phrase) + end + @search_count = @repository_search_count_total + end + def search_assets @asset_results = [] @asset_results = search_by_name(Asset) if @asset_search_count > 0 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2c437a42c..501fa65ac 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -225,7 +225,8 @@ module ApplicationHelper user_name << ' ' + I18n.t('atwho.res.removed') if !user_still_in_team raw("") + + "alt='avatar' class='atwho-user-img-popover'" \ + " ref='#{'missing-img' if missing_avatar(user, :icon_small)}'>") + raw('#{text}".html_safe end - def sanitize_report_pdf(text, tags = [], attributes = []) - ActionController::Base.helpers.sanitize( - text, - tags: Constants::WHITELISTED_TAGS + tags, - attributes: Constants::WHITELISTED_ATTRIBUTES + attributes - ) + # Fixes issues with avatar images in reports + def fix_smart_annotation_image(html) + html_doc = Nokogiri::HTML(html) + html_doc.search('.atwho-user-popover').each do |el| + text = el.content + el.replace("#{text}") + end + html_doc.search('[ref="missing-img"]').each do |el| + tag = wicked_pdf_image_tag('icon_small/missing.png') + el.replace(tag) + end + html_doc.to_s end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 855bbc523..c2ac03add 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,10 +1,11 @@ class Repository < ApplicationRecord + include SearchableModel + belongs_to :team, optional: true belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User', optional: true - has_many :repository_columns has_many :repository_rows has_many :repository_table_states, inverse_of: :repository, dependent: :destroy @@ -18,6 +19,47 @@ class Repository < ApplicationRecord validates :team, presence: true validates :created_by, presence: true + def self.search( + user, + _include_archived, + query = nil, + page = 1, + current_team = nil, + options = {} + ) + team_ids = + if current_team + current_team.id + else + Team.joins(:user_teams) + .where('user_teams.user_id = ?', user.id) + .distinct + .pluck(:id) + end + + row_ids = RepositoryRow + .search(nil, query, Constants::SEARCH_NO_LIMIT, options) + .select(:id) + + new_query = Repository + .select('repositories.*, COUNT(repository_rows.id) AS counter') + .joins(:team) + .joins('LEFT OUTER JOIN repository_rows ON ' \ + 'repositories.id = repository_rows.repository_id') + .where(team: team_ids) + .where('repository_rows.id IN (?)', row_ids) + .group('repositories.id') + + # Show all results if needed + if page == Constants::SEARCH_NO_LIMIT + new_query + else + new_query + .limit(Constants::SEARCH_LIMIT) + .offset((page - 1) * Constants::SEARCH_LIMIT) + end + end + def open_spreadsheet(file) filename = file.original_filename file_path = file.path diff --git a/app/models/repository_row.rb b/app/models/repository_row.rb index aa727b893..5e9cfc320 100644 --- a/app/models/repository_row.rb +++ b/app/models/repository_row.rb @@ -1,4 +1,6 @@ class RepositoryRow < ApplicationRecord + include SearchableModel + belongs_to :repository, optional: true belongs_to :created_by, foreign_key: :created_by_id, @@ -19,4 +21,47 @@ class RepositoryRow < ApplicationRecord presence: true, length: { maximum: Constants::NAME_MAX_LENGTH } validates :created_by, presence: true + + def self.search(repository, query, page = 1, options) + new_query = distinct + .joins(:created_by) + .joins( + "LEFT OUTER JOIN ( + SELECT repository_cells.repository_row_id, + repository_text_values.data AS text_value, + to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI') + AS date_value + FROM repository_cells + INNER JOIN repository_text_values + ON repository_text_values.id = repository_cells.value_id + FULL OUTER JOIN repository_date_values + ON repository_date_values.id = repository_cells.value_id + ) AS values + ON values.repository_row_id = repository_rows.id" + ) + .where_attributes_like( + ['repository_rows.name', 'users.full_name', + 'values.text_value', 'values.date_value'], + query, options + ) + + if repository + new_query = new_query + .preload( + :repository_columns, + :created_by, + repository_cells: :value + ) + .where(repository: repository) + end + + # Show all results if needed + if page == Constants::SEARCH_NO_LIMIT + new_query + else + new_query + .limit(Constants::SEARCH_LIMIT) + .offset((page - 1) * Constants::SEARCH_LIMIT) + end + end end diff --git a/app/models/user_team.rb b/app/models/user_team.rb index 4d5605cf0..6850ac154 100644 --- a/app/models/user_team.rb +++ b/app/models/user_team.rb @@ -62,8 +62,8 @@ class UserTeam < ApplicationRecord end # Also, make new owner author of all protocols that belong - # to the departing user. - p_ids = user.added_protocols.pluck(:id) + # to the departing user and belong to this team. + p_ids = user.added_protocols.where(team: team).pluck(:id) Protocol.find(p_ids).each do |protocol| protocol.record_timestamps = false protocol.added_by = new_owner diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index 7ab6584cb..fd4eb4afc 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -59,7 +59,7 @@
- + diff --git a/app/views/protocols/index.html.erb b/app/views/protocols/index.html.erb index 3b07639e4..d9a48073d 100644 --- a/app/views/protocols/index.html.erb +++ b/app/views/protocols/index.html.erb @@ -12,7 +12,7 @@ diff --git a/app/views/reports/report.pdf.erb b/app/views/reports/report.pdf.erb index 36812a514..aaa96c774 100644 --- a/app/views/reports/report.pdf.erb +++ b/app/views/reports/report.pdf.erb @@ -10,7 +10,9 @@ diff --git a/app/views/repositories/_delete_record_modal.html.erb b/app/views/repositories/_delete_record_modal.html.erb index cb4adc55f..6e54f5833 100644 --- a/app/views/repositories/_delete_record_modal.html.erb +++ b/app/views/repositories/_delete_record_modal.html.erb @@ -7,7 +7,7 @@ <%= t("repositories.modal_delete_record.notice") %>