diff --git a/app/contracts/work_packages/copy_contract.rb b/app/contracts/work_packages/copy_contract.rb index 797c03de4e27..1e8dc6430c1e 100644 --- a/app/contracts/work_packages/copy_contract.rb +++ b/app/contracts/work_packages/copy_contract.rb @@ -32,6 +32,7 @@ module WorkPackages class CopyContract < CreateContract + REQUIRED_PERMISSION = :copy_work_packages # As % Complete can be set while Work and Remaining work are not, copying is # a scenario where this field must be writable attribute :done_ratio, diff --git a/app/contracts/work_packages/create_contract.rb b/app/contracts/work_packages/create_contract.rb index 7ca7bd43c892..1be7f6596f76 100644 --- a/app/contracts/work_packages/create_contract.rb +++ b/app/contracts/work_packages/create_contract.rb @@ -30,6 +30,9 @@ module WorkPackages class CreateContract < BaseContract + # Temporary for testing the permission + REQUIRED_PERMISSION = :copy_work_packages + include AdminWritableTimestamps allow_writable_timestamps :created_at @@ -40,9 +43,9 @@ class CreateContract < BaseContract # Overriding permission from WP base contract to ignore change_work_package_status for creation, # because we don't require that permission for writable status during WP creation. # Note that nil would not override and [] would ignore the default permission, so we use the default here: - permission: :add_work_packages + permission: REQUIRED_PERMISSION - default_attribute_permission :add_work_packages + default_attribute_permission REQUIRED_PERMISSION validate :user_allowed_to_add validate :user_allowed_to_manage_file_links @@ -50,8 +53,8 @@ class CreateContract < BaseContract private def user_allowed_to_add - if (model.project && !@user.allowed_in_project?(:add_work_packages, model.project)) || - !@user.allowed_in_any_project?(:add_work_packages) + if (model.project && !@user.allowed_in_project?(REQUIRED_PERMISSION, model.project)) || + !@user.allowed_in_any_project?(REQUIRED_PERMISSION) errors.add(:base, :error_unauthorized) end end diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts index 07c3c612d787..f7eb39d1b252 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts @@ -31,6 +31,7 @@ import { ApiV3WorkPackagePaths } from 'core-app/core/apiv3/endpoints/work_packag import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; import { ApiV3WorkPackageForm } from 'core-app/core/apiv3/endpoints/work_packages/apiv3-work-package-form'; +// import { ApiV3WorkPackageCopyForm } from 'core-app/core/apiv3/endpoints/work_packages/apiv3-work-package-copy-form'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ApiV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; @@ -47,16 +48,22 @@ export class ApiV3WorkPackagesPaths extends ApiV3Collection extends SimpleResource { cls:Constructor = ApiV3GettableResource as unknown as Constructor, ):R { // eslint-disable-next-line new-cap + // if (segment === "form"){ + // debugger; + // } return new cls(this.apiRoot, this.path, segment, this); } } diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts index f95df3593984..6fbbc190beba 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts @@ -146,10 +146,11 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { // Ideally we would make an empty request before to get the create schema (cannot use the update schema of the source changeset) // to get all the writable attributes and only send those. // But as this would require an additional request, we don't. + return this .apiV3Service .work_packages - .form + .copy_form .post(request) .toPromise() .then((form:FormResource) => { diff --git a/lib/api/v3/utilities/path_helper.rb b/lib/api/v3/utilities/path_helper.rb index c3a04f70a83d..1280c2cfb2d2 100644 --- a/lib/api/v3/utilities/path_helper.rb +++ b/lib/api/v3/utilities/path_helper.rb @@ -520,6 +520,14 @@ def self.work_package_schema(project_id, type_id) "#{root}/work_packages/schemas/#{project_id}-#{type_id}" end + def self.work_package_copy(id) + "#{root}/work_packages/#{id}/copy" + end + + def self.work_package_copy_form(id) + "#{root}/work_packages/#{id}/copy/form" + end + def self.work_package_activities(id) "#{work_package(id)}/activities" end diff --git a/lib/api/v3/work_packages/copy/copy_api.rb b/lib/api/v3/work_packages/copy/copy_api.rb new file mode 100644 index 000000000000..b2bd10f6fe48 --- /dev/null +++ b/lib/api/v3/work_packages/copy/copy_api.rb @@ -0,0 +1,59 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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. +#++ + +require "api/v3/users/user_collection_representer" + +module API + module V3 + module WorkPackages + module Copy + class CopyAPI < ::API::OpenProjectAPI + resource :copy do + after_validation do + # binding.pry + # authorize_in_project(:copy_work_packages, project: @project) + end + + post &::API::V3::Utilities::Endpoints::Create.new(model: WorkPackage, + parse_service: WorkPackages::ParseParamsService, + process_contract: ::WorkPackages::CopyContract, + # parse_representer: WorkPackageCopyPayloadRepresenter, + render_representer: CreateFormRepresenter, + params_modifier: ->(attributes) { + attributes[:send_notifications] = notify_according_to_params + attributes + }) + .mount + + mount ::API::V3::WorkPackages::Copy::CreateFormAPI + end + end + end + end + end +end diff --git a/lib/api/v3/work_packages/copy/create_form_api.rb b/lib/api/v3/work_packages/copy/create_form_api.rb new file mode 100644 index 000000000000..5be766690864 --- /dev/null +++ b/lib/api/v3/work_packages/copy/create_form_api.rb @@ -0,0 +1,44 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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. +#++ + +module API + module V3 + module WorkPackages + module Copy + class CreateFormAPI < ::API::OpenProjectAPI + resource :form do + post &::API::V3::Utilities::Endpoints::CreateForm.new(model: WorkPackage, + parse_service: WorkPackages::ParseParamsService, + process_contract: ::WorkPackages::CopyContract) + .mount + end + end + end + end + end +end diff --git a/lib/api/v3/work_packages/copy/create_form_representer.rb b/lib/api/v3/work_packages/copy/create_form_representer.rb new file mode 100644 index 000000000000..09dcf580108e --- /dev/null +++ b/lib/api/v3/work_packages/copy/create_form_representer.rb @@ -0,0 +1,47 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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. +#++ + +module API + module V3 + module WorkPackages + module Copy + class CreateFormRepresenter < FormRepresenter + include API::Decorators::CreateForm + + def form_url + api_v3_paths.work_package_copy_form(meta.source.id) + end + + def resource_url + api_v3_paths.work_package_copy(meta.source.id) + end + end + end + end + end +end diff --git a/lib/api/v3/work_packages/copy/work_package_copy_payload_representer.rb b/lib/api/v3/work_packages/copy/work_package_copy_payload_representer.rb new file mode 100644 index 000000000000..ab00f001cc3c --- /dev/null +++ b/lib/api/v3/work_packages/copy/work_package_copy_payload_representer.rb @@ -0,0 +1,50 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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. +#++ + +module API + module V3 + module WorkPackages + module Copy + class WorkPackageCopyPayloadRepresenter < ::API::V3::WorkPackages::WorkPackageRepresenter + include ::API::Utilities::PayloadRepresenter + # include ::API::Utilities::MetaProperty + + cached_representer disabled: true + # This might be required but not sure where yet! + # def meta_representer_class + # ProjectCopyMetaRepresenter + # end + + def writable_attributes + super + %w[status] + end + end + end + end + end +end diff --git a/lib/api/v3/work_packages/work_package_representer.rb b/lib/api/v3/work_packages/work_package_representer.rb index 14ac3de591a6..4b683fe6e812 100644 --- a/lib/api/v3/work_packages/work_package_representer.rb +++ b/lib/api/v3/work_packages/work_package_representer.rb @@ -112,7 +112,7 @@ def self_v3_path(*) end link :copy, - cache_if: -> { add_work_packages_allowed? } do + cache_if: -> { copy_work_packages_allowed? } do next if represented.new_record? { @@ -629,6 +629,11 @@ def add_work_packages_allowed? current_user.allowed_in_project?(:add_work_packages, represented.project) end + def copy_work_packages_allowed? + @copy_work_packages_allowed ||= + current_user.allowed_in_project?(:copy_work_packages, represented.project) + end + def relations self_path = api_v3_paths.work_package_relations(represented.id) visible_relations = represented diff --git a/lib/api/v3/work_packages/work_packages_api.rb b/lib/api/v3/work_packages/work_packages_api.rb index 152033faa665..f922ff6de1ec 100644 --- a/lib/api/v3/work_packages/work_packages_api.rb +++ b/lib/api/v3/work_packages/work_packages_api.rb @@ -38,8 +38,11 @@ class WorkPackagesAPI < ::API::OpenProjectAPI # The endpoint needs to be mounted before the GET :work_packages/:id. # Otherwise, the matcher for the :id also seems to match available_projects. # This is also true when the :id param is declared to be of type: Integer. + # Note: Adding `requirements: /\d*/` to the :id definition matches numbers only. mount ::API::V3::WorkPackages::AvailableProjectsOnCreateAPI mount ::API::V3::WorkPackages::Schema::WorkPackageSchemasAPI + mount ::API::V3::WorkPackages::CreateFormAPI + mount ::API::V3::WorkPackages::Copy::CopyAPI get do authorize_in_any_work_package(:view_work_packages) @@ -61,7 +64,7 @@ class WorkPackagesAPI < ::API::OpenProjectAPI }) .mount - route_param :id, type: Integer, desc: "Work package ID" do + route_param :id, type: Integer, requirements: { id: /[0-9]*/ }, desc: "Work package ID" do helpers WorkPackagesSharedHelpers helpers do @@ -89,18 +92,16 @@ class WorkPackagesAPI < ::API::OpenProjectAPI delete &::API::V3::Utilities::Endpoints::Delete.new(model: WorkPackage) .mount - mount ::API::V3::WorkPackages::WatchersAPI mount ::API::V3::Activities::ActivitiesByWorkPackageAPI mount ::API::V3::Attachments::AttachmentsByWorkPackageAPI mount ::API::V3::Repositories::RevisionsByWorkPackageAPI - mount ::API::V3::WorkPackages::UpdateFormAPI mount ::API::V3::WorkPackages::AvailableAssigneesAPI mount ::API::V3::WorkPackages::AvailableProjectsOnEditAPI mount ::API::V3::WorkPackages::AvailableRelationCandidatesAPI + mount ::API::V3::WorkPackages::UpdateFormAPI + mount ::API::V3::WorkPackages::WatchersAPI mount ::API::V3::WorkPackages::WorkPackageRelationsAPI end - - mount ::API::V3::WorkPackages::CreateFormAPI end end end