Add methods to publish and unpublish a project list based on permission

This commit is contained in:
Klaus Zanders
2024-05-19 16:07:25 +02:00
parent 3768e72b62
commit ca6e583cdb
9 changed files with 187 additions and 54 deletions
@@ -81,6 +81,28 @@
end
if query.persisted?
if can_publish?
if query.public?
menu.with_item(
label: t(:button_unpublish),
scheme: :danger,
href: unpublish_projects_query_path(query),
content_arguments: { data: { method: :post } }
) do |item|
item.with_leading_visual_icon(icon: 'eye-closed')
end
else
menu.with_item(
label: t(:button_publish),
scheme: :default,
href: publish_projects_query_path(query),
content_arguments: { data: { method: :post } }
) do |item|
item.with_leading_visual_icon(icon: 'eye')
end
end
end
menu.with_item(
label: t(:button_delete),
scheme: :danger,
@@ -92,7 +114,6 @@
end
end
%>
<%= render(Projects::ConfigureViewModalComponent.new(query:)) %>
<%= render(Projects::DeleteListModalComponent.new(query:)) if query.persisted? %>
<%= render(Projects::ExportListModalComponent.new(query:)) %>
@@ -74,6 +74,10 @@ class Projects::IndexPageHeaderComponent < ApplicationComponent
def can_rename? = may_save_as? && query.persisted? && query.user == current_user && !query.changed?
def can_publish?
current_user.allowed_globally?(:manage_public_project_queries) && query.persisted?
end
def show_state?
state == :show
end
+21 -21
View File
@@ -46,19 +46,19 @@ module Projects
def favored
render(Primer::Beta::IconButton.new(
icon: currently_favored? ? "star-fill" : "star",
scheme: :invisible,
mobile_icon: currently_favored? ? "star-fill" : "star",
size: :medium,
tag: :a,
tooltip_direction: :e,
href: helpers.build_favorite_path(project, format: :html),
data: { method: currently_favored? ? :delete : :post },
classes: currently_favored? ? "op-primer--star-icon " : "op-project-row-component--favorite",
label: currently_favored? ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite),
aria: { label: currently_favored? ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite) },
test_selector: 'project-list-favorite-button'
))
icon: currently_favored? ? "star-fill" : "star",
scheme: :invisible,
mobile_icon: currently_favored? ? "star-fill" : "star",
size: :medium,
tag: :a,
tooltip_direction: :e,
href: helpers.build_favorite_path(project, format: :html),
data: { method: currently_favored? ? :delete : :post },
classes: currently_favored? ? "op-primer--star-icon " : "op-project-row-component--favorite",
label: currently_favored? ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite),
aria: { label: currently_favored? ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite) },
test_selector: "project-list-favorite-button"
))
end
def currently_favored?
@@ -197,7 +197,7 @@ module Projects
def additional_css_class(column)
if column.attribute == :name
"project--hierarchy #{project.archived? ? 'archived' : ''}"
elsif [:status_explanation, :description].include?(column.attribute)
elsif %i[status_explanation description].include?(column.attribute)
"project-long-text-container"
elsif custom_field_column?(column)
cf = column.custom_field
@@ -254,7 +254,7 @@ module Projects
href: helpers.build_favorite_path(project, format: :html),
data: { method: :post },
label: I18n.t(:button_favorite),
aria: { label: I18n.t(:button_favorite) },
aria: { label: I18n.t(:button_favorite) }
}
end
@@ -269,7 +269,7 @@ module Projects
data: { method: :delete },
classes: "op-primer--star-icon",
label: I18n.t(:button_unfavorite),
aria: { label: I18n.t(:button_unfavorite) },
aria: { label: I18n.t(:button_unfavorite) }
}
end
@@ -302,7 +302,7 @@ module Projects
scheme: :default,
icon: :check,
label: I18n.t(:label_project_activity),
href: project_activity_index_path(project, event_types: ["project_attributes"]),
href: project_activity_index_path(project, event_types: ["project_attributes"])
}
end
end
@@ -317,7 +317,7 @@ module Projects
data: {
confirm: t("project.archive.are_you_sure", name: project.name),
method: :post
},
}
}
end
end
@@ -340,7 +340,7 @@ module Projects
scheme: :default,
icon: :copy,
label: I18n.t(:button_copy),
href: copy_project_path(project),
href: copy_project_path(project)
}
end
end
@@ -351,7 +351,7 @@ module Projects
scheme: :danger,
icon: :trash,
label: I18n.t(:button_delete),
href: confirm_destroy_project_path(project),
href: confirm_destroy_project_path(project)
}
end
end
@@ -361,7 +361,7 @@ module Projects
end
def custom_field_column?(column)
column.is_a?(Queries::Projects::Selects::CustomField)
column.is_a?(::Queries::Projects::Selects::CustomField)
end
end
end
+12 -6
View File
@@ -64,11 +64,18 @@ module Projects
# We don't return the project row
# but the [project, level] array from the helper
def rows
<<<<<<< HEAD
@rows ||=
begin
projects_enumerator = ->(model) { to_enum(:projects_with_levels_order_sensitive, model).to_a }
instance_exec(model, &projects_enumerator)
end
=======
@rows ||= begin
projects_enumerator = ->(model) { to_enum(:projects_with_levels_order_sensitive, model).to_a }
instance_exec(model, &projects_enumerator)
end
>>>>>>> 0e1a80b408 (Add methods to publish and unpublish a project list based on permission)
end
def initialize_sorted_model
@@ -113,12 +120,11 @@ module Projects
end
def columns
@columns ||=
begin
columns = query.selects.reject { |select| select.is_a?(Queries::Selects::NotExistingSelect) }
@columns ||= begin
columns = query.selects.reject { |select| select.is_a?(::Queries::Selects::NotExistingSelect) }
index = columns.index { |column| column.attribute == :name }
columns.insert(index, Queries::Projects::Selects::Default.new(:hierarchy)) if index
index = columns.index { |column| column.attribute == :name }
columns.insert(index, ::Queries::Projects::Selects::Default.new(:hierarchy)) if index
columns
end
@@ -156,7 +162,7 @@ module Projects
end
def favored_project_ids
@favored_projects ||= Favorite.where(user: current_user, favored_type: 'Project').pluck(:favored_id)
@favored_projects ||= Favorite.where(user: current_user, favored_type: "Project").pluck(:favored_id)
end
def sorted_by_lft?
@@ -0,0 +1,39 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class Projects::Queries::PublishContract < BaseContract
validate :allowed_to_modify_public_flag
protected
def allowed_to_modify_public_flag
unless user.allowed_globally?(:manage_public_project_queries)
errors.add :base, :error_unauthorized
end
end
end
+31 -21
View File
@@ -31,7 +31,7 @@ class Projects::QueriesController < ApplicationController
# No need for a more specific authorization check. That is carried out in the contracts.
before_action :require_login
before_action :find_query, only: %i[rename update destroy]
before_action :find_query, only: %i[rename update destroy publish unpublish]
before_action :build_query_or_deny_access, only: %i[new create]
current_menu_item [:new, :rename, :create, :update] do
@@ -55,17 +55,7 @@ class Projects::QueriesController < ApplicationController
.new(from: @query, user: current_user)
.call(permitted_query_params)
if call.success?
flash[:notice] = I18n.t("lists.create.success")
redirect_to projects_path(query_id: call.result.id)
else
flash[:error] = I18n.t("lists.create.failure", errors: call.errors.full_messages.join("\n"))
render template: "/projects/index",
layout: "global",
locals: { query: call.result, state: :edit }
end
render_result(call, success_i18n_key: "lists.create.success", error_i18n_key: "lists.create.failure")
end
def update
@@ -73,17 +63,23 @@ class Projects::QueriesController < ApplicationController
.new(user: current_user, model: @query)
.call(permitted_query_params)
if call.success?
flash[:notice] = I18n.t("lists.update.success")
render_result(call, success_i18n_key: "lists.update.success", error_i18n_key: "lists.update.failure")
end
redirect_to projects_path(query_id: call.result.id)
else
flash[:error] = I18n.t("lists.update.failure", errors: call.errors.full_messages.join("\n"))
def publish
call = Queries::Projects::ProjectQueries::PublishService
.new(user: current_user, model: @query)
.call(public: true)
render template: "/projects/index",
layout: "global",
locals: { query: call.result, state: :edit }
end
render_result(call, success_i18n_key: "lists.publish.success", error_i18n_key: "lists.publish.failure")
end
def unpublish
call = Queries::Projects::ProjectQueries::PublishService
.new(user: current_user, model: @query)
.call(public: false)
render_result(call, success_i18n_key: "lists.unpublish.success", error_i18n_key: "lists.unpublish.failure")
end
def destroy
@@ -95,6 +91,20 @@ class Projects::QueriesController < ApplicationController
private
def render_result(service_call, success_i18n_key:, error_i18n_key:)
if service_call.success?
flash[:notice] = I18n.t(success_i18n_key)
redirect_to projects_path(query_id: service_call.result.id)
else
flash[:error] = I18n.t(error_i18n_key, errors: service_call.errors.full_messages.join("\n"))
render template: "/projects/index",
layout: "global",
locals: { query: service_call.result, state: :edit }
end
end
def find_query
@query = Queries::Projects::ProjectQuery.find(params[:id])
end
+5 -5
View File
@@ -30,10 +30,10 @@ module Projects
private
def load_query(duplicate:)
Queries::Projects::Factory.find(params[:query_id],
params: permitted_query_params,
user: current_user,
duplicate:)
::Queries::Projects::Factory.find(params[:query_id],
params: permitted_query_params,
user: current_user,
duplicate:)
end
def load_query_or_deny_access
@@ -55,7 +55,7 @@ module Projects
query_params.merge!(params.require(:query).permit(:name))
end
query_params.merge!(Queries::ParamsParser.parse(params))
query_params.merge!(::Queries::ParamsParser.parse(params))
query_params.with_indifferent_access
end
@@ -0,0 +1,50 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
class Queries::Projects::ProjectQueries::PublishService < BaseServices::BaseContracted
include Contracted
def initialize(user:, model:, contract_class: Projects::Queries::PublishContract)
super(user:, contract_class:)
self.model = model
end
private
def after_validate(params, service_call)
model.public = params[:public]
service_call
end
def persist(service_call)
model.save
service_call
end
end
+3
View File
@@ -184,6 +184,9 @@ Rails.application.routes.draw do
resources :queries, only: %i[new create update destroy] do
member do
get :rename
post :publish
post :unpublish
end
end
end