mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-02-16 03:43:13 +08:00
Merge branch 'zd_SCI_2222' of git://github.com/ZmagoD/scinote-web into ZmagoD-zd_SCI_2222
This commit is contained in:
commit
b7c31d704f
8 changed files with 330 additions and 63 deletions
|
@ -117,68 +117,7 @@ module ApplicationHelper
|
||||||
# Check if text have smart annotations of resources
|
# Check if text have smart annotations of resources
|
||||||
# and outputs a link to resource
|
# and outputs a link to resource
|
||||||
def smart_annotation_filter_resources(text)
|
def smart_annotation_filter_resources(text)
|
||||||
sa_reg = /\[\#(.*?)~(prj|exp|tsk|sam)~([0-9a-zA-Z]+)\]/
|
SmartAnnotations::TagToHtml.new(current_user, text).html
|
||||||
new_text = text.gsub(sa_reg) do |el|
|
|
||||||
match = el.match(sa_reg)
|
|
||||||
case match[2]
|
|
||||||
when 'prj'
|
|
||||||
project = Project.find_by_id(match[3].base62_decode)
|
|
||||||
next unless project
|
|
||||||
if project.archived?
|
|
||||||
"<span class='sa-type'>" \
|
|
||||||
"#{sanitize_input(match[2])}</span>" \
|
|
||||||
"#{link_to project.name,
|
|
||||||
projects_archive_path} #{I18n.t('atwho.res.archived')}"
|
|
||||||
else
|
|
||||||
"<span class='sa-type'>" \
|
|
||||||
"#{sanitize_input(match[2])}</span>" \
|
|
||||||
"#{link_to project.name,
|
|
||||||
project_path(project)}"
|
|
||||||
end
|
|
||||||
when 'exp'
|
|
||||||
experiment = Experiment.find_by_id(match[3].base62_decode)
|
|
||||||
next unless experiment
|
|
||||||
if experiment.archived?
|
|
||||||
"<span class='sa-type'>" \
|
|
||||||
"#{sanitize_input(match[2])}</span>" \
|
|
||||||
"#{link_to experiment.name,
|
|
||||||
experiment_archive_project_path(experiment.project)} " \
|
|
||||||
"#{I18n.t('atwho.res.archived')}"
|
|
||||||
else
|
|
||||||
"<span class='sa-type'>"\
|
|
||||||
"#{sanitize_input(match[2])}</span>" \
|
|
||||||
"#{link_to experiment.name,
|
|
||||||
canvas_experiment_path(experiment)}"
|
|
||||||
end
|
|
||||||
when 'tsk'
|
|
||||||
my_module = MyModule.find_by_id(match[3].base62_decode)
|
|
||||||
next unless my_module
|
|
||||||
if my_module.archived?
|
|
||||||
"<span class='sa-type'>" \
|
|
||||||
"#{sanitize_input(match[2])}</span>" \
|
|
||||||
"#{link_to my_module.name,
|
|
||||||
module_archive_experiment_path(my_module.experiment)} " \
|
|
||||||
"#{I18n.t('atwho.res.archived')}"
|
|
||||||
else
|
|
||||||
"<span class='sa-type'>" \
|
|
||||||
"#{sanitize_input(match[2])}</span>" \
|
|
||||||
"#{link_to my_module.name,
|
|
||||||
protocols_my_module_path(my_module)}"
|
|
||||||
end
|
|
||||||
when 'sam'
|
|
||||||
sample = Sample.find_by_id(match[3].base62_decode)
|
|
||||||
if sample
|
|
||||||
"<span class='glyphicon glyphicon-tint'></span>" \
|
|
||||||
"#{link_to(sample.name,
|
|
||||||
sample_path(sample.id),
|
|
||||||
class: 'sample-info-link')}"
|
|
||||||
else
|
|
||||||
"<span class='glyphicon glyphicon-tint'></span>" \
|
|
||||||
"#{match[1]} #{I18n.t('atwho.res.deleted')}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
new_text
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if text have smart annotations of users
|
# Check if text have smart annotations of users
|
||||||
|
|
|
@ -16,7 +16,6 @@ Canaid::Permissions.register_for(Project) do
|
||||||
# project: read, read activities, read comments, read users, read archive,
|
# project: read, read activities, read comments, read users, read archive,
|
||||||
# read notifications
|
# read notifications
|
||||||
# reports: read
|
# reports: read
|
||||||
# samples: read
|
|
||||||
can :read_project do |user, project|
|
can :read_project do |user, project|
|
||||||
user.is_member_of_project?(project) ||
|
user.is_member_of_project?(project) ||
|
||||||
user.is_admin_of_team?(project.team) ||
|
user.is_admin_of_team?(project.team) ||
|
||||||
|
|
35
app/services/smart_annotations/permision_eval.rb
Normal file
35
app/services/smart_annotations/permision_eval.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module SmartAnnotations
|
||||||
|
class PermissionEval
|
||||||
|
class << self
|
||||||
|
include Canaid::Helpers::PermissionsHelper
|
||||||
|
|
||||||
|
def check(user, type, object)
|
||||||
|
send("validate_#{type}_permissions", user, object)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_prj_permissions(user, object)
|
||||||
|
can_read_project?(user, object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_exp_permissions(user, object)
|
||||||
|
can_read_experiment?(user, object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_tsk_permissions(user, object)
|
||||||
|
can_read_experiment?(user, object.experiment)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_sam_permissions(user, object)
|
||||||
|
can_read_team?(user, object.team)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_rep_item_permissions(user, object)
|
||||||
|
can_read_team?(user, object.repository.team)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
73
app/services/smart_annotations/preview.rb
Normal file
73
app/services/smart_annotations/preview.rb
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module SmartAnnotations
|
||||||
|
class Preview
|
||||||
|
class << self
|
||||||
|
def html(name, type, object)
|
||||||
|
send("generate_#{type}_snippet", name, object)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
ROUTES = Rails.application.routes.url_helpers
|
||||||
|
|
||||||
|
def generate_prj_snippet(_, object)
|
||||||
|
if object.archived?
|
||||||
|
return "<span class='sa-type'>PRJ</span><a href='" \
|
||||||
|
"#{ROUTES.projects_archive_path}'>#{object.name}</a>" \
|
||||||
|
"#{I18n.t('atwho.res.archived')}"
|
||||||
|
end
|
||||||
|
"<span class='sa-type'>PRJ</span>" \
|
||||||
|
"<a href='#{ROUTES.project_path(object)}'>#{object.name}</a>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_exp_snippet(_, object)
|
||||||
|
if object.archived?
|
||||||
|
return "<span class='sa-type'>EXP</span><a href='" \
|
||||||
|
"#{ROUTES.experiment_archive_project_path(object.project)}'>" \
|
||||||
|
"#{object.name}</a> #{I18n.t('atwho.res.archived')}"
|
||||||
|
end
|
||||||
|
"<span class='sa-type'>EXP</span>" \
|
||||||
|
"<a href='#{ROUTES.canvas_experiment_path(object)}'>#{object.name}</a>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_tsk_snippet(_, object)
|
||||||
|
if object.archived?
|
||||||
|
return "<span class='sa-type'>TSK</span><a href='" \
|
||||||
|
"#{ROUTES.module_archive_experiment_path(
|
||||||
|
object.experiment
|
||||||
|
)}'>#{object.name}</a> #{I18n.t('atwho.res.archived')}"
|
||||||
|
end
|
||||||
|
"<span class='sa-type'>TSK</span>" \
|
||||||
|
"<a href='#{ROUTES.protocols_my_module_path(object)}'>" \
|
||||||
|
"#{object.name}</a>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_sam_snippet(name, object)
|
||||||
|
if object
|
||||||
|
return "<span class='glyphicon glyphicon-tint'></span>" \
|
||||||
|
"<a href='#{ROUTES.sample_path(object.id)}' " \
|
||||||
|
"class='sample-info-link'>#{object.name}</a>"
|
||||||
|
end
|
||||||
|
"<span class='glyphicon glyphicon-tint'></span>" \
|
||||||
|
"#{name} #{I18n.t('atwho.res.deleted')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_rep_item_snippet(name, object)
|
||||||
|
if object
|
||||||
|
repository_name = object.repository.name
|
||||||
|
return "<span class='sa-type'>" \
|
||||||
|
"#{trim_repository_name(repository_name)}</span>" \
|
||||||
|
"<a href='#{ROUTES.repository_row_path(object)}' " \
|
||||||
|
"class='record-info-link'>#{object.name}</a>"
|
||||||
|
end
|
||||||
|
"<span class='sa-type'>REP</span>" \
|
||||||
|
"#{name} #{I18n.t('atwho.res.deleted')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def trim_repository_name(name)
|
||||||
|
name.strip.slice(0..2).upcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
67
app/services/smart_annotations/tag_to_html.rb
Normal file
67
app/services/smart_annotations/tag_to_html.rb
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'smart_annotations/permision_eval'
|
||||||
|
require 'smart_annotations/preview'
|
||||||
|
|
||||||
|
module SmartAnnotations
|
||||||
|
class TagToHtml
|
||||||
|
attr_reader :html
|
||||||
|
|
||||||
|
def initialize(user, text)
|
||||||
|
parse(user, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
REGEX = /\[\#(.*?)~(prj|exp|tsk|sam|rep_item)~([0-9a-zA-Z]+)\]/
|
||||||
|
OBJECT_MAPPINGS = { prj: Project,
|
||||||
|
exp: Experiment,
|
||||||
|
tsk: MyModule,
|
||||||
|
sam: Sample,
|
||||||
|
rep_item: RepositoryRow }.freeze
|
||||||
|
|
||||||
|
def parse(user, text)
|
||||||
|
@html = text.gsub(REGEX) do |el|
|
||||||
|
value = extract_values(el)
|
||||||
|
type = value[:object_type]
|
||||||
|
begin
|
||||||
|
object = fetch_object(type, value[:object_id])
|
||||||
|
# handle samples/repository_items edge case
|
||||||
|
if type.in? %w(sam rep_item)
|
||||||
|
sample_or_repository_item(value[:name], user, type, object)
|
||||||
|
else
|
||||||
|
next unless object && SmartAnnotations::PermissionEval.check(user,
|
||||||
|
type,
|
||||||
|
object)
|
||||||
|
SmartAnnotations::Preview.html(nil, type, object)
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sample_or_repository_item(name, user, type, object)
|
||||||
|
if object && SmartAnnotations::PermissionEval.check(user, type, object)
|
||||||
|
return SmartAnnotations::Preview.html(nil, type, object)
|
||||||
|
end
|
||||||
|
SmartAnnotations::Preview.html(name, type, object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_values(element)
|
||||||
|
match = element.match(REGEX)
|
||||||
|
{
|
||||||
|
name: match[1],
|
||||||
|
object_type: match[2],
|
||||||
|
object_id: match[3].base62_decode
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_object(type, id)
|
||||||
|
klass = OBJECT_MAPPINGS.fetch(type.to_sym) do
|
||||||
|
raise ActiveRecord::RecordNotFound.new("#{type} does not exist")
|
||||||
|
end
|
||||||
|
klass.find_by_id(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
47
spec/services/smart_annotations/permission_eval_spec.rb
Normal file
47
spec/services/smart_annotations/permission_eval_spec.rb
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'smart_annotations/permision_eval'
|
||||||
|
|
||||||
|
describe SmartAnnotations::PermissionEval do
|
||||||
|
let(:subject) { described_class }
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:team) { create :team }
|
||||||
|
let(:user_team) { create :user_team, user: user, team: team, role: 2 }
|
||||||
|
let(:project) { create :project, name: 'my project' }
|
||||||
|
let(:experiment) do
|
||||||
|
create :experiment, name: 'my experiment',
|
||||||
|
project: project,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
let(:task) { create :my_module, name: 'task', experiment: experiment }
|
||||||
|
let(:repository) { create :repository, team: team, created_by: user }
|
||||||
|
let(:repository_item) { create :repository_row, repository: repository }
|
||||||
|
|
||||||
|
describe '#validate_prj_permissions/2' do
|
||||||
|
it 'returns a boolean' do
|
||||||
|
value = subject.send(:validate_prj_permissions, user, project)
|
||||||
|
expect(value).to be_in([true, false])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#validate_exp_permissions/2' do
|
||||||
|
it 'returns a boolean' do
|
||||||
|
value = subject.send(:validate_exp_permissions, user, experiment)
|
||||||
|
expect(value).to be_in([true, false])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#validate_tsk_permissions/2' do
|
||||||
|
it 'returns a boolean' do
|
||||||
|
value = subject.send(:validate_tsk_permissions, user, task)
|
||||||
|
expect(value).to be_in([true, false])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#validate_rep_item_permissions/2' do
|
||||||
|
it 'returns a boolean' do
|
||||||
|
value = subject.send(:validate_rep_item_permissions, user, repository_item)
|
||||||
|
expect(value).to be_in([true, false])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
63
spec/services/smart_annotations/preview_spec.rb
Normal file
63
spec/services/smart_annotations/preview_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'smart_annotations/preview'
|
||||||
|
|
||||||
|
describe SmartAnnotations::Preview do
|
||||||
|
let(:subject) { described_class }
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:project) { create :project, name: 'my project' }
|
||||||
|
let(:experiment) do
|
||||||
|
create :experiment, name: 'my experiment',
|
||||||
|
project: project,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
let(:task) { create :my_module, name: 'task', experiment: experiment }
|
||||||
|
|
||||||
|
describe 'Project annotations with type prj' do
|
||||||
|
it 'returns a html snippet' do
|
||||||
|
snippet = subject.html(nil, 'prj', project)
|
||||||
|
expect(snippet).to eq(
|
||||||
|
"<span class='sa-type'>PRJ</span>" \
|
||||||
|
"<a href='/projects/#{project.id}'>my project</a>"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Experiment annotations with type exp' do
|
||||||
|
it 'returns a html snippet' do
|
||||||
|
snippet = subject.html(nil, 'exp', experiment)
|
||||||
|
expect(snippet).to eq(
|
||||||
|
"<span class='sa-type'>EXP</span>" \
|
||||||
|
"<a href='/experiments/#{experiment.id}/canvas'>my experiment</a>"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'MyModule annotations with type tsk' do
|
||||||
|
it 'returns a html snippet' do
|
||||||
|
snippet = subject.html(nil, 'tsk', task)
|
||||||
|
expect(snippet).to eq(
|
||||||
|
"<span class='sa-type'>TSK</span>" \
|
||||||
|
"<a href='/modules/#{task.id}/protocols'>task</a>"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Repository item annotations with type rep_item' do
|
||||||
|
it 'returns a html snippet' do
|
||||||
|
snippet = subject.html('my item', 'rep_item', nil)
|
||||||
|
expect(snippet).to eq(
|
||||||
|
'<span class=\'sa-type\'>REP</span>my item (deleted)'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#trim_repository_name/1' do
|
||||||
|
it 'is returns a 3 letter upcase string' do
|
||||||
|
trimmed_repository_name = subject.__send__(
|
||||||
|
:trim_repository_name, 'banana'
|
||||||
|
)
|
||||||
|
expect(trimmed_repository_name).to eq('BAN')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
44
spec/services/smart_annotations/tag_to_html_spec.rb
Normal file
44
spec/services/smart_annotations/tag_to_html_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe SmartAnnotations::TagToHtml do
|
||||||
|
let!(:user) { create :user }
|
||||||
|
let!(:team) { create :team }
|
||||||
|
let!(:user_team) { create :user_team, user: user, team: team, role: 2 }
|
||||||
|
let!(:project) { create :project, name: 'my project', team: team }
|
||||||
|
let!(:user_project) do
|
||||||
|
create :user_project, project: project, user: user, role: 0
|
||||||
|
end
|
||||||
|
let(:text) do
|
||||||
|
"My annotation of [#my project~prj~#{project.id.base62_encode}]"
|
||||||
|
end
|
||||||
|
let(:subject) { described_class.new(user, text) }
|
||||||
|
describe 'Parsed text' do
|
||||||
|
it 'returns a existing string with smart annotation' do
|
||||||
|
expect(subject.html).to eq(
|
||||||
|
"My annotation of <span class='sa-type'>PRJ</span>"\
|
||||||
|
"<a href='/projects/#{project.id}'>my project</a>"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#extract_values/1' do
|
||||||
|
it 'returns a parsed hash of smart annotation' do
|
||||||
|
values = subject.send(:extract_values, '[#my project~prj~1]')
|
||||||
|
expect(values[:name]).to eq 'my project'
|
||||||
|
expect(values[:object_id]).to eq 1
|
||||||
|
expect(values[:object_type]).to eq 'prj'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#fetch_object/2' do
|
||||||
|
it 'rises an error if type is not valid' do
|
||||||
|
expect {
|
||||||
|
subject.send(:fetch_object, 'banana', project.id)
|
||||||
|
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the required object' do
|
||||||
|
expect(subject.send(:fetch_object, 'prj', project.id)).to eq project
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue