From 1a8090402f8cb361d63af93aee70ad896d40148f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 24 Sep 2018 12:05:47 +0200 Subject: [PATCH] try to save highlighted attributes --- .../columns/work_package_column.rb | 1 - app/models/query/highlighting.rb | 7 ++- .../update_query_from_params_service.rb | 1 + .../highlighting/row-highlight-render-pass.ts | 2 +- .../state/wp-table-highlighting.service.ts | 44 ++++++++------- .../wp-fast-table/wp-table-highlight.ts | 49 +++++++++++++++++ .../components/wp-query/url-params-helper.ts | 12 +++++ .../tabs/highlighting-tab.component.html | 6 +-- .../tabs/highlighting-tab.component.ts | 36 ++++++++----- .../wp-table/table-state/table-state.ts | 3 +- .../common/enterprise/banners.service.ts | 53 +++++++++++++++++++ .../multi-toggled-select.component.ts | 2 +- .../common/openproject-common.module.ts | 2 + .../src/app/modules/hal/hal-link/hal-link.ts | 5 ++ .../modules/hal/resources/query-resource.ts | 1 + 15 files changed, 183 insertions(+), 41 deletions(-) create mode 100644 frontend/src/app/components/wp-fast-table/wp-table-highlight.ts create mode 100644 frontend/src/app/modules/common/enterprise/banners.service.ts diff --git a/app/models/queries/work_packages/columns/work_package_column.rb b/app/models/queries/work_packages/columns/work_package_column.rb index 2426770d9a..078e3fee6c 100644 --- a/app/models/queries/work_packages/columns/work_package_column.rb +++ b/app/models/queries/work_packages/columns/work_package_column.rb @@ -29,7 +29,6 @@ #++ class Queries::WorkPackages::Columns::WorkPackageColumn < Queries::Columns::Base - attr_accessor :highlightable alias_method :highlightable?, :highlightable diff --git a/app/models/query/highlighting.rb b/app/models/query/highlighting.rb index debaf83c31..00efc935bb 100644 --- a/app/models/query/highlighting.rb +++ b/app/models/query/highlighting.rb @@ -42,11 +42,10 @@ module Query::Highlighting allow_blank: true validates_inclusion_of :highlighted_attributes, - in: ->(*) { self.available_highlighting_columns.map{ |col| col.name.to_sym } }, + in: ->(*) { available_highlighting_columns.map { |col| col.name.to_sym } }, allow_nil: true, allow_blank: true - def available_highlighting_columns @available_highlighting_columns ||= available_columns.select(&:highlightable?) end @@ -59,6 +58,10 @@ module Query::Highlighting .uniq end + def highlighted_attributes + super.presence || [] + end + def highlighting_mode return :none unless EnterpriseToken.allows_to?(:conditional_highlighting) diff --git a/app/services/update_query_from_params_service.rb b/app/services/update_query_from_params_service.rb index c4ec80df0c..821ffea652 100644 --- a/app/services/update_query_from_params_service.rb +++ b/app/services/update_query_from_params_service.rb @@ -98,6 +98,7 @@ class UpdateQueryFromParamsService def apply_highlighting(params) query.highlighting_mode = params[:highlighting_mode] if params.key?(:highlighting_mode) + query.highlighted_attributes = params[:highlighted_attributes] if params.key?(:highlighted_attributes) end def disable_hierarchy_when_only_grouped_by(params) diff --git a/frontend/src/app/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts b/frontend/src/app/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts index ba7bd45c63..aa469f86c9 100644 --- a/frontend/src/app/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts +++ b/frontend/src/app/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts @@ -21,7 +21,7 @@ export class HighlightingRenderPass { return; } - const highlightAttribute = this.wpTableHighlighting.current; + const highlightAttribute = this.wpTableHighlighting.current.mode; // Get the computed style to identify bright properties const styles = window.getComputedStyle(document.body); diff --git a/frontend/src/app/components/wp-fast-table/state/wp-table-highlighting.service.ts b/frontend/src/app/components/wp-fast-table/state/wp-table-highlighting.service.ts index 0a6a0a4976..acd21a1846 100644 --- a/frontend/src/app/components/wp-fast-table/state/wp-table-highlighting.service.ts +++ b/frontend/src/app/components/wp-fast-table/state/wp-table-highlighting.service.ts @@ -5,36 +5,37 @@ import {Injectable} from '@angular/core'; import {States} from 'core-components/states.service'; import {HighlightingMode} from "core-components/wp-fast-table/builders/highlighting/highlighting-mode.const"; import {DynamicCssService} from "../../../modules/common/dynamic-css/dynamic-css.service"; +import {WorkPackageTableHighlight} from "core-components/wp-fast-table/wp-table-highlight"; +import {BannersService} from "core-app/modules/common/enterprise/banners.service"; @Injectable() -export class WorkPackageTableHighlightingService extends WorkPackageTableBaseService implements WorkPackageQueryStateService { - public eeShowBanners:boolean = false; - +export class WorkPackageTableHighlightingService extends WorkPackageTableBaseService implements WorkPackageQueryStateService { public constructor(readonly states:States, + readonly Banners:BannersService, readonly dynamicCssService:DynamicCssService, readonly tableState:TableState) { super(tableState); - this.eeShowBanners = jQuery('body').hasClass('ee-banners-visible'); } public get state() { return this.tableState.highlighting; } - public get current():HighlightingMode { - return this.filteredMode(this.state.getValueOr('inline')); + public get current():WorkPackageTableHighlight { + let value = this.state.getValueOr(new WorkPackageTableHighlight('inline')); + return this.filteredValue(value); } public get isInline() { - return this.current === 'inline'; + return this.current.mode === 'inline'; } public get isDisabled() { - return this.current === 'none'; + return this.current.mode === 'none'; } - public update(value:HighlightingMode) { - super.update(this.filteredMode(value)); + public update(value:WorkPackageTableHighlight) { + super.update(this.filteredValue(value)); // Load dynamic highlighting CSS if enabled if (!this.isDisabled) { @@ -42,25 +43,28 @@ export class WorkPackageTableHighlightingService extends WorkPackageTableBaseSer } } - public valueFromQuery(query:QueryResource):HighlightingMode { - return query.highlightingMode || this.filteredMode('inline'); + public valueFromQuery(query:QueryResource):WorkPackageTableHighlight { + return this.filteredValue(new WorkPackageTableHighlight(query.highlightingMode, query.highlightedAttributes)); } public hasChanged(query:QueryResource) { - return query.highlightingMode !== this.current; + return query.highlightingMode !== this.current.mode || + !_.isEqual(query.highlightedAttributes, this.current.selectedAttributes); } public applyToQuery(query:QueryResource):boolean { - query.highlightingMode = this.current; + const current = this.current; + query.highlightingMode = current.mode; + + if (current.selectedAttributes) { + query.highlightedAttributes = current.selectedAttributes; + } return false; } - private filteredMode(mode:HighlightingMode):HighlightingMode { - if (this.eeShowBanners) { - return 'none'; - } else { - return mode; - } + private filteredValue(value:WorkPackageTableHighlight):WorkPackageTableHighlight { + this.Banners.conditional(() => value.mode = 'none'); + return value; } } diff --git a/frontend/src/app/components/wp-fast-table/wp-table-highlight.ts b/frontend/src/app/components/wp-fast-table/wp-table-highlight.ts new file mode 100644 index 0000000000..eeec530bb6 --- /dev/null +++ b/frontend/src/app/components/wp-fast-table/wp-table-highlight.ts @@ -0,0 +1,49 @@ +// -- 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 {QueryResource} from 'core-app/modules/hal/resources/query-resource'; +import {HighlightingMode} from "core-components/wp-fast-table/builders/highlighting/highlighting-mode.const"; +import {HalResource} from "core-app/modules/hal/resources/hal-resource"; + +export class WorkPackageTableHighlight { + constructor(public mode:HighlightingMode = 'inline', + public selectedAttributes:HalResource[]|undefined = undefined) { + } + + public update(query:QueryResource|null) { + if (!query) { + this.mode = 'inline'; + this.selectedAttributes = undefined; + return; + } + + + this.mode = query.highlightingMode; + this.selectedAttributes = query.selectedAttributes; + } +} diff --git a/frontend/src/app/components/wp-query/url-params-helper.ts b/frontend/src/app/components/wp-query/url-params-helper.ts index 2f49a70a48..30c7dec663 100644 --- a/frontend/src/app/components/wp-query/url-params-helper.ts +++ b/frontend/src/app/components/wp-query/url-params-helper.ts @@ -86,6 +86,10 @@ export class UrlParamsHelperService { paramsData.hl = query.highlightingMode; } + if (query.highlightingMode === 'inline' && query.highlightedAttributes) { + paramsData.hla = query.highlightedAttributes.map(el => el.href); + } + paramsData.hi = !!query.showHierarchies; paramsData.g = _.get(query.groupBy, 'id', ''); if (query.sortBy) { @@ -153,6 +157,10 @@ export class UrlParamsHelperService { queryData.highlightingMode = properties.hl; } + if (properties.hla) { + queryData.highlightedAttributes = properties.hla; + } + if (properties.hi === false || properties.hi === true) { queryData.showHierarchies = properties.hi; } @@ -212,6 +220,10 @@ export class UrlParamsHelperService { queryData.highlightingMode = query.highlightingMode; } + if (query.highlightedAttributes && query.highlightingMode === 'inline') { + queryData.highlightedAttributes = query.highlightedAttributes.map(el => el.href); + } + queryData.showHierarchies = !!query.showHierarchies; queryData.groupBy = _.get(query.groupBy, 'id', ''); diff --git a/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.html b/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.html index 20e6611b38..451b185bb7 100644 --- a/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.html +++ b/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.html @@ -24,9 +24,9 @@ {{ text.highlighting_mode.inline }}   + [availableOptions]="availableMappedHighlightedAttributes" + [initialSelection]="selectedAttributes" + (onValueChange)="selectedAttributes = $event"> diff --git a/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts b/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts index f6ef5618ec..930ff98029 100644 --- a/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts +++ b/frontend/src/app/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts @@ -6,6 +6,7 @@ import {HighlightingMode} from "core-components/wp-fast-table/builders/highlight import {MultiToggledSelectOption} from "core-app/modules/common/multi-toggled-select/multi-toggled-select.component"; import {HalResource} from "core-app/modules/hal/resources/hal-resource"; import {States} from "core-app/components/states.service"; +import {WorkPackageTableHighlight} from "core-components/wp-fast-table/wp-table-highlight"; @Component({ templateUrl: './highlighting-tab.component.html' @@ -13,13 +14,13 @@ import {States} from "core-app/components/states.service"; export class WpTableConfigurationHighlightingTab implements TabComponent { // Display mode - public highlightingMode:HighlightingMode|'entire-row' = 'inline'; + public highlightingMode:HighlightingMode = 'inline'; public entireRowMode:boolean = false; public lastEntireRowAttribute:HighlightingMode = 'status'; public eeShowBanners:boolean = false; - public availableHighlightedAttributes:MultiToggledSelectOption[] = []; - public selectedInlineOption:MultiToggledSelectOption|MultiToggledSelectOption[] = []; + public availableMappedHighlightedAttributes:MultiToggledSelectOption[] = []; + public selectedAttributes:undefined|MultiToggledSelectOption|MultiToggledSelectOption[] = []; public text = { title: this.I18n.t('js.work_packages.table_configuration.highlighting'), @@ -45,11 +46,17 @@ export class WpTableConfigurationHighlightingTab implements TabComponent { } public onSave() { - this.wpTableHighlight.update(this.highlightingMode as HighlightingMode); - - if (!Array.isArray(this.selectedInlineOption) && this.selectedInlineOption.value === 'all') { + let mode = this.highlightingMode; + let selectedAttributes:HalResource[]|undefined = undefined; + if (this.selectedAttributes !== undefined && _.get(this.selectedAttributes, 'value') !== 'all') { + selectedAttributes = _.castArray(this.selectedAttributes) + .map(el => _.find(this.availableHighlightedAttributes, 'href', el.value)!); } + + const newValue = new WorkPackageTableHighlight(mode, selectedAttributes); + this.wpTableHighlight.update(newValue); + } public updateMode(mode:HighlightingMode|'entire-row') { @@ -72,19 +79,24 @@ export class WpTableConfigurationHighlightingTab implements TabComponent { } ngOnInit() { - this.availableHighlightedAttributes = - [this.allAttributesOption].concat(this.getAvailableAttributes(this.states.query.form.value!.schema)); - this.selectedInlineOption = this.availableHighlightedAttributes[0]; + this.availableMappedHighlightedAttributes = + [this.allAttributesOption].concat(this.getAvailableAttributes()); + this.selectedAttributes = this.availableMappedHighlightedAttributes[0]; this.eeShowBanners = jQuery('body').hasClass('ee-banners-visible'); - this.updateMode(this.wpTableHighlight.current); + this.updateMode(this.wpTableHighlight.current.mode); if (this.eeShowBanners) { this.updateMode('none'); } } - public getAvailableAttributes(schema:any):MultiToggledSelectOption[] { - return schema.highlightedAttributes.allowedValues.map((el:HalResource) => ({ name: el.name, value: el.id })); + public get availableHighlightedAttributes():HalResource[] { + const schema = this.states.query.form.value!.schema; + return schema.highlightedAttributes.allowedValues; + } + + public getAvailableAttributes():MultiToggledSelectOption[] { + return this.availableHighlightedAttributes.map((el:HalResource) => ({ name: el.name, value: el.href! })); } private get allAttributesOption():MultiToggledSelectOption { diff --git a/frontend/src/app/components/wp-table/table-state/table-state.ts b/frontend/src/app/components/wp-table/table-state/table-state.ts index f08c188155..817bed7822 100644 --- a/frontend/src/app/components/wp-table/table-state/table-state.ts +++ b/frontend/src/app/components/wp-table/table-state/table-state.ts @@ -21,6 +21,7 @@ import { } from 'core-app/modules/hal/resources/wp-collection-resource'; import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; import {HighlightingMode} from "core-components/wp-fast-table/builders/highlighting/highlighting-mode.const"; +import {WorkPackageTableHighlight} from "core-components/wp-fast-table/wp-table-highlight"; @Injectable() export class TableState extends StatesGroup { @@ -60,7 +61,7 @@ export class TableState extends StatesGroup { // Hierarchies of table hierarchies = input(); // Highlighting mode - highlighting = input(); + highlighting = input(); // State to be updated when the table is up to date rendered = input(); diff --git a/frontend/src/app/modules/common/enterprise/banners.service.ts b/frontend/src/app/modules/common/enterprise/banners.service.ts new file mode 100644 index 0000000000..c106081974 --- /dev/null +++ b/frontend/src/app/modules/common/enterprise/banners.service.ts @@ -0,0 +1,53 @@ +// -- 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 {Inject, Injectable} from '@angular/core'; +import {DOCUMENT} from "@angular/common"; +import {PathHelperService} from "../path-helper/path-helper.service"; + +@Injectable() +export class BannersService { + + private readonly _banners:boolean = true; + + constructor(@Inject(DOCUMENT) protected documentElement:Document) { + this._banners = documentElement.body.classList.contains('ee-banners-visible'); + } + + public get eeShowBanners():boolean { + return this._banners; + } + + public conditional(bannersVisible?:() => void, bannersNotVisible?:() => void) { + this._banners ? this.callMaybe(bannersVisible) : this.callMaybe(bannersNotVisible); + } + + private callMaybe(func?:Function) { + func && func(); + } +} diff --git a/frontend/src/app/modules/common/multi-toggled-select/multi-toggled-select.component.ts b/frontend/src/app/modules/common/multi-toggled-select/multi-toggled-select.component.ts index c82e9beb16..905e2a55c8 100644 --- a/frontend/src/app/modules/common/multi-toggled-select/multi-toggled-select.component.ts +++ b/frontend/src/app/modules/common/multi-toggled-select/multi-toggled-select.component.ts @@ -31,7 +31,7 @@ import {I18nService} from "core-app/modules/common/i18n/i18n.service"; export interface MultiToggledSelectOption { name:string; - singleOnly:true|undefined; + singleOnly?:true; value:any; } diff --git a/frontend/src/app/modules/common/openproject-common.module.ts b/frontend/src/app/modules/common/openproject-common.module.ts index 2da97a2eed..c4205f38ef 100644 --- a/frontend/src/app/modules/common/openproject-common.module.ts +++ b/frontend/src/app/modules/common/openproject-common.module.ts @@ -64,6 +64,7 @@ import {CKEditorPreviewService} from "core-app/modules/common/ckeditor/ckeditor- import {ColorsAutocompleter} from "core-app/modules/common/colors/colors-autocompleter.component"; import {DynamicCssService} from "./dynamic-css/dynamic-css.service"; import {MultiToggledSelectComponent} from "core-app/modules/common/multi-toggled-select/multi-toggled-select.component"; +import {BannersService} from "core-app/modules/common/enterprise/banners.service"; export function bootstrapModule(injector:Injector) { return () => { @@ -159,6 +160,7 @@ export function bootstrapModule(injector:Injector) { { provide: APP_INITIALIZER, useFactory: bootstrapModule, deps: [Injector], multi: true }, I18nService, DynamicCssService, + BannersService, NotificationsService, FocusHelperService, LoadingIndicatorService, diff --git a/frontend/src/app/modules/hal/hal-link/hal-link.ts b/frontend/src/app/modules/hal/hal-link/hal-link.ts index f7af705988..584da05094 100644 --- a/frontend/src/app/modules/hal/hal-link/hal-link.ts +++ b/frontend/src/app/modules/hal/hal-link/hal-link.ts @@ -42,6 +42,11 @@ export interface HalLinkInterface { identifier?:string; } +export interface HalLinkSource { + href:string|null; + title:string; +} + export interface CallableHalLink extends HalLinkInterface { $link:this; data?:Promise; diff --git a/frontend/src/app/modules/hal/resources/query-resource.ts b/frontend/src/app/modules/hal/resources/query-resource.ts index 8bd763f37a..c9caafd0af 100644 --- a/frontend/src/app/modules/hal/resources/query-resource.ts +++ b/frontend/src/app/modules/hal/resources/query-resource.ts @@ -66,6 +66,7 @@ export class QueryResource extends HalResource { public timelineVisible:boolean; public timelineZoomLevel:TimelineZoomLevel; public highlightingMode:HighlightingMode; + public highlightedAttributes:HalResource[]|undefined; public timelineLabels:TimelineLabels; public showHierarchies:boolean; public public:boolean;