Merge pull request #8097 from artoscinote/ma_SCI_11355

Add permissions and permission controllers for forms [SCI-11355]
This commit is contained in:
Martin Artnik 2024-12-12 15:53:39 +01:00 committed by GitHub
commit 6339c71d8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 587 additions and 58 deletions

View file

@ -0,0 +1,186 @@
# frozen_string_literal: true
module AccessPermissions
class FormsController < ApplicationController
include InputSanitizeHelper
before_action :set_form
before_action :check_read_permissions, only: %i(show)
before_action :check_manage_permissions, except: %i(show)
before_action :available_users, only: %i(new create)
def show
render json: @form.user_assignments.includes(:user_role, :user).order('users.full_name ASC'),
each_serializer: UserAssignmentSerializer, user: current_user
end
def new
render json: @available_users, each_serializer: UserSerializer, user: current_user
end
def edit; end
def create
ActiveRecord::Base.transaction do
created_count = 0
if permitted_create_params[:user_id] == 'all'
@form.update!(visibility: :visible, default_public_user_role_id: permitted_create_params[:user_role_id])
log_activity(:form_access_granted_all_team_members,
{ team: @form.team.id, role: @form.default_public_user_role&.name })
else
user_assignment = UserAssignment.find_or_initialize_by(
assignable: @form,
user_id: permitted_create_params[:user_id],
team: current_team
)
user_assignment.update!(
user_role_id: permitted_create_params[:user_role_id],
assigned_by: current_user,
assigned: :manually
)
log_activity(:form_access_granted, { user_target: user_assignment.user.id,
role: user_assignment.user_role.name })
created_count += 1
end
@message = if created_count.zero?
t('access_permissions.create.success', member_name: t('access_permissions.all_team'))
else
t('access_permissions.create.success', member_name: escape_input(user_assignment.user.name))
end
render json: { message: @message }
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error e.message
errors = @form.errors.present? ? @form.errors&.map(&:message)&.join(',') : e.message
render json: { flash: errors }, status: :unprocessable_entity
raise ActiveRecord::Rollback
end
end
def update
@user_assignment = @form.user_assignments.find_by(
user_id: permitted_update_params[:user_id],
team: current_team
)
# prevent role change if it would result in no manually assigned users having the user management permission
new_user_role = UserRole.find(permitted_update_params[:user_role_id])
if !new_user_role.has_permission?(FormPermissions::USERS_MANAGE) &&
@user_assignment.last_with_permission?(FormPermissions::USERS_MANAGE, assigned: :manually)
raise ActiveRecord::RecordInvalid
end
@user_assignment.update!(permitted_update_params)
log_activity(:form_access_changed, { user_target: @user_assignment.user.id,
role: @user_assignment.user_role.name })
render :form_member
rescue ActiveRecord::RecordInvalid
render json: { flash: t('access_permissions.update.failure') }, status: :unprocessable_entity
end
def destroy
user = @form.assigned_users.find(params[:user_id])
user_assignment = @form.user_assignments.find_by(user: user, team: current_team)
# prevent deletion of last manually assigned user that can manage users
raise ActiveRecord::RecordInvalid if user_assignment.last_with_permission?(FormPermissions::USERS_MANAGE, assigned: :manually)
Protocol.transaction do
if @form.visible?
user_assignment.update!(
user_role: @form.default_public_user_role,
assigned: :automatically
)
else
user_assignment.destroy!
end
log_activity(:form_access_revoked, { user_target: user_assignment.user.id,
role: user_assignment.user_role.name })
end
render json: { message: t('access_permissions.destroy.success', member_name: user.full_name) }
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error e.message
render json: { message: t('access_permissions.destroy.failure') }, status: :unprocessable_entity
raise ActiveRecord::Rollback
end
def update_default_public_user_role
ActiveRecord::Base.transaction do
current_role = @form.default_public_user_role.name
@form.update!(permitted_default_public_user_role_params)
# revoke all team members access
if permitted_default_public_user_role_params[:default_public_user_role_id].blank?
log_activity(:form_access_revoked_all_team_members,
{ team: @form.team.id, role: current_role })
render json: { flash: t('access_permissions.update.revoke_all_team_members') }, status: :ok
else
# update all team members access
log_activity(:form_access_changed_all_team_members,
{ team: @form.team.id, role: @form.default_public_user_role&.name })
end
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error e.message
render json: { flash: @form.errors&.map(&:message)&.join(',') }, status: :unprocessable_entity
raise ActiveRecord::Rollback
end
end
private
def permitted_default_public_user_role_params
params.require(:object).permit(:default_public_user_role_id)
end
def permitted_update_params
params.require(:user_assignment)
.permit(%i(user_role_id user_id))
end
def permitted_create_params
params.require(:user_assignment)
.permit(%i(user_id user_role_id))
end
def available_users
# automatically assigned or not assigned to project
@available_users = current_team.users.where(
id: @form.user_assignments.automatically_assigned.select(:user_id)
).or(
current_team.users.where.not(id: @form.users.select(:id))
).order('users.full_name ASC')
end
def set_form
@form = current_team.forms.includes(user_assignments: %i(user user_role)).find_by(id: params[:id])
return render_404 unless @form
@form = @form.parent if @form.parent_id
end
def check_manage_permissions
render_403 unless can_manage_form_users?(@form)
end
def check_read_permissions
render_403 unless can_read_form?(@form)
end
def log_activity(type_of, message_items = {})
message_items = { form: @form.id }.merge(message_items)
Activities::CreateActivityService
.call(activity_type: type_of,
owner: current_user,
subject: @form,
team: @form.team,
project: nil,
message_items: message_items)
end
end
end

View file

@ -3,6 +3,7 @@
class FormFieldsController < ApplicationController
before_action :load_form
before_action :load_form_field, only: %i(update destroy)
before_action :check_manage_permissions, only: %i(create update destroy reorder)
def create
ActiveRecord::Base.transaction do
@ -47,14 +48,14 @@ class FormFieldsController < ApplicationController
def reorder
ActiveRecord::Base.transaction do
params.permit(form_field_positions: %i(position id))[:form_field_positions].each do |data|
form_field = @form.form_fields.find(data[:id])
form_field.insert_at(data[:position].to_i)
@form_field = @form.form_fields.find(data[:id])
@form_field.insert_at!(data[:position].to_i)
end
end
render json: params[:form_field_positions], status: :ok
rescue ActiveRecord::RecordInvalid
render json: { errors: form_field.errors }, status: :conflict
render json: { errors: @form_field.errors }, status: :unprocessable_entity
end
private
@ -62,13 +63,17 @@ class FormFieldsController < ApplicationController
def load_form
@form = Form.find_by(id: params[:form_id])
return render_404 unless @form
render_404 unless @form
end
def load_form_field
@form_field = @form.form_fields.find_by(id: params[:id])
return render_404 unless @form_field
render_404 unless @form_field
end
def check_manage_permissions
render_403 unless @form && can_manage_form?(@form)
end
def form_field_params

View file

@ -1,8 +1,12 @@
# frozen_string_literal: true
class FormsController < ApplicationController
include UserRolesHelper
before_action :load_form, only: %i(show update publish unpublish)
before_action :set_breadcrumbs_items, only: %i(index show)
before_action :check_manage_permissions, only: %i(update publish unpublish)
before_action :check_create_permissions, only: :create
def index
respond_to do |format|
@ -34,7 +38,7 @@ class FormsController < ApplicationController
)
if @form.save
render json: @form, serializer: Lists::FormSerializer, user: current_user
render json: @form, serializer: FormSerializer, user: current_user
else
render json: { error: @form.errors.full_messages }, status: :unprocessable_entity
end
@ -44,7 +48,7 @@ class FormsController < ApplicationController
def update
ActiveRecord::Base.transaction do
if @form.update(form_params.merge({ last_modified_by: current_user }))
render json: @form, serializer: Lists::FormSerializer, user: current_user
render json: @form, serializer: FormSerializer, user: current_user
else
render json: { error: @form.errors.full_messages }, status: :unprocessable_entity
end
@ -58,7 +62,7 @@ class FormsController < ApplicationController
published_on: DateTime.now
)
render json: @form, serializer: Lists::FormSerializer, user: current_user
render json: @form, serializer: FormSerializer, user: current_user
end
end
@ -69,13 +73,14 @@ class FormsController < ApplicationController
published_on: nil
)
render json: @form, serializer: Lists::FormSerializer, user: current_user
render json: @form, serializer: FormSerializer, user: current_user
end
end
def archive
forms = current_team.forms.active.where(id: params[:form_ids])
return render_404 if forms.blank?
return render_403 unless forms.all? { |f| can_archive_form?(f) }
counter = 0
@ -99,6 +104,7 @@ class FormsController < ApplicationController
def restore
forms = current_team.forms.archived.where(id: params[:form_ids])
return render_404 if forms.blank?
return render_403 unless forms.all? { |f| can_restore_form?(f) }
counter = 0
@ -129,6 +135,10 @@ class FormsController < ApplicationController
}
end
def user_roles
render json: { data: user_roles_collection(Form.new).map(&:reverse) }
end
private
def set_breadcrumbs_items
@ -150,9 +160,18 @@ class FormsController < ApplicationController
end
def load_form
@form = Form.find_by(id: params[:id])
@form = current_team.forms.readable_by_user(current_user).find_by(id: params[:id])
return render_404 unless @form
render_404 unless @form
end
def check_create_permissions
render_403 unless can_create_forms?(current_team)
end
def check_manage_permissions
render_403 unless @form && can_manage_form?(@form)
end
def form_params

View file

@ -8,8 +8,11 @@
:actionsUrl="actionsUrl"
@tableReloaded="reloadingTable = false"
@create="createForm"
@access="access"
/>
</div>
<AccessModal v-if="accessModalParams" :params="accessModalParams"
@close="accessModalParams = null" @refresh="this.reloadingTable = true" />
</template>
<script>
@ -20,13 +23,19 @@ import axios from '../../packs/custom_axios.js';
import DataTable from '../shared/datatable/table.vue';
import DeleteModal from '../shared/confirmation_modal.vue';
import NameRenderer from './renderers/name.vue';
import UsersRenderer from '../projects/renderers/users.vue';
import AccessModal from '../shared/access_modal/modal.vue';
export default {
name: 'FormsTable',
components: {
DataTable,
DeleteModal,
NameRenderer
NameRenderer,
UsersRenderer,
AccessModal
},
props: {
dataSource: {
@ -39,11 +48,16 @@ export default {
},
createUrl: {
type: String
},
userRolesUrl: {
type: String,
required: true
}
},
data() {
return {
reloadingTable: false,
accessModalParams: null,
columnDefs: [
{
field: 'name',
@ -63,9 +77,12 @@ export default {
headerName: this.i18n.t('forms.index.table.used_in_protocols'),
sortable: true
}, {
field: 'access',
field: 'assigned_users',
headerName: this.i18n.t('forms.index.table.access'),
sortable: true
sortable: true,
cellRenderer: 'UsersRenderer',
minWidth: 210,
notSelectable: true
}, {
field: 'published_by',
headerName: this.i18n.t('forms.index.table.published_by'),
@ -106,6 +123,12 @@ export default {
axios.post(action.path).then((response) => {
window.location.href = response.data.data.attributes.urls.show;
});
},
access(_event, rows) {
this.accessModalParams = {
object: rows[0],
roles_path: this.userRolesUrl
};
}
}
};

View file

@ -4,6 +4,8 @@ class Form < ApplicationRecord
ID_PREFIX = 'FR'
include PrefixedIdModel
include ArchivableModel
include PermissionCheckableModel
include Assignable
belongs_to :team
belongs_to :parent, class_name: 'Form', optional: true
@ -12,9 +14,52 @@ class Form < ApplicationRecord
belongs_to :published_by, class_name: 'User', optional: true
belongs_to :archived_by, class_name: 'User', optional: true
belongs_to :restored_by, class_name: 'User', optional: true
belongs_to :default_public_user_role, class_name: 'UserRole', optional: true
has_many :form_fields, inverse_of: :form, dependent: :destroy
has_many :users, through: :user_assignments
validates :name, length: { minimum: Constants::NAME_MIN_LENGTH, maximum: Constants::NAME_MAX_LENGTH }
validates :description, length: { maximum: Constants::NAME_MAX_LENGTH }
after_create :update_automatic_user_assignments, if: -> { visible? }
before_update :change_visibility, if: :default_public_user_role_id_changed?
after_update :update_automatic_user_assignments,
if: -> { saved_change_to_default_public_user_role_id? }
enum visibility: { hidden: 0, visible: 1 }
def permission_parent
nil
end
def create_or_update_public_user_assignments!(assigned_by)
public_role = default_public_user_role || UserRole.find_predefined_viewer_role
team.user_assignments.where.not(user: assigned_by).find_each do |team_user_assignment|
new_user_assignment = user_assignments.find_or_initialize_by(user: team_user_assignment.user)
next if new_user_assignment.manually_assigned?
new_user_assignment.user_role = public_role
new_user_assignment.assigned_by = assigned_by
new_user_assignment.assigned = :automatically
new_user_assignment.save!
end
end
private
def update_automatic_user_assignments
return if skip_user_assignments
case visibility
when 'visible'
create_or_update_public_user_assignments!(last_modified_by)
when 'hidden'
automatic_user_assignments.where.not(user: last_modified_by).destroy_all
end
end
def change_visibility
self.visibility = default_public_user_role_id.present? ? :visible : :hidden
end
end

41
app/permissions/form.rb Normal file
View file

@ -0,0 +1,41 @@
# frozen_string_literal: true
Canaid::Permissions.register_for(Form) do
%i(manage_form
clone_form
publish_form)
.each do |perm|
can perm do |_, form|
form.active?
end
end
can :read_form do |user, form|
form.permission_granted?(user, FormPermissions::READ)
end
can :manage_form do |user, form|
form.permission_granted?(user, FormPermissions::MANAGE)
end
can :manage_form_users do |user, form|
form.permission_granted?(user, FormPermissions::USERS_MANAGE) ||
form.team.permission_granted?(user, TeamPermissions::MANAGE)
end
can :restore_form do |user, form|
form.archived? && form.permission_granted?(user, FormPermissions::MANAGE)
end
can :archive_form do |user, form|
form.active? && form.permission_granted?(user, FormPermissions::MANAGE)
end
can :clone_form do |user, form|
can_read_form?(user, form) && can_create_forms?(user, form.team)
end
can :publish_form do |user, form|
form.permission_granted?(user, FormPermissions::MANAGE)
end
end

View file

@ -62,6 +62,10 @@ Canaid::Permissions.register_for(Team) do
can :manage_label_templates do |user, team|
team.permission_granted?(user, TeamPermissions::LABEL_TEMPLATES_MANAGE)
end
can :create_forms do |user, team|
team.permission_granted?(user, TeamPermissions::FORMS_CREATE)
end
end
Canaid::Permissions.register_for(ProjectFolder) do

View file

@ -4,7 +4,7 @@ class FormSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers
attributes :id, :name, :published_on, :published_by, :updated_at, :urls
attributes :id, :name, :published_on, :published_by, :updated_at, :description, :urls
has_many :form_fields,
key: :form_fields,

View file

@ -5,7 +5,8 @@ module Lists
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers
attributes :id, :name, :published_on, :published_by, :updated_at, :urls, :code
attributes :id, :name, :published_on, :published_by, :updated_at, :urls, :code, :top_level_assignable, :hidden,
:team, :default_public_user_role_id, :permissions, :assigned_users
def published_by
object.published_by&.full_name
@ -19,10 +20,48 @@ module Lists
I18n.l(object.updated_at, format: :full) if object.updated_at
end
def urls
def top_level_assignable
true
end
def hidden
object.hidden?
end
def team
object.team.name
end
def assigned_users
object.user_assignments.map do |ua|
{
avatar: avatar_path(ua.user, :icon_small),
full_name: ua.user_name_with_role
}
end
end
def permissions
{
show: form_path(object)
manage_users_assignments: can_manage_form_users?(object)
}
end
def urls
urls_list = {
show: form_path(object),
show_access: access_permissions_form_path(object)
}
if can_manage_form_users?(object)
urls_list[:update_access] = access_permissions_form_path(object)
urls_list[:new_access] = new_access_permissions_form_path(id: object.id)
urls_list[:create_access] = access_permissions_forms_path(id: object.id)
urls_list[:default_public_user_role_path] =
update_default_public_user_role_access_permissions_form_path(object)
end
urls_list
end
end
end

View file

@ -8,7 +8,7 @@ module Lists
end
def fetch_records
@records = Form.where(team: @team)
@records = Form.where(team: @team).readable_by_user(@user)
end
def filter_records; end

View file

@ -9,7 +9,7 @@ module Toolbars
def initialize(current_user, form_ids: [])
@current_user = current_user
@forms = Form.where(id: forms_ids)
@forms = Form.where(id: form_ids)
@single = @forms.length == 1
end
@ -17,10 +17,22 @@ module Toolbars
def actions
return [] if @forms.none?
[].compact
[
access_action
].compact
end
private
def access_action
return unless @single
{
name: 'access',
label: I18n.t('forms.index.toolbar.access'),
icon: 'sn-icon sn-icon-project-member-access',
type: :emit
}
end
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
json.html controller.render_to_string(
partial: 'access_permissions/modals/edit_modal',
formats: [:html],
locals: {
assignable: @form,
top_level_assignable: @form,
manually_assigned_users: @form.manually_assigned_users,
update_path: access_permissions_form_path(@form),
new_assignment_path: new_access_permissions_form_path(id: @form)
},
layout: false
)
json.flash @message

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
json.form controller.render_to_string(
partial: 'access_permissions/partials/member_field',
formats: [:html],
locals: {
user: @user_assignment.user,
assignable: @form,
update_path: access_permissions_form_path(@form),
delete_path: access_permissions_form_path(@form, user_id: @user_assignment.user_id)
},
layout: false
)

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
json.html controller.render_to_string(
partial: 'access_permissions/partials/new_assignments_form',
formats: [:html],
locals: {
assignable: @form,
form_object: @user_assignment,
users: current_team.users.where.not(id: @form.manually_assigned_users.select(:id)),
create_path: access_permissions_forms_path(id: @form.id),
assignable_path: edit_access_permissions_form_path(@form)
},
layout: false
)

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
json.modal controller.render_to_string(
partial: 'access_permissions/modals/show_modal',
formats: [:html],
locals: {
assignable: @form,
top_level_assignable: @form,
manually_assigned_users: @form.manually_assigned_users
},
layout: false
)

View file

@ -12,6 +12,7 @@
actions-url="<%= actions_toolbar_forms_url %>"
data-source="<%= forms_path(format: :json) %>"
create-url="<%= forms_path %>"
user-roles-url="<%= user_roles_forms_path %>"
/>
</div>
</div>

View file

@ -0,0 +1,15 @@
<%= render partial: "global_activities/references/team",
locals: { team: team, subject: team, breadcrumbs: breadcrumbs, values: values, type_of: type_of } %>
<div class="ga-breadcrumb">
<span class="sn-icon sn-icon-reports"></span>
<% if subject %>
<%= route_to_other_team(form_path(subject, team: subject.team.id, preview_report_id: subject.id, preview_type: :pdf),
team,
subject.name&.truncate(Constants::NAME_TRUNCATION_LENGTH),
title: subject.name) %>
<% else %>
<span title="<%= breadcrumbs['form'] %>">
<%= breadcrumbs['form']&.truncate(Constants::NAME_TRUNCATION_LENGTH) %>
</span>
<% end %>
</div>

View file

@ -190,7 +190,7 @@ class Extends
ACTIVITY_SUBJECT_TYPES = %w(
Team RepositoryBase Project Experiment MyModule Result Protocol Report RepositoryRow
ProjectFolder Asset Step LabelTemplate StorageLocation StorageLocationRepositoryRow
ProjectFolder Asset Step LabelTemplate StorageLocation StorageLocationRepositoryRow Form
).freeze
SEARCHABLE_ACTIVITY_SUBJECT_TYPES = %w(
@ -520,7 +520,13 @@ class Extends
task_step_restore_asset_version: 327,
task_result_restore_asset_version: 328,
repository_column_restore_asset_version: 329,
protocol_step_restore_asset_version: 330
protocol_step_restore_asset_version: 330,
form_access_granted: 331,
form_access_changed: 332,
form_access_revoked: 333,
form_access_granted_all_team_members: 334,
form_access_changed_all_team_members: 335,
form_access_revoked_all_team_members: 336
}
ACTIVITY_GROUPS = {
@ -543,10 +549,11 @@ class Extends
label_templates: [*216..219],
storage_locations: [*309..315],
container_storage_locations: [*316..322, 326],
storage_location_repository_rows: [*323..325]
storage_location_repository_rows: [*323..325],
forms: [331, 332, 333, 334, 335, 336]
}
TOP_LEVEL_ASSIGNABLES = %w(Project Team Protocol Repository).freeze
TOP_LEVEL_ASSIGNABLES = %w(Project Team Protocol Repository Form).freeze
SHARED_OBJECTS_PERMISSION_LEVELS = {
not_shared: 0,

View file

@ -10,6 +10,7 @@ module PermissionExtends
PROJECTS_CREATE
INVENTORIES_CREATE
PROTOCOLS_CREATE
FORMS_CREATE
REPORTS_CREATE
LABEL_TEMPLATES_READ
LABEL_TEMPLATES_MANAGE
@ -33,6 +34,16 @@ module PermissionExtends
).each { |permission| const_set(permission, "protocol_#{permission.parameterize}") }
end
module FormPermissions
%w(
NONE
READ
READ_ARCHIVED
MANAGE
USERS_MANAGE
).each { |permission| const_set(permission, "form_#{permission.parameterize}") }
end
module ReportPermissions
%w(
NONE
@ -140,6 +151,7 @@ module PermissionExtends
OWNER_PERMISSIONS = (
TeamPermissions.constants.map { |const| TeamPermissions.const_get(const) } +
ProtocolPermissions.constants.map { |const| ProtocolPermissions.const_get(const) } +
FormPermissions.constants.map { |const| FormPermissions.const_get(const) } +
ReportPermissions.constants.map { |const| ReportPermissions.const_get(const) } +
ProjectPermissions.constants.map { |const| ProjectPermissions.const_get(const) } +
ExperimentPermissions.constants.map { |const| ExperimentPermissions.const_get(const) } +
@ -151,6 +163,7 @@ module PermissionExtends
TeamPermissions::PROJECTS_CREATE,
TeamPermissions::PROTOCOLS_CREATE,
TeamPermissions::REPORTS_CREATE,
TeamPermissions::FORMS_CREATE,
TeamPermissions::LABEL_TEMPLATES_READ,
TeamPermissions::LABEL_TEMPLATES_MANAGE,
TeamPermissions::STORAGE_LOCATIONS_READ,
@ -162,6 +175,8 @@ module PermissionExtends
ProtocolPermissions::READ,
ProtocolPermissions::READ_ARCHIVED,
ProtocolPermissions::MANAGE_DRAFT,
FormPermissions::READ,
FormPermissions::READ_ARCHIVED,
ReportPermissions::READ,
ReportPermissions::MANAGE,
ProjectPermissions::READ,
@ -259,6 +274,8 @@ module PermissionExtends
TeamPermissions::STORAGE_LOCATION_CONTAINERS_READ,
ProtocolPermissions::READ,
ProtocolPermissions::READ_ARCHIVED,
FormPermissions::READ,
FormPermissions::READ_ARCHIVED,
ReportPermissions::READ,
ProjectPermissions::READ,
ProjectPermissions::READ_ARCHIVED,

View file

@ -1066,6 +1066,7 @@ en:
head_title: "Forms"
toolbar:
new: 'New form'
access: 'Access'
table:
name: 'Form name'
code: 'ID'
@ -4239,6 +4240,8 @@ en:
experiment_tooltip_inherit: "This role was inherited from the experiment."
mymodule: "Task"
mymodule_tooltip: "This role was set on this task"
form_tooltip: "This role was set on this form."
form_tooltip_inherit: "This role was inherited from the form."
public_members_dropdown:
title: "Members of team %{team}"
@ -4268,6 +4271,12 @@ en:
edit_modal:
title: "Manage access for %{resource_name}"
description: "Changing a role will take away any permissions inherited from the project or the experiment. New permissions will apply only to this specific task."
forms:
modals:
show_modal:
title: "Access to %{resource_name}"
edit_modal:
title: "Manage access for %{resource_name}"
zip_export:
repositories_modal_label: 'Export inventory'
notification_title: 'Your requested export package is ready!'

View file

@ -344,6 +344,12 @@ en:
task_result_restore_asset_version_html: "%{user} restored version <strong>%{version}</strong> of the file <strong>%{file}</strong> on result %{result} on task %{my_module}."
repository_column_restore_asset_version_html: "%{user} restored version <strong>%{version}</strong> of the file <strong>%{file}</strong> in inventory column %{repository_column} in inventory %{repository}."
protocol_step_restore_asset_version_html: "%{user} restored version <strong>%{version}</strong> of the file <strong>%{file}</strong> on Protocol's Template %{protocol} step <strong>%{step}</strong> in Protocol repository."
form_access_granted_html: "%{user} granted access to %{user_target} with user role %{role} to form %{form}."
form_access_changed_html: "%{user} changed %{user_target}'s role on form %{form} to %{role}."
form_access_revoked_html: "%{user} removed %{user_target} with user role %{role} from form %{form}."
form_access_granted_all_team_members_html: "%{user} granted access to all team members of %{team} team with user role %{role} to form template %{form}."
form_access_changed_all_team_members_html: "%{user} changed %{team}'s role on form template %{form} to %{role}."
form_access_revoked_all_team_members_html: "%{user} removed %{team} team members with user role %{role} from form template %{form}."
activity_name:
create_project: "Project created"
rename_project: "Project renamed"
@ -661,6 +667,7 @@ en:
storage_locations: "Inventory locations"
container_storage_locations: "Inventory location boxes"
storage_location_repository_rows: "Item assignments to location"
forms: "Forms"
subject_name:
repository: "Inventory"
project: "Project"
@ -671,3 +678,4 @@ en:
step: "Step"
report: "Report"
storage_location: "Location"
form: "Form"

View file

@ -320,6 +320,10 @@ Rails.application.routes.draw do
put :update_default_public_user_role, on: :member
end
resources :forms, defaults: { format: 'json' } do
put :update_default_public_user_role, on: :member
end
resources :experiments, only: %i(show update edit)
resources :my_modules, only: %i(show update edit)
end
@ -860,6 +864,7 @@ Rails.application.routes.draw do
get :actions_toolbar
post :archive
post :restore
get :user_roles
end
resources :form_fields, only: %i(create update destroy) do

View file

@ -0,0 +1,47 @@
# frozen_string_literal: true
class AddFormPermissions < ActiveRecord::Migration[7.0]
FORM_MANAGE_PERMISSION = [
TeamPermissions::FORMS_CREATE,
FormPermissions::READ,
FormPermissions::READ_ARCHIVED,
FormPermissions::MANAGE
].freeze
FORM_READ_PERMISSION = [
FormPermissions::READ,
FormPermissions::READ_ARCHIVED
].freeze
def up
@owner_role = UserRole.find_predefined_owner_role
@normal_user_role = UserRole.find_predefined_normal_user_role
@viewer_user_role = UserRole.find_predefined_viewer_role
@owner_role.permissions = @owner_role.permissions | (FORM_MANAGE_PERMISSION + [FormPermissions::USERS_MANAGE]) |
FORM_READ_PERMISSION
@normal_user_role.permissions = @normal_user_role.permissions | FORM_MANAGE_PERMISSION |
FORM_READ_PERMISSION
@viewer_user_role.permissions = @viewer_user_role.permissions | FORM_READ_PERMISSION
@owner_role.save(validate: false)
@normal_user_role.save(validate: false)
@viewer_user_role.save(validate: false)
end
def down
@owner_role = UserRole.find_predefined_owner_role
@normal_user_role = UserRole.find_predefined_normal_user_role
@viewer_user_role = UserRole.find_predefined_viewer_role
@owner_role.permissions = @owner_role.permissions - FORM_MANAGE_PERMISSION -
FORM_READ_PERMISSION - [FormPermissions::USERS_MANAGE]
@normal_user_role.permissions = @normal_user_role.permissions - FORM_MANAGE_PERMISSION -
FORM_READ_PERMISSION
@viewer_user_role.permissions = @viewer_user_role.permissions - FORM_READ_PERMISSION
@owner_role.save(validate: false)
@normal_user_role.save(validate: false)
@viewer_user_role.save(validate: false)
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddPublicVisibilityColumnsToForms < ActiveRecord::Migration[7.0]
def change
add_column :forms, :visibility, :integer, default: 0
add_index :forms, :visibility
add_reference :forms, :default_public_user_role, foreign_key: { to_table: :user_roles }
end
end

View file

@ -7,13 +7,13 @@ describe FormFieldsController, type: :controller do
include_context 'reference_project_structure'
let!(:form) { create :form, team: team }
let!(:form_field) { create :form_field, form: form }
let!(:form_fields) { create_list :form_field, 5, form: form }
let!(:form) { create :form, team: team, created_by: user }
let!(:form_field) { create :form_field, form: form, created_by: user }
let!(:form_fields) { create_list :form_field, 5, form: form, created_by: user }
describe 'POST create' do
let(:action) { post :create, params: params, format: :json }
let(:params) do
let(:params) do
{
form_id: form.id,
form_field: {
@ -49,7 +49,7 @@ describe FormFieldsController, type: :controller do
describe 'PUT update' do
let(:action) { put :update, params: params, format: :json }
let(:params) do
let(:params) do
{
form_id: form.id,
id: form_field.id,

View file

@ -7,12 +7,12 @@ describe FormsController, type: :controller do
include_context 'reference_project_structure'
let!(:form) { create :form, team: team }
let!(:form2) { create :form, team: team }
let!(:form_field) { create :form_field, form: form2}
let!(:form) { create :form, team: team, created_by: user }
let!(:form2) { create :form, team: team, created_by: user }
let!(:form_field) { create :form_field, form: form2, created_by: user }
describe '#index' do
let(:params) { { team: team.id } }
let(:params) { { team: team.id, per_page: 20, page: 1 } }
it 'returns success response' do
get :index, params: params, format: :json
@ -20,14 +20,6 @@ describe FormsController, type: :controller do
response_body = JSON.parse(response.body)
expect(response_body).to match_array(
JSON.parse(
ActiveModelSerializers::SerializableResource
.new(team.forms,
each_serializer: Lists::FormSerializer)
.to_json
)
)
expect(response_body['data'].length).to eq 2
expect(response.body).to include(form.name)
expect(response.body).to include(form2.name)
@ -44,18 +36,8 @@ describe FormsController, type: :controller do
it 'successful response' do
get :show, format: :json, params: { id: form2.id }
expect(response).to have_http_status(:success)
response_body = JSON.parse(response.body)
expect(response_body).to match_array(
JSON.parse(
ActiveModelSerializers::SerializableResource
.new(form2,
serializer: Lists::FormSerializer,
include: :form_fields)
.to_json
)
)
response_body = JSON.parse(response.body)
expect(response_body['included'].first['attributes']['name']).to eq form_field.name
end
end
@ -113,7 +95,7 @@ describe FormsController, type: :controller do
expect(response).to have_http_status(:success)
expect(response.media_type).to eq 'application/json'
response_body = JSON.parse(response.body)
expect(response_body['data']['attributes']['published_by']).to eq user.full_name
expect(response_body['data']['attributes']['published_on']).not_to eq nil
end
@ -137,7 +119,7 @@ describe FormsController, type: :controller do
expect(response).to have_http_status(:success)
expect(response.media_type).to eq 'application/json'
response_body = JSON.parse(response.body)
expect(response_body['data']['attributes']['published_by']).to eq nil
expect(response_body['data']['attributes']['published_on']).to eq nil
end
@ -166,7 +148,7 @@ describe FormsController, type: :controller do
end
describe 'POST restore' do
let!(:form_archived) { create :form, :archived, team: team }
let!(:form_archived) { create :form, :archived, team: team, created_by: user }
let(:action) { put :restore, params: params, format: :json }
let(:params) do
{
@ -181,5 +163,5 @@ describe FormsController, type: :controller do
form_archived.reload
expect(form_archived.archived?).to eq false
end
end
end
end

View file

@ -27,7 +27,6 @@ describe FormField, type: :model do
it { should have_db_column :uid }
it { should have_db_column :created_at }
it { should have_db_column :updated_at }
it { should have_db_column :discarded_at }
end
describe 'Relations' do