diff --git a/apps/dashboard/app/assets/stylesheets/projects.scss b/apps/dashboard/app/assets/stylesheets/projects.scss index ef3d52e912..dba4c97ecd 100644 --- a/apps/dashboard/app/assets/stylesheets/projects.scss +++ b/apps/dashboard/app/assets/stylesheets/projects.scss @@ -1,3 +1,7 @@ +.vertical-align-middle { + vertical-align: middle !important; +} + .project-card { width: fit-content; } @@ -66,4 +70,4 @@ width: 100%; margin: .2rem 0px; text-wrap:nowrap; -} +} \ No newline at end of file diff --git a/apps/dashboard/app/controllers/projects_controller.rb b/apps/dashboard/app/controllers/projects_controller.rb index 72e1723cf3..9dd62c26ea 100644 --- a/apps/dashboard/app/controllers/projects_controller.rb +++ b/apps/dashboard/app/controllers/projects_controller.rb @@ -236,7 +236,7 @@ def templates def project_params params .require(:project) - .permit(:name, :directory, :description, :icon, :id, :template) + .permit(:name, :directory, :description, :icon, :id, :template, :group_owner) end def show_project_params diff --git a/apps/dashboard/app/models/project.rb b/apps/dashboard/app/models/project.rb index 3c6d35c4d6..91e114e824 100644 --- a/apps/dashboard/app/models/project.rb +++ b/apps/dashboard/app/models/project.rb @@ -111,7 +111,7 @@ def importable_directories end end - attr_reader :id, :name, :description, :icon, :directory, :template, :files + attr_reader :id, :name, :description, :icon, :directory, :template, :files, :group_owner validates :name, presence: { message: :required }, on: [:create, :update] validates :id, :directory, :icon, presence: { message: :required }, on: [:update] @@ -128,6 +128,7 @@ def initialize(attributes = {}) @directory = attributes[:directory] @directory = File.expand_path(@directory) unless @directory.blank? @template = attributes[:template] + @group_owner = attributes[:group_owner] || directory_group_owner return if new_record? @@ -160,7 +161,7 @@ def save @directory = Project.dataroot.join(id.to_s).to_s if directory.blank? @icon = 'fas://cog' if icon.blank? - make_dir && update_permission && sync_template && store_manifest(:save) + make_root && update_permission && make_dir && sync_template && store_manifest(:save) end def update(attributes) @@ -203,6 +204,30 @@ def remove_from_lookup false end + def private? + project_dataroot.to_s.start_with?(CurrentUser.home) + end + + def directory_group_owner + if project_dataroot != Project.dataroot && project_dataroot.grpowned? + Etc.getgrgid(project_dataroot.stat.gid).name + else + nil + end + end + + def chgrp_directory + return true if private? || group_owner == directory_group_owner + + begin + group_gid = group_owner.nil? ? nil : Etc.getgrnam(group_owner).gid + FileUtils.chown(nil, group_gid, project_dataroot) + rescue StandardError => e + errors.add(:save, "Unable to set group with error #{e.class}:#{e.message}") + false + end + end + def editable? File.writable?(manifest_path) end @@ -303,8 +328,15 @@ def update_attrs(attributes) end end + def make_root + project_dataroot.mkpath unless project_dataroot.exist? + true + rescue StandardError => e + errors.add(:save, "Failed to initialize project directory: #{e.message}") + false + end + def make_dir - project_dataroot.mkpath unless project_dataroot.exist? configuration_directory.mkpath unless configuration_directory.exist? workflow_directory = Workflow.workflow_dir(project_dataroot) workflow_directory.mkpath unless workflow_directory.exist? @@ -318,7 +350,7 @@ def make_dir def update_permission project_dataroot.chmod(0750) - true + chgrp_directory rescue StandardError => e errors.add(:save, "Failed to update permissions of the directory: #{e.message}") false diff --git a/apps/dashboard/app/views/projects/_form.html.erb b/apps/dashboard/app/views/projects/_form.html.erb index dec46709da..10b5541b1f 100644 --- a/apps/dashboard/app/views/projects/_form.html.erb +++ b/apps/dashboard/app/views/projects/_form.html.erb @@ -53,6 +53,22 @@
<%= form.text_area :description, placeholder: I18n.t('dashboard.jobs_project_description_placeholder') %>
+ <% unless @project.private? && edit_project_action %> +
+ <% help_html = + if edit_project_action + '' + else + "" + end + %> + <%= form.select(:group_owner, + CurrentUser.group_names, + { label: "#{I18n.t('dashboard.jobs_project_group_owner')} #{help_html}".html_safe }, + { disabled: edit_project_action }) + %> +
+ <% end %> diff --git a/apps/dashboard/config/locales/en.yml b/apps/dashboard/config/locales/en.yml index f3c5941954..afd7f2d4a6 100644 --- a/apps/dashboard/config/locales/en.yml +++ b/apps/dashboard/config/locales/en.yml @@ -175,6 +175,8 @@ en: jobs_project_directory_error: Project directory path is not set for this workflow jobs_project_directory_placeholder: Project directory absolute path jobs_project_generic_error: 'There was an error processing your request: %{error}' + jobs_project_group_owner: Group + jobs_project_group_help: Make sure to choose the group that includes all intended collaborators. If this is not a collaborative project, the default group is recommended jobs_project_invalid_configuration_clusters: An HPC cluster is required. Contact your administrator to add one to the system. jobs_project_invalid_configuration_scripts: An executable script is required for your project. Upload a script using the file application. jobs_project_job_deleted: Successfully deleted job %{job_id}