mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
attachments for meeting contents (MeetingAgenda, MeetingMinutes)
This commit is contained in:
committed by
Oliver Günther
parent
4fda229361
commit
bc5adfcba7
@@ -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)
|
||||
|
||||
@@ -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<any>;
|
||||
}
|
||||
|
||||
class MeetingContentBaseResource extends HalResource {
|
||||
public $links:MeetingContentResourceLinks;
|
||||
|
||||
private attachmentsBackend = false;
|
||||
}
|
||||
|
||||
export const MeetingContentResource = Attachable(MeetingContentBaseResource);
|
||||
|
||||
export interface MeetingContentResource extends HalResource {
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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}" },
|
||||
|
||||
@@ -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) %>
|
||||
|
||||
<p>
|
||||
<%= 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
|
||||
)
|
||||
%>
|
||||
</p>
|
||||
<%= 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) %>
|
||||
|
||||
<p><label for="<%= content_type %>_comment"><%= Meeting.human_attribute_name(:comments) %></label><%= f.text_field :comment, :size => 120 %></p>
|
||||
<p><%= f.text_field :comment, :size => 120 %></p>
|
||||
<p><%= 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' %>
|
||||
|
||||
@@ -54,4 +54,7 @@ See doc/COPYRIGHT.md for more details.
|
||||
<% end -%>
|
||||
</div>
|
||||
|
||||
<% resource = ::API::V3::MeetingContents::MeetingContentRepresenter.new(content, current_user: current_user, embed_links: true) %>
|
||||
<%= list_attachments(resource) %>
|
||||
|
||||
<%= render :partial => 'shared/meeting_header' %>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+150
-61
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user