Compare commits

...

5 Commits

Author SHA1 Message Date
Eric Schubert 6d81ee01e0
[#40228] added work packages filter docs 3 years ago
Eric Schubert 713e5919cf
[#40228] reworked creation request 3 years ago
Eric Schubert e6b382d595
[#40228] added proposal for openapi spec 3 years ago
Wieland Lindenthal 19ac1eb942
Fix errors in FileLink model. 3 years ago
Wieland Lindenthal 3152d09384
Integrate with file storages such as Nextcloud 3 years ago
  1. 6
      Gemfile.lock
  2. 1
      Gemfile.modules
  3. 9
      config/initializers/menus.rb
  4. 14
      db/migrate/20220113144323_create_storage.rb
  5. 18
      db/migrate/20220113144759_create_file_links.rb
  6. 2
      docs/api/apiv3/components/schemas/aggregations.yml
  7. 2
      docs/api/apiv3/components/schemas/attachments.yml
  8. 138
      docs/api/apiv3/components/schemas/file_link_model.yml
  9. 37
      docs/api/apiv3/components/schemas/file_link_origin_data_model.yml
  10. 16
      docs/api/apiv3/openapi-spec.yml
  11. 3
      docs/api/apiv3/paths/users.yml
  12. 732
      docs/api/apiv3/paths/work_package.yml
  13. 119
      docs/api/apiv3/paths/work_package_file_link.yml
  14. 62
      docs/api/apiv3/paths/work_package_file_link_download.yml
  15. 62
      docs/api/apiv3/paths/work_package_file_link_open.yml
  16. 212
      docs/api/apiv3/paths/work_package_file_links.yml
  17. 214
      docs/api/apiv3/paths/work_package_watchers.yml
  18. 259
      docs/api/apiv3/paths/work_packages.yml
  19. 4
      docs/api/apiv3/tags/file_links.yml
  20. 41
      modules/storages/app/cells/storages/row_cell.rb
  21. 35
      modules/storages/app/cells/storages/table_cell.rb
  22. 83
      modules/storages/app/controllers/storages/admin/storages_controller.rb
  23. 35
      modules/storages/app/models/storages/file_link.rb
  24. 39
      modules/storages/app/models/storages/storage.rb
  25. 46
      modules/storages/app/views/storages/admin/_form.html.erb
  26. 15
      modules/storages/app/views/storages/admin/index.html.erb
  27. 9
      modules/storages/app/views/storages/admin/new.html.erb
  28. 59
      modules/storages/app/views/storages/admin/show.html.erb
  29. 30
      modules/storages/config/locales/en.yml
  30. 4
      modules/storages/config/locales/js-en.yml
  31. 5
      modules/storages/config/routes.rb
  32. 33
      modules/storages/lib/open_project/storages.rb
  33. 49
      modules/storages/lib/open_project/storages/engine.rb
  34. 1
      modules/storages/lib/openproject-storages.rb
  35. 13
      modules/storages/openproject-storages.gemspec
  36. 68
      modules/storages/spec/contracts/views/create_contract_file_link_spec.rb
  37. 36
      modules/storages/spec/features/shared_context.rb
  38. 36
      modules/storages/spec/permissions/manage_storage_in_project_spec.rb
  39. 104
      modules/storages/spec/requests/api/v3/views/create_resource_spec.rb
  40. 37
      modules/storages/spec/routing/storages_routing_spec.rb
  41. 126
      modules/storages/spec/support/pages/storage_tab.rb

@ -155,6 +155,11 @@ PATH
openproject-reporting (1.0.0)
costs
PATH
remote: modules/storages
specs:
openproject-storages (1.0.0)
PATH
remote: modules/team_planner
specs:
@ -1063,6 +1068,7 @@ DEPENDENCIES
openproject-pdf_export!
openproject-recaptcha!
openproject-reporting!
openproject-storages!
openproject-team_planner!
openproject-token (~> 2.2.0)
openproject-two_factor_authentication!

@ -46,6 +46,7 @@ group :opf_plugins do
gem 'budgets', path: 'modules/budgets'
gem 'openproject-team_planner', path: 'modules/team_planner'
gem 'openproject-calendar', path: 'modules/calendar'
gem 'openproject-storages', path: 'modules/storages'
gem 'openproject-bim', path: 'modules/bim'
end

@ -407,6 +407,12 @@ Redmine::MenuManager.map :admin_menu do |menu|
if: Proc.new { User.current.admin? },
caption: :label_setting_plural,
parent: :admin_backlogs
menu.push :storages_settings,
{ controller: '/storages/admin/storages', action: :index },
if: Proc.new { User.current.admin? },
caption: :project_module_storages,
icon: 'icon2 icon-hosting'
end
Redmine::MenuManager.map :project_menu do |menu|
@ -475,7 +481,8 @@ Redmine::MenuManager.map :project_menu do |menu|
categories: :label_work_package_category_plural,
repository: :label_repository,
time_entry_activities: :enumeration_activities,
storage: :label_required_disk_storage
storage: :label_required_disk_storage,
storages: :'storages.storage'
}.each do |key, caption|
menu.push :"settings_#{key}",
{ controller: "/projects/settings/#{key}", action: 'show' },

@ -0,0 +1,14 @@
class CreateStorage < ActiveRecord::Migration[6.1]
def change
create_table :storages do |t|
t.string :provider_type
t.string :name
t.bigint :creator_id, null: false, foreign_key: true
t.string :identifier, null: false
t.timestamps
t.index :identifier
end
end
end

@ -0,0 +1,18 @@
class CreateFileLinks < ActiveRecord::Migration[6.1]
def change
create_table :file_links do |t|
t.references :storage, foreign_key: true
t.bigint :creator, null: false, foreign_key: true
t.bigint :container, null: false, foreign_key: true
t.string :container_type
t.string :origin_id
t.string :origin_name
t.string :origin_mime_type
t.timestamp :origin_created_at
t.timestamp :origin_updated_at
t.string :origin_last_modified_by_name
t.timestamps
end
end
end

@ -1,2 +0,0 @@
# Schema: Aggregations
--- {}

@ -1,2 +0,0 @@
# Schema: Attachments
--- {}

@ -0,0 +1,138 @@
# Schema: File_LinkModel
---
type: object
required:
- id
- _type
- storageType
- originData
properties:
id:
type: integer
description: File link id
readOnly: true
_type:
type: string
enum:
- FileLink
storageType:
type: string
readOnly: true
createdAt:
type: string
format: date-time
description: Time of creation
readOnly: true
updatedAt:
type: string
format: date-time
description: Time of the most recent change to the file link
readOnly: true
originData:
$ref: '#/components/schemas/File_Link_Origin_DataModel'
_links:
type: object
required:
- self
- storage
- container
- delete
- downloadLocation
- staticDownloadLocation
- originOpen
properties:
self:
allOf:
- $ref: './link.yml'
- description: |-
This file link
**Resource**: FileLink
readOnly: true
storage:
allOf:
- $ref: './link.yml'
- description: |-
The storage url the file link references to.
**Resource**: N/A
readOnly: true
container:
allOf:
- $ref: './link.yml'
- description: |-
The container the origin file is linked to.
Can be one of the following **Resources**:
- WorkPackage
readOnly: true
delete:
allOf:
- $ref: './link.yml'
- description: |-
The uri to delete the file link
**Resource**: N/A
readOnly: true
downloadLocation:
allOf:
- $ref: './link.yml'
- description: |-
The uri to download the origin file from
**Resource**: N/A
readOnly: true
staticDownloadLocation:
allOf:
- $ref: './link.yml'
- description: |-
A static uri to fetch the download uri
**Resource**: N/A
readOnly: true
originOpen:
allOf:
- $ref: './link.yml'
- description: |-
The uri to open the origin file on the origin itself
**Resource**: N/A
readOnly: true
example:
id: 1337
_type: FileLink
storageType: nextcloud
createdAt: '2021-12-20T13:37:00.211Z'
updatedAt: '2021-12-20T13:37:00.211Z'
originData:
id: 5503
name: logo.png
mimeType: image/png
createdAt: '2021-12-19T09:42:10.170Z'
lastModifiedAt: '2021-12-20T14:00:13.987Z'
createdByName: Luke Skywalker
lastModifiedByName: Anakin Skywalker
_links:
self:
href: /api/v3/work_package/17/file_links/1337
title: file link
storage:
href: https://nextcloud.deathstar.rocks/
title: Link to storage
container:
href: /api/v3/work_package/17
title: work package
delete:
href: /api/v3/work_package/17/file_links/1337
title: file link delete API
downloadLocation:
href: https://nextcloud.deathstar.rocks/index.php/dl?fileid=5503
title: file download
staticDownloadLocation:
href: /api/v3/work_package/17/file_links/1337/download
title: file link static download API
originOpen:
href: https://nextcloud.deathstar.rocks/index.php/f?fileid=5503
title: file open

@ -0,0 +1,37 @@
# Schema: File_Link_Origin_DataModel
---
type: object
required:
- id
- name
- mimeType
- createdAt
- lastModifiedAt
- createdByName
- lastModifiedByName
properties:
id:
type: string
description: Linked file's id on the origin
name:
type: string
description: Linked file's name on the origin
mimeType:
type: string
minLength: 0
description: MIME type of the linked file
createdAt:
type: string
format: date-time
description: Timestamp of the creation datetime of the file on the origin
lastModifiedAt:
type: string
format: date-time
description: Timestamp of the datetime of the last modification of the file on the origin
createdByName:
type: string
description: Display name of the author that created the file on the origin
lastModifiedByName:
type: string
description: Display name of the author that modified the file on the origin last

@ -392,6 +392,14 @@ paths:
"$ref": "./paths/work_package_available_relation_candidates.yml"
"/api/v3/work_packages/{id}/available_watchers":
"$ref": "./paths/work_package_available_watchers.yml"
"/api/v3/work_packages/{id}/file_links":
"$ref": "./paths/work_package_file_links.yml"
"/api/v3/work_packages/{id}/file_links/{file_link_id}":
"$ref": "./paths/work_package_file_link.yml"
"/api/v3/work_packages/{id}/file_links/{file_link_id}/download":
"$ref": "./paths/work_package_file_link_download.yml"
"/api/v3/work_packages/{id}/file_links/{file_link_id}/open":
"$ref": "./paths/work_package_file_link_open.yml"
"/api/v3/work_packages/{id}/form":
"$ref": "./paths/work_package_form.yml"
"/api/v3/work_packages/{id}/revisions":
@ -423,12 +431,8 @@ components:
schemas:
ActivityModel:
"$ref": "./components/schemas/activity_model.yml"
Aggregations:
"$ref": "./components/schemas/aggregations.yml"
AttachmentModel:
"$ref": "./components/schemas/attachment_model.yml"
Attachments:
"$ref": "./components/schemas/attachments.yml"
Attachments_by_post:
"$ref": "./components/schemas/attachments_by_post.yml"
Attachments_by_postModel:
@ -517,6 +521,10 @@ components:
"$ref": "./components/schemas/example_schema_model.yml"
Execute_custom_action:
"$ref": "./components/schemas/execute_custom_action.yml"
File_LinkModel:
"$ref": "./components/schemas/file_link_model.yml"
File_Link_Origin_DataModel:
"$ref": "./components/schemas/file_link_origin_data_model.yml"
FormModel:
"$ref": "./components/schemas/form_model.yml"
Formattable:

@ -19,7 +19,8 @@ get:
type: integer
- description: |-
JSON specifying filter conditions.
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/) endpoint. Currently supported filters are:
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/)
endpoint. Currently supported filters are:
+ status: Status the user has

@ -2,13 +2,13 @@
---
delete:
parameters:
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
- description: Work package id
example: 1
in: path
name: id
required: true
schema:
type: integer
responses:
'204':
description: |-
@ -16,41 +16,37 @@ delete:
Note that the response body is empty as of now. In future versions of the API a body
*might* be returned along with an appropriate HTTP status.
headers: {}
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to delete this work package.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to delete this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** delete work package
headers: {}
headers: { }
'404':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: |-
Deletes the work package, as well as:
@ -61,425 +57,409 @@ delete:
summary: Delete Work Package
get:
parameters:
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
responses:
'200':
content:
application/hal+json:
examples:
response:
value:
_links:
addAttachment:
href: "/api/v3/work_packages/1528/attachments"
method: post
addComment:
href: "/api/v3/work_packages/1528/activities"
method: post
title: Add comment
addRelation:
href: "/api/v3/relations"
method: post
title: Add relation
addWatcher:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/{user_id}"
templated: true
ancestors:
- href: "/api/v3/work_packages/1290"
title: Root node of hierarchy
- href: "/api/v3/work_packages/1291"
title: Intermediate node of hierarchy
- href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
assignee:
href: "/api/v3/users/11"
title: Emmie Okuneva - Adele5450
attachments:
href: "/api/v3/work_packages/1528/attachments"
author:
example:
_links:
addAttachment:
href: "/api/v3/work_packages/1528/attachments"
method: post
addComment:
href: "/api/v3/work_packages/1528/activities"
method: post
title: Add comment
addRelation:
href: "/api/v3/relations"
method: post
title: Add relation
addWatcher:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/{user_id}"
templated: true
ancestors:
- href: "/api/v3/work_packages/1290"
title: Root node of hierarchy
- href: "/api/v3/work_packages/1291"
title: Intermediate node of hierarchy
- href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
assignee:
href: "/api/v3/users/11"
title: Emmie Okuneva - Adele5450
attachments:
href: "/api/v3/work_packages/1528/attachments"
author:
href: "/api/v3/users/1"
title: OpenProject Admin - admin
availableWatchers:
href: "/api/v3/work_packages/1528/available_watchers"
category:
href: "/api/v3/categories/1298"
title: eligend isi
changeParent:
href: "/api/v3/work_packages/694"
method: patch
title: Change parent of Bug in OpenProject
children:
- href: "/api/v3/work_packages/1529"
title: Write API documentation
customActions:
- href: "/api/v3/work_packages/1528/custom_actions/153/execute"
method: post
title: Reset
- href: "/api/v3/work_packages/1528/custom_actions/94/execute"
method: post
title: Forward to accounting
customField3:
href: api/v3/users/14
delete:
href: "/work_packages/bulk?ids=1528"
method: delete
title: Delete Develop API
logTime:
href: "/work_packages/1528/time_entries/new"
title: Log time on Develop API
type: text/html
move:
href: "/work_packages/1528/move/new"
title: Move Develop API
type: text/html
parent:
href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
priority:
href: "/api/v3/priorities/2"
title: Normal
project:
href: "/api/v3/projects/1"
title: A Test Project
relations:
href: "/api/v3/work_packages/1528/relations"
title: Show relations
removeWatcher:
href: "/api/v3/work_packages/1528/watchers/{user_id}"
method: delete
templated: true
responsible:
href: "/api/v3/users/23"
title: Laron Leuschke - Alaina5788
revisions:
href: "/api/v3/work_packages/1528/revisions"
schema:
href: "/api/v3/work_packages/schemas/11-2"
self:
href: "/api/v3/work_packages/1528"
title: Develop API
status:
href: "/api/v3/statuses/1"
title: New
timeEntries:
href: "/work_packages/1528/time_entries"
title: Time entries
type: text/html
type:
href: "/api/v3/types/1"
title: A Type
update:
href: "/api/v3/work_packages/1528"
method: patch
title: Update Develop API
version:
href: "/api/v3/versions/1"
title: Version 1
watch:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/1"
title: OpenProject Admin - admin
availableWatchers:
href: "/api/v3/work_packages/1528/available_watchers"
category:
href: "/api/v3/categories/1298"
title: eligend isi
changeParent:
href: "/api/v3/work_packages/694"
method: patch
title: Change parent of Bug in OpenProject
children:
- href: "/api/v3/work_packages/1529"
title: Write API documentation
customActions:
- href: "/api/v3/work_packages/1528/custom_actions/153/execute"
method: post
title: Reset
- href: "/api/v3/work_packages/1528/custom_actions/94/execute"
method: post
title: Forward to accounting
customField3:
href: api/v3/users/14
delete:
href: "/work_packages/bulk?ids=1528"
method: delete
title: Delete Develop API
logTime:
href: "/work_packages/1528/time_entries/new"
title: Log time on Develop API
type: text/html
move:
href: "/work_packages/1528/move/new"
title: Move Develop API
type: text/html
parent:
href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
priority:
href: "/api/v3/priorities/2"
title: Normal
project:
href: "/api/v3/projects/1"
title: A Test Project
relations:
href: "/api/v3/work_packages/1528/relations"
title: Show relations
removeWatcher:
href: "/api/v3/work_packages/1528/watchers/{user_id}"
method: delete
templated: true
responsible:
href: "/api/v3/users/23"
title: Laron Leuschke - Alaina5788
revisions:
href: "/api/v3/work_packages/1528/revisions"
schema:
href: "/api/v3/work_packages/schemas/11-2"
self:
href: "/api/v3/work_packages/1528"
title: Develop API
status:
href: "/api/v3/statuses/1"
title: New
timeEntries:
href: "/work_packages/1528/time_entries"
title: Time entries
type: text/html
type:
href: "/api/v3/types/1"
title: A Type
update:
href: "/api/v3/work_packages/1528"
method: patch
title: Update Develop API
version:
href: "/api/v3/versions/1"
title: Version 1
watch:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/1"
watchers:
href: "/api/v3/work_packages/1528/watchers"
_type: WorkPackage
createdAt: '2014-08-29T12:40:53Z'
customField1: Foo
customField2: 42
derivedDueDate:
derivedEstimatedTime: PT10H
derivedStartDate:
description:
format: markdown
html: "<p>Develop super cool OpenProject API.</p>"
raw: Develop super cool OpenProject API.
dueDate:
estimatedTime: PT2H
id: 1528
percentageDone: 0
scheduleManually: false
startDate:
subject: Develop API
updatedAt: '2014-08-29T12:44:41Z'
watchers:
href: "/api/v3/work_packages/1528/watchers"
_type: WorkPackage
createdAt: '2014-08-29T12:40:53Z'
customField1: Foo
customField2: 42
derivedDueDate:
derivedEstimatedTime: PT10H
derivedStartDate:
description:
format: markdown
html: "<p>Develop super cool OpenProject API.</p>"
raw: Develop super cool OpenProject API.
dueDate:
estimatedTime: PT2H
id: 1528
percentageDone: 0
scheduleManually: false
startDate:
subject: Develop API
updatedAt: '2014-08-29T12:44:41Z'
schema:
"$ref": "../components/schemas/work_package_model.yml"
description: OK
headers: {}
headers: { }
'404':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: ''
operationId: View_Work_Package
summary: View Work Package
patch:
parameters:
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
- description: |-
Indicates whether change notifications (e.g. via E-Mail) should be sent.
Note that this controls notifications for all users interested in changes to the work package (e.g. watchers, author and assignee),
not just the current user.
example: 'false'
in: query
name: notify
required: false
schema:
default: 'true'
type: boolean
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
- description: |-
Indicates whether change notifications (e.g. via E-Mail) should be sent.
Note that this controls notifications for all users interested in changes to the work package (e.g. watchers, author and assignee),
not just the current user.
example: 'false'
in: query
name: notify
required: false
schema:
default: 'true'
type: boolean
responses:
'200':
content:
application/hal+json:
examples:
response:
value:
_links:
addAttachment:
href: "/api/v3/work_packages/1528/attachments"
method: post
addComment:
href: "/api/v3/work_packages/1528/activities"
method: post
title: Add comment
addRelation:
href: "/api/v3/relations"
method: post
title: Add relation
addWatcher:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/{user_id}"
templated: true
ancestors:
- href: "/api/v3/work_packages/1290"
title: Root node of hierarchy
- href: "/api/v3/work_packages/1291"
title: Intermediate node of hierarchy
- href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
assignee:
href: "/api/v3/users/11"
title: Emmie Okuneva - Adele5450
attachments:
href: "/api/v3/work_packages/1528/attachments"
author:
example:
_links:
addAttachment:
href: "/api/v3/work_packages/1528/attachments"
method: post
addComment:
href: "/api/v3/work_packages/1528/activities"
method: post
title: Add comment
addRelation:
href: "/api/v3/relations"
method: post
title: Add relation
addWatcher:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/{user_id}"
templated: true
ancestors:
- href: "/api/v3/work_packages/1290"
title: Root node of hierarchy
- href: "/api/v3/work_packages/1291"
title: Intermediate node of hierarchy
- href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
assignee:
href: "/api/v3/users/11"
title: Emmie Okuneva - Adele5450
attachments:
href: "/api/v3/work_packages/1528/attachments"
author:
href: "/api/v3/users/1"
title: OpenProject Admin - admin
availableWatchers:
href: "/api/v3/work_packages/1528/available_watchers"
category:
href: "/api/v3/categories/1298"
title: eligend isi
changeParent:
href: "/api/v3/work_packages/694"
method: patch
title: Change parent of Bug in OpenProject
children:
- href: "/api/v3/work_packages/1529"
title: Write API documentation
customActions:
- href: "/api/v3/work_packages/1528/custom_actions/153/execute"
method: post
title: Reset
- href: "/api/v3/work_packages/1528/custom_actions/94/execute"
method: post
title: Forward to accounting
customField3:
href: api/v3/users/14
delete:
href: "/work_packages/bulk?ids=1528"
method: delete
title: Delete Develop API
logTime:
href: "/work_packages/1528/time_entries/new"
title: Log time on Develop API
type: text/html
move:
href: "/work_packages/1528/move/new"
title: Move Develop API
type: text/html
parent:
href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
priority:
href: "/api/v3/priorities/2"
title: Normal
project:
href: "/api/v3/projects/1"
title: A Test Project
relations:
href: "/api/v3/work_packages/1528/relations"
title: Show relations
removeWatcher:
href: "/api/v3/work_packages/1528/watchers/{user_id}"
method: delete
templated: true
responsible:
href: "/api/v3/users/23"
title: Laron Leuschke - Alaina5788
revisions:
href: "/api/v3/work_packages/1528/revisions"
schema:
href: "/api/v3/work_packages/schemas/11-2"
self:
href: "/api/v3/work_packages/1528"
title: Develop API
status:
href: "/api/v3/statuses/1"
title: New
timeEntries:
href: "/work_packages/1528/time_entries"
title: Time entries
type: text/html
type:
href: "/api/v3/types/1"
title: A Type
update:
href: "/api/v3/work_packages/1528"
method: patch
title: Update Develop API
version:
href: "/api/v3/versions/1"
title: Version 1
watch:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/1"
title: OpenProject Admin - admin
availableWatchers:
href: "/api/v3/work_packages/1528/available_watchers"
category:
href: "/api/v3/categories/1298"
title: eligend isi
changeParent:
href: "/api/v3/work_packages/694"
method: patch
title: Change parent of Bug in OpenProject
children:
- href: "/api/v3/work_packages/1529"
title: Write API documentation
customActions:
- href: "/api/v3/work_packages/1528/custom_actions/153/execute"
method: post
title: Reset
- href: "/api/v3/work_packages/1528/custom_actions/94/execute"
method: post
title: Forward to accounting
customField3:
href: api/v3/users/14
delete:
href: "/work_packages/bulk?ids=1528"
method: delete
title: Delete Develop API
logTime:
href: "/work_packages/1528/time_entries/new"
title: Log time on Develop API
type: text/html
move:
href: "/work_packages/1528/move/new"
title: Move Develop API
type: text/html
parent:
href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
priority:
href: "/api/v3/priorities/2"
title: Normal
project:
href: "/api/v3/projects/1"
title: A Test Project
relations:
href: "/api/v3/work_packages/1528/relations"
title: Show relations
removeWatcher:
href: "/api/v3/work_packages/1528/watchers/{user_id}"
method: delete
templated: true
responsible:
href: "/api/v3/users/23"
title: Laron Leuschke - Alaina5788
revisions:
href: "/api/v3/work_packages/1528/revisions"
schema:
href: "/api/v3/work_packages/schemas/11-2"
self:
href: "/api/v3/work_packages/1528"
title: Develop API
status:
href: "/api/v3/statuses/1"
title: New
timeEntries:
href: "/work_packages/1528/time_entries"
title: Time entries
type: text/html
type:
href: "/api/v3/types/1"
title: A Type
update:
href: "/api/v3/work_packages/1528"
method: patch
title: Update Develop API
version:
href: "/api/v3/versions/1"
title: Version 1
watch:
href: "/api/v3/work_packages/1528/watchers"
method: post
payload:
user:
href: "/api/v3/users/1"
watchers:
href: "/api/v3/work_packages/1528/watchers"
_type: WorkPackage
createdAt: '2014-08-29T12:40:53Z'
customField1: Foo
customField2: 42
derivedDueDate:
derivedEstimatedTime: PT10H
derivedStartDate:
description:
format: markdown
html: "<p>Develop super cool OpenProject API.</p>"
raw: Develop super cool OpenProject API.
dueDate:
estimatedTime: PT2H
id: 1528
percentageDone: 0
scheduleManually: false
startDate:
subject: Develop API
updatedAt: '2014-08-29T12:44:41Z'
watchers:
href: "/api/v3/work_packages/1528/watchers"
_type: WorkPackage
createdAt: '2014-08-29T12:40:53Z'
customField1: Foo
customField2: 42
derivedDueDate:
derivedEstimatedTime: PT10H
derivedStartDate:
description:
format: markdown
html: "<p>Develop super cool OpenProject API.</p>"
raw: Develop super cool OpenProject API.
dueDate:
estimatedTime: PT2H
id: 1528
percentageDone: 0
scheduleManually: false
startDate:
subject: Develop API
updatedAt: '2014-08-29T12:44:41Z'
schema:
"$ref": "../components/schemas/work_package_model.yml"
description: OK
headers: {}
headers: { }
'400':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:InvalidRequestBody
message: The request body was not a single JSON object.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:InvalidRequestBody
message: The request body was not a single JSON object.
description: Occurs when the client did not send a valid JSON object in the
request body.
headers: {}
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to edit the content of the work package.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to edit the content of the work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** edit work package, assign version, manage subtasks or move work package
headers: {}
headers: { }
'404':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
headers: {}
headers: { }
'409':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:UpdateConflict
message: Your changes could not be saved, because the work package
was changed since you've seen it the last time.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:UpdateConflict
message: Your changes could not be saved, because the work package
was changed since you've seen it the last time.
description: Returned if the resource was changed since the client requested
it. This is determined using the `lockVersion` property.
headers: {}
headers: { }
'422':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_embedded:
details:
attribute: Subject
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:PropertyConstraintViolation
message: The subject might not be blank.
example:
_embedded:
details:
attribute: Subject
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:PropertyConstraintViolation
message: The subject might not be blank.
description: |-
Returned if:
@ -488,9 +468,9 @@ patch:
* a constraint for a property was violated (`PropertyConstraintViolation`)
* the client provides a link to an invalid resource (`ResourceTypeMismatch`)
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: |-
When calling this endpoint the client provides a single object, containing the properties and links that it wants to change, in the body.
Note that it is only allowed to provide properties or links supporting the **write** operation.

@ -0,0 +1,119 @@
# /api/v3/work_packages/{id}/file_links/{file_link_id}
---
get:
summary: Gets a file link.
operationId: Work_Package_Get_File_Link
tags:
- Work Packages
- File links
description: |-
Gets a single file link resource of a work package.
parameters:
- name: id
description: Work package id
in: path
required: true
schema:
type: integer
example: 1337
- name: file_link_id
description: File link id
in: path
required: true
schema:
type: integer
example: 42
responses:
'200':
description: OK
content:
application/hal+json:
schema:
$ref: '#/components/schemas/File_LinkModel'
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to delete file links of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** view file links
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
'404':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
delete:
summary: Removes a file link.
operationId: Work_Package_Delete_File_Link
tags:
- Work Packages
- File links
description: |-
Removes a file link on a work package.
The request contains only the file link identifier as a path parameter. No request body is needed.
parameters:
- name: id
description: Work package id
in: path
required: true
schema:
type: integer
example: 1337
- name: file_link_id
description: File link id
in: path
required: true
schema:
type: integer
example: 42
responses:
'200':
description: OK
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to delete file links of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** manage file links
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
'404':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package

@ -0,0 +1,62 @@
# /api/v3/work_packages/{id}/file_links/{file_link_id}/download
---
post:
summary: Creates a download uri of the linked file.
operationId: Work_Package_File_Link_Download
tags:
- Work Packages
- File links
description: |-
Creates a download uri of the origin file linked by the given file link. This uri depends on the storage type and
always is located on the origin storage itself.
parameters:
- name: id
description: Work package id
in: path
required: true
schema:
type: integer
example: 1337
- name: file_link_id
description: File link id
in: path
required: true
schema:
type: integer
example: 42
responses:
'200':
description: OK
content:
application/hal+json:
schema:
$ref: '#/components/schemas/Link'
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to delete file links of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** view file links
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
'404':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package

@ -0,0 +1,62 @@
# /api/v3/work_packages/{id}/file_links/{file_link_id}/open
---
post:
summary: Creates an opening uri of the linked file.
operationId: Work_Package_File_Link_Open
tags:
- Work Packages
- File links
description: |-
Creates a uri to open the origin file linked by the given file link. This uri depends on the storage type and
always is located on the origin storage itself.
parameters:
- name: id
description: Work package id
in: path
required: true
schema:
type: integer
example: 1337
- name: file_link_id
description: File link id
in: path
required: true
schema:
type: integer
example: 42
responses:
'200':
description: OK
content:
application/hal+json:
schema:
$ref: '#/components/schemas/Link'
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to delete file links of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** view file links
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
'404':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package

@ -0,0 +1,212 @@
# /api/v3/work_packages/{id}/file_links/
---
post:
summary: Creates file links.
operationId: Work_Package_Create_File_Links
tags:
- Work Packages
- File links
description: |-
Creates file links on a work package.
The request body must contain the generated storage token, and not the storage id. In addition, the request must
send the origin file id and the stored meta data.
If there is already a link on the given work package with the same file id from the same storage, the previous
data is not overwritten.
As it is possible to link folders from a storage to a work package, the _mimeType_ of this entity can be empty.
parameters:
- name: id
description: Work package id
in: path
required: true
schema:
type: integer
example: 1337
requestBody:
content:
application/json:
schema:
type: object
required:
- _type
- count
- total
- _embedded
properties:
_type:
type: string
enum:
- Collection
count:
type: integer
minimum: 0
total:
type: integer
minimum: 0
_embedded:
type: object
required:
- elements
properties:
elements:
type: array
items:
allOf:
- $ref: '#/components/schemas/File_LinkModel'
- type: object
required:
- storageUrl
properties:
storageUrl:
type: string
format: uri
responses:
'201':
description: Created
content:
application/hal+json:
schema:
type: object
required:
- _type
- count
- total
- _embedded
properties:
_type:
type: string
enum:
- Collection
count:
type: integer
minimum: 0
total:
type: integer
minimum: 0
_embedded:
type: object
required:
- elements
properties:
elements:
type: array
items:
$ref: '#/components/schemas/File_LinkModel'
'400':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:InvalidRequestBody
message: The request body was invalid.
description: Occurs when the client did not send a valid JSON object in the request body.
'403':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to create file links of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** manage file links
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
'404':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
get:
summary: Gets all file links of a work package
operationId: Work_Package_Get_File_Links
tags:
- Work Packages
- File links
description: |-
Gets all file links of a work package.
The request can be filtered for the storage types and ids.
parameters:
- name: id
description: Work package id
in: path
required: true
schema:
type: integer
example: 1337
responses:
'200':
description: OK
content:
application/hal+json:
schema:
type: object
required:
- _type
- count
- total
- _embedded
properties:
_type:
type: string
enum:
- Collection
count:
type: integer
minimum: 0
total:
type: integer
minimum: 0
_embedded:
type: object
required:
- elements
properties:
elements:
type: array
items:
$ref: '#/components/schemas/File_LinkModel'
'403':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to create file links of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** view file links
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
'404':
content:
application/hal+json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The requested resource could not be found.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package

@ -2,147 +2,139 @@
---
get:
parameters:
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
- description: Work package id
example: 1
in: path
name: id
required: true
schema:
type: integer
responses:
'200':
content:
application/hal+json:
examples:
response:
value:
_embedded:
elements:
- _links:
delete:
href: "/api/v3/users/1"
method: DELETE
title: Delete j.sheppard
lock:
href: "/api/v3/users/1/lock"
method: POST
title: Set lock on j.sheppard
self:
href: "/api/v3/users/1"
title: John Sheppard - j.sheppard
showUser:
href: "/users/1"
type: text/html
_type: User
avatar: https://example.org/users/1/avatar
createdAt: '2014-05-21T08:51:20Z'
firstName: John
id: 1
lastName: Sheppard
login: j.sheppard
mail: shep@mail.com
status: active
updatedAt: '2014-05-21T08:51:20Z'
- _links:
delete:
href: "/api/v3/users/2"
method: DELETE
title: Delete j.sheppard2
lock:
href: "/api/v3/users/2/lock"
method: POST
title: Set lock on j.sheppard2
self:
href: "/api/v3/users/2"
title: Jim Sheppard - j.sheppard2
_type: User
avatar: https://example.org/users/1/avatar
createdAt: '2014-05-21T08:51:20Z'
firstName: Jim
id: 2
lastName: Sheppard
login: j.sheppard2
mail: shep@mail.net
status: active
updatedAt: '2014-05-21T08:51:20Z'
_links:
self:
href: "/api/v3/work_packages/14/watchers"
_type: Collection
count: 2
total: 2
example:
_embedded:
elements:
- _links:
delete:
href: "/api/v3/users/1"
method: DELETE
title: Delete j.sheppard
lock:
href: "/api/v3/users/1/lock"
method: POST
title: Set lock on j.sheppard
self:
href: "/api/v3/users/1"
title: John Sheppard - j.sheppard
showUser:
href: "/users/1"
type: text/html
_type: User
avatar: https://example.org/users/1/avatar
createdAt: '2014-05-21T08:51:20Z'
firstName: John
id: 1
lastName: Sheppard
login: j.sheppard
mail: shep@mail.com
status: active
updatedAt: '2014-05-21T08:51:20Z'
- _links:
delete:
href: "/api/v3/users/2"
method: DELETE
title: Delete j.sheppard2
lock:
href: "/api/v3/users/2/lock"
method: POST
title: Set lock on j.sheppard2
self:
href: "/api/v3/users/2"
title: Jim Sheppard - j.sheppard2
_type: User
avatar: https://example.org/users/1/avatar
createdAt: '2014-05-21T08:51:20Z'
firstName: Jim
id: 2
lastName: Sheppard
login: j.sheppard2
mail: shep@mail.net
status: active
updatedAt: '2014-05-21T08:51:20Z'
_links:
self:
href: "/api/v3/work_packages/14/watchers"
_type: Collection
count: 2
total: 2
schema:
"$ref": "../components/schemas/watchers_model.yml"
description: OK
headers: {}
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to see the watchers of this work package.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to see the watchers of this work package.
description: |-
Returned if the client does not have sufficient permissions.
**Required permission:** view work package watchers
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
headers: {}
headers: { }
'404':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
*Note that you will effectively not be able to see the watchers of a work package without being able to see the work package.*
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: ''
operationId: List_watchers
summary: List watchers
post:
parameters:
- description: Work package id
example: '1'
in: path
name: id
required: true
schema:
type: integer
- description: Work package id
example: 1
in: path
name: id
required: true
schema:
type: integer
responses:
'200':
description: OK
headers: {}
headers: { }
'201':
description: Created
headers: {}
headers: { }
'400':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:InvalidRequestBody
message: The request body was not a single JSON object.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:InvalidRequestBody
message: The request body was not a single JSON object.
description: |-
Occurs when the client did not send a valid JSON object in the request body.
@ -153,18 +145,16 @@ post:
* The JSON object did not contain the key `user`
* The value of `users` was not a link object
headers: {}
headers: { }
'403':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to add watchers to this work package.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission
message: You are not allowed to add watchers to this work package.
description: |-
Returned if the client does not have sufficient permissions.
@ -175,25 +165,23 @@ post:
* add work package watchers (for other users)
*Note that you will only receive this error, if you are at least allowed to see the corresponding work package.*
headers: {}
headers: { }
'404':
content:
application/hal+json:
schema:
$ref: "../components/schemas/error_response.yml"
examples:
response:
value:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
example:
_type: Error
errorIdentifier: urn:openproject-org:api:v3:errors:NotFound
message: The specified work package does not exist.
description: |-
Returned if the work package does not exist or the client does not have sufficient permissions to see it.
**Required permission:** view work package
*Note that you will effectively not be able to change the watchers of a work package without being able to see the work package.*
headers: {}
headers: { }
'422':
description: |-
Returned if:
@ -203,9 +191,9 @@ post:
* the user specified is not allowed to watch that work package (`PropertyConstraintViolation`)
* the user specified does not exist (`PropertyConstraintViolation`)
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: |-
Adds a watcher to the specified work package.

@ -2,57 +2,112 @@
---
get:
parameters:
- description: Page number inside the requested collection.
example: '25'
in: query
name: offset
required: false
schema:
default: 1
type: integer
- description: Number of elements to display per page.
example: '25'
in: query
name: pageSize
required: false
schema:
type: integer
- description: |-
JSON specifying filter conditions.
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/) endpoint. If no filter is to be applied, the client should send an empty array (`[]`).
example: '[{ "type_id": { "operator": "=", "values": [''1'', ''2''] }}]'
in: query
name: filters
required: false
schema:
default: '[{ "status_id": { "operator": "o", "values": null }}]'
type: string
- description: |-
JSON specifying sort criteria.
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/) endpoint.
example: '[["status", "asc"]]'
in: query
name: sortBy
required: false
schema:
default: '[["id", "asc"]]'
type: string
- description: The column to group by.
example: status
in: query
name: groupBy
required: false
schema:
type: string
- description: Indicates whether properties should be summed up if they support
it.
example: 'true'
in: query
name: showSums
required: false
schema:
default: 'false'
type: boolean
- description: Page number inside the requested collection.
example: '25'
in: query
name: offset
required: false
schema:
default: 1
type: integer
- description: Number of elements to display per page.
example: '25'
in: query
name: pageSize
required: false
schema:
type: integer
- description: |-
JSON specifying filter conditions.
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/)
endpoint. If no filter is to be applied, the client should send an empty array (`[]`), otherwise a default
filter is applied. A Currently supported filters are (there are additional filters added by modules):
- assigned_to
- assignee_or_group
- attachment_base
- attachment_content
- attachment_file_name
- author
- blocked
- blocks
- category
- comment
- created_at
- custom_field
- dates_interval
- description
- done_ratio
- due_date
- duplicated
- duplicates
- estimated_hours
- file_link_origin_id
- follows
- group
- id
- includes
- manual_sort
- milestone
- only_subproject
- parent
- partof
- precedes
- principal_base
- priority
- project
- relatable
- relates
- required
- requires
- responsible
- role
- search
- start_date
- status
- storage_url
- subject
- subject_or_id
- subproject
- type
- typeahead
- updated_at
- version
- watcher
- work_package
example: '[{ "type_id": { "operator": "=", "values": ["1", "2"] }}]'
in: query
name: filters
required: false
schema:
default: '[{ "status_id": { "operator": "o", "values": null }}]'
type: string
- description: |-
JSON specifying sort criteria.
Accepts the same format as returned by the [queries](https://www.openproject.org/docs/api/endpoints/queries/) endpoint.
example: '[["status", "asc"]]'
in: query
name: sortBy
required: false
schema:
default: '[["id", "asc"]]'
type: string
- description: The column to group by.
example: status
in: query
name: groupBy
required: false
schema:
type: string
- description: Indicates whether properties should be summed up if they support
it.
example: 'true'
in: query
name: showSums
required: false
schema:
default: 'false'
type: boolean
responses:
'200':
content:
@ -62,18 +117,18 @@ get:
value:
_embedded:
elements:
- _links:
self:
href: "/api/v3/work_packages/1"
_type: WorkPackage
id: 1
subject: Skipped other properties for brevity
- _links:
self:
href: "/api/v3/work_packages/2"
_type: WorkPackage
id: 2
subject: Skipped other properties for brevity
- _links:
self:
href: "/api/v3/work_packages/1"
_type: WorkPackage
id: 1
subject: Skipped other properties for brevity
- _links:
self:
href: "/api/v3/work_packages/2"
_type: WorkPackage
id: 2
subject: Skipped other properties for brevity
_links:
self:
href: "/api/v3/work_packages"
@ -83,7 +138,7 @@ get:
schema:
"$ref": "../components/schemas/work_packages_model.yml"
description: OK
headers: {}
headers: { }
'400':
content:
application/hal+json:
@ -98,7 +153,7 @@ get:
description: |-
Returned if the client sends a query parameter, that the server knows, but does not understand.
E.g. by providing a syntactically incorrect `filters` parameter.
headers: {}
headers: { }
'403':
content:
application/hal+json:
@ -114,25 +169,25 @@ get:
Returned if the client does not have sufficient permissions.
**Required permission:** view work packages (globally or in any project)
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: ''
operationId: List_work_packages
summary: List work packages
post:
parameters:
- description: |-
Indicates whether change notifications (e.g. via E-Mail) should be sent.
Note that this controls notifications for all users interested in changes to the work package (e.g. watchers, author and assignee),
not just the current user.
example: 'false'
in: query
name: notify
required: false
schema:
default: 'true'
type: boolean
- description: |-
Indicates whether change notifications (e.g. via E-Mail) should be sent.
Note that this controls notifications for all users interested in changes to the work package (e.g. watchers, author and assignee),
not just the current user.
example: 'false'
in: query
name: notify
required: false
schema:
default: 'true'
type: boolean
responses:
'200':
content:
@ -160,12 +215,12 @@ post:
href: "/api/v3/users/{user_id}"
templated: true
ancestors:
- href: "/api/v3/work_packages/1290"
title: Root node of hierarchy
- href: "/api/v3/work_packages/1291"
title: Intermediate node of hierarchy
- href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
- href: "/api/v3/work_packages/1290"
title: Root node of hierarchy
- href: "/api/v3/work_packages/1291"
title: Intermediate node of hierarchy
- href: "/api/v3/work_packages/1298"
title: nisi eligendi officiis eos delectus quis voluptas dolores
assignee:
href: "/api/v3/users/11"
title: Emmie Okuneva - Adele5450
@ -184,15 +239,15 @@ post:
method: patch
title: Change parent of Bug in OpenProject
children:
- href: "/api/v3/work_packages/1529"
title: Write API documentation
- href: "/api/v3/work_packages/1529"
title: Write API documentation
customActions:
- href: "/api/v3/work_packages/1528/custom_actions/153/execute"
method: post
title: Reset
- href: "/api/v3/work_packages/1528/custom_actions/94/execute"
method: post
title: Forward to accounting
- href: "/api/v3/work_packages/1528/custom_actions/153/execute"
method: post
title: Reset
- href: "/api/v3/work_packages/1528/custom_actions/94/execute"
method: post
title: Forward to accounting
customField3:
href: api/v3/users/14
delete:
@ -262,25 +317,25 @@ post:
createdAt: '2014-08-29T12:40:53Z'
customField1: Foo
customField2: 42
derivedDueDate:
derivedDueDate:
derivedEstimatedTime: PT10H
derivedStartDate:
derivedStartDate:
description:
format: markdown
html: "<p>Develop super cool OpenProject API.</p>"
raw: Develop super cool OpenProject API.
dueDate:
dueDate:
estimatedTime: PT2H
id: 1528
percentageDone: 0
scheduleManually: false
startDate:
startDate:
subject: Develop API
updatedAt: '2014-08-29T12:44:41Z'
schema:
"$ref": "../components/schemas/work_package_model.yml"
description: OK
headers: {}
headers: { }
'400':
content:
application/hal+json:
@ -294,7 +349,7 @@ post:
message: The request body was not a single JSON object.
description: Occurs when the client did not send a valid JSON object in the
request body.
headers: {}
headers: { }
'403':
content:
application/hal+json:
@ -312,7 +367,7 @@ post:
**Required permission:** add work packages
*Note that you will only receive this error, if you are at least allowed to see the corresponding project.*
headers: {}
headers: { }
'404':
content:
application/hal+json:
@ -332,7 +387,7 @@ post:
*Note: A client without sufficient permissions shall not be able to test for the existence of a project.
That's why a 404 is returned here, even if a 403 might be more appropriate.*
headers: {}
headers: { }
'422':
content:
application/hal+json:
@ -355,9 +410,9 @@ post:
* a constraint for a property was violated
* a property was provided in an unreadable format
headers: {}
headers: { }
tags:
- Work Packages
- Work Packages
description: |-
When calling this endpoint the client provides a single object, containing at least the properties and links that are required, in the body.
The required fields of a WorkPackage can be found in its schema, which is embedded in the respective form.

@ -0,0 +1,4 @@
---
description: |-
TBD
name: File links

@ -0,0 +1,41 @@
module Storages
class RowCell < ::RowCell
include ::IconsHelper
include ::AvatarHelper
include ::Redmine::I18n
def name
model.name
end
def provider_type
model.provider_type
end
def creator
icon = avatar model.creator, size: :mini
icon + model.creator.name
end
def button_links
[edit_link, delete_link]
end
def delete_link
link_to '',
storage_path(model),
class: 'icon icon-delete',
data: { confirm: I18n.t(:text_are_you_sure) },
title: I18n.t(:button_delete),
method: :delete
end
def edit_link
link_to '',
edit_storage_path(model),
class: 'icon icon-edit',
accesskey: accesskey(:edit),
title: I18n.t(:button_edit)
end
end
end

@ -0,0 +1,35 @@
module Storages
class TableCell < ::TableCell
include ::IconsHelper
columns :name, :provider_type, :creator, :created_at
def initial_sort
%i[created_at asc]
end
def sortable?
false
end
def inline_create_link
link_to(new_storage_path,
class: 'wp-inline-create--add-link',
title: I18n.t('storages.label_new_storage')) do
op_icon('icon icon-add')
end
end
def empty_row_message
I18n.t 'storages.no_results'
end
def headers
[
['name', { caption: Storages::Storage.human_attribute_name(:name) }],
['provider_type', { caption: I18n.t('storages.provider_types.label') }],
['creator', { caption: I18n.t('storages.label_creator') }],
['created_at', { caption: Storages::Storage.human_attribute_name(:created_at) }]
]
end
end
end

@ -0,0 +1,83 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 Storages::Admin::StoragesController < ApplicationController
layout 'admin'
before_action :require_admin
menu_item :storages_settings
def index
@storages = Storages::Storage.all
render 'storages/admin/index'
end
def show
@storage = Storages::Storage.find_by id: params[:id]
render 'storages/admin/show'
end
def new
@storage = Storages::Storage.new(provider_type: 'nextcloud', name: I18n.t('storages.provider_types.nextcloud'))
render 'storages/admin/new'
end
def create
combined_params = permitted_storage_params
.to_h
.reverse_merge(creator_id: current_user.id)
@storage = Storages::Storage.create combined_params
redirect_to storage_path(@storage)
end
def update
# tbd
end
def delete
# tbd
end
def default_breadcrumb
t(:project_module_storages)
end
def show_local_breadcrumb
true
end
private
def permitted_storage_params
params
.require(:storages_storage)
.permit('name', 'provider_type')
end
end

@ -0,0 +1,35 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 Storages::FileLink < ApplicationRecord
belongs_to :storage
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
belongs_to :container, foreign_key: 'container_id', class_name: 'WorkPackage' # This needs to become more flexible in the future
end

@ -0,0 +1,39 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 Storages::Storage < ApplicationRecord
has_many :file_links, class_name: 'Storages::FileLink'
belongs_to :creator, class_name: 'User'
has_secure_token :identifier
PROVIDER_TYPES = %w[nextcloud].freeze
validates_uniqueness_of :name
end

@ -0,0 +1,46 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2021 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.
++#%>
<%= error_messages_for_contract @storage, @errors %>
<section class="form--section">
<div class="form--field -required">
<%= f.select :provider_type,
Storages::Storage::PROVIDER_TYPES.map { |provider_type| [I18n.t("storages.provider_types.#{provider_type}"), provider_type] },
selected: 'nextcloud',
container_class: '-slim' %>
</div>
<div class="form--field -required">
<%= f.text_field :name, required: true, size: 25, container_class: '-slim' %>
<span class="form--field-instructions">
<%= t('storages.instructions.name') %>
</span>
</div>
</section>

@ -0,0 +1,15 @@
<% html_title t(:label_administration), t("project_module_storages") %>
<%= toolbar title: t("project_module_storages") do %>
<li class="toolbar-item">
<%= link_to new_storage_path,
{ class: 'button -alt-highlight',
aria: { label: t(:'storages.label_new_storage') },
title: t(:'ifc_models.label_new_storage') } do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= ::Storages::Storage.model_name.human %></span>
<% end %>
</li>
<% end %>
<%= rails_cell ::Storages::TableCell, @storages %>

@ -0,0 +1,9 @@
<% html_title t(:label_administration), t("project_module_storages") %>
<% local_assigns[:additional_breadcrumb] = t('storages.label_new_storage') %>
<%= toolbar title: t("storages.label_new_storage") %>
<%= labelled_tabular_form_for @storage, url: storages_path(@storage) do |f| -%>
<%= error_messages_for_contract @storage, @errors %>
<%= render :partial => 'storages/admin/form', locals: { f: f } %>
<%= styled_button_tag t(:button_create), class: "-highlight -with-icon icon-checkmark" %>
<% end %>

@ -0,0 +1,59 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2021 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.
++#%>
<% html_title t(:label_administration), t('project_module_storages'), t(@storage.name) -%>
<% local_assigns[:additional_breadcrumb] = @storage.name %>
<%= toolbar title: "#{t('project_module_storages')} - #{@storage.name}",
title_class: 'no-padding-bottom' do %>
<li class="toolbar-item">
<%= link_to edit_storage_path(@storage),
class: 'button' do %>
<%= op_icon('button--icon icon-edit') %>
<span class="button--text"><%= t(:button_edit) %></span>
<% end %>
</li>
<li class="toolbar-item">
<%= link_to storage_path(@storage),
method: :delete,
data: { confirm: I18n.t(:text_are_you_sure) },
class: 'button -danger' do %>
<%= op_icon('button--icon icon-delete') %>
<span class="button--text"><%= t(:button_delete) %></span>
<% end %>
</li>
<% end %>
<ul>
<li><%= @storage.id %></li>
<li><%= @storage.name %></li>
<li><%= @storage.provider_type %></li>
<li><%= @storage.creator.name %></li>
<li><%= @storage.created_at %></li>
<li><%= @storage.identifier %></li>
</ul>

@ -0,0 +1,30 @@
# English strings go here
en:
permission_view_file_links: "View file links"
permission_manage_file_links: "Manage file links"
permission_manage_storage_in_project: "Manage file storage in project"
project_module_storages: "File storages"
activerecord:
models:
storages/storage: "Storage"
attributes:
storages/storage:
name: "Name"
creator: "Creator"
provider_type: "Provider type"
storages:
instructions:
name: "Please use a short name. It will get used as a tab title in the work package view."
label_creator: "Creator"
label_file_link: "File link"
label_file_links: "File links"
label_name: "Name"
label_new_storage: "New storage"
label_storage: "Storage"
label_storages: "Storages"
no_results: "No storage setup, yet."
provider_types:
label: "Provider type"
nextcloud: "Nextcloud"

@ -0,0 +1,4 @@
# English strings go here
en:
js:
storages:

@ -0,0 +1,5 @@
OpenProject::Application.routes.draw do
scope 'admin/settings' do
resources :storages, controller: 'storages/admin/storages'
end
end

@ -0,0 +1,33 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 OpenProject
module Storages
require 'open_project/storages/engine'
end
end

@ -0,0 +1,49 @@
# OpenProject Team Planner module
#
# Copyright (C) 2021 OpenProject GmbH
#
# 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.
module OpenProject::Storages
class Engine < ::Rails::Engine
engine_name :openproject_storages
include OpenProject::Plugins::ActsAsOpEngine
register 'openproject-storages',
author_url: 'https://www.openproject.org',
bundled: true,
settings: {},
name: 'OpenProject Storages' do
project_module :storages, dependencies: :work_package_tracking do
permission :view_file_links,
{},
dependencies: %i[view_work_packages]
permission :manage_file_links,
{},
dependencies: %i[view_file_links]
permission :manage_storage_in_project,
{},
dependencies: %i[select_project_modules]
end
# Menu extensions
menu :project_menu,
:storages,
{ controller: '/storages/storages', action: 'index' },
caption: :'storages.label_storage'
end
end
end

@ -0,0 +1 @@
require 'open_project/storages'

@ -0,0 +1,13 @@
# encoding: UTF-8
Gem::Specification.new do |s|
s.name = 'openproject-storages'
s.version = '1.0.0'
s.authors = 'OpenProject GmbH'
s.email = 'info@openproject.com'
s.summary = 'OpenProject Storages'
s.description = 'Allows linking work packages to files in external storages, such as Nextcloud.'
s.license = 'GPLv3'
s.files = Dir['{app,config,db,lib}/**/*']
end

@ -0,0 +1,68 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 'spec_helper'
require 'contracts/shared/model_contract_shared_context'
require 'contracts/views/shared_contract_examples'
describe Views::CreateContract do
# it_behaves_like 'view contract', true do
# let(:view) do
# View.new(query: view_query,
# type: view_type)
# end
# let(:view_type) do
# 'team_planner'
# end
# let(:permissions) { %i[view_work_packages save_queries manage_team_planner] }
#
# subject(:contract) do
# described_class.new(view, current_user)
# end
#
# describe 'validation' do
# context 'with the type being nil' do
# let(:view_type) { nil }
#
# it_behaves_like 'contract is invalid', type: :inclusion
# end
#
# context 'with the type not being one of the configured' do
# let(:view_type) { 'blubs' }
#
# it_behaves_like 'contract is invalid', type: :inclusion
# end
#
# context 'without the :manage_team_planner permission' do
# let(:permissions) { %i[view_work_packages save_queries] }
#
# it_behaves_like 'contract is invalid', base: :error_unauthorized
# end
# end
# end
end

@ -0,0 +1,36 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 'spec_helper'
require_relative '../support/pages/storage_tab'
shared_context 'with storages full access' do
current_user { user }
end

@ -0,0 +1,36 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 'spec_helper'
require 'support/permission_specs'
# describe Storages::BlaBlaController, 'manage_storage_in_proejct permission', type: :controller do
# include PermissionSpecs
#
# check_permission_required_for('team_planner/team_planner#index', :view_team_planner)
# end

@ -0,0 +1,104 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 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 'spec_helper'
describe ::API::V3::Views::ViewsAPI,
'create',
content_type: :json,
type: :request do
# include API::V3::Utilities::PathHelper
#
# shared_let(:permitted_user) { FactoryBot.create(:user) }
# shared_let(:role) do
# FactoryBot.create(:role,
# permissions: %w[view_work_packages
# view_file_links])
# end
# shared_let(:project) do
# FactoryBot.create(:project,
# members: { permitted_user => role })
# end
#
# let(:additional_setup) do
# # to be overwritten by some specs
# end
#
# let(:body) do
# {
# _links: {
# query: {
# href: api_v3_paths.query(public_query.id)
# }
# }
# }.to_json
# end
#
# let(:send_request) do
# post api_v3_paths.views_type('team_planner'), body
# end
#
# current_user { permitted_user }
#
# subject(:response) { last_response }
#
# before do
# additional_setup
#
# send_request
# end
#
# describe 'POST /api/v3/views/team_planner' do
# context 'with a user allowed to save the query' do
# it 'returns 201 CREATED' do
# expect(response.status)
# .to eq(201)
# end
#
# it 'returns the view' do
# expect(response.body)
# .to be_json_eql('Views::TeamPlanner'.to_json)
# .at_path('_type')
#
# expect(response.body)
# .to be_json_eql(View.last.id.to_json)
# .at_path('id')
# end
# end
#
# context 'with a user not allowed to manage team planners' do
# let(:additional_setup) do
# role.update_attribute(:permissions,
# %w[view_work_packages
# save_queries
# manage_public_queries])
# end
#
# it_behaves_like 'unauthorized access'
# end
# end
end

@ -0,0 +1,37 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 'spec_helper'
describe 'Storages routing', type: :routing do
# it 'routes to team_planner#index' do
# expect(subject)
# .to route(:get, '/projects/foobar/team_planner/state')
# .to(controller: 'team_planner/team_planner', action: 'index', project_id: 'foobar', state: 'state')
# end
end

@ -0,0 +1,126 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 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 'support/pages/page'
module Pages
class StorageTab < ::Pages::Page
# attr_reader :project
#
# def initialize(project)
# super()
#
# @project = project
# end
#
# def path
# project_team_planner_path(project)
# end
#
# def expect_title(title = 'Unnamed team planner')
# expect(page).to have_selector '.editable-toolbar-title--fixed', text: title
# end
#
# def expect_empty_state(present: true)
# expect(page).to have_conditional_selector(present, '.op-team-planner--no-data', text: 'Add assignees to set up your team planner.')
# end
#
# def expect_assignee(user, present: true)
# name = user.is_a?(User) ? user.name : user.to_s
# expect(page).to have_conditional_selector(present, '.fc-resource', text: name, wait: 10)
# end
#
# def add_item(assignee, start_date, end_date)
# script = <<~JS
# var event = new CustomEvent(
# 'teamPlannerSelectDate',
# {
# detail: {
# assignee: arguments[0],
# start: arguments[1],
# end: arguments[2]
# }
# });
#
# document.dispatchEvent(event);
# JS
#
# page.execute_script(script, assignee, start_date, end_date)
# ::Pages::SplitWorkPackageCreate.new project: project
# end
#
# def remove_assignee(user)
# page.find(%([data-qa-remove-assignee="#{user.id}"])).click
# end
#
# def within_lane(user, &block)
# raise ArgumentError.new("Expected instance of principal") unless user.is_a?(Principal)
#
# type = ::API::V3::Principals::PrincipalType.for(user)
# href = ::API::V3::Utilities::PathHelper::ApiV3Path.send(type, user.id)
#
# page.within(%(.fc-timeline-lane[data-resource-id="#{href}"]), &block)
# end
#
# def expect_event(work_package, present: true)
# expect(page).to have_conditional_selector(present, '.fc-event', text: work_package.subject)
# end
#
# def open_split_view(work_package)
# page
# .find('.fc-event', text: work_package.subject)
# .click
#
# ::Pages::SplitWorkPackage.new(work_package, project)
# end
#
# def add_assignee(name)
# click_add_user
# search_user_to_add name
# select_user_to_add name
# end
#
# def click_add_user
# # Close the existing, if it is open
# is_open = page.all('[data-qa-selector="tp-add-assignee"] input').first
# page.find('[data-qa-selector="tp-assignee-add-button"]').click unless is_open
# end
#
# def select_user_to_add(name)
# select_autocomplete page.find('[data-qa-selector="tp-add-assignee"]'),
# query: name,
# results_selector: 'body'
# end
#
# def search_user_to_add(name)
# search_autocomplete page.find('[data-qa-selector="tp-add-assignee"]'),
# query: name,
# results_selector: 'body'
# end
# end
end
Loading…
Cancel
Save