Merge branch 'zd_SCI_2222' of git://github.com/ZmagoD/scinote-web into ZmagoD-zd_SCI_2222

This commit is contained in:
Oleksii Kriuchykhin 2018-04-01 11:10:18 +02:00
commit b7c31d704f
8 changed files with 330 additions and 63 deletions

View file

@ -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

View file

@ -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) ||

View 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

View 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

View 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

View 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

View 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

View 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