Merge pull request #10220 from opf/feature/41278/index-action-calendar

[41278] Add index action for calendars
pull/10230/head
Oliver Günther 3 years ago committed by GitHub
commit 22da742051
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      frontend/src/app/features/calendar/calendar.lazy-routes.ts
  2. 4
      frontend/src/app/features/calendar/calendar.routes.ts
  3. 70
      modules/calendar/app/cells/calendar/row_cell.rb
  4. 23
      modules/calendar/app/cells/calendar/table_cell.rb
  5. 76
      modules/calendar/app/controllers/calendar/calendars_controller.rb
  6. 0
      modules/calendar/app/views/calendar/calendars/_menu.html.erb
  7. 44
      modules/calendar/app/views/calendar/calendars/index.html.erb
  8. 2
      modules/calendar/app/views/calendar/calendars/show.html.erb
  9. 4
      modules/calendar/config/locales/en.yml
  10. 10
      modules/calendar/config/routes.rb
  11. 18
      modules/calendar/lib/open_project/calendar/engine.rb
  12. 14
      modules/calendar/spec/controllers/calendar_controller_spec.rb
  13. 115
      modules/calendar/spec/features/calendars_index_spec.rb
  14. 10
      modules/calendar/spec/features/calendars_spec.rb
  15. 85
      modules/calendar/spec/features/query_handling_spec.rb
  16. 25
      modules/calendar/spec/routing/calendar_routing_spec.rb
  17. 8
      modules/calendar/spec/support/pages/calendar.rb
  18. 6
      modules/reporting/spec/features/menu_spec.rb

@ -32,7 +32,7 @@ export const CALENDAR_LAZY_ROUTES:Ng2StateDeclaration[] = [
{
name: 'calendar.**',
parent: 'optional_project',
url: '/calendar',
url: '/calendars',
loadChildren: () => import('./openproject-calendar.module').then((m) => m.OpenprojectCalendarModule),
},
];

@ -37,13 +37,13 @@ export const CALENDAR_ROUTES:Ng2StateDeclaration[] = [
{
name: 'calendar',
parent: 'optional_project',
url: '/calendar?query_id&query_props&cdate&cview',
url: '/calendars/:query_id?&query_props&cdate&cview',
redirectTo: 'calendar.page',
views: {
'!$default': { component: WorkPackagesBaseComponent },
},
params: {
query_id: { type: 'query', dynamic: true },
query_id: { type: 'opQueryId', dynamic: true },
cdate: { type: 'string', dynamic: true },
cview: { type: 'string', dynamic: true },
// Use custom encoder/decoder that ensures validity of URL string

@ -0,0 +1,70 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-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 COPYRIGHT and LICENSE files for more details.
#++
module Calendar
class RowCell < ::RowCell
include ApplicationHelper
include ::Redmine::I18n
def query
model
end
delegate :project, to: :query
def name
link_to query.name, project_calendar_path(project, query.id)
end
def created_at
format_time(query.created_at)
end
def button_links
[delete_link].compact
end
def delete_link
if table.current_user.allowed_to?(:manage_calendars, project)
link_to(
'',
project_calendar_path(project, query.id),
method: :delete,
class: 'icon icon-delete',
data: {
confirm: I18n.t(:text_are_you_sure),
'qa-selector': "calendar-remove-#{query.id}"
},
title: t(:button_delete)
)
end
end
end
end

@ -2,13 +2,13 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
# Copyright (C) 2012-2020 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
@ -28,13 +28,20 @@
# See COPYRIGHT and LICENSE files for more details.
#++
module ::Calendar
class CalendarController < ApplicationController
menu_item :calendar_view
before_action :find_optional_project
module Calendar
class TableCell < ::TableCell
options :current_user
columns :name, :created_at
def index
render layout: 'angular/angular'
def sortable?
false
end
def headers
[
['name', { caption: I18n.t(:label_name) }],
['created_at', { caption: I18n.t('attributes.created_at') }]
]
end
end
end

@ -0,0 +1,76 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module ::Calendar
class CalendarsController < ApplicationController
before_action :find_optional_project
before_action :authorize
before_action :find_calendar, only: %i[destroy]
menu_item :calendar_view
def index
@views = visible_views
end
def show
render layout: 'angular/angular'
end
def destroy
if @view.destroy
flash[:notice] = t(:notice_successful_delete)
else
flash[:error] = t(:error_can_not_delete_entry)
end
redirect_to action: :index
end
private
def visible_views
Query
.visible(current_user)
.joins(:views)
.where('views.type' => 'work_packages_calendar')
.where('queries.project_id' => @project.id)
.order('queries.name ASC')
end
def find_calendar
@view = Query
.visible(current_user)
.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end
end

@ -0,0 +1,44 @@
<%#-- copyright
OpenProject is an open source project management software.
Copyright (C) 2012-2021 the OpenProject GmbH
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3.
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
Copyright (C) 2006-2013 Jean-Philippe Lang
Copyright (C) 2010-2013 the ChiliProject Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
See COPYRIGHT and LICENSE files for more details.
++#%>
<% html_title(t(:label_calendar_plural)) -%>
<%= toolbar title: t(:label_calendar_plural) do %>
<% if current_user.allowed_to?(:manage_calendars, @project) %>
<li class="toolbar-item">
<%= link_to new_project_calendars_path(@project),
class: 'button -alt-highlight',
title: t(:button_create) do %>
<%= op_icon('button--icon icon-add') %>
<span class="button--text"><%= t(:button_create) %></span>
<% end %>
</li>
<% end %>
<% end %>
<%= rails_cell ::Calendar::TableCell, @views, current_user: current_user %>

@ -27,4 +27,4 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<% html_title(t(:label_calendar)) -%>
<% html_title(t(:label_calendar_plural)) -%>

@ -1,4 +1,6 @@
# English strings go here
en:
permission_view_calendar: "View calendar"
label_calendar_plural: "Calendars"
permission_view_calendars: "View calendars"
permission_manage_calendars: "Manage calendars"
project_module_calendar_view: "Calendar"

@ -1,7 +1,11 @@
OpenProject::Application.routes.draw do
scope 'projects/:project_id', as: 'project' do
get '/calendar(/*state)', to: 'calendar/calendar#index', as: :calendar
resources :calendars,
controller: 'calendar/calendars',
only: %i[index destroy],
as: :calendars do
get '/new' => 'calendar/calendars#show', on: :collection, as: 'new'
get '(/*state)' => 'calendar/calendars#show', on: :member, as: ''
end
end
get '/calendar(/*state)', to: 'calendar/calendar#index', as: :calendar
end

@ -29,23 +29,29 @@ module OpenProject::Calendar
name: 'OpenProject Calendar' do
project_module :calendar_view, dependencies: :work_package_tracking do
permission :view_calendar,
{ 'calendar/calendar': %i[index] }
{ 'calendar/calendars': %i[index show] },
dependencies: %i[view_work_packages],
contract_actions: { calendar: %i[read] }
permission :manage_calendars,
{ 'calendar/calendars': %i[index show new destroy] },
dependencies: %i[view_calendar add_work_packages edit_work_packages save_queries manage_public_queries],
contract_actions: { calendar: %i[create update destroy] }
end
menu :project_menu,
:calendar_view,
{ controller: '/calendar/calendar', action: 'index' },
caption: :label_calendar,
{ controller: '/calendar/calendars', action: 'index' },
caption: :label_calendar_plural,
icon: 'icon2 icon-calendar',
after: :work_packages
menu :project_menu,
:calendar_menu,
{ controller: '/calendar/calendar', action: 'index' },
{ controller: '/calendar/calendars', action: 'index' },
parent: :calendar_view,
partial: 'calendar/calendar/menu',
partial: 'calendar/calendars/menu',
last: true,
caption: :label_calendar
caption: :label_calendar_plural
end
add_view :WorkPackagesCalendar,

@ -28,7 +28,7 @@
require 'spec_helper'
describe Calendar::CalendarController, type: :controller do
describe ::Calendar::CalendarsController, type: :controller do
let(:project) do
build_stubbed(:project).tap do |p|
allow(Project)
@ -42,7 +42,7 @@ describe Calendar::CalendarController, type: :controller do
build_stubbed(:user).tap do |user|
allow(user)
.to receive(:allowed_to?) do |permission, p, global:|
permission[:controller] == 'calendar/calendar' &&
permission[:controller] == 'calendar/calendars' &&
permission[:action] == 'index' &&
(p.nil? || p == project)
end
@ -57,15 +57,7 @@ describe Calendar::CalendarController, type: :controller do
it { is_expected.to be_successful }
it { is_expected.to render_template('calendar/calendar/index') }
end
context 'cross-project' do
before do
get :index
end
it_behaves_like 'calendar#index'
it { is_expected.to render_template('calendar/calendars/index') }
end
context 'project' do

@ -0,0 +1,115 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
require 'spec_helper'
describe 'Team planner index', type: :feature, js: true, with_ee: %i[team_planner_view] do
shared_let(:project) do
create(:project, enabled_module_names: %w[work_package_tracking calendar_view])
end
shared_let(:user) do
create :user,
member_in_project: project,
member_with_permissions: %w[
view_work_packages
edit_work_packages
save_queries
save_public_queries
view_calendar
manage_calendars
]
end
let(:query) do
create(:query_with_view_work_packages_calendar,
project: project,
user: user,
public: true)
end
let(:current_user) { user }
before do
login_as current_user
query
visit project_calendars_path(project)
end
context 'with no view' do
let(:query) { nil }
it 'shows an index action' do
expect(page).to have_text 'There is currently nothing to display.'
expect(page).to have_selector '.button', text: 'Create'
end
end
context 'with an existing view' do
it 'shows that view' do
expect(page).to have_selector 'td', text: query.name
expect(page).to have_selector "[data-qa-selector='calendar-remove-#{query.id}']"
end
context 'with another user with limited access' do
let(:current_user) do
create :user,
firstname: 'Bernd',
member_in_project: project,
member_with_permissions: %w[view_work_packages view_calendar]
end
it 'does not show the create button' do
expect(page).to have_selector 'td', text: query.name
# Does not show the delete
expect(page).to have_no_selector "[data-qa-selector='calendar-remove-#{query.id}']"
# Does not show the create button
expect(page).to have_no_selector '.button', text: 'Create'
end
context 'when the view is non-public' do
let(:query) { create :query, user: user, project: project, public: false }
it 'does not show a non-public view' do
expect(page).to have_text 'There is currently nothing to display.'
expect(page).to have_no_selector 'td', text: query.name
# Does not show the delete
expect(page).to have_no_selector "[data-qa-selector='team-planner-remove-#{query.id}']"
# Does not show the create button
expect(page).to have_no_selector '.button', text: 'Create'
end
end
end
end
end

@ -35,7 +35,7 @@ describe 'Work package calendars', type: :feature, js: true do
let(:user) do
create(:user,
member_in_project: project,
member_with_permissions: %i[view_work_packages view_calendar])
member_with_permissions: %i[view_work_packages view_calendar manage_calendars])
end
let!(:current_work_package) do
create(:work_package,
@ -76,9 +76,15 @@ describe 'Work package calendars', type: :feature, js: true do
visit project_path(project)
within '#main-menu' do
click_link 'Calendar'
click_link 'Calendars'
end
# Expect empty index
expect(page).to have_text 'There is currently nothing to display.'
# Open a new calendar from there
click_on 'Create'
loading_indicator_saveguard
# should open the calendar with the current month displayed

@ -86,9 +86,12 @@ describe 'Calendar query handling', type: :feature, js: true do
current_user { user }
before do
login_as user
calendar_page.visit!
it 'shows that query on the index page' do
visit project_calendars_path(project)
expect(page).to have_text saved_query.name
click_link saved_query.name
loading_indicator_saveguard
@ -96,48 +99,60 @@ describe 'Calendar query handling', type: :feature, js: true do
calendar_page.expect_event bug
end
it 'allows saving the calendar' do
filters.expect_filter_count("1")
filters.open
context 'when on the show page of the calendar' do
before do
login_as user
calendar_page.visit!
filters.add_filter_by('Type', 'is', [type_bug.name])
loading_indicator_saveguard
filters.expect_filter_count("2")
calendar_page.expect_event task
calendar_page.expect_event bug
end
calendar_page.expect_event bug
calendar_page.expect_event task, present: false
it 'allows saving the calendar' do
filters.expect_filter_count("1")
filters.open
query_title.expect_changed
query_title.press_save_button
filters.add_filter_by('Type', 'is', [type_bug.name])
calendar_page.expect_and_dismiss_toaster(message: I18n.t('js.notice_successful_create'))
end
filters.expect_filter_count("2")
it 'shows only calendar queries' do
# Go to calendar where a query is already shown
query_menu.expect_menu_entry saved_query.name
calendar_page.expect_event bug
calendar_page.expect_event task, present: false
# Change filter
filters.open
filters.add_filter_by('Type', 'is', [type_bug.name])
filters.expect_filter_count("2")
query_title.expect_changed
query_title.press_save_button
# Save current filters
query_title.expect_changed
query_title.rename 'I am your Query'
calendar_page.expect_and_dismiss_toaster(message: I18n.t('js.notice_successful_create'))
calendar_page.expect_and_dismiss_toaster(message: I18n.t('js.notice_successful_create'))
end
# The saved query appears in the side menu...
query_menu.expect_menu_entry 'I am your Query'
query_menu.expect_menu_entry saved_query.name
it 'shows only calendar queries' do
# Go to calendar where a query is already shown
query_menu.expect_menu_entry saved_query.name
# .. but not in the work packages module
work_package_page.visit!
query_menu.expect_menu_entry_not_visible 'I am your Query'
end
# Change filter
filters.open
filters.add_filter_by('Type', 'is', [type_bug.name])
filters.expect_filter_count("2")
# Save current filters
query_title.expect_changed
query_title.rename 'I am your Query'
calendar_page.expect_and_dismiss_toaster(message: I18n.t('js.notice_successful_create'))
# The saved query appears in the side menu...
query_menu.expect_menu_entry 'I am your Query'
query_menu.expect_menu_entry saved_query.name
# .. but not in the work packages module
work_package_page.visit!
query_menu.expect_menu_entry_not_visible 'I am your Query'
end
it_behaves_like 'module specific query view management' do
let(:module_page) { calendar_page }
let(:default_name) { 'Unnamed calendar' }
it_behaves_like 'module specific query view management' do
let(:module_page) { calendar_page }
let(:default_name) { 'Unnamed calendar' }
end
end
end

@ -28,15 +28,24 @@
require 'spec_helper'
describe Calendar::CalendarController, type: :routing do
it 'connects GET /calendar to calendar#index' do
expect(get('/calendar')).to route_to(controller: 'calendar/calendar',
action: 'index')
describe Calendar::CalendarsController, type: :routing do
it do
expect(get('/projects/1/calendars')).to route_to(controller: 'calendar/calendars',
action: 'index',
project_id: '1')
end
it 'connects GET /project/1/calendar to calendar#index' do
expect(get('/projects/1/calendar')).to route_to(controller: 'calendar/calendar',
action: 'index',
project_id: '1')
it do
expect(get('/projects/1/calendars/2')).to route_to(controller: 'calendar/calendars',
action: 'show',
id: '2',
project_id: '1')
end
it do
expect(delete('/projects/1/calendars/2')).to route_to(controller: 'calendar/calendars',
action: 'destroy',
id: '2',
project_id: '1')
end
end

@ -31,17 +31,19 @@ require 'support/pages/page'
module Pages
class Calendar < ::Pages::Page
attr_reader :project,
:filters
:filters,
:query
def initialize(project)
def initialize(project, query = nil)
super()
@project = project
@filters = ::Components::WorkPackages::Filters.new
@query = query
end
def path
project_calendar_path(project)
project_calendar_path(project, id: query&.id || 'new')
end
def add_item(start_date, end_date)

@ -79,8 +79,8 @@ describe 'project menu', type: :feature do
it_behaves_like 'it leads to the project costs reports'
end
context "when on the project's calendar" do
let(:current_path) { '/projects/ponyo/calendar' }
context "when on the project's calendars" do
let(:current_path) { '/projects/ponyo/calendars' }
it_behaves_like 'it leads to the project costs reports'
end
@ -111,7 +111,7 @@ describe 'project menu', type: :feature do
end
context "when on the project's calendar" do
let(:current_path) { '/projects/ponyo/calendar' }
let(:current_path) { '/projects/ponyo/calendars' }
it_behaves_like 'it leads to the cost reports'
end

Loading…
Cancel
Save