Merge branch 'release/11.2' into dev

pull/9119/head
ulferts 4 years ago
commit 344f43cf87
No known key found for this signature in database
GPG Key ID: A205708DE1284017
  1. 45
      app/controllers/application_controller.rb
  2. 8
      app/models/work_package.rb
  3. 4
      app/services/work_packages/delete_service.rb
  4. 10
      docs/enterprise-edition-guide/support/installation-support/README.md
  5. 3
      docs/installation-and-operations/operation/monitoring/README.md
  6. 33
      docs/release-notes/11-2-1/README.md
  7. 7
      docs/release-notes/README.md
  8. 6
      frontend/src/app/globals/global-helpers.ts
  9. 6
      frontend/src/app/modules/boards/board/board-actions/cached-board-action.service.ts
  10. 2
      frontend/src/app/modules/fields/edit/field-types/multi-select-edit-field.component.ts
  11. 13
      frontend/src/app/modules/grids/widgets/time-entries/current-user/time-entries-current-user-configuration-modal/services/time-entries-current-user-configuration-modal/time-entries-current-user-configuration-modal.service.ts
  12. 7
      modules/backlogs/app/helpers/rb_common_helper.rb
  13. 13
      modules/boards/spec/features/action_boards/status_board_spec.rb
  14. 4
      modules/boards/spec/features/support/board_page.rb
  15. 4
      spec/models/work_package_spec.rb
  16. 36
      spec/services/work_packages/delete_service_integration_spec.rb
  17. 4
      spec/services/work_packages/delete_service_spec.rb

@ -131,10 +131,10 @@ class ApplicationController < ActionController::Base
end
before_action :user_setup,
:set_localization,
:check_if_login_required,
:log_requesting_user,
:reset_i18n_fallbacks,
:set_localization,
:check_session_lifetime,
:stop_if_feeds_disabled,
:set_cache_buster,
@ -176,6 +176,7 @@ class ApplicationController < ActionController::Base
def openproject_cookie_missing?
request.cookies[OpenProject::Configuration['session_cookie_name']].nil?
end
helper_method :openproject_cookie_missing?
##
@ -330,31 +331,32 @@ class ApplicationController < ActionController::Base
def find_belongs_to_chained_objects(associations, start_object = nil)
associations.inject([start_object].compact) do |instances, association|
scope_name, scope_association = if association.is_a?(Hash)
[association.keys.first.to_s.downcase, association.values.first]
else
[association.to_s.downcase, association.to_s.downcase]
end
[association.keys.first.to_s.downcase, association.values.first]
else
[association.to_s.downcase, association.to_s.downcase]
end
# TODO: Remove this hidden dependency on params
instances << (if instances.last.nil?
scope_name.camelize.constantize.find(params[:"#{scope_name}_id"])
else
instances.last.send(scope_association.to_sym)
end)
instances << (
if instances.last.nil?
scope_name.camelize.constantize.find(params[:"#{scope_name}_id"])
else
instances.last.send(scope_association.to_sym)
end)
instances
end
end
def self.model_object(model, options = {})
self._model_object = model
self._model_scope = Array(options[:scope]) if options[:scope]
self._model_scope = Array(options[:scope]) if options[:scope]
end
# Filter for bulk work package operations
def find_work_packages
@work_packages = WorkPackage.includes(:project)
.where(id: params[:work_package_id] || params[:ids])
.order('id ASC')
.where(id: params[:work_package_id] || params[:ids])
.order('id ASC')
fail ActiveRecord::RecordNotFound if @work_packages.empty?
@projects = @work_packages.map(&:project).compact.uniq
@ -452,13 +454,13 @@ class ApplicationController < ActionController::Base
def render_validation_errors(object)
options = { status: :unprocessable_entity, layout: false }
errors = case params[:format]
when 'xml'
{ xml: object.errors }
when 'json'
{ json: { 'errors' => object.errors } } # ActiveResource client compliance
else
fail "Unknown format #{params[:format]} in #render_validation_errors"
end
when 'xml'
{ xml: object.errors }
when 'json'
{ json: { 'errors' => object.errors } } # ActiveResource client compliance
else
fail "Unknown format #{params[:format]} in #render_validation_errors"
end
options.merge! errors
render options
end
@ -489,17 +491,20 @@ class ApplicationController < ActionController::Base
I18n.t(label + '_plural',
default: label.to_sym)
end
helper_method :default_breadcrumb
def show_local_breadcrumb
false
end
helper_method :show_local_breadcrumb
def admin_first_level_menu_entry
menu_item = admin_menu_item(current_menu_item)
menu_item.parent
end
helper_method :admin_first_level_menu_entry
def check_session_lifetime

@ -684,12 +684,12 @@ class WorkPackage < ApplicationRecord
def attribute_users
related = [author]
[responsible, assigned_to].each do |user|
case user
[responsible, assigned_to].each do |principal|
case principal
when Group
related += user.users
related += principal.users.active
when User
related << user
related << principal
end
end

@ -49,6 +49,10 @@ class WorkPackages::DeleteService < ::BaseServices::Delete
result
end
def destroy(work_package)
work_package.reload.destroy
end
def destroy_descendants(descendants, result)
descendants.each do |descendant|
result.add_dependent!(ServiceResult.new(success: descendant.destroy, result: descendant))

@ -2,20 +2,20 @@
sidebar_navigation:
title: Installation support
priority: 980
description: Installation support for the OpenProject Enterprise Edition.
description: Installation support for OpenProject Enterprise on-premises.
robots: index, follow
keywords: installation support
---
# Installation support for the Enterprise Edition
# Installation support for Enterprise on-premises
We deliver the confidence of a tested, supported and certified enterprise-class business application.
If you want to work with the on premise OpenProject Enterprise Edition but do not have a Community version running, you can order **installation support** during the [booking process of your Enterprise Edition](../../activate-enterprise-edition).
If you want to work with OpenProject Enterprise on-premises but do not have a Community Edition running, you can order **installation support** during the [booking process of your Enterprise on-premises edition](../../activate-enterprise-edition).
The cost for the installation support of a Community version is 150 €. In the price there is no migration included. If you need migration support, please [contact us](mailto:info@openproject.com) for a detailed quotation.
The cost for the installation support of a Community version is 150 € (excluding VAT). In the price there is no migration included. If you need migration support, please [contact us](mailto:info@openproject.com) for a detailed quotation.
## Installation checklist
Please have a look at our [installation checklist](https://1t1rycb9er64f1pgy2iuseow-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/OpenProject_Installation_Checklist_v1.4.pdf) and send us the completed document prior to the on premise installation.
Please have a look at our [installation checklist](https://1t1rycb9er64f1pgy2iuseow-wpengine.netdna-ssl.com/wp-content/uploads/2021/03/OpenProject_Installation_Checklist_v1.8.pdf) and send us the completed document prior to the on-premise installation.

@ -17,10 +17,13 @@ See example below:
sudo openproject logs --tail
```
You can abort this using Ctrl + C.
Note:
* On distributions that are based on systemd, all the logs are sent to journald, so you can also display them via `journalctl`.
* On older distributions that use either sysvinit or upstart, all the logs are stored in `/var/log/openproject/`.
* If you need to share the logs you can save them in a file using `tee` like this: `sudo openproject logs --tail | tee openproject.log`
In a docker-based installation, all logs are redirected to STDOUT so you can use the normal docker tools to manage your logs.

@ -0,0 +1,33 @@
---
title: OpenProject 11.2.1
sidebar_navigation:
title: 11.2.1
release_version: 11.2.1
release_date: 2021-03-23
---
# OpenProject 11.2.1
Release date: 2021-03-23
We released [OpenProject 11.2.1](https://community.openproject.com/versions/1472).
The release contains several bug fixes and we recommend updating to the newest version.
<!--more-->
#### Bug fixes and changes
- Fixed: Inbound Emails - Email headers not handled correctly \[[#35834](https://community.openproject.com/wp/35834)\]
- Fixed: Configuration and display of days in My spent time widget on my Page do not match \[[#35920](https://community.openproject.com/wp/35920)\]
- Fixed: All data lost when switching from details view to fullscreen view while creating a work package \[[#35968](https://community.openproject.com/wp/35968)\]
- Fixed: missing translation "ja.js.units.hour.one" \[[#36269](https://community.openproject.com/wp/36269)\]
- Fixed: Internal Error when Wiki entry contains single "!" \[[#36345](https://community.openproject.com/wp/36345)\]
- Fixed: Missing word in deletion confirmation for placeholder users \[[#36516](https://community.openproject.com/wp/36516)\]
- Fixed: Cannot select assignee on WP create when filtering for multi-select custom field of type list \[[#36607](https://community.openproject.com/wp/36607)\]
- Fixed: Error message in wrong language \[[#36688](https://community.openproject.com/wp/36688)\]
#### Contributions
A big thanks to community members for reporting bugs and helping us identifying and providing fixes.
Special thanks for reporting and finding bugs go to
Benjamin Tey, wataru shoji

@ -12,6 +12,13 @@ Stay up to date and get an overview of the new features included in the releases
<!--- New release notes are generated below. Do not remove comment. -->
<!--- RELEASE MARKER -->
## 11.2.1
Release date: 2021-03-23
[Release Notes](11-2-1/)
## 11.2.0
Release date: 2021-03-09

@ -34,9 +34,9 @@
*/
export class GlobalHelpers {
public checkAll(selector:any, checked:any) {
jQuery('#' + selector + ' input:checkbox').not(':disabled').each(function(this:HTMLInputElement) {
this.checked = checked;
});
document
.querySelectorAll(`#${selector} input[type="checkbox"]:not([disabled])`)
.forEach((el:HTMLInputElement) => el.checked = checked);
}
public toggleCheckboxesBySelector(selector:any) {

@ -3,7 +3,7 @@ import { BoardActionService } from "core-app/modules/boards/board/board-actions/
import { input } from "reactivestates";
import { HalResource } from "core-app/modules/hal/resources/hal-resource";
import { Observable } from "rxjs";
import { filter, map, take } from "rxjs/operators";
import { map, take } from "rxjs/operators";
import { Board } from "core-app/modules/boards/board/board";
@Injectable()
@ -21,7 +21,9 @@ export abstract class CachedBoardActionService extends BoardActionService {
.pipe(
map(results => {
if (matching) {
return results.filter(resource => resource.name.includes(matching));
return results.filter(resource =>
new RegExp(matching, 'i').test(resource.name)
);
} else {
return results;
}

@ -94,7 +94,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements
*/
public buildSelectedOption() {
const value:HalResource[] = this.resource[this.name];
return value ? value.map(val => this.findValueOption(val)) : [];
return value ? _.castArray(value).map(val => this.findValueOption(val)) : [];
}
public get selectedOption() {

@ -1,4 +1,5 @@
import { Injectable } from '@angular/core';
import {DisplayedDays} from "core-app/modules/calendar/te-calendar/te-calendar.component";
@Injectable()
export class TimeEntriesCurrentUserConfigurationModalService {
@ -50,8 +51,18 @@ export class TimeEntriesCurrentUserConfigurationModalService {
}
getCheckedValuesInOriginalOrder(days:IDayData[]) {
return days
const configuredDays = days
.sort((a, b) => a.originalIndex < b.originalIndex ? -1 : 1)
.map(localeDayData => localeDayData.checked);
return this.validDays(configuredDays as DisplayedDays);
}
private validDays(days:DisplayedDays) {
if (days.every((value) => !value)) {
return Array.apply(null, Array(7)).map(() => true);
} else {
return days;
}
}
}

@ -159,7 +159,12 @@ module RbCommonHelper
end
def type_name_or_empty(story)
story.type.nil? ? '' : h(backlogs_types_by_id[story.type_id].name)
return '' if story.type_id.nil?
type = backlogs_types_by_id[story.type_id]
return '' if type.nil?
h(type.name)
end
def date_string_with_milliseconds(d, add = 0)

@ -84,6 +84,19 @@ describe 'Status action board', type: :feature, js: true do
end
context 'with full boards permissions' do
it 'can add a case-insensitive list (Regression #35744)' do
board_index.visit!
# Create new board
board_page = board_index.create_board action: :Status
# expect lists of default status
board_page.expect_list 'Open'
board_page.add_list option: 'Closed', query: 'closed'
board_page.expect_list 'Closed'
end
it 'allows management of boards' do
board_index.visit!

@ -177,7 +177,7 @@ module Pages
.perform
end
def add_list(option: nil)
def add_list(option: nil, query: option)
if option.nil? && action?
raise "Must pass value option for action boards"
end
@ -188,7 +188,7 @@ module Pages
page.find('.boards-list--add-item').click
expect(page).to have_selector('.board-list--container', count: count + 1)
else
open_and_fill_add_list_modal option
open_and_fill_add_list_modal query
page.find('.ng-option-label', text: option, wait: 10).click
click_on 'Add'
end

@ -684,7 +684,7 @@ describe WorkPackage, type: :model do
let(:assignee) do
group = FactoryBot.build_stubbed(:group)
allow(group)
.to receive(:users)
.to receive_message_chain(:users, :active)
.and_return([user1, user2, user3])
group
end
@ -700,7 +700,7 @@ describe WorkPackage, type: :model do
let(:responsible) do
group = FactoryBot.build_stubbed(:group)
allow(group)
.to receive(:users)
.to receive_message_chain(:users, :active)
.and_return([user1, user2, user3])
group
end

@ -29,21 +29,16 @@
require 'spec_helper'
describe WorkPackages::DeleteService, 'integration', type: :model do
let(:user) do
shared_let(:project) { FactoryBot.create(:project) }
shared_let(:role) do
FactoryBot.create(:role,
permissions: %i[delete_work_packages view_work_packages add_work_packages manage_subtasks])
end
shared_let(:user) do
FactoryBot.create(:user,
member_in_project: project,
member_through_role: role)
end
let(:role) do
FactoryBot.create(:role,
permissions: permissions)
end
let(:permissions) do
%i(delete_work_packages view_work_packages add_work_packages manage_subtasks)
end
let(:project) { FactoryBot.create(:project) }
describe 'deleting a child with estimated_hours set' do
let(:parent) { FactoryBot.create(:work_package, project: project) }
@ -78,4 +73,23 @@ describe WorkPackages::DeleteService, 'integration', type: :model do
expect(parent.estimated_hours).to eq(nil)
end
end
describe 'with a stale work package reference' do
let!(:work_package) { FactoryBot.create :work_package, project: project }
let(:instance) do
described_class.new(user: user,
model: work_package)
end
subject { instance.call }
it 'still destroys it' do
# Cause lock version changes
WorkPackage.where(id: work_package.id).update_all(lock_version: work_package.lock_version + 1)
expect(subject).to be_success
expect { work_package.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end

@ -47,6 +47,10 @@ describe WorkPackages::DeleteService do
subject { instance.call }
before do
expect(work_package)
.to receive(:reload)
.and_return(work_package)
expect(work_package)
.to receive(:destroy)
.and_return(destroyed_result)

Loading…
Cancel
Save