diff --git a/app/models/attachment.rb b/app/models/attachment.rb index c698ed1396d..85fc65a3f70 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -175,6 +175,21 @@ class Attachment < ActiveRecord::Base content_type || fallback end + def copy(&block) + attachment = dup + attachment.file = diskfile + + yield attachment if block_given? + + attachment + end + + def copy!(&block) + attachment = copy &block + + attachment.save! + end + def extract_fulltext return unless OpenProject::Database.allows_tsv? job = ExtractFulltextJob.new(id) diff --git a/frontend/src/app/modules/hal/resources/meeting-content-resource.ts b/frontend/src/app/modules/hal/resources/meeting-content-resource.ts new file mode 100644 index 00000000000..1d9d59e730c --- /dev/null +++ b/frontend/src/app/modules/hal/resources/meeting-content-resource.ts @@ -0,0 +1,46 @@ +//-- copyright +// OpenProject is a project management system. +// Copyright (C) 2012-2015 the OpenProject Foundation (OPF) +// +// 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 doc/COPYRIGHT.rdoc for more details. +//++ + +import {HalResource} from 'core-app/modules/hal/resources/hal-resource'; +import {Attachable} from 'core-app/modules/hal/resources/mixins/attachable-mixin'; + + +export interface MeetingContentResourceLinks { + addAttachment(attachment:HalResource):Promise; +} + +class MeetingContentBaseResource extends HalResource { + public $links:MeetingContentResourceLinks; + + private attachmentsBackend = false; +} + +export const MeetingContentResource = Attachable(MeetingContentBaseResource); + +export interface MeetingContentResource extends HalResource { +} diff --git a/frontend/src/app/modules/hal/services/hal-resource.config.ts b/frontend/src/app/modules/hal/services/hal-resource.config.ts index ed3961ce0c4..1450c9285a5 100644 --- a/frontend/src/app/modules/hal/services/hal-resource.config.ts +++ b/frontend/src/app/modules/hal/services/hal-resource.config.ts @@ -51,6 +51,7 @@ import { import {Injectable} from '@angular/core'; import {HalResource} from 'core-app/modules/hal/resources/hal-resource'; import {WikiPageResource} from "core-app/modules/hal/resources/wiki-page-resource"; +import {MeetingContentResource} from "core-app/modules/hal/resources/meeting-content-resource"; import {PostResource} from "core-app/modules/hal/resources/post-resource"; import {StatusResource} from "core-app/modules/hal/resources/status-resource"; import {GridWidgetResource} from "core-app/modules/hal/resources/grid-widget-resource"; @@ -158,6 +159,9 @@ const halResourceDefaultConfig:{ [typeName:string]:HalResourceFactoryConfigInter WikiPage: { cls: WikiPageResource }, + MeetingContent: { + cls: MeetingContentResource + }, Post: { cls: PostResource }, diff --git a/modules/meeting/app/controllers/meeting_contents_controller.rb b/modules/meeting/app/controllers/meeting_contents_controller.rb index 2eee4de6edc..dc9cd99ff2d 100644 --- a/modules/meeting/app/controllers/meeting_contents_controller.rb +++ b/modules/meeting/app/controllers/meeting_contents_controller.rb @@ -51,6 +51,7 @@ class MeetingContentsController < ApplicationController (render_403; return) unless @content.editable? # TODO: not tested! @content.attributes = content_params @content.author = User.current + @content.attach_files(permitted_params.attachments.to_h) if @content.save flash[:notice] = l(:notice_successful_update) redirect_back_or_default controller: '/meetings', action: 'show', id: @meeting diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index cce0093aa4d..e795a1a790d 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -181,8 +181,22 @@ class Meeting < ActiveRecord::Base end def close_agenda_and_copy_to_minutes! - agenda.lock! - create_minutes(text: agenda.text, comment: 'Minutes created') + Meeting.transaction do + agenda.lock! + + attachments = agenda.attachments.map { |a| [a, a.copy] } + minutes = create_minutes(text: agenda.text, comment: 'Minutes created', attachments: attachments.map(&:last)) + + # substitute attachment references in text to use the respective copied attachments + text = agenda.text.gsub(/(?<=\(\/api\/v3\/attachments\/)\d+(?=\/content\))/) do |id| + old_id = id.to_i + new_id = attachments.select { |a, _| a.id == old_id }.map { |_, a| a.id }.first + + new_id || -1 + end + + minutes.update text: text + end end alias :original_participants_attributes= :participants_attributes= diff --git a/modules/meeting/app/models/meeting_content.rb b/modules/meeting/app/models/meeting_content.rb index af86590d505..1c13caf682b 100644 --- a/modules/meeting/app/models/meeting_content.rb +++ b/modules/meeting/app/models/meeting_content.rb @@ -25,6 +25,8 @@ require_dependency 'wiki_page' require 'wiki_page' class MeetingContent < ActiveRecord::Base + include OpenProject::Journal::AttachmentHelper + belongs_to :meeting belongs_to :author, class_name: 'User', foreign_key: 'author_id' @@ -34,6 +36,16 @@ class MeetingContent < ActiveRecord::Base before_save :comment_to_journal_notes + acts_as_attachable( + after_remove: :attachments_changed, + order: "#{Attachment.table_name}.file", + add_on_new_permission: :create_meetings, + add_on_persisted_permission: :edit_meetings, + view_permission: :view_meetings, + delete_permission: :edit_meetings, + modification_blocked: ->(*) { false } + ) + acts_as_journalized acts_as_event type: Proc.new { |o| "#{o.class.to_s.underscore.dasherize}" }, title: Proc.new { |o| "#{o.class.model_name.human}: #{o.meeting.title}" }, diff --git a/modules/meeting/app/views/meeting_contents/_form.html.erb b/modules/meeting/app/views/meeting_contents/_form.html.erb index 140f3d5dd0a..5e338f27a46 100644 --- a/modules/meeting/app/views/meeting_contents/_form.html.erb +++ b/modules/meeting/app/views/meeting_contents/_form.html.erb @@ -19,20 +19,28 @@ See doc/COPYRIGHT.md for more details. ++#%> -<%= form_for content, :url => {:controller => '/' + content_type.pluralize, :action => 'update', :meeting_id => content.meeting}, :html => {:id => "#{content_type}_form", :method => :put} do |f| %> +<%= labelled_tabular_form_for content, :url => {:controller => '/' + content_type.pluralize, :action => 'update', :meeting_id => content.meeting}, :html => {:id => "#{content_type}_form", :method => :put} do |f| %> <%= error_messages_for content_type %> + <% resource = ::API::V3::MeetingContents::MeetingContentRepresenter.new(content, current_user: current_user, embed_links: true) %> +

- <%= f.text_area :text, - class: 'wiki-edit' %> + <%= + f.text_area( + :text, + class: 'wiki-edit wiki-toolbar', + resource: resource, + label_options: { class: 'hidden-for-sighted' }, + with_text_formatting: true + ) + %>

<%= f.hidden_field :lock_version %> <% path = send("preview_#{content_type}_path", content.meeting) %> - <%= wikitoolbar_for "#{content_type}_text", preview_context: preview_context(content.meeting, @project) %> -

<%= f.text_field :comment, :size => 120 %>

+

<%= f.text_field :comment, :size => 120 %>

<%= styled_button_tag l(:button_save), class: '-highlight -with-icon icon-checkmark button--save-agenda' %> - <%= link_to l(:button_cancel), "#", data: { 'content-type': content_type }, class: 'button button--cancel-agenda' %> + <%= link_to t(:button_cancel), "#", data: { 'content-type': content_type }, class: 'button button--cancel-agenda' %> <% end %> <%= render :partial => 'shared/meeting_header' %> diff --git a/modules/meeting/app/views/meeting_contents/_show.html.erb b/modules/meeting/app/views/meeting_contents/_show.html.erb index f9a58741447..48eacedcfd4 100644 --- a/modules/meeting/app/views/meeting_contents/_show.html.erb +++ b/modules/meeting/app/views/meeting_contents/_show.html.erb @@ -54,4 +54,7 @@ See doc/COPYRIGHT.md for more details. <% end -%> +<% resource = ::API::V3::MeetingContents::MeetingContentRepresenter.new(content, current_user: current_user, embed_links: true) %> +<%= list_attachments(resource) %> + <%= render :partial => 'shared/meeting_header' %> diff --git a/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb b/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb new file mode 100644 index 00000000000..f821a902130 --- /dev/null +++ b/modules/meeting/lib/api/v3/attachments/attachments_by_meeting_content_api.rb @@ -0,0 +1,52 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module Attachments + class AttachmentsByMeetingContentAPI < ::API::OpenProjectAPI + resources :attachments do + helpers API::V3::Attachments::AttachmentsByContainerAPI::Helpers + + helpers do + def container + meeting_content + end + + def get_attachment_self_path + api_v3_paths.attachments_by_meeting_content container.id + end + end + + get &API::V3::Attachments::AttachmentsByContainerAPI.read + post &API::V3::Attachments::AttachmentsByContainerAPI.create + end + end + end + end +end diff --git a/modules/meeting/lib/api/v3/meeting_agendas/meeting_agenda_representer.rb b/modules/meeting/lib/api/v3/meeting_agendas/meeting_agenda_representer.rb new file mode 100644 index 00000000000..13a7ddbd641 --- /dev/null +++ b/modules/meeting/lib/api/v3/meeting_agendas/meeting_agenda_representer.rb @@ -0,0 +1,38 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module MeetingAgendas + class MeetingAgendaRepresenter < API::V3::MeetingContents::MeetingContentRepresenter + end + end + end +end diff --git a/modules/meeting/lib/api/v3/meeting_contents/meeting_content_representer.rb b/modules/meeting/lib/api/v3/meeting_contents/meeting_content_representer.rb new file mode 100644 index 00000000000..0f58dbdc879 --- /dev/null +++ b/modules/meeting/lib/api/v3/meeting_contents/meeting_content_representer.rb @@ -0,0 +1,58 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module MeetingContents + class MeetingContentRepresenter < ::API::Decorators::Single + include API::Decorators::LinkedResource + include API::Caching::CachedRepresenter + include ::API::V3::Attachments::AttachableRepresenterMixin + + self_link title_getter: ->(*) { nil } + + property :id + + associated_resource :project, + link: ->(*) do + next unless represented.project.present? + { + href: api_v3_paths.project(represented.project.id), + title: represented.project.name + } + end + + def _type + 'MeetingContent' + end + end + end + end +end diff --git a/modules/meeting/lib/api/v3/meeting_minutes/meeting_minutes_representer.rb b/modules/meeting/lib/api/v3/meeting_minutes/meeting_minutes_representer.rb new file mode 100644 index 00000000000..86fd4302605 --- /dev/null +++ b/modules/meeting/lib/api/v3/meeting_minutes/meeting_minutes_representer.rb @@ -0,0 +1,38 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module MeetingMinutes + class MeetingMinutesRepresenter < API::V3::MeetingContents::MeetingContentRepresenter + end + end + end +end diff --git a/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb b/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb new file mode 100644 index 00000000000..6476528aea8 --- /dev/null +++ b/modules/meeting/lib/api/v3/meetings/meeting_contents_api.rb @@ -0,0 +1,53 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module Meetings + class MeetingContentsAPI < ::API::OpenProjectAPI + resources :meeting_contents do + helpers do + def meeting_content + MeetingContent.find params[:id] + end + end + + route_param :id do + get do + ::API::V3::MeetingContents::MeetingContentRepresenter.new( + meeting_content, current_user: current_user, embed_links: true + ) + end + + mount ::API::V3::Attachments::AttachmentsByMeetingContentAPI + end + end + end + end + end +end diff --git a/modules/meeting/lib/open_project/meeting/engine.rb b/modules/meeting/lib/open_project/meeting/engine.rb index 1a108c06c15..6890139a243 100644 --- a/modules/meeting/lib/open_project/meeting/engine.rb +++ b/modules/meeting/lib/open_project/meeting/engine.rb @@ -69,6 +69,10 @@ module OpenProject::Meeting patch_with_namespace :OpenProject, :TextFormatting, :Formats, :Markdown, :TextileConverter + add_api_endpoint 'API::V3::Root' do + mount ::API::V3::Meetings::MeetingContentsAPI + end + initializer 'meeting.precompile_assets' do Rails.application.config.assets.precompile += %w(meeting/meeting.css meeting/meeting.js) end @@ -91,5 +95,29 @@ module OpenProject::Meeting PermittedParams.permit(:search, :meetings) end + + add_api_path :meeting_content do |id| + "#{root}/meeting_contents/#{id}" + end + + add_api_path :meeting_agenda do |id| + meeting_content(id) + end + + add_api_path :meeting_minutes do |id| + meeting_content(id) + end + + add_api_path :attachments_by_meeting_content do |id| + "#{meeting_content(id)}/attachments" + end + + add_api_path :attachments_by_meeting_agenda do |id| + attachments_by_meeting_content id + end + + add_api_path :attachments_by_meeting_minutes do |id| + attachments_by_meeting_content id + end end end diff --git a/modules/meeting/spec/features/meetings_attachments_spec.rb b/modules/meeting/spec/features/meetings_attachments_spec.rb new file mode 100644 index 00000000000..7eb7a5b68b1 --- /dev/null +++ b/modules/meeting/spec/features/meetings_attachments_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' +require 'features/page_objects/notification' + +describe 'Add an attachment to a meeting (agenda)', js: true do + let(:role) do + FactoryBot.create :role, permissions: %i[view_meetings edit_meetings create_meeting_agendas] + end + + let(:dev) do + FactoryBot.create :user, member_in_project: project, member_through_role: role + end + + let(:project) { FactoryBot.create(:project) } + + let(:meeting) do + FactoryBot.create( + :meeting, + project: project, + title: "Versammlung", + agenda: FactoryBot.create(:meeting_agenda, text: "Versammlung") + ) + end + + let(:attachments) { ::Components::Attachments.new } + let(:image_fixture) { Rails.root.join('spec/fixtures/files/image.png') } + let(:editor) { Components::WysiwygEditor.new } + + before do + login_as(dev) + + visit "/meetings/#{meeting.id}" + + within "#tab-content-agenda .toolbar" do + click_button "Edit" + end + end + + describe 'wysiwyg editor' do + context 'on an existing page' do + it 'can upload an image via drag & drop' do + target = find('.ck-content') + + editor.expect_button 'Insert image' + + editor.drag_attachment image_fixture, 'Some image caption' + + click_on "Save" + + content = find("div.meeting_content.meeting_agenda") + + expect(content).to have_selector('img') + expect(content).to have_content('Some image caption') + end + end + end + + describe 'attachment dropzone' do + it 'can upload an image via attaching and drag & drop' do + # called the same for all Wysiwyg dditors no matter if for work packages + # or not + container = page.find('.wp-attachment-upload') + scroll_to_element(container) + + ## + # Attach file manually + expect(page).to have_no_selector('.work-package--attachments--filename') + attachments.attach_file_on_input(image_fixture) + expect(page).not_to have_selector('notification-upload-progress') + expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png', wait: 5) + + ## + # and via drag & drop + attachments.drag_and_drop_file(container, Rails.root.join('spec/fixtures/files/image.png')) + expect(page).not_to have_selector('notification-upload-progress') + expect(page).to have_selector('.work-package--attachments--filename', text: 'image.png', count: 2, wait: 5) + end + end +end diff --git a/modules/meeting/spec/requests/api/v3/attachments/meeting_agenda_spec.rb b/modules/meeting/spec/requests/api/v3/attachments/meeting_agenda_spec.rb new file mode 100644 index 00000000000..7ae3b43fa86 --- /dev/null +++ b/modules/meeting/spec/requests/api/v3/attachments/meeting_agenda_spec.rb @@ -0,0 +1,43 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require 'requests/api/v3/attachments/attachment_resource_shared_examples' + +describe "meeting agenda attachments" do + it_behaves_like "an APIv3 attachment resource" do + let(:attachment_type) { :meeting_content } + + let(:create_permission) { :create_meetings } + let(:read_permission) { :view_meetings } + let(:update_permission) { :edit_meetings } + + let(:meeting_content) { FactoryBot.create :meeting_agenda, meeting: meeting } + let(:meeting) { FactoryBot.create :meeting, project: project } + end +end diff --git a/modules/meeting/spec/requests/api/v3/attachments/meeting_minutes_spec.rb b/modules/meeting/spec/requests/api/v3/attachments/meeting_minutes_spec.rb new file mode 100644 index 00000000000..64185a7af7d --- /dev/null +++ b/modules/meeting/spec/requests/api/v3/attachments/meeting_minutes_spec.rb @@ -0,0 +1,43 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require 'requests/api/v3/attachments/attachment_resource_shared_examples' + +describe "meeting minutes attachments" do + it_behaves_like "an APIv3 attachment resource" do + let(:attachment_type) { :meeting_content } + + let(:create_permission) { :create_meetings } + let(:read_permission) { :view_meetings } + let(:update_permission) { :edit_meetings } + + let(:meeting_content) { FactoryBot.create :meeting_minutes, meeting: meeting } + let(:meeting) { FactoryBot.create :meeting, project: project } + end +end diff --git a/spec/requests/api/v3/attachments/attachment_resource_spec.rb b/spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb similarity index 58% rename from spec/requests/api/v3/attachments/attachment_resource_spec.rb rename to spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb index 79c155b7e8c..bd0019feff9 100644 --- a/spec/requests/api/v3/attachments/attachment_resource_spec.rb +++ b/spec/requests/api/v3/attachments/attachment_resource_shared_examples.rb @@ -29,30 +29,37 @@ require 'spec_helper' require 'rack/test' -describe 'API v3 Attachment resource', type: :request, content_type: :json do +shared_examples 'an APIv3 attachment resource', type: :request, content_type: :json do |include_by_container = true| include Rack::Test::Methods include API::V3::Utilities::PathHelper include FileHelpers - let(:current_user) do + let(:current_user) { user_with_permissions } + + let(:user_with_permissions) do FactoryBot.create(:user, member_in_project: project, member_through_role: role) end + let(:author) do current_user end + let(:project) { FactoryBot.create(:project, is_public: false) } let(:role) { FactoryBot.create(:role, permissions: permissions) } - let(:permissions) do - %i[view_work_packages view_wiki_pages delete_wiki_pages_attachments - edit_work_packages edit_wiki_pages edit_messages] - end - let(:work_package) { FactoryBot.create(:work_package, author: current_user, project: project) } + let(:attachment) { FactoryBot.create(:attachment, container: container, author: author) } - let(:wiki) { FactoryBot.create(:wiki, project: project) } - let(:wiki_page) { FactoryBot.create(:wiki_page, wiki: wiki) } - let(:forum) { FactoryBot.create(:forum, project: project) } - let(:forum_message) { FactoryBot.create(:message, forum: forum) } - let(:container) { work_package } + let(:container) { send attachment_type } + + let(:attachment_type) { raise "attachment type goes here, e.g. work_package" } + let(:permissions) { all_permissions } + + let(:all_permissions) { Array([create_permission, read_permission, update_permission]).flatten.compact } + + let(:create_permission) { raise "permissions go here, e.g. add_work_packages" } + let(:read_permission) { raise "permissions go here, e.g. view_work_packages" } + let(:update_permission) { raise "permissions go here, e.g. edit_work_packages" } + + let(:missing_permissions_user) { user_with_permissions } before do allow(User).to receive(:current).and_return current_user @@ -62,43 +69,36 @@ describe 'API v3 Attachment resource', type: :request, content_type: :json do subject(:response) { last_response } let(:get_path) { api_v3_paths.attachment attachment.id } - %i[wiki_page work_package forum_message].each do |attachment_type| - context "with a #{attachment_type} attachment" do - let(:container) { send(attachment_type) } + let(:container) { send(attachment_type) } - context 'logged in user' do - before do - get get_path - end + context 'logged in user' do + before do + get get_path + end - it 'should respond with 200' do - expect(subject.status).to eq(200) - end + it 'should respond with 200' do + expect(subject.status).to eq(200) + end - it 'should respond with correct attachment' do - expect(subject.body).to be_json_eql(attachment.filename.to_json).at_path('fileName') - end + it 'should respond with correct attachment' do + expect(subject.body).to be_json_eql(attachment.filename.to_json).at_path('fileName') + end - context 'requesting nonexistent attachment' do - let(:get_path) { api_v3_paths.attachment 9999 } + context 'requesting nonexistent attachment' do + let(:get_path) { api_v3_paths.attachment 9999 } - it_behaves_like 'not found' do - let(:id) { 9999 } - let(:type) { 'Attachment' } - end - end + it_behaves_like 'not found' do + let(:id) { 9999 } + let(:type) { 'Attachment' } + end + end - context 'requesting attachments without sufficient permissions' do - if attachment_type == :forum_message - let(:current_user) { FactoryBot.create(:user) } - else - let(:permissions) { [] } - end + context 'requesting attachments without sufficient permissions' do + let(:current_user) { missing_permissions_user } + let(:permissions) { all_permissions - Array(read_permission) } - it_behaves_like 'not found' do - let(:type) { 'Attachment' } - end - end + it_behaves_like 'not found' do + let(:type) { 'Attachment' } end end end @@ -208,31 +208,25 @@ describe 'API v3 Attachment resource', type: :request, content_type: :json do end end - %i[wiki_page work_package forum_message].each do |attachment_type| - context "with a #{attachment_type} attachment" do - let(:container) { send(attachment_type) } + context 'with required permissions' do + it_behaves_like 'deletes the attachment' - context 'with required permissions' do - it_behaves_like 'deletes the attachment' + context 'for a non-existent attachment' do + let(:path) { api_v3_paths.attachment 1337 } - context 'for a non-existent attachment' do - let(:path) { api_v3_paths.attachment 1337 } - - it_behaves_like 'not found' do - let(:id) { 1337 } - let(:type) { 'Attachment' } - end - end - end - - context 'without required permissions' do - let(:permissions) { %i[view_work_packages view_wiki_pages] } - - it_behaves_like 'does not delete the attachment' + it_behaves_like 'not found' do + let(:id) { 1337 } + let(:type) { 'Attachment' } end end end + context 'without required permissions' do + let(:permissions) { all_permissions - Array(update_permission) } + + it_behaves_like 'does not delete the attachment' + end + context "with an uncontainered attachment" do let(:container) { nil } @@ -308,4 +302,99 @@ describe 'API v3 Attachment resource', type: :request, content_type: :json do end end end + + context 'by container', if: include_by_container do + subject(:response) { last_response } + + describe '#get' do + let(:get_path) { api_v3_paths.send "attachments_by_#{attachment_type}", container.id } + + before do + FactoryBot.create_list(:attachment, 2, container: container) + get get_path + end + + it 'should respond with 200' do + expect(subject.status).to eq(200) + end + + it_behaves_like 'API V3 collection response', 2, 2, 'Attachment' + end + + describe '#post' do + let(:request_path) { api_v3_paths.send "attachments_by_#{attachment_type}", container.id } + let(:request_parts) { { metadata: metadata, file: file } } + let(:metadata) { { fileName: 'cat.png' }.to_json } + let(:file) { mock_uploaded_file(name: 'original-filename.txt') } + let(:max_file_size) { 1 } # given in kiB + + before do + allow(Setting).to receive(:attachment_max_size).and_return max_file_size.to_s + post request_path, request_parts + end + + it 'should respond with HTTP Created' do + expect(subject.status).to eq(201) + end + + it 'should return the new attachment' do + expect(subject.body).to be_json_eql('Attachment'.to_json).at_path('_type') + end + + it 'ignores the original file name' do + expect(subject.body).to be_json_eql('cat.png'.to_json).at_path('fileName') + end + + context 'metadata section is missing' do + let(:request_parts) { { file: file } } + + it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') + end + + context 'file section is missing' do + # rack-test won't send a multipart request without a file being present + # however as long as we depend on correctly named sections this test should do just fine + let(:request_parts) { { metadata: metadata, wrongFileSection: file } } + + it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') + end + + context 'metadata section is no valid JSON' do + let(:metadata) { '"fileName": "cat.png"' } + + it_behaves_like 'parse error' + end + + context 'metadata is missing the fileName' do + let(:metadata) { Hash.new.to_json } + + it_behaves_like 'constraint violation' do + let(:message) { "fileName #{I18n.t('activerecord.errors.messages.blank')}" } + end + end + + context 'file is too large' do + let(:file) { mock_uploaded_file(content: 'a' * 2.kilobytes) } + let(:expanded_localization) do + I18n.t('activerecord.errors.messages.file_too_large', count: max_file_size.kilobytes) + end + + it_behaves_like 'constraint violation' do + let(:message) { "File #{expanded_localization}" } + end + end + + context 'only allowed to add, but not to edit' do + let(:permissions) { all_permissions - Array(update_permission) } + + it_behaves_like 'unauthorized access' + end + + context 'only allowed to view' do + let(:permissions) { Array(read_permission) } + + it_behaves_like 'unauthorized access' + end + end + end end diff --git a/spec/requests/api/v3/attachments/attachments_by_post_resource_spec.rb b/spec/requests/api/v3/attachments/attachments_by_post_resource_spec.rb deleted file mode 100644 index 438812be26b..00000000000 --- a/spec/requests/api/v3/attachments/attachments_by_post_resource_spec.rb +++ /dev/null @@ -1,146 +0,0 @@ -#-- copyright -# OpenProject is a project management system. -# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) -# -# 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-2017 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 docs/COPYRIGHT.rdoc for more details. -#++ - -require 'spec_helper' -require 'rack/test' - -describe 'API v3 Attachments by post resource', type: :request do - include Rack::Test::Methods - include API::V3::Utilities::PathHelper - include FileHelpers - - let(:current_user) do - FactoryBot.create(:user, - member_in_project: project, - member_through_role: role) - end - let(:project) { FactoryBot.create(:project) } - let(:role) { FactoryBot.create(:role, permissions: permissions) } - let(:permissions) { [:view_messages] } - let(:forum) { FactoryBot.create(:forum, project: project) } - let(:forum_message) { FactoryBot.create(:message, forum: forum) } - - subject(:response) { last_response } - - before do - allow(User).to receive(:current).and_return current_user - end - - describe '#get' do - let(:get_path) { api_v3_paths.attachments_by_post forum_message.id } - - before do - FactoryBot.create_list(:attachment, 2, container: forum_message) - get get_path - end - - it 'should respond with 200' do - expect(subject.status).to eq(200) - end - - it_behaves_like 'API V3 collection response', 2, 2, 'Attachment' - end - - describe '#post' do - let(:permissions) { %i[view_messages edit_messages] } - - let(:request_path) { api_v3_paths.attachments_by_post forum_message.id } - let(:request_parts) { { metadata: metadata, file: file } } - let(:metadata) { { fileName: 'cat.png' }.to_json } - let(:file) { mock_uploaded_file(name: 'original-filename.txt') } - let(:max_file_size) { 1 } # given in kiB - - before do - allow(Setting).to receive(:attachment_max_size).and_return max_file_size.to_s - post request_path, request_parts - end - - it 'should respond with HTTP Created' do - expect(subject.status).to eq(201) - end - - it 'should return the new attachment' do - expect(subject.body).to be_json_eql('Attachment'.to_json).at_path('_type') - end - - it 'ignores the original file name' do - expect(subject.body).to be_json_eql('cat.png'.to_json).at_path('fileName') - end - - context 'metadata section is missing' do - let(:request_parts) { { file: file } } - - it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') - end - - context 'file section is missing' do - # rack-test won't send a multipart request without a file being present - # however as long as we depend on correctly named sections this test should do just fine - let(:request_parts) { { metadata: metadata, wrongFileSection: file } } - - it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') - end - - context 'metadata section is no valid JSON' do - let(:metadata) { '"fileName": "cat.png"' } - - it_behaves_like 'parse error' - end - - context 'metadata is missing the fileName' do - let(:metadata) { Hash.new.to_json } - - it_behaves_like 'constraint violation' do - let(:message) { "fileName #{I18n.t('activerecord.errors.messages.blank')}" } - end - end - - context 'file is too large' do - let(:file) { mock_uploaded_file(content: 'a' * 2.kilobytes) } - let(:expanded_localization) do - I18n.t('activerecord.errors.messages.file_too_large', count: max_file_size.kilobytes) - end - - it_behaves_like 'constraint violation' do - let(:message) { "File #{expanded_localization}" } - end - end - - context 'only allowed to add messages, but no edit permission' do - let(:permissions) { %i[view_messages add_messages] } - - it_behaves_like 'unauthorized access' - end - - context 'only allowed to view messages' do - let(:permissions) { [:view_messages] } - - it_behaves_like 'unauthorized access' - end - end -end diff --git a/spec/requests/api/v3/attachments/attachments_by_wiki_page_resource_spec.rb b/spec/requests/api/v3/attachments/attachments_by_wiki_page_resource_spec.rb deleted file mode 100644 index 0e5153e553b..00000000000 --- a/spec/requests/api/v3/attachments/attachments_by_wiki_page_resource_spec.rb +++ /dev/null @@ -1,140 +0,0 @@ -#-- copyright -# OpenProject is a project management system. -# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) -# -# 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-2017 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 docs/COPYRIGHT.rdoc for more details. -#++ - -require 'spec_helper' -require 'rack/test' - -describe 'API v3 Attachments by wiki page resource', type: :request do - include Rack::Test::Methods - include API::V3::Utilities::PathHelper - include FileHelpers - - let(:current_user) do - FactoryBot.create(:user, - member_in_project: project, - member_through_role: role) - end - let(:project) { FactoryBot.create(:project) } - let(:role) { FactoryBot.create(:role, permissions: permissions) } - let(:permissions) { [:view_wiki_pages] } - let(:wiki) { FactoryBot.create(:wiki, project: project) } - let(:wiki_page) { FactoryBot.create(:wiki_page, wiki: wiki) } - - subject(:response) { last_response } - - before do - allow(User).to receive(:current).and_return current_user - end - - describe '#get' do - let(:get_path) { api_v3_paths.attachments_by_wiki_page wiki_page.id } - - before do - FactoryBot.create_list(:attachment, 2, container: wiki_page) - get get_path - end - - it 'should respond with 200' do - expect(subject.status).to eq(200) - end - - it_behaves_like 'API V3 collection response', 2, 2, 'Attachment' - end - - describe '#post' do - let(:permissions) { %i[view_wiki_pages edit_wiki_pages] } - - let(:request_path) { api_v3_paths.attachments_by_wiki_page wiki_page.id } - let(:request_parts) { { metadata: metadata, file: file } } - let(:metadata) { { fileName: 'cat.png' }.to_json } - let(:file) { mock_uploaded_file(name: 'original-filename.txt') } - let(:max_file_size) { 1 } # given in kiB - - before do - allow(Setting).to receive(:attachment_max_size).and_return max_file_size.to_s - post request_path, request_parts - end - - it 'should respond with HTTP Created' do - expect(subject.status).to eq(201) - end - - it 'should return the new attachment' do - expect(subject.body).to be_json_eql('Attachment'.to_json).at_path('_type') - end - - it 'ignores the original file name' do - expect(subject.body).to be_json_eql('cat.png'.to_json).at_path('fileName') - end - - context 'metadata section is missing' do - let(:request_parts) { { file: file } } - - it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') - end - - context 'file section is missing' do - # rack-test won't send a multipart request without a file being present - # however as long as we depend on correctly named sections this test should do just fine - let(:request_parts) { { metadata: metadata, wrongFileSection: file } } - - it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') - end - - context 'metadata section is no valid JSON' do - let(:metadata) { '"fileName": "cat.png"' } - - it_behaves_like 'parse error' - end - - context 'metadata is missing the fileName' do - let(:metadata) { Hash.new.to_json } - - it_behaves_like 'constraint violation' do - let(:message) { "fileName #{I18n.t('activerecord.errors.messages.blank')}" } - end - end - - context 'file is too large' do - let(:file) { mock_uploaded_file(content: 'a' * 2.kilobytes) } - let(:expanded_localization) do - I18n.t('activerecord.errors.messages.file_too_large', count: max_file_size.kilobytes) - end - - it_behaves_like 'constraint violation' do - let(:message) { "File #{expanded_localization}" } - end - end - - context 'only allowed to view wiki pages' do - let(:permissions) { [:view_wiki_pages] } - - it_behaves_like 'unauthorized access' - end - end -end diff --git a/spec/requests/api/v3/attachments/attachments_by_work_package_resource_spec.rb b/spec/requests/api/v3/attachments/attachments_by_work_package_resource_spec.rb deleted file mode 100644 index 5a40c848bca..00000000000 --- a/spec/requests/api/v3/attachments/attachments_by_work_package_resource_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -#-- copyright -# OpenProject is a project management system. -# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) -# -# 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-2017 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 docs/COPYRIGHT.rdoc for more details. -#++ - -require 'spec_helper' -require 'rack/test' - -describe 'API v3 Attachments by work package resource', type: :request do - include Rack::Test::Methods - include API::V3::Utilities::PathHelper - include FileHelpers - - let(:current_user) do - FactoryBot.create(:user, - member_in_project: project, - member_through_role: role) - end - let(:project) { FactoryBot.create(:project, is_public: false) } - let(:role) { FactoryBot.create(:role, permissions: permissions) } - let(:permissions) { [:view_work_packages] } - let(:work_package) { FactoryBot.create(:work_package, author: current_user, project: project) } - - subject(:response) { last_response } - - before do - allow(User).to receive(:current).and_return current_user - end - - describe '#get' do - let(:get_path) { api_v3_paths.attachments_by_work_package work_package.id } - - before do - FactoryBot.create_list(:attachment, 2, container: work_package) - get get_path - end - - it 'should respond with 200' do - expect(subject.status).to eq(200) - end - - it_behaves_like 'API V3 collection response', 2, 2, 'Attachment' - end - - describe '#post' do - let(:permissions) { %i[view_work_packages edit_work_packages] } - - let(:request_path) { api_v3_paths.attachments_by_work_package work_package.id } - let(:request_parts) { { metadata: metadata, file: file } } - let(:metadata) { { fileName: 'cat.png' }.to_json } - let(:file) { mock_uploaded_file(name: 'original-filename.txt') } - let(:max_file_size) { 1 } # given in kiB - - before do - allow(Setting).to receive(:attachment_max_size).and_return max_file_size.to_s - post request_path, request_parts - end - - it 'should respond with HTTP Created' do - expect(subject.status).to eq(201) - end - - it 'should return the new attachment' do - expect(subject.body).to be_json_eql('Attachment'.to_json).at_path('_type') - end - - it 'ignores the original file name' do - expect(subject.body).to be_json_eql('cat.png'.to_json).at_path('fileName') - end - - context 'metadata section is missing' do - let(:request_parts) { { file: file } } - - it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') - end - - context 'file section is missing' do - # rack-test won't send a multipart request without a file being present - # however as long as we depend on correctly named sections this test should do just fine - let(:request_parts) { { metadata: metadata, wrongFileSection: file } } - - it_behaves_like 'invalid request body', I18n.t('api_v3.errors.multipart_body_error') - end - - context 'metadata section is no valid JSON' do - let(:metadata) { '"fileName": "cat.png"' } - - it_behaves_like 'parse error' - end - - context 'metadata is missing the fileName' do - let(:metadata) { Hash.new.to_json } - - it_behaves_like 'constraint violation' do - let(:message) { "fileName #{I18n.t('activerecord.errors.messages.blank')}" } - end - end - - context 'file is too large' do - let(:file) { mock_uploaded_file(content: 'a' * 2.kilobytes) } - let(:expanded_localization) do - I18n.t('activerecord.errors.messages.file_too_large', count: max_file_size.kilobytes) - end - - it_behaves_like 'constraint violation' do - let(:message) { "File #{expanded_localization}" } - end - end - - context 'only allowed to add work packages, but no edit permission' do - let(:permissions) { %i[view_work_packages add_work_packages] } - - it_behaves_like 'unauthorized access' - end - - context 'only allowed to view work packages' do - let(:permissions) { [:view_work_packages] } - - it_behaves_like 'unauthorized access' - end - end -end diff --git a/spec/requests/api/v3/attachments/forum_message_spec.rb b/spec/requests/api/v3/attachments/forum_message_spec.rb new file mode 100644 index 00000000000..1cf654279be --- /dev/null +++ b/spec/requests/api/v3/attachments/forum_message_spec.rb @@ -0,0 +1,45 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require_relative './attachment_resource_shared_examples' + +describe "forum message attachments" do + it_behaves_like "an APIv3 attachment resource", include_by_container = false do + let(:attachment_type) { :forum_message } + + let(:create_permission) { nil } + let(:read_permission) { nil } + let(:update_permission) { :edit_messages } + + let(:forum) { FactoryBot.create(:forum, project: project) } + let(:forum_message) { FactoryBot.create(:message, forum: forum) } + + let(:missing_permissions_user) { FactoryBot.create(:user) } + end +end diff --git a/spec/requests/api/v3/attachments/wiki_page_spec.rb b/spec/requests/api/v3/attachments/wiki_page_spec.rb new file mode 100644 index 00000000000..6e284862e2b --- /dev/null +++ b/spec/requests/api/v3/attachments/wiki_page_spec.rb @@ -0,0 +1,43 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require_relative './attachment_resource_shared_examples' + +describe "wiki page attachments" do + it_behaves_like "an APIv3 attachment resource" do + let(:attachment_type) { :wiki_page } + + let(:create_permission) { nil } + let(:read_permission) { :view_wiki_pages } + let(:update_permission) { %i(delete_wiki_pages_attachments edit_wiki_pages) } + + let(:wiki) { FactoryBot.create(:wiki, project: project) } + let(:wiki_page) { FactoryBot.create(:wiki_page, wiki: wiki) } + end +end diff --git a/spec/requests/api/v3/attachments/work_package_spec.rb b/spec/requests/api/v3/attachments/work_package_spec.rb new file mode 100644 index 00000000000..07ee15bb362 --- /dev/null +++ b/spec/requests/api/v3/attachments/work_package_spec.rb @@ -0,0 +1,44 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2018 the OpenProject Foundation (OPF) +# +# 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-2017 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 docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require_relative './attachment_resource_shared_examples' + +describe "work package attachments" do + it_behaves_like "an APIv3 attachment resource" do + let(:attachment_type) { :work_package } + + let(:create_permission) { :add_work_packages } + let(:read_permission) { :view_work_packages } + let(:update_permission) { :edit_work_packages } + + let(:work_package) do + FactoryBot.create :work_package, author: current_user, project: project + end + end +end