mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-02-11 09:26:37 +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
|
||||
# and outputs a link to resource
|
||||
def smart_annotation_filter_resources(text)
|
||||
sa_reg = /\[\#(.*?)~(prj|exp|tsk|sam)~([0-9a-zA-Z]+)\]/
|
||||
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
|
||||
SmartAnnotations::TagToHtml.new(current_user, text).html
|
||||
end
|
||||
|
||||
# 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,
|
||||
# read notifications
|
||||
# reports: read
|
||||
# samples: read
|
||||
can :read_project do |user, project|
|
||||
user.is_member_of_project?(project) ||
|
||||
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