mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-11-10 17:36:33 +08:00
Merge pull request #5336 from aignatov-bio/ai-sci-8233-add-backend-for-navigator
Add backend for navigator [SCI-8233]
This commit is contained in:
commit
32dc12f4d0
20 changed files with 487 additions and 47 deletions
|
@ -12,11 +12,6 @@ var Sidebar = (function() {
|
|||
|
||||
function reloadSidebar(params) {
|
||||
let url = $(SIDEBAR_CONTAINER).data('sidebar-url');
|
||||
$.get(url, params, function(result) {
|
||||
$(SIDEBAR_CONTAINER).find('.sidebar-body').html(result.html);
|
||||
showSelectedLeaf();
|
||||
$(SIDEBAR_CONTAINER).data('scrollBar').update();
|
||||
});
|
||||
}
|
||||
|
||||
function initSideBar() {
|
||||
|
|
|
@ -9,7 +9,6 @@ var SideBarToggle = (function() {
|
|||
$(SIDEBAR_CONTAINER).removeClass('collapsed');
|
||||
$(WRAPPER).css('paddingLeft', 'var(--wrapper-width)');
|
||||
$('.navbar-secondary').removeClass("navbar-without-sidebar");
|
||||
$.post($(LAYOUT).data('navitgator-state-url'), {state: 'open'});
|
||||
$(WRAPPER).trigger('sideBar::show');
|
||||
$(WRAPPER).one("transitionend", function() {
|
||||
$(WRAPPER).trigger('sideBar::shown');
|
||||
|
@ -21,7 +20,6 @@ var SideBarToggle = (function() {
|
|||
$(SIDEBAR_CONTAINER).addClass('collapsed');
|
||||
$(WRAPPER).css('paddingLeft', '0');
|
||||
$('.navbar-secondary').addClass("navbar-without-sidebar");
|
||||
$.post($(LAYOUT).data('navitgator-state-url'), {state: 'collapsed'});
|
||||
$(WRAPPER).trigger('sideBar::hide');
|
||||
$(WRAPPER).one("transitionend", function() {
|
||||
$(WRAPPER).trigger('sideBar::hidden');
|
||||
|
|
|
@ -116,3 +116,7 @@ $color-dd-hover: #f5f5f5;
|
|||
.sn-color-primary {
|
||||
color: var(--sn-blue);
|
||||
}
|
||||
|
||||
.sn-background-background-violet {
|
||||
background-color: var(--sn-light-grey);
|
||||
}
|
||||
|
|
|
@ -663,6 +663,9 @@ class ExperimentsController < ApplicationController
|
|||
end
|
||||
|
||||
def set_navigator
|
||||
@navigator = true
|
||||
@navigator = {
|
||||
url: tree_navigator_experiment_path(@experiment),
|
||||
id: @experiment.code
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -612,6 +612,9 @@ class MyModulesController < ApplicationController
|
|||
end
|
||||
|
||||
def set_navigator
|
||||
@navigator = true
|
||||
@navigator = {
|
||||
url: tree_navigator_my_module_path(@my_module),
|
||||
id: @my_module.code
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
155
app/controllers/navigator/base_controller.rb
Normal file
155
app/controllers/navigator/base_controller.rb
Normal file
|
@ -0,0 +1,155 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Navigator
|
||||
class BaseController < ApplicationController
|
||||
private
|
||||
|
||||
def project_serializer(project)
|
||||
{
|
||||
id: project.code,
|
||||
name: project.name,
|
||||
url: project_path(project),
|
||||
archived: project.archived,
|
||||
type: :project,
|
||||
has_children: project.has_children,
|
||||
children_url: navigator_project_path(project)
|
||||
}
|
||||
end
|
||||
|
||||
def folder_serializer(folder)
|
||||
{
|
||||
id: folder.code,
|
||||
name: folder.name,
|
||||
url: projects_path(project_folder_id: folder.id),
|
||||
archived: folder.archived,
|
||||
type: :folder,
|
||||
has_children: folder.has_children,
|
||||
children_url: navigator_project_folder_path(folder)
|
||||
}
|
||||
end
|
||||
|
||||
def experiment_serializer(experiment)
|
||||
{
|
||||
id: experiment.code,
|
||||
name: experiment.name,
|
||||
url: canvas_experiment_path(experiment),
|
||||
archived: experiment.archived,
|
||||
type: :experiment,
|
||||
has_children: experiment.has_children,
|
||||
children_url: navigator_experiment_path(experiment)
|
||||
}
|
||||
end
|
||||
|
||||
def my_module_serializer(my_module)
|
||||
{
|
||||
id: my_module.code,
|
||||
name: my_module.name,
|
||||
type: :my_module,
|
||||
url: protocols_my_module_path(my_module),
|
||||
archived: my_module.archived,
|
||||
has_children: false
|
||||
}
|
||||
end
|
||||
|
||||
def fetch_projects(folder = nil, archived = false)
|
||||
has_children_sql = if !archived
|
||||
'SUM(CASE WHEN experiments.archived IS FALSE THEN 1 ELSE 0 END) > 0 AS has_children'
|
||||
else
|
||||
'SUM(CASE WHEN experiments.archived IS TRUE OR my_modules.archived IS TRUE
|
||||
THEN 1 ELSE 0 END) > 0 AS has_children'
|
||||
end
|
||||
current_team.projects
|
||||
.where(project_folder_id: folder)
|
||||
.viewable_by_user(current_user, current_team)
|
||||
.with_children_viewable_by_user(current_user)
|
||||
.where('
|
||||
projects.archived = :archived OR
|
||||
(
|
||||
(
|
||||
experiments.archived = :archived OR
|
||||
my_modules.archived = :archived
|
||||
) AND
|
||||
:archived IS TRUE
|
||||
)
|
||||
', archived: archived)
|
||||
.select(
|
||||
'projects.id',
|
||||
'projects.name',
|
||||
'projects.archived',
|
||||
has_children_sql
|
||||
).group('projects.id')
|
||||
end
|
||||
|
||||
def fetch_project_folders(folder = nil, archived = false)
|
||||
current_team.project_folders.where(parent_folder: folder)
|
||||
.left_outer_joins(projects: { user_assignments: :user_role }, project_folders: {})
|
||||
.where(project_folders: { archived: archived })
|
||||
.where('
|
||||
user_assignments.user_id = ? AND
|
||||
user_roles.permissions @> ARRAY[?]::varchar[] OR
|
||||
projects.id IS NULL
|
||||
', current_user.id, ProjectPermissions::READ)
|
||||
.select(
|
||||
'project_folders.id',
|
||||
'project_folders.name',
|
||||
'project_folders.archived',
|
||||
'SUM(CASE WHEN projects.id IS NOT NULL OR project_folders_project_folders.id IS NOT NULL
|
||||
THEN 1 ELSE 0 END) > 0 AS has_children'
|
||||
).group('project_folders.id')
|
||||
end
|
||||
|
||||
def fetch_experiments(project, archived = false)
|
||||
has_children_sql = if !archived
|
||||
'SUM(CASE WHEN my_modules.archived IS FALSE THEN 1 ELSE 0 END) > 0 AS has_children'
|
||||
else
|
||||
'SUM(CASE WHEN my_modules.archived IS TRUE THEN 1 ELSE 0 END) > 0 AS has_children'
|
||||
end
|
||||
project.experiments
|
||||
.viewable_by_user(current_user, current_team)
|
||||
.with_children_viewable_by_user(current_user)
|
||||
.where('
|
||||
experiments.archived = :archived OR
|
||||
my_modules.archived = :archived AND
|
||||
:archived IS TRUE
|
||||
', archived: archived)
|
||||
.select(
|
||||
'experiments.id',
|
||||
'experiments.name',
|
||||
'experiments.archived',
|
||||
has_children_sql
|
||||
).group('experiments.id')
|
||||
end
|
||||
|
||||
def fetch_my_modules(experiment, archived = false)
|
||||
experiment.my_modules
|
||||
.viewable_by_user(current_user, current_team)
|
||||
.where(archived: archived)
|
||||
end
|
||||
|
||||
def build_folder_tree(folder, children, archived = false)
|
||||
parent_folder = folder.parent_folder
|
||||
tree = fetch_projects(parent_folder, archived).map { |i| project_serializer(i) } +
|
||||
fetch_project_folders(parent_folder, archived).map { |i| folder_serializer(i) }
|
||||
tree.find { |i| i[:id] == folder.code }[:children] = children
|
||||
tree = build_folder_tree(parent_folder, tree, archived) if parent_folder.present?
|
||||
tree
|
||||
end
|
||||
|
||||
def project_level_branch(folder = nil, archived = false)
|
||||
fetch_projects(folder, archived)
|
||||
.map { |i| project_serializer(i) } +
|
||||
fetch_project_folders(folder, archived)
|
||||
.map { |i| folder_serializer(i) }
|
||||
end
|
||||
|
||||
def experiment_level_branch(project, archived = false)
|
||||
fetch_experiments(project, archived)
|
||||
.map { |i| experiment_serializer(i) }
|
||||
end
|
||||
|
||||
def my_module_level_branch(experiment, archived = false)
|
||||
fetch_my_modules(experiment, archived)
|
||||
.map { |i| my_module_serializer(i) }
|
||||
end
|
||||
end
|
||||
end
|
36
app/controllers/navigator/experiments_controller.rb
Normal file
36
app/controllers/navigator/experiments_controller.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Navigator
|
||||
class ExperimentsController < BaseController
|
||||
before_action :load_experiment
|
||||
before_action :check_read_permissions
|
||||
|
||||
def show
|
||||
my_modules = my_module_level_branch(@experiment, params[:archived] == 'true')
|
||||
render json: { items: my_modules }
|
||||
end
|
||||
|
||||
def tree
|
||||
my_modules = my_module_level_branch(@experiment, params[:archived] == 'true')
|
||||
experiments = experiment_level_branch(@experiment.project, params[:archived] == 'true')
|
||||
experiments.find { |i| i[:id] == @experiment.code }[:children] = my_modules
|
||||
|
||||
tree = project_level_branch(@experiment.project.project_folder, params[:archived] == 'true')
|
||||
tree.find { |i| i[:id] == @experiment.project.code }[:children] = experiments
|
||||
|
||||
tree = build_folder_tree(@experiment.project.project_folder, tree) if @experiment.project.project_folder
|
||||
|
||||
render json: { items: tree }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_experiment
|
||||
@experiment = Experiment.find_by(id: params[:id])
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 and return unless can_read_experiment?(@experiment)
|
||||
end
|
||||
end
|
||||
end
|
33
app/controllers/navigator/my_modules_controller.rb
Normal file
33
app/controllers/navigator/my_modules_controller.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Navigator
|
||||
class MyModulesController < BaseController
|
||||
before_action :load_my_module
|
||||
before_action :check_read_permissions
|
||||
|
||||
def tree
|
||||
my_modules = my_module_level_branch(@experiment, params[:archived] == 'true')
|
||||
experiments = experiment_level_branch(@experiment.project, params[:archived] == 'true')
|
||||
experiments.find { |i| i[:id] == @experiment.code }[:children] = my_modules
|
||||
|
||||
tree = project_level_branch(@experiment.project.project_folder, params[:archived] == 'true')
|
||||
tree.find { |i| i[:id] == @experiment.project.code }[:children] = experiments
|
||||
|
||||
tree = build_folder_tree(@experiment.project.project_folder, tree) if @experiment.project.project_folder
|
||||
|
||||
render json: { items: tree }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_my_module
|
||||
@my_module = MyModule.find_by(id: params[:id])
|
||||
|
||||
@experiment = @my_module.experiment
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 and return unless can_read_my_module?(@my_module)
|
||||
end
|
||||
end
|
||||
end
|
23
app/controllers/navigator/project_folders_controller.rb
Normal file
23
app/controllers/navigator/project_folders_controller.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Navigator
|
||||
class ProjectFoldersController < BaseController
|
||||
before_action :load_project_folder
|
||||
|
||||
def show
|
||||
folder = project_level_branch(@project_folder, params[:archived] == 'true')
|
||||
render json: { items: folder }
|
||||
end
|
||||
|
||||
def tree
|
||||
tree = folder_tree_branch(@project_folder, tree)
|
||||
render json: { items: tree }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_project_folder
|
||||
@project_folder = current_team.project_folders.find_by(id: params[:id])
|
||||
end
|
||||
end
|
||||
end
|
38
app/controllers/navigator/projects_controller.rb
Normal file
38
app/controllers/navigator/projects_controller.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Navigator
|
||||
class ProjectsController < BaseController
|
||||
before_action :load_project
|
||||
before_action :check_read_permissions, except: :index
|
||||
|
||||
def index
|
||||
project_and_folders = project_level_branch(nil, params[:archived] == 'true')
|
||||
render json: { items: project_and_folders }
|
||||
end
|
||||
|
||||
def show
|
||||
experiments = experiment_level_branch(@project, params[:archived] == 'true')
|
||||
render json: { items: experiments }
|
||||
end
|
||||
|
||||
def tree
|
||||
experiments = experiment_level_branch(@project, params[:archived] == 'true')
|
||||
tree = project_level_branch(@project.project_folder, params[:archived] == 'true')
|
||||
tree.find { |i| i[:id] == @project.code }[:children] = experiments
|
||||
|
||||
tree = build_folder_tree(@project.project_folder, tree) if @project.project_folder
|
||||
|
||||
render json: { items: tree }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_project
|
||||
@project = current_team.projects.find_by(id: params[:id])
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 and return unless can_read_project?(@project)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -482,6 +482,20 @@ class ProjectsController < ApplicationController
|
|||
end
|
||||
|
||||
def set_navigator
|
||||
@navigator = true
|
||||
@navigator = if @project
|
||||
{
|
||||
url: tree_navigator_project_path(@project),
|
||||
id: @project.code
|
||||
}
|
||||
elsif current_folder
|
||||
{
|
||||
url: tree_navigator_project_folder_path(current_folder),
|
||||
id: current_folder.code
|
||||
}
|
||||
else
|
||||
{
|
||||
url: navigator_projects_path
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,10 @@ Vue.use(PerfectScrollbar);
|
|||
|
||||
Vue.prototype.i18n = window.I18n;
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
window.addEventListener('turbolinks:load', () => {
|
||||
if ($('#sciNavigationNavigatorContainer').length === 0) return;
|
||||
if ($('.navigator-container').length > 0) return;
|
||||
|
||||
const navigator = new Vue({
|
||||
el: '#sciNavigationNavigatorContainer',
|
||||
components: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="w-72 h-full border rounded bg-white flex flex-col right-0 absolute">
|
||||
<div class="w-72 h-full border rounded bg-white flex flex-col right-0 absolute navigator-container">
|
||||
<div class="p-3 flex items-center">
|
||||
<i class="fas fa-bars p-2 cursor-pointer"></i>
|
||||
<div class="font-bold text-base">
|
||||
|
@ -7,9 +7,9 @@
|
|||
</div>
|
||||
<i @click="$emit('navigator:colapse')" class="fas fa-times ml-auto cursor-pointer"></i>
|
||||
</div>
|
||||
<div class="grow px-2 py-4">
|
||||
<NavigatorItem v-for="item in sortedMenuItems" :key="item.id" :item="item" />
|
||||
</div>
|
||||
<perfect-scrollbar class="grow px-2 py-4 relative">
|
||||
<NavigatorItem v-for="item in sortedMenuItems" :key="item.id" :currentItemId="currentItemId" :item="item" />
|
||||
</perfect-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -25,28 +25,39 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
menuItems: [],
|
||||
navigatorCollapsed: false
|
||||
navigatorCollapsed: false,
|
||||
navigatorUrl: null,
|
||||
currentItemId: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sortedMenuItems() {
|
||||
|
||||
return this.menuItems.sort((a, b) => a.name - b.name)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.menuItems = [
|
||||
{id: 'p1', name: 'Project 1', url: '/', archive: false, children: [
|
||||
{id: 'e1', name: 'Experiment 1', url: '/', archive: false}
|
||||
]},
|
||||
{id: 'f1', name: 'Folder', url: '/', archive: false, icon: 'fas fa-folder', children: [
|
||||
{id: 'p2', name: 'Project 2', url: '/', archive: false, children: [
|
||||
{id: 'e2', name: 'Experiment 2', url: '/', archive: false, children: [
|
||||
{id: 't1', name: 'Task', url: '/', archive: false}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
]
|
||||
}
|
||||
this.changePage();
|
||||
this.loadTree();
|
||||
|
||||
$(document).on('turbolinks:load', () => {
|
||||
this.changePage();
|
||||
if ($(`[navigator-item-id="${this.currentItemId}"]`).length === 0) {
|
||||
this.loadTree();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
changePage() {
|
||||
this.navigatorUrl = $('#active_navigator_url').val();
|
||||
this.currentItemId = $('#active_navigator_item').val();
|
||||
},
|
||||
loadTree() {
|
||||
if (!this.navigatorUrl) return;
|
||||
|
||||
$.get(this.navigatorUrl, {archived: false}, (data) => {
|
||||
this.menuItems = data.items
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<template>
|
||||
<div class="sn-color-primary pl-7 pt-4 flex items-center flex-wrap">
|
||||
<div class="w-5 mr-2 flex justify-start">
|
||||
<i v-if="haveChildren"
|
||||
class="fas cursor-pointer"
|
||||
:class="{'fa-chevron-right': !childrenOpen, 'fa-chevron-down': childrenOpen }"
|
||||
@click="childrenOpen = !childrenOpen"></i>
|
||||
<div class="sn-color-primary pl-7 w-64 flex justify-center flex-col" :navigator-item-id="item.id">
|
||||
<div class="p-2 flex items-center whitespace-nowrap" :class="{ 'sn-background-background-violet': activeItem }">
|
||||
<div class="w-5 mr-2 flex justify-start shrink-0">
|
||||
<i v-if="hasChildren"
|
||||
class="fas cursor-pointer"
|
||||
:class="{'fa-chevron-right': !childrenExpanded, 'fa-chevron-down': childrenExpanded }"
|
||||
@click="toggleChildren"></i>
|
||||
</div>
|
||||
<i v-if="itemIcon" class="mr-2" :class="itemIcon"></i>
|
||||
<a :href="item.url" class="text-ellipsis overflow-hidden">
|
||||
{{ item.name }}
|
||||
</a>
|
||||
</div>
|
||||
<i v-if="item.icon" class="mr-2" :class="item.icon"></i>
|
||||
{{ item.name }}
|
||||
<div v-if="childrenOpen" class="basis-full">
|
||||
<NavigatorItem v-for="item in sortedMenuItems" :key="item.id" :item="item" />
|
||||
<div class="basis-full" :class="{'hidden': !childrenExpanded}">
|
||||
<NavigatorItem v-for="item in sortedMenuItems" @item:expand="treeExpand" :key="item.id" :currentItemId="currentItemId" :item="item" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -18,20 +22,67 @@
|
|||
export default {
|
||||
name: 'NavigatorItem',
|
||||
props: {
|
||||
item: Object
|
||||
item: Object,
|
||||
currentItemId: String
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
childrenOpen: false
|
||||
}
|
||||
childrenExpanded: false,
|
||||
children: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
haveChildren: function() {
|
||||
return this.item.children && this.item.children.length > 0
|
||||
hasChildren: function() {
|
||||
return this.item.has_children;
|
||||
},
|
||||
sortedMenuItems: function() {
|
||||
if (!this.haveChildren) return []
|
||||
return this.item.children.sort((a, b) => a.name - b.name)
|
||||
return this.children.sort((a, b) => a.name - b.name);
|
||||
},
|
||||
activeItem: function() {
|
||||
return this.item.id == this.currentItemId;
|
||||
},
|
||||
itemIcon: function() {
|
||||
switch(this.item.type) {
|
||||
case 'folder':
|
||||
return 'fas fa-folder';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
if (this.item.children) this.children = this.item.children;
|
||||
},
|
||||
mounted: function() {
|
||||
this.selectItem();
|
||||
},
|
||||
watch: {
|
||||
currentItemId: function() {
|
||||
this.selectItem();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleChildren: function() {
|
||||
this.childrenExpanded = !this.childrenExpanded;
|
||||
if (this.childrenExpanded) this.loadChildren();
|
||||
},
|
||||
loadChildren: function() {
|
||||
$.get(this.item.children_url, {archived: false}, (data) => {
|
||||
this.children = data.items;
|
||||
});
|
||||
},
|
||||
treeExpand: function() {
|
||||
this.childrenExpanded = true;
|
||||
this.$emit('item:expand');
|
||||
},
|
||||
selectItem: function() {
|
||||
if (this.activeItem && !this.childrenExpanded) {
|
||||
this.$emit('item:expand');
|
||||
if (this.hasChildren) {
|
||||
this.childrenExpanded = true;
|
||||
this.loadChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -89,6 +89,21 @@ class Experiment < ApplicationRecord
|
|||
.where(project: Project.viewable_by_user(user, teams))
|
||||
end
|
||||
|
||||
def self.with_children_viewable_by_user(user)
|
||||
joins("
|
||||
LEFT OUTER JOIN my_modules ON my_modules.experiment_id = experiments.id
|
||||
LEFT OUTER JOIN user_assignments my_module_user_assignments
|
||||
ON my_module_user_assignments.assignable_id = my_modules.id AND
|
||||
my_module_user_assignments.assignable_type = 'MyModule'
|
||||
LEFT OUTER JOIN user_roles my_module_user_roles
|
||||
ON my_module_user_roles.id = my_module_user_assignments.user_role_id
|
||||
")
|
||||
.where('
|
||||
(my_module_user_assignments.user_id = ? AND my_module_user_roles.permissions @> ARRAY[?]::varchar[]
|
||||
OR my_modules.id IS NULL)
|
||||
', user.id, MyModulePermissions::READ)
|
||||
end
|
||||
|
||||
def self.filter_by_teams(teams = [])
|
||||
return self if teams.blank?
|
||||
|
||||
|
|
|
@ -109,6 +109,29 @@ class Project < ApplicationRecord
|
|||
.or(projects.with_granted_permissions(user, ProjectPermissions::READ)).distinct
|
||||
end
|
||||
|
||||
def self.with_children_viewable_by_user(user)
|
||||
joins("
|
||||
LEFT OUTER JOIN experiments ON experiments.project_id = projects.id
|
||||
LEFT OUTER JOIN user_assignments experiment_user_assignments
|
||||
ON experiment_user_assignments.assignable_id = experiments.id AND
|
||||
experiment_user_assignments.assignable_type = 'Experiment'
|
||||
LEFT OUTER JOIN user_roles experiment_user_roles
|
||||
ON experiment_user_roles.id = experiment_user_assignments.user_role_id
|
||||
LEFT OUTER JOIN my_modules ON my_modules.experiment_id = experiments.id
|
||||
LEFT OUTER JOIN user_assignments my_module_user_assignments
|
||||
ON my_module_user_assignments.assignable_id = my_modules.id AND
|
||||
my_module_user_assignments.assignable_type = 'MyModule'
|
||||
LEFT OUTER JOIN user_roles my_module_user_roles
|
||||
ON my_module_user_roles.id = my_module_user_assignments.user_role_id
|
||||
")
|
||||
.where('
|
||||
(experiment_user_assignments.user_id = ? AND experiment_user_roles.permissions @> ARRAY[?]::varchar[]
|
||||
OR experiments.id IS NULL) AND
|
||||
(my_module_user_assignments.user_id = ? AND my_module_user_roles.permissions @> ARRAY[?]::varchar[]
|
||||
OR my_modules.id IS NULL)
|
||||
', user.id, ExperimentPermissions::READ, user.id, MyModulePermissions::READ)
|
||||
end
|
||||
|
||||
def self.filter_by_teams(teams = [])
|
||||
teams.blank? ? self : where(team: teams)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProjectFolder < ApplicationRecord
|
||||
ID_PREFIX = 'PF'
|
||||
include PrefixedIdModel
|
||||
|
||||
include ArchivableModel
|
||||
include SearchableModel
|
||||
include SearchableByNameModel
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<% if @navigator %>
|
||||
<%= hidden_field_tag :active_navigator_item, @navigator[:id] %>
|
||||
<%= hidden_field_tag :active_navigator_url, @navigator[:url] %>
|
||||
<div id="sciNavigationNavigatorContainer"
|
||||
class="contents"
|
||||
data-navigator-state-url="<%= navigator_state_navigations_path %>"
|
||||
|
|
|
@ -292,6 +292,32 @@ Rails.application.routes.draw do
|
|||
resources :my_modules, only: %i(show update edit)
|
||||
end
|
||||
|
||||
namespace :navigator do
|
||||
resources :project_folders, only: %i(show) do
|
||||
member do
|
||||
get :tree
|
||||
end
|
||||
end
|
||||
|
||||
resources :projects, only: %i(show index) do
|
||||
member do
|
||||
get :tree
|
||||
end
|
||||
end
|
||||
|
||||
resources :experiments, only: %i(show) do
|
||||
member do
|
||||
get :tree
|
||||
end
|
||||
end
|
||||
|
||||
resources :my_modules, only: %i(show) do
|
||||
member do
|
||||
get :tree
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resources :projects, except: [:destroy] do
|
||||
resources :project_comments,
|
||||
path: '/comments',
|
||||
|
|
|
@ -15,6 +15,10 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
},
|
||||
blocklist: [
|
||||
'collapse',
|
||||
'container'
|
||||
],
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
|
|
Loading…
Reference in a new issue