diff --git a/Gemfile.lock b/Gemfile.lock index 46b0401f7..0b815e936 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,7 +17,7 @@ GIT GIT remote: https://github.com/biosistemika/yomu - revision: 8845246f3e6a6cbc49b902cd4b908ba70553cbdd + revision: 063b855d672e9dd9de1e6e585b349a9b63e120c3 branch: master specs: yomu (0.2.4) @@ -338,9 +338,9 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (0.9.2) - mime-types (3.3) + mime-types (3.3.1) mime-types-data (~> 3.2015) - mime-types-data (3.2019.0904) + mime-types-data (3.2020.0512) mimemagic (0.3.5) mini_magick (4.9.5) mini_mime (1.0.2) diff --git a/VERSION b/VERSION index 98adfe8e1..0044d6cb9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.19.7 +1.20.1 diff --git a/app/assets/javascripts/protocols/header.js b/app/assets/javascripts/protocols/header.js index b1a1683d8..13f678e7d 100644 --- a/app/assets/javascripts/protocols/header.js +++ b/app/assets/javascripts/protocols/header.js @@ -22,8 +22,12 @@ var ProtocolRepositoryHeader = (function() { function initEditDescription() { var viewObject = $('#protocol_description_view'); - viewObject.on('click', function() { + viewObject.on('click', function(e) { + if ($(e.target).hasClass('record-info-link')) return; TinyMCE.init('#protocol_description_textarea'); + }).on('click', 'a', function(e) { + if ($(this).hasClass('record-info-link')) return; + e.stopPropagation(); }); TinyMCE.initIfHasDraft(viewObject); } diff --git a/app/assets/stylesheets/constants.scss b/app/assets/stylesheets/constants.scss index 00661294f..e22b9cccd 100644 --- a/app/assets/stylesheets/constants.scss +++ b/app/assets/stylesheets/constants.scss @@ -1,5 +1,3 @@ -@import url(https://fonts.googleapis.com/css?family=Lato:400,400i,700&subset=latin-ext); - //============================================================================== // Colors //============================================================================== diff --git a/app/controllers/active_storage/representations_controller.rb b/app/controllers/active_storage/representations_controller.rb index 928d292f1..68cca5585 100644 --- a/app/controllers/active_storage/representations_controller.rb +++ b/app/controllers/active_storage/representations_controller.rb @@ -31,7 +31,9 @@ module ActiveStorage unless processing ActiveStorage::PreviewJob.perform_later(@blob.id) - @blob.attachments.take.record.update(file_processing: true) + ActiveRecord::Base.no_touching do + @blob.attachments.take.record.update(file_processing: true) + end end false diff --git a/app/controllers/api/v1/assets_controller.rb b/app/controllers/api/v1/assets_controller.rb index 358e49708..140fe992a 100644 --- a/app/controllers/api/v1/assets_controller.rb +++ b/app/controllers/api/v1/assets_controller.rb @@ -23,14 +23,14 @@ module Api raise PermissionError.new(Asset, :create) unless can_manage_protocol_in_module?(@protocol) if @form_multipart_upload - asset = @step.assets.new(asset_params) + asset = @step.assets.new(asset_params.merge({ team_id: @team.id })) else blob = ActiveStorage::Blob.create_after_upload!( io: StringIO.new(Base64.decode64(asset_params[:file_data])), filename: asset_params[:file_name], content_type: asset_params[:file_type] ) - asset = @step.assets.new(file: blob) + asset = @step.assets.new(file: blob, team: @team) end asset.save!(context: :on_api_upload) diff --git a/app/controllers/api/v1/results_controller.rb b/app/controllers/api/v1/results_controller.rb index 6d6bcde2d..0760ed813 100644 --- a/app/controllers/api/v1/results_controller.rb +++ b/app/controllers/api/v1/results_controller.rb @@ -104,10 +104,10 @@ module Api Result.transaction do @result = @task.results.create!(result_params.merge(user_id: current_user.id)) if @form_multipart_upload - asset = Asset.create!(result_file_params) + asset = Asset.create!(result_file_params.merge({ team_id: @team.id })) else blob = create_blob_from_params - asset = Asset.create!(file: blob) + asset = Asset.create!(file: blob, team: @team) end ResultAsset.create!(asset: asset, result: @result) end diff --git a/app/controllers/api/v1/user_projects_controller.rb b/app/controllers/api/v1/user_projects_controller.rb index d583c77bd..1e10d47ca 100644 --- a/app/controllers/api/v1/user_projects_controller.rb +++ b/app/controllers/api/v1/user_projects_controller.rb @@ -6,6 +6,7 @@ module Api before_action :load_team before_action :load_project before_action :load_user_project, only: :show + before_action :load_user_project_for_managing, only: %i(update destroy) def index user_projects = @project.user_projects @@ -23,11 +24,44 @@ module Api include: :user end + def create + raise PermissionError.new(Project, :manage) unless can_manage_project?(@project) + + user_project = @project.user_projects.create!(user_project_params.merge!(assigned_by: current_user)) + + render jsonapi: user_project, serializer: UserProjectSerializer, status: :created + end + + def update + @user_project.role = user_project_params[:role] + return render body: nil, status: :no_content unless @user_project.changed? + + @user_project.assigned_by = current_user + @user_project.save! + render jsonapi: @user_project, serializer: UserProjectSerializer, status: :ok + end + + def destroy + @user_project.destroy! + render body: nil + end + private def load_user_project @user_project = @project.user_projects.find(params.require(:id)) end + + def load_user_project_for_managing + @user_project = @project.user_projects.find(params.require(:id)) + raise PermissionError.new(Project, :manage) unless can_manage_project?(@project) + end + + def user_project_params + raise TypeError unless params.require(:data).require(:type) == 'user_projects' + + params.require(:data).require(:attributes).permit(:user_id, :role) + end end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2268c2e1c..db89d7e6f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -37,7 +37,7 @@ class ApplicationController < ActionController::Base # Sets current team for all controllers def current_team - Team.find_by_id(current_user.current_team_id) + @current_team ||= current_user.teams.find_by(id: current_user.current_team_id) end def to_user_date_format @@ -83,13 +83,12 @@ class ApplicationController < ActionController::Base private def update_current_team - current_team = Team.find_by_id(current_user.current_team_id) - if (current_team.nil? || !current_user.is_member_of_team?(current_team)) && - current_user.teams.count.positive? + return if current_team.present? && current_team.id == current_user.current_team_id - current_user.update( - current_team_id: current_user.teams.first.id - ) + if current_user.current_team_id + @current_team = current_user.teams.find_by(id: current_user.current_team_id) + elsif current_user.teams.any? + current_user.update(current_team_id: current_user.teams.first.id) end end diff --git a/app/controllers/dashboards_controller.rb b/app/controllers/dashboards_controller.rb index 2e4880fad..bbc91737d 100644 --- a/app/controllers/dashboards_controller.rb +++ b/app/controllers/dashboards_controller.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true class DashboardsController < ApplicationController - def show; end + def show + @my_module_status_flows = MyModuleStatusFlow.all.preload(my_module_statuses: :my_module_status_consequences) + end end diff --git a/app/controllers/my_module_status_flow_controller.rb b/app/controllers/my_module_status_flow_controller.rb index eb738f8db..972dcc6d4 100644 --- a/app/controllers/my_module_status_flow_controller.rb +++ b/app/controllers/my_module_status_flow_controller.rb @@ -5,7 +5,10 @@ class MyModuleStatusFlowController < ApplicationController before_action :check_view_permissions def show - my_module_statuses = @my_module.my_module_status_flow.my_module_statuses.sort_by_position + my_module_statuses = @my_module.my_module_status_flow + .my_module_statuses + .preload(:my_module_status_implications, next_status: :my_module_status_conditions) + .sort_by_position render json: { html: render_to_string(partial: 'my_modules/modals/status_flow_modal_body.html.erb', locals: { my_module_statuses: my_module_statuses }) } end diff --git a/app/controllers/my_module_tags_controller.rb b/app/controllers/my_module_tags_controller.rb index cfce58d2f..789ea0931 100644 --- a/app/controllers/my_module_tags_controller.rb +++ b/app/controllers/my_module_tags_controller.rb @@ -158,10 +158,6 @@ class MyModuleTagsController < ApplicationController render_403 unless can_manage_module?(@my_module) end - def init_gui - @tags = @my_module.unassigned_tags - end - def mt_params params.require(:my_module_tag).permit(:my_module_id, :tag_id) end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 9a6c6682c..73dc1f083 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,6 +4,7 @@ class ProjectsController < ApplicationController include TeamsHelper include InputSanitizeHelper + before_action :switch_team_with_param, only: :index before_action :load_vars, only: %i(show edit update notifications reports samples experiment_archive @@ -37,7 +38,6 @@ class ProjectsController < ApplicationController } end format.html do - current_team_switch(Team.find_by_id(params[:team])) if params[:team] @teams = current_user.teams # New project for create new project modal @project = Project.new diff --git a/app/controllers/protocols_controller.rb b/app/controllers/protocols_controller.rb index 1eaa2b9b3..d649d539c 100644 --- a/app/controllers/protocols_controller.rb +++ b/app/controllers/protocols_controller.rb @@ -325,7 +325,7 @@ class ProtocolsController < ApplicationController @protocol.unlink rescue Exception transaction_error = true - raise ActiveRecord:: Rollback + raise ActiveRecord::Rollback end end @@ -353,13 +353,11 @@ class ProtocolsController < ApplicationController if @protocol.can_destroy? transaction_error = false Protocol.transaction do - begin - # Revert is basically update from parent - @protocol.update_from_parent(current_user) - rescue Exception - transaction_error = true - raise ActiveRecord:: Rollback - end + # Revert is basically update from parent + @protocol.update_from_parent(current_user) + rescue StandardError + transaction_error = true + raise ActiveRecord::Rollback end if transaction_error @@ -397,12 +395,10 @@ class ProtocolsController < ApplicationController if @protocol.parent.can_destroy? transaction_error = false Protocol.transaction do - begin - @protocol.update_parent(current_user) - rescue Exception - transaction_error = true - raise ActiveRecord:: Rollback - end + @protocol.update_parent(current_user) + rescue StandardError + transaction_error = true + raise ActiveRecord::Rollback end if transaction_error @@ -440,12 +436,10 @@ class ProtocolsController < ApplicationController if @protocol.can_destroy? transaction_error = false Protocol.transaction do - begin - @protocol.update_from_parent(current_user) - rescue Exception - transaction_error = true - raise ActiveRecord:: Rollback - end + @protocol.update_from_parent(current_user) + rescue StandardError + transaction_error = true + raise ActiveRecord::Rollback end if transaction_error @@ -483,12 +477,10 @@ class ProtocolsController < ApplicationController if @protocol.can_destroy? transaction_error = false Protocol.transaction do - begin - @protocol.load_from_repository(@source, current_user) - rescue Exception - transaction_error = true - raise ActiveRecord:: Rollback - end + @protocol.load_from_repository(@source, current_user) + rescue StandardError + transaction_error = true + raise ActiveRecord::Rollback end if transaction_error diff --git a/app/controllers/user_my_modules_controller.rb b/app/controllers/user_my_modules_controller.rb index c7d4d2269..2a6cbf97b 100644 --- a/app/controllers/user_my_modules_controller.rb +++ b/app/controllers/user_my_modules_controller.rb @@ -121,10 +121,6 @@ class UserMyModulesController < ApplicationController render_403 unless can_manage_users_in_module?(@my_module) end - def init_gui - @users = @my_module.unassigned_users - end - def um_params params.require(:user_my_module).permit(:user_id, :my_module_id) end diff --git a/app/controllers/user_projects_controller.rb b/app/controllers/user_projects_controller.rb index ac29ddfbd..b469dada1 100644 --- a/app/controllers/user_projects_controller.rb +++ b/app/controllers/user_projects_controller.rb @@ -3,7 +3,7 @@ class UserProjectsController < ApplicationController include InputSanitizeHelper before_action :load_vars - before_action :load_up_var, only: %i(update destroy) + before_action :load_user_project, only: %i(update destroy) before_action :check_view_permissions, only: :index before_action :check_manage_users_permissions, only: :index_edit before_action :check_create_permissions, only: :create @@ -26,9 +26,9 @@ class UserProjectsController < ApplicationController end def index_edit - @users = @project.user_projects + @user_projects = @project.user_projects @unassigned_users = @project.unassigned_users - @up = UserProject.new(project: @project) + @new_user_project = UserProject.new(project: @project) respond_to do |format| format.json do @@ -48,10 +48,10 @@ class UserProjectsController < ApplicationController end def create - @up = UserProject.new(up_params.merge(project: @project)) - @up.assigned_by = current_user + @user_project = @project.user_projects.new(user_project_params) + @user_project.assigned_by = current_user - if @up.save + if @user_project.save log_activity(:assign_user_to_project) respond_to do |format| @@ -61,23 +61,23 @@ class UserProjectsController < ApplicationController end else error = t('user_projects.create.can_add_user_to_project') - error = t('user_projects.create.select_user_role') unless @up.role + error = t('user_projects.create.select_user_role') unless @user_project.role respond_to do |format| - format.json { - render :json => { + format.json do + render json: { status: 'error', error: error } - } + end end end end def update - @up.role = up_params[:role] + @user_project.role = user_project_params[:role] - if @up.save + if @user_project.save log_activity(:change_user_role_on_project) respond_to do |format| @@ -90,7 +90,7 @@ class UserProjectsController < ApplicationController format.json do render json: { status: 'error', - errors: @up.errors + errors: @user_project.errors } end end @@ -98,20 +98,20 @@ class UserProjectsController < ApplicationController end def destroy - if @up.destroy + if @user_project.destroy log_activity(:unassign_user_from_project) respond_to do |format| format.json do redirect_to project_users_edit_path(format: :json), turbolinks: false, - status: 303 + status: :see_other end end else respond_to do |format| format.json do render json: { - errors: @up.errors + errors: @user_project.errors } end end @@ -121,13 +121,13 @@ class UserProjectsController < ApplicationController private def load_vars - @project = Project.find_by_id(params[:project_id]) + @project = Project.find_by(id: params[:project_id]) render_404 unless @project end - def load_up_var - @up = UserProject.find(params[:id]) - render_404 unless @up + def load_user_project + @user_project = @project.user_projects.find(params[:id]) + render_404 unless @user_project end def check_view_permissions @@ -139,19 +139,14 @@ class UserProjectsController < ApplicationController end def check_create_permissions - render_403 unless can_create_projects?(current_team) + render_403 unless can_manage_project?(@project) end def check_manage_permissions - render_403 unless can_manage_project?(@project) && - @up.user_id != current_user.id + render_403 unless can_manage_project?(@project) && @user_project.user_id != current_user.id end - def init_gui - @users = @project.unassigned_users - end - - def up_params + def user_project_params params.require(:user_project).permit(:user_id, :project_id, :role) end @@ -163,7 +158,7 @@ class UserProjectsController < ApplicationController team: @project.team, project: @project, message_items: { project: @project.id, - user_target: @up.user.id, - role: @up.role_str }) + user_target: @user_project.user.id, + role: @user_project.role_str }) end end diff --git a/app/helpers/teams_helper.rb b/app/helpers/teams_helper.rb index 9c5d116bb..c5e69bfb1 100644 --- a/app/helpers/teams_helper.rb +++ b/app/helpers/teams_helper.rb @@ -1,9 +1,10 @@ module TeamsHelper # resets the current team if needed def current_team_switch(team) - if team != current_team + if team != current_team && current_user.is_member_of_team?(team) current_user.current_team_id = team.id current_user.save + update_current_team end end @@ -17,11 +18,7 @@ module TeamsHelper end end - def team_created_by(team) - User.find_by_id(team.created_by_id) - end - def switch_team_with_param - current_team_switch(Team.find_by(id: params[:team])) if params[:team] + current_team_switch(current_user.teams.find_by(id: params[:team])) if params[:team] end end diff --git a/app/javascript/packs/fonts.js b/app/javascript/packs/fonts.js new file mode 100644 index 000000000..9147247fb --- /dev/null +++ b/app/javascript/packs/fonts.js @@ -0,0 +1 @@ +require('typeface-lato'); diff --git a/app/jobs/active_storage/preview_job.rb b/app/jobs/active_storage/preview_job.rb index f1eda5812..2d1941ca6 100644 --- a/app/jobs/active_storage/preview_job.rb +++ b/app/jobs/active_storage/preview_job.rb @@ -6,7 +6,9 @@ class ActiveStorage::PreviewJob < ActiveStorage::BaseJob discard_on StandardError do |job, error| blob = ActiveStorage::Blob.find_by(id: job.arguments.first) - blob&.attachments&.take&.record&.update(file_processing: false) + ActiveRecord::Base.no_touching do + blob&.attachments&.take&.record&.update(file_processing: false) + end Rails.logger.error "Couldn't generate preview for Blob with id: #{job.arguments.first}. Error:\n #{error}" end @@ -24,6 +26,8 @@ class ActiveStorage::PreviewJob < ActiveStorage::BaseJob Rails.logger.info "Preview for the Blod with id: #{blob.id} - successfully generated.\n" \ "Transformations applied: #{preview.variation.transformations}" - blob.attachments.take.record.update(file_processing: false) + ActiveRecord::Base.no_touching do + blob.attachments.take.record.update(file_processing: false) + end end end diff --git a/app/models/asset.rb b/app/models/asset.rb index f6ec993ec..ea036f300 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -32,17 +32,15 @@ class Asset < ApplicationRecord optional: true belongs_to :team, optional: true has_one :step_asset, inverse_of: :asset, dependent: :destroy - has_one :step, through: :step_asset, dependent: :nullify + has_one :step, through: :step_asset, touch: true, dependent: :nullify has_one :result_asset, inverse_of: :asset, dependent: :destroy - has_one :result, through: :result_asset, dependent: :nullify + has_one :result, through: :result_asset, touch: true, dependent: :nullify has_one :repository_asset_value, inverse_of: :asset, dependent: :destroy has_one :repository_cell, through: :repository_asset_value, dependent: :nullify has_many :report_elements, inverse_of: :asset, dependent: :destroy has_one :asset_text_datum, inverse_of: :asset, dependent: :destroy - after_save { result&.touch; step&.touch } - attr_accessor :file_content, :file_info, :in_template def self.search( @@ -222,8 +220,7 @@ class Asset < ApplicationRecord Rails.logger.info "Asset #{id}: Creating extract text job" # The extract_asset_text also includes # estimated size calculation - Asset.delay(queue: :assets, run_at: 20.minutes.from_now) - .extract_asset_text_delayed(id, in_template) + Asset.delay(queue: :assets).extract_asset_text_delayed(id, in_template) elsif marvinjs? extract_asset_text else diff --git a/app/models/protocol.rb b/app/models/protocol.rb index 96a9269ea..4ddafea6a 100644 --- a/app/models/protocol.rb +++ b/app/models/protocol.rb @@ -229,14 +229,16 @@ class Protocol < ApplicationRecord # Deep-clone given array of assets def self.deep_clone_assets(assets_to_clone) - assets_to_clone.each do |src_id, dest_id| - src = Asset.find_by(id: src_id) - dest = Asset.find_by(id: dest_id) - dest.destroy! if src.blank? && dest.present? - next unless src.present? && dest.present? + ActiveRecord::Base.no_touching do + assets_to_clone.each do |src_id, dest_id| + src = Asset.find_by(id: src_id) + dest = Asset.find_by(id: dest_id) + dest.destroy! if src.blank? && dest.present? + next unless src.present? && dest.present? - # Clone file - src.duplicate_file(dest) + # Clone file + src.duplicate_file(dest) + end end end @@ -524,12 +526,14 @@ class Protocol < ApplicationRecord end def update_parent(current_user) - # First, destroy parent's step contents - parent.destroy_contents - parent.reload + ActiveRecord::Base.no_touching do + # First, destroy parent's step contents + parent.destroy_contents + parent.reload - # Now, clone step contents - Protocol.clone_contents(self, parent, current_user, false) + # Now, clone step contents + Protocol.clone_contents(self, parent, current_user, false) + end # Lastly, update the metadata parent.reload @@ -542,11 +546,13 @@ class Protocol < ApplicationRecord end def update_from_parent(current_user) - # First, destroy step contents - destroy_contents + ActiveRecord::Base.no_touching do + # First, destroy step contents + destroy_contents - # Now, clone parent's step contents - Protocol.clone_contents(parent, self, current_user, false) + # Now, clone parent's step contents + Protocol.clone_contents(parent, self, current_user, false) + end # Lastly, update the metadata reload @@ -558,11 +564,13 @@ class Protocol < ApplicationRecord end def load_from_repository(source, current_user) - # First, destroy step contents - destroy_contents + ActiveRecord::Base.no_touching do + # First, destroy step contents + destroy_contents - # Now, clone source's step contents - Protocol.clone_contents(source, self, current_user, false) + # Now, clone source's step contents + Protocol.clone_contents(source, self, current_user, false) + end # Lastly, update the metadata reload @@ -588,12 +596,14 @@ class Protocol < ApplicationRecord # Don't proceed further if clone is invalid return clone if clone.invalid? - # Okay, clone seems to be valid: let's clone it - clone = deep_clone(clone, current_user) + ActiveRecord::Base.no_touching do + # Okay, clone seems to be valid: let's clone it + clone = deep_clone(clone, current_user) - # If the above operation went well, update published_on - # timestamp - clone.update(published_on: Time.now) if clone.in_repository_public? + # If the above operation went well, update published_on + # timestamp + clone.update(published_on: Time.zone.now) if clone.in_repository_public? + end # Link protocols if neccesary if link_protocols diff --git a/app/permissions/experiment.rb b/app/permissions/experiment.rb index 8cf52fe4b..4fc625673 100644 --- a/app/permissions/experiment.rb +++ b/app/permissions/experiment.rb @@ -26,7 +26,10 @@ Canaid::Permissions.register_for(Experiment) do # assign/reassign/unassign tags can :manage_experiment do |user, experiment| user.is_user_or_higher_of_project?(experiment.project) && - MyModule.joins(:experiment).where(experiment: experiment).all? do |my_module| + MyModule.joins(:experiment) + .where(experiment: experiment) + .preload(my_module_status: :my_module_status_implications) + .all? do |my_module| if my_module.my_module_status my_module.my_module_status.my_module_status_implications.all? { |implication| implication.call(my_module) } else diff --git a/app/permissions/project.rb b/app/permissions/project.rb index 5b026e637..b557a69fd 100644 --- a/app/permissions/project.rb +++ b/app/permissions/project.rb @@ -38,7 +38,10 @@ Canaid::Permissions.register_for(Project) do # project: update/delete, assign/reassign/unassign users can :manage_project do |user, project| user.is_owner_of_project?(project) && - MyModule.joins(experiment: :project).where(experiments: { project: project }).all? do |my_module| + MyModule.joins(experiment: :project) + .where(experiments: { project: project }) + .preload(my_module_status: :my_module_status_implications) + .all? do |my_module| if my_module.my_module_status my_module.my_module_status.my_module_status_implications.all? { |implication| implication.call(my_module) } else diff --git a/app/views/dashboards/_current_tasks.html.erb b/app/views/dashboards/_current_tasks.html.erb index 7a0cf0a27..1a659aa5d 100644 --- a/app/views/dashboards/_current_tasks.html.erb +++ b/app/views/dashboards/_current_tasks.html.erb @@ -27,10 +27,10 @@ data-select-multiple-name="<%= t("dashboard.current_tasks.filter.statuses.selected") %>" multiple > - <% MyModuleStatusFlow.find_each do |status_flow| %> + <% @my_module_status_flows.each do |status_flow| %> <% status_flow.my_module_statuses.each do |status| %> <% end %> diff --git a/app/views/dashboards/show.html.erb b/app/views/dashboards/show.html.erb index a4632c1d2..2c4a62741 100644 --- a/app/views/dashboards/show.html.erb +++ b/app/views/dashboards/show.html.erb @@ -1,8 +1,10 @@ <% provide :head_title, t('nav.label.dashboard') %> -