Merge branch 'release/10.5' into dev

pull/8344/head
ulferts 5 years ago
commit 7fbc15c31c
No known key found for this signature in database
GPG Key ID: A205708DE1284017
  1. 1
      app/assets/stylesheets/content/_notifications.sass
  2. 91
      app/assets/stylesheets/layout/_main_menu.sass
  3. 4
      app/views/layouts/base.html.erb
  4. 8
      config/initializers/doorkeeper.rb
  5. 5
      docs/system-admin-guide/authentication/README.md
  6. 12
      frontend/src/app/components/resizer/main-menu-resizer.component.ts
  7. 26
      frontend/src/app/init-globals.ts
  8. 2
      frontend/src/app/modules/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts
  9. 8
      frontend/src/app/modules/calendar/te-calendar/te-calendar.component.ts
  10. 13
      frontend/src/app/modules/fields/edit/field-types/duration-edit-field.component.ts
  11. 22
      frontend/src/app/modules/global_search/input/global-search-input.component.ts
  12. 27
      frontend/src/main.ts
  13. 4
      lib/open_project/configuration.rb
  14. 7
      lib/open_project/configuration/helpers.rb
  15. 2
      modules/bim/app/assets/stylesheets/bim/ifc_viewer/generic.sass
  16. 20
      modules/bim/app/models/bim/queries/work_packages/columns/bcf_thumbnail_column.rb
  17. 6
      modules/bim/app/models/bim/queries/work_packages/filter/bcf_issue_associated_filter.rb
  18. 4
      modules/bim/lib/open_project/bim/engine.rb
  19. 3
      modules/bim/spec/api/v3/queries/filters/query_filter_instance_representer_spec.rb
  20. 35
      modules/bim/spec/features/viewer/show_viewpoint_spec.rb
  21. 24
      modules/bim/spec/models/queries/work_packages/columns/bcf_thumbnail_column_spec.rb
  22. 56
      modules/bim/spec/models/queries/work_packages/filter/bcf_issue_associated_filter_spec.rb

@ -231,6 +231,7 @@ $nm-upload-box-padding: rem-calc(15) rem-calc(25)
> p
margin-bottom: 0
@include varprop(color, body-font-color)
progress[value]
@extend %progress-styles

@ -336,6 +336,21 @@ a.main-menu--parent-node
padding-left: $hierarchy-span-width
font-size: 0.7rem
#main-menu ul ul.main-menu--children ul.pages-hierarchy
.tree-menu--hierarchy-indicator
@include varprop(color, main-menu-font-color)
.tree-menu--item
&.-selected
@include varprop(background, main-menu-bg-selected-background)
.tree-menu--title
@include varprop(color, main-menu-selected-font-color)
&:hover
@include varprop(background, main-menu-bg-hover-background)
.tree-menu--title
@include varprop(color, main-menu-hover-font-color)
text-decoration: none
// Resizer & toggle styles
.main-menu--resizer
background: none
height: 100vh
@ -345,73 +360,43 @@ a.main-menu--parent-node
border-left-width: 2px
border-left-style: solid
border-left-color: transparent
left: calc(#{$main-menu-width} - 2px)
left: calc(var(--main-menu-width) - 2px)
vertical-align: middle
z-index: 1
cursor: col-resize
&:hover
@include varprop(border-left-color, main-menu-resizer-color)
.main-menu--navigation-toggler
opacity: 1
i:before
@include varprop(color, main-menu-navigation-toggler-font-hover-color)
i:before
@include varprop(color, main-menu-navigation-toggler-font-hover-color)
&.show
left: $main-menu-folded-width
.main-menu--navigation-toggler
.hidden-navigation &
text-align: right
opacity: 0
cursor: col-resize
&:hover
@include varprop(color, main-menu-navigation-toggler-font-hover-color)
i:before
@include icon-mixin-arrow-right2
i:before
@include icon-mixin-arrow-right2
position: absolute
top: 0
height: 40px
text-align: left
.resizer-toggle-container
margin-top: 50vh
margin-left: -14px
padding: 0
// Center toggle icon vertically. The element is higher than it looks like.
line-height: 38px
i
display: inline-block
width: 30px
margin-left: -25px
display: inline-block
.main-menu--navigation-toggler
cursor: pointer
line-height: 40px
&:before
color: $light-gray
padding-right: 0
padding-left: 2px
@include icon-common
font-size: 11px
font-weight: 400
&:not(.open):before
@include icon-mixin-arrow-right2
position: absolute
right: 0
&:hover
@include varprop(color, main-menu-navigation-toggler-font-hover-color)
font-size: 11px
i:before
padding-left: 0
@include icon-mixin-arrow-left2
#main-menu ul ul.main-menu--children ul.pages-hierarchy
.tree-menu--hierarchy-indicator
@include varprop(color, main-menu-font-color)
.tree-menu--item
&.-selected
@include varprop(background, main-menu-bg-selected-background)
.tree-menu--title
@include varprop(color, main-menu-selected-font-color)
&:hover
@include varprop(background, main-menu-bg-hover-background)
.tree-menu--title
@include varprop(color, main-menu-hover-font-color)
text-decoration: none
.main-menu--navigation-toggler
background-color: var(--main-menu-bg-color)
&.open:before
@include icon-mixin-arrow-left2
i
display: inline-block
width: 12px
&:before
vertical-align: middle
color: $light-gray
// Badges for menu items such as "EXPERIMENTAL" or "BETA"
$badge_offset: 4px

@ -53,8 +53,8 @@ See docs/COPYRIGHT.rdoc for more details.
data-id="<%= User.current.id %>" />
<% end %>
<% if OpenProject::Configuration.frontend_sentry? %>
<%= tag :meta, name: 'openproject_sentry', data: { dsn: OpenProject::Configuration.sentry_dsn } %>
<% if OpenProject::Configuration.sentry_frontend_dsn %>
<%= tag :meta, name: 'openproject_sentry', data: { dsn: OpenProject::Configuration.sentry_frontend_dsn } %>
<% end %>
<meta name="openproject_initializer"

@ -114,14 +114,6 @@ Doorkeeper.configure do
#
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
# Change the native redirect uri for client apps
# When clients register with the following redirect uri, they won't be redirected to any server and
# the authorizationcode will be displayed within the provider
# The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
#
native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
# Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
# by default in non-development environments). OAuth2 delegates security in
# communication to the HTTPS protocol so it is wise to keep this enabled.

@ -24,3 +24,8 @@ Configure **authentication** settings and authentication providers in OpenProjec
| [LDAP authentication](ldap-authentication) | How to set up LDAP authentication in OpenProject? |
| [LDAP group synchronization](ldap-authentication/ldap-group-synchronization) | How to configure LDAP group synchronization in OpenProject (Premium feature)? |
## Frequently asked questions (FAQ)
### Which authentication provider are supported for single sign-on?
We do support the main authentication provider, such as CAS, SAML, OpenID Connect, Kerberos, and Okta. Please note that single sign-on is a premium feature and can only be activated for the Cloud Edition and the Enterprise Edition.

@ -43,13 +43,15 @@ export const mainMenuResizerSelector = 'main-menu-resizer';
(end)="resizeEnd()"
(start)="resizeStart()"
(move)="resizeMove($event)">
<a href="#"
[attr.title]="toggleTitle"
class="main-menu--navigation-toggler"
(accessibleClick)="toggleService.toggleNavigation($event)">
<div class="resizer-toggle-container">
<i [attr.title]="toggleTitle"
class="main-menu--navigation-toggler"
[ngClass]="{'open': toggleService.showNavigation}"
(accessibleClick)="toggleService.toggleNavigation($event)"></i>
<i class="icon-resizer-vertical-lines"
aria-hidden="true"></i>
</a>
</div>
</resizer>
`
})

@ -26,15 +26,8 @@
// See docs/COPYRIGHT.rdoc for more details.
// ++
import {whenDebugging} from 'core-app/helpers/debug_output';
import {enableReactiveStatesLogging} from "reactivestates";
import 'hammerjs';
// Ensure we set the correct dynamic frontend path
// based on the RAILS_RELATIVE_URL_ROOT setting
// https://webpack.js.org/guides/public-path/
const ASSET_BASE_PATH = '/assets/frontend/';
// Global scripts previously part of the application.js
// Avoid require.context since that crashes angular regularly
require('./globals/augmenting/modal-wrapper.augment.service');
@ -42,22 +35,3 @@ require('./globals/dynamic-bootstrapper');
require('./globals/global-listeners');
require('./globals/openproject');
require('./globals/tree-menu');
// Sets the relative base path
window.appBasePath = jQuery('meta[name=app_base_path]').attr('content') || '';
// Ensure to set the asset base for dynamic code loading
// https://webpack.js.org/guides/public-path/
__webpack_public_path__ = window.appBasePath + ASSET_BASE_PATH;
const meta = jQuery('meta[name=openproject_initializer]');
I18n.locale = meta.data('defaultLocale');
I18n.locale = meta.data('locale');
I18n.firstDayOfWeek = parseInt(meta.data('firstDayOfWeek'), 10);
// Enable debug logging for reactive states
whenDebugging(() => {
(window as any).enableReactiveStatesLogging = () => enableReactiveStatesLogging(true);
(window as any).disableReactiveStatesLogging = () => enableReactiveStatesLogging(false);
});

@ -150,7 +150,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements
this.viewerBridge.showViewpoint(data);
} else {
window.location.href = this.pathHelper.bimDetailsPath(
this.currentProject.identifier!,
this.workPackage.project.identifier,
this.workPackage.id!,
index
);

@ -202,7 +202,13 @@ export class TimeEntryCalendarComponent implements OnInit, AfterViewInit {
}
if (oldRatio !== this.scaleRatio) {
this.ucCalendar.getApi().render();
// This is a hack.
// We already set the same function (different object) via angular.
// But it will trigger repainting the calendar.
// Weirdly, this.ucCalendar.getApi().rerender() does not.
this.ucCalendar.getApi().setOption('slotLabelFormat', (info:any) => {
return (this.maxHour - info.date.hour) / this.scaleRatio;
});
}
}

@ -63,7 +63,18 @@ export class DurationEditFieldComponent extends EditFieldComponent {
}
protected parseValue(val:moment.Moment | null) {
return val === null ? null : val.toISOString();
if (val === null) {
return val
}
let parsedValue;
if (val.isValid()) {
parsedValue = val.toISOString();
} else {
parsedValue = this.resource[this.name];
}
return parsedValue;
}
}

@ -49,7 +49,7 @@ import {DeviceService} from "core-app/modules/common/browser/device.service";
import {NgSelectComponent} from "@ng-select/ng-select";
import {Observable, of} from "rxjs";
import {Highlighting} from "core-components/wp-fast-table/builders/highlighting/highlighting.functions";
import {map, tap} from "rxjs/internal/operators";
import {map, tap, take, filter} from "rxjs/internal/operators";
import {HalResourceNotificationService} from "core-app/modules/hal/services/hal-resource-notification.service";
import {DebouncedRequestSwitchmap, errorNotificationHandler} from "core-app/helpers/rxjs/debounced-input-switchmap";
import {LinkHandling} from "core-app/modules/common/link-handling/link-handling";
@ -205,13 +205,21 @@ export class GlobalSearchInputComponent implements OnInit, OnDestroy {
this.openCloseMenu(this.currentValue);
}
// If Enter key is pressed before result list is loaded submit search in current scope
// If Enter key is pressed before result list is loaded, wait for the results to come
// in and then decide what to do. If a direct hit is present, follow that. Otherwise,
// go to the search in the current scope.
public onEnterBeforeResultsLoaded() {
if (!this.requests.hasResults) {
this.searchInScope(this.currentScope);
} else {
this.followSelectedItem();
}
this.requests.loading$.pipe(
filter(value => value === false),
take(1)
)
.subscribe(() => {
if (this.selectedItem) {
this.followSelectedItem();
} else {
this.searchInScope(this.currentScope);
}
});
}
public statusHighlighting(statusId:string) {

@ -3,22 +3,44 @@ import {enableProdMode} from '@angular/core';
import * as jQuery from "jquery";
import {environment} from './environments/environment';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {SentryReporter} from "core-app/sentry/sentry-reporter";
import {whenDebugging} from "core-app/helpers/debug_output";
import {enableReactiveStatesLogging} from "reactivestates";
(window as any).global = window;
// Ensure we set the correct dynamic frontend path
// based on the RAILS_RELATIVE_URL_ROOT setting
// https://webpack.js.org/guides/public-path/
const ASSET_BASE_PATH = '/assets/frontend/';
// Sets the relative base path
window.appBasePath = jQuery('meta[name=app_base_path]').attr('content') || '';
// Ensure to set the asset base for dynamic code loading
// https://webpack.js.org/guides/public-path/
__webpack_public_path__ = window.appBasePath + ASSET_BASE_PATH;
/** Load sentry integration as soon as possible */
import {SentryReporter} from "core-app/sentry/sentry-reporter";
window.ErrorReporter = new SentryReporter();
require('core-app/init-vendors');
require('./app/init-globals');
const meta = jQuery('meta[name=openproject_initializer]');
I18n.locale = meta.data('defaultLocale');
I18n.locale = meta.data('locale');
I18n.firstDayOfWeek = parseInt(meta.data('firstDayOfWeek'), 10);
if (environment.production) {
enableProdMode();
}
// Enable debug logging for reactive states
whenDebugging(() => {
(window as any).enableReactiveStatesLogging = () => enableReactiveStatesLogging(true);
(window as any).disableReactiveStatesLogging = () => enableReactiveStatesLogging(false);
});
jQuery(function () {
// Due to the behaviour of the Edge browser we need to wait for 'DOM ready'
@ -28,4 +50,3 @@ jQuery(function () {
jQuery('body').addClass('__ng2-bootstrap-has-run');
});
});

@ -165,8 +165,8 @@ module OpenProject
# Log errors to sentry instance
'sentry_dsn' => nil,
# Allow error reporting for frontend errors
'sentry_report_js' => false,
# Allow separate error reporting for frontend errors
'sentry_frontend_dsn' => nil,
'sentry_host' => 'https://sentry.openproject.com',
# Allow connection to Augur

@ -92,13 +92,6 @@ module OpenProject
self['edition'] == 'bim'
end
##
# Whether we want to report to sentry
def frontend_sentry?
self['sentry_dsn'].present? && sentry_report_js?
end
def available_file_uploaders
uploaders = {
file: ::LocalFileUploader

@ -30,7 +30,7 @@
flex-direction: column
.ifc-model-viewer--container
height: 33vh
height: 33vh !important
[data-name="bim"] .main-menu--children
overflow-x: hidden !important

@ -28,28 +28,16 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
module OpenProject::Bim
class QueryBcfThumbnailColumn < Queries::WorkPackages::Columns::WorkPackageColumn
module ::Bim::Queries::WorkPackages::Columns
class BcfThumbnailColumn < Queries::WorkPackages::Columns::WorkPackageColumn
def caption
I18n.t('attributes.bcf_thumbnail')
end
class_attribute :bcf_thumbnail_columns
self.bcf_thumbnail_columns = {
bcf_thumbnail: {
summable: false,
groupable: false,
sortable: false
}
}
def self.instances(_context = nil)
# return [] if context && !context.module_enabled?(:bcf_module)
return [] unless OpenProject::Configuration.bim?
bcf_thumbnail_columns.map do |name, options|
new(name, options)
end
[new(:bcf_thumbnail, { summable: false, groupable: false, sortable: false })]
end
end
end

@ -28,7 +28,7 @@
# See docs/COPYRIGHT.rdoc for more details.
#++
module ::OpenProject::Bim
module ::Bim::Queries::WorkPackages::Filter
class BcfIssueAssociatedFilter < ::Queries::WorkPackages::Filter::WorkPackageFilter
attr_reader :join_table_suffix
@ -65,6 +65,10 @@ module ::OpenProject::Bim
'::API::V3::Queries::Schemas::BooleanFilterDependencyRepresenter'
end
def available?
OpenProject::Configuration.bim?
end
private
def associated?

@ -207,8 +207,8 @@ module OpenProject::Bim
::WorkPackage::Exporter
.register_for_list(:bcf, OpenProject::Bim::BcfXml::Exporter)
::Queries::Register.filter ::Query, OpenProject::Bim::BcfIssueAssociatedFilter
::Queries::Register.column ::Query, OpenProject::Bim::QueryBcfThumbnailColumn
::Queries::Register.filter ::Query, ::Bim::Queries::WorkPackages::Filter::BcfIssueAssociatedFilter
::Queries::Register.column ::Query, ::Bim::Queries::WorkPackages::Columns::BcfThumbnailColumn
::API::Root.class_eval do
content_type :binary, 'application/octet-stream'

@ -31,7 +31,8 @@ require 'spec_helper'
describe ::API::V3::Queries::Filters::QueryFilterInstanceRepresenter do
let(:operator) { '=' }
let(:filter) do
::OpenProject::Bim::BcfIssueAssociatedFilter.create!(name: "bcf_issue_associated", operator: operator, values: values)
::Bim::Queries::WorkPackages::Filter::BcfIssueAssociatedFilter
.create!(name: "bcf_issue_associated", operator: operator, values: values)
end
let(:representer) { described_class.new(filter) }

@ -32,7 +32,12 @@ describe 'Show viewpoint in model viewer',
with_config: { edition: 'bim' },
type: :feature,
js: true do
let(:project) { FactoryBot.create :project, enabled_module_names: [:bim, :work_package_tracking] }
let(:project) do
FactoryBot.create(:project,
enabled_module_names: [:bim, :work_package_tracking],
parent: parent_project)
end
let(:parent_project) { nil }
let(:user) { FactoryBot.create :admin }
let!(:work_package) { FactoryBot.create(:work_package, project: project) }
@ -94,14 +99,30 @@ describe 'Show viewpoint in model viewer',
context 'when in work packages details view' do
let(:wp_details) { ::Pages::SplitWorkPackage.new(work_package, project) }
shared_examples "moves to the BCF page" do
it 'moves to the bcf page' do
wp_details.visit!
bcf_details.expect_viewpoint_count 1
bcf_details.show_current_viewpoint
it 'moves to the bcf page' do
wp_details.visit!
bcf_details.expect_viewpoint_count 1
bcf_details.show_current_viewpoint
path = Regexp.escape("bcf/split/details/#{work_package.id}/overview")
expect(page).to have_current_path /#{path}/
project_identifier = Regexp.escape(project.identifier)
expect(page).to have_current_path /#{project_identifier}/
end
end
context "current project is the work package's project" do
it_behaves_like 'moves to the BCF page'
end
context "current project is a parent of the work package's project" do
let(:parent_project) { FactoryBot.create :project, enabled_module_names: [:work_package_tracking] }
let(:wp_details) { ::Pages::SplitWorkPackage.new(work_package, parent_project) }
path = Regexp.escape("bcf/split/details/#{work_package.id}/overview")
expect(page).to have_current_path /#{path}/
it_behaves_like "moves to the BCF page"
end
context 'when user only has view_linked_issues permission' do

@ -0,0 +1,24 @@
require 'spec_helper'
require Rails.root + 'spec/models/queries/work_packages/columns/shared_query_column_specs'
describe Bim::Queries::WorkPackages::Columns::BcfThumbnailColumn, type: :model do
let(:instance) { described_class.new(:query_column) }
it_behaves_like 'query column'
describe 'instances' do
context 'bim edition', with_config: { edition: 'bim' } do
it 'the bcf_thumbnail column exists' do
expect(described_class.instances.map(&:name))
.to include :bcf_thumbnail
end
end
context 'vanilla edition' do
it 'the bcf_thumbnail column does not exist' do
expect(described_class.instances.map(&:name))
.not_to include :bcf_thumbnail
end
end
end
end

@ -0,0 +1,56 @@
#-- 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 docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe Bim::Queries::WorkPackages::Filter::BcfIssueAssociatedFilter, type: :model do
include_context 'filter tests'
let(:values) { [OpenProject::Database::DB_VALUE_TRUE] }
it_behaves_like 'basic query filter' do
let(:class_key) { :bcf_issue_associated }
let(:type) { :list }
describe '#available?' do
context 'if bim is enabled', with_config: { edition: 'bim' } do
it 'is available' do
expect(instance)
.to be_available
end
end
context 'if bim is disabled' do
it 'is not available' do
expect(instance)
.not_to be_available
end
end
end
end
end
Loading…
Cancel
Save