Merge pull request #4067 from FinnStutzenstein/prettify

Using prettify instead of pretty-quick-wrapper
This commit is contained in:
Jochen Saalfeld 2019-01-10 13:12:02 +01:00 committed by GitHub
commit 2f7336b257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 514 additions and 504 deletions

View File

@ -68,7 +68,7 @@ matrix:
install: install:
- npm install - npm install
script: script:
- npm run prettify-all - npm run prettify-check
- language: node_js - language: node_js
name: "Client: Testing" name: "Client: Testing"

View File

@ -18,8 +18,8 @@
"compodoc": "./node_modules/.bin/compodoc --hideGenerator -p src/tsconfig.app.json -n 'OpenSlides Documentation' -d ../Compodoc -s -w -t -o --port", "compodoc": "./node_modules/.bin/compodoc --hideGenerator -p src/tsconfig.app.json -n 'OpenSlides Documentation' -d ../Compodoc -s -w -t -o --port",
"extract": "ngx-translate-extract -i ./src -o ./src/assets/i18n/template-en.pot -clean --sort --format pot -m _", "extract": "ngx-translate-extract -i ./src -o ./src/assets/i18n/template-en.pot -clean --sort --format pot -m _",
"po2json": "./node_modules/.bin/po2json -f mf src/assets/i18n/de.po src/assets/i18n/de.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/cs.po src/assets/i18n/cs.json", "po2json": "./node_modules/.bin/po2json -f mf src/assets/i18n/de.po src/assets/i18n/de.json && ./node_modules/.bin/po2json -f mf src/assets/i18n/cs.po src/assets/i18n/cs.json",
"prettify-changes": "pretty-quick --staged", "prettify-check": "prettier --config ./.prettierrc --list-different \"src/{app,environments}/**/*{.ts,.js,.json,.css,.scss}\"",
"prettify-all": "pretty-quick" "prettify-write": "prettier --config ./.prettierrc --write \"src/{app,environments}/**/*{.ts,.js,.json,.css,.scss}\""
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^7.1.1", "@angular/animations": "^7.1.1",
@ -71,7 +71,6 @@
"karma-jasmine-html-reporter": "^0.2.2", "karma-jasmine-html-reporter": "^0.2.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^1.15.3", "prettier": "^1.15.3",
"pretty-quick": "^1.8.0",
"protractor": "^5.4.1", "protractor": "^5.4.1",
"ts-node": "~7.0.1", "ts-node": "~7.0.1",
"tslint": "~5.11.0", "tslint": "~5.11.0",

View File

@ -54,7 +54,6 @@ export class AppComponent {
* TODO: Overloading can be extended to more functions. * TODO: Overloading can be extended to more functions.
*/ */
private overloadArrayToString(): void { private overloadArrayToString(): void {
Array.prototype.toString = function(): string { Array.prototype.toString = function(): string {
let string = ''; let string = '';
const iterations = Math.min(this.length, 3); const iterations = Math.min(this.length, 3);

View File

@ -1,7 +1,7 @@
// import { _ } from '@biesbjerg/ngx-translate-extract'; // import { _ } from '@biesbjerg/ngx-translate-extract';
function _(key: string | string[]): string | string[] { function _(key: string | string[]): string | string[] {
return key; return key;
} }
/** /**
@ -19,10 +19,12 @@ function _(key: string | string[]): string | string[] {
// Core config strings // Core config strings
_('Presentation and assembly system'); _('Presentation and assembly system');
_('Event name'); _('Event name');
_('<a href="http://www.openslides.org">OpenSlides</a> is a free ' + _(
'<a href="http://www.openslides.org">OpenSlides</a> is a free ' +
'web based presentation and assembly system for visualizing ' + 'web based presentation and assembly system for visualizing ' +
'and controlling agenda, motions and elections of an ' + 'and controlling agenda, motions and elections of an ' +
'assembly.'); 'assembly.'
);
_('General'); _('General');
_('Event'); _('Event');
_('Short description of event'); _('Short description of event');
@ -94,7 +96,6 @@ _('Public item');
_('Internal item'); _('Internal item');
_('Hidden item'); _('Hidden item');
// Motions config strings // Motions config strings
// subgroup general // subgroup general
_('General'); _('General');
@ -115,13 +116,17 @@ _('Hide recommendation on projector');
_('Stop submitting new motions by non-staff users'); _('Stop submitting new motions by non-staff users');
_('Allow to disable versioning'); _('Allow to disable versioning');
_('Name of recommender'); _('Name of recommender');
_('Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.'); _(
'Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.'
);
_('Name of recommender for statute amendments'); _('Name of recommender for statute amendments');
_('Will be displayed as label before selected recommendation for statute amendments. Use an empty value to disable the recommendation system.'); _(
'Will be displayed as label before selected recommendation for statute amendments. Use an empty value to disable the recommendation system.'
);
_('Default text version for change recommendations'); _('Default text version for change recommendations');
// subgroup Amendments // subgroup Amendments
_('Amendments'); _('Amendments');
_('Activate statute amendments') _('Activate statute amendments');
_('Activate amendments'); _('Activate amendments');
_('Show amendments together with motions'); _('Show amendments together with motions');
_('Prefix for the identifier for amendments'); _('Prefix for the identifier for amendments');
@ -210,7 +215,6 @@ _('Called with');
_('Recommendation'); _('Recommendation');
_('Motion block'); _('Motion block');
// Assignment config strings // Assignment config strings
_('Election method'); _('Election method');
_('Automatic assign of method'); _('Automatic assign of method');
@ -220,10 +224,12 @@ _('Always Yes/No per candidate');
_('Elections'); _('Elections');
_('Ballot and ballot papers'); _('Ballot and ballot papers');
_('The 100-%-base of an election result consists of'); _('The 100-%-base of an election result consists of');
_('For Yes/No/Abstain per candidate and Yes/No per candidate the 100-%-base ' + _(
'For Yes/No/Abstain per candidate and Yes/No per candidate the 100-%-base ' +
'depends on the election method: If there is only one option per candidate, ' + 'depends on the election method: If there is only one option per candidate, ' +
'the sum of all votes of all candidates is 100 %. Otherwise for each ' + 'the sum of all votes of all candidates is 100 %. Otherwise for each ' +
'candidate the sum of all votes is 100 %.'); 'candidate the sum of all votes is 100 %.'
);
_('Yes/No/Abstain per candidate'); _('Yes/No/Abstain per candidate');
_('Yes/No per candidate'); _('Yes/No per candidate');
_('All valid ballots'); _('All valid ballots');
@ -316,7 +322,9 @@ _('Email subject');
_('Your login for {event_name}'); _('Your login for {event_name}');
_('You can use {event_name} as a placeholder.'); _('You can use {event_name} as a placeholder.');
_('Email body'); _('Email body');
_('Dear {name},\n\nthis is your OpenSlides login for the event {event_name}:\n\n {url}\n username: {username}\n password: {password}\n\nThis email was generated automatically.'); _(
'Dear {name},\n\nthis is your OpenSlides login for the event {event_name}:\n\n {url}\n username: {username}\n password: {password}\n\nThis email was generated automatically.'
);
_('Use these placeholders: {name}, {event_name}, {url}, {username}, {password}. The url referrs to the system url.'); _('Use these placeholders: {name}, {event_name}, {url}, {username}, {password}. The url referrs to the system url.');
// default groups // default groups

View File

@ -1,4 +1,3 @@
type QueryParamValue = string | number | boolean; type QueryParamValue = string | number | boolean;
/** /**

View File

@ -79,7 +79,11 @@ export class AppLoadService {
// between (ModelConstructor<BaseModel>) and (new (...args: any[]) => (BaseModel & Searchable)), we would not have // between (ModelConstructor<BaseModel>) and (new (...args: any[]) => (BaseModel & Searchable)), we would not have
// to check if the result of the contructor (the model instance) is really a searchable. // to check if the result of the contructor (the model instance) is really a searchable.
if (!isSearchable(new entry.model())) { if (!isSearchable(new entry.model())) {
throw Error(`Wrong configuration for ${entry.collectionString}: you gave a searchOrder, but the model is not searchable.`); throw Error(
`Wrong configuration for ${
entry.collectionString
}: you gave a searchOrder, but the model is not searchable.`
);
} }
return true; return true;
} }

View File

@ -115,40 +115,44 @@ export class CsvExportService {
csvContent.push(header); csvContent.push(header);
// create lines // create lines
csvContent = csvContent.concat(models.map(model => { csvContent = csvContent.concat(
return columns.map(column => { models.map(model => {
let value: string; return columns.map(column => {
let value: string;
if (isPropertyDefinition(column)) { if (isPropertyDefinition(column)) {
const property: any = model[column.property]; const property: any = model[column.property];
if (typeof property === 'number') { if (typeof property === 'number') {
value = property.toString(10); value = property.toString(10);
} else if (!property) { } else if (!property) {
value = ''; value = '';
} else if (property === true) { } else if (property === true) {
value = '1'; value = '1';
} else if (property === false) { } else if (property === false) {
value = '0'; value = '0';
} else { } else {
value = property.toString(); value = property.toString();
}
} else if (isMapDefinition(column)) {
value = column.map(model);
} }
} else if (isMapDefinition(column)) { tsList = this.checkCsvTextSafety(value, tsList);
value = column.map(model);
}
tsList = this.checkCsvTextSafety(value, tsList);
return value; return value;
}); });
})); })
);
// assemble lines, putting text separator in place // assemble lines, putting text separator in place
if (!tsList.length) { if (!tsList.length) {
throw new Error('no usable text separator left for valid csv text'); throw new Error('no usable text separator left for valid csv text');
} }
const csvContentAsString: string = csvContent.map(line => { const csvContentAsString: string = csvContent
return line.map(entry => tsList[0] + entry + tsList[0]).join(columnSeparator); .map(line => {
}).join(lineSeparator); return line.map(entry => tsList[0] + entry + tsList[0]).join(columnSeparator);
})
.join(lineSeparator);
this.exporter.saveFile(csvContentAsString, filename); this.exporter.saveFile(csvContentAsString, filename);
} }

View File

@ -97,7 +97,9 @@ export class DataStoreService {
/** /**
* Observable subject for changed or deleted models in the datastore. * Observable subject for changed or deleted models in the datastore.
*/ */
private readonly changedOrDeletedSubject: Subject<BaseModel | DeletedInformation> = new Subject<BaseModel | DeletedInformation>(); private readonly changedOrDeletedSubject: Subject<BaseModel | DeletedInformation> = new Subject<
BaseModel | DeletedInformation
>();
/** /**
* Observe the datastore for changes and deletions. * Observe the datastore for changes and deletions.
@ -358,7 +360,7 @@ export class DataStoreService {
collection: collectionString, collection: collectionString,
id: +id // needs casting, because Objects.keys gives all keys as strings... id: +id // needs casting, because Objects.keys gives all keys as strings...
}); });
}) });
}); });
if (models && models.length) { if (models && models.length) {
await this.add(models, newMaxChangeId); await this.add(models, newMaxChangeId);

View File

@ -5,7 +5,6 @@ import { BaseModel } from '../../shared/models/base/base-model';
import { BaseViewModel } from '../../site/base/base-view-model'; import { BaseViewModel } from '../../site/base/base-view-model';
import { StorageService } from './storage.service'; import { StorageService } from './storage.service';
/** /**
* Describes the available filters for a listView. * Describes the available filters for a listView.
* @param isActive: the current state of the filter * @param isActive: the current state of the filter
@ -18,7 +17,7 @@ import { StorageService } from './storage.service';
export interface OsFilter { export interface OsFilter {
property: string; property: string;
label?: string; label?: string;
options: (OsFilterOption | string )[]; options: (OsFilterOption | string)[];
count?: number; count?: number;
} }
@ -31,15 +30,12 @@ export interface OsFilterOption {
isActive?: boolean; isActive?: boolean;
} }
/** /**
* Filter for the list view. List views can subscribe to its' dataService (providing filter definitions) * Filter for the list view. List views can subscribe to its' dataService (providing filter definitions)
* and will receive their filtered data as observable * and will receive their filtered data as observable
*/ */
export abstract class FilterListService<M extends BaseModel, V extends BaseViewModel> { export abstract class FilterListService<M extends BaseModel, V extends BaseViewModel> {
/** /**
* stores the currently used raw data to be used for the filter * stores the currently used raw data to be used for the filter
*/ */
@ -70,11 +66,14 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
* Initializes the filterService. Returns the filtered data as Observable * Initializes the filterService. Returns the filtered data as Observable
*/ */
public filter(): Observable<V[]> { public filter(): Observable<V[]> {
this.repo.getViewModelListObservable().pipe(auditTime(100)).subscribe( data => { this.repo
this.currentRawData = data; .getViewModelListObservable()
this.filteredData = this.filterData(data); .pipe(auditTime(100))
this.filterDataOutput.next(this.filteredData); .subscribe(data => {
}); this.currentRawData = data;
this.filteredData = this.filterData(data);
this.filterDataOutput.next(this.filteredData);
});
this.loadStorageDefinition(this.filterDefinitions); this.loadStorageDefinition(this.filterDefinitions);
return this.filterDataOutput; return this.filterDataOutput;
} }
@ -84,11 +83,12 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
* @param filter * @param filter
*/ */
public addFilterOption(filterName: string, option: OsFilterOption): void { public addFilterOption(filterName: string, option: OsFilterOption): void {
const filter = this.filterDefinitions.find(f => f.property === filterName ); const filter = this.filterDefinitions.find(f => f.property === filterName);
if (filter) { if (filter) {
const filterOption = filter.options.find(o => const filterOption = filter.options.find(
(typeof o !== 'string') && o.condition === option.condition) as OsFilterOption; o => typeof o !== 'string' && o.condition === option.condition
if (filterOption && !filterOption.isActive){ ) as OsFilterOption;
if (filterOption && !filterOption.isActive) {
filterOption.isActive = true; filterOption.isActive = true;
filter.count += 1; filter.count += 1;
} }
@ -103,11 +103,12 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
} }
public removeFilterOption(filterName: string, option: OsFilterOption): void { public removeFilterOption(filterName: string, option: OsFilterOption): void {
const filter = this.filterDefinitions.find(f => f.property === filterName ); const filter = this.filterDefinitions.find(f => f.property === filterName);
if (filter) { if (filter) {
const filterOption = filter.options.find(o => const filterOption = filter.options.find(
(typeof o !== 'string') && o.condition === option.condition) as OsFilterOption; o => typeof o !== 'string' && o.condition === option.condition
if (filterOption && filterOption.isActive){ ) as OsFilterOption;
if (filterOption && filterOption.isActive) {
filterOption.isActive = false; filterOption.isActive = false;
filter.count -= 1; filter.count -= 1;
this.filteredData = this.filterData(this.currentRawData); this.filteredData = this.filterData(this.currentRawData);
@ -126,11 +127,10 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
option.isActive ? this.removeFilterOption(filterName, option) : this.addFilterOption(filterName, option); option.isActive ? this.removeFilterOption(filterName, option) : this.addFilterOption(filterName, option);
} }
public updateFilterDefinitions(filters: OsFilter[]) : void { public updateFilterDefinitions(filters: OsFilter[]): void {
this.loadStorageDefinition(filters); this.loadStorageDefinition(filters);
} }
/** /**
* Retrieve the currently saved filter definition from the StorageService, * Retrieve the currently saved filter definition from the StorageService,
* check their match with current definitions and set the current filter * check their match with current definitions and set the current filter
@ -141,35 +141,39 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
return; return;
} }
const me = this; const me = this;
this.store.get('filter_' + this.name).then(function(storedData: { name: string, data: OsFilter[] }): void { this.store.get('filter_' + this.name).then(
const storedFilters = (storedData && storedData.data) ? storedData.data : []; function(storedData: { name: string; data: OsFilter[] }): void {
definitions.forEach(definedFilter => { const storedFilters = storedData && storedData.data ? storedData.data : [];
const matchingStoreFilter = storedFilters.find(f => f.property === definedFilter.property); definitions.forEach(definedFilter => {
let count = 0; const matchingStoreFilter = storedFilters.find(f => f.property === definedFilter.property);
definedFilter.options.forEach(option => { let count = 0;
if (typeof option === 'string'){ definedFilter.options.forEach(option => {
return; if (typeof option === 'string') {
}; return;
if (matchingStoreFilter && matchingStoreFilter.options){
const storedOption = matchingStoreFilter.options.find(o =>
typeof o !== 'string' && o.condition === option.condition) as OsFilterOption;
if (storedOption) {
option.isActive = storedOption.isActive;
} }
} if (matchingStoreFilter && matchingStoreFilter.options) {
if (option.isActive) { const storedOption = matchingStoreFilter.options.find(
count += 1; o => typeof o !== 'string' && o.condition === option.condition
} ) as OsFilterOption;
if (storedOption) {
option.isActive = storedOption.isActive;
}
}
if (option.isActive) {
count += 1;
}
});
definedFilter.count = count;
}); });
definedFilter.count = count; me.filterDefinitions = definitions;
}); me.filteredData = me.filterData(me.currentRawData);
me.filterDefinitions = definitions; me.filterDataOutput.next(me.filteredData);
me.filteredData = me.filterData(me.currentRawData); },
me.filterDataOutput.next(me.filteredData); function(error: any): void {
}, function(error: any) : void { me.filteredData = me.filterData(me.currentRawData);
me.filteredData = me.filterData(me.currentRawData); me.filterDataOutput.next(me.filteredData);
me.filterDataOutput.next(me.filteredData); }
}); );
} }
/** /**
@ -177,10 +181,11 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
*/ */
private setStorageDefinition(): void { private setStorageDefinition(): void {
this.store.set('filter_' + this.name, { this.store.set('filter_' + this.name, {
name: 'filter_' + this.name, data: this.filterDefinitions}); name: 'filter_' + this.name,
data: this.filterDefinitions
});
} }
/** /**
* Takes an array of data and applies current filters * Takes an array of data and applies current filters
*/ */
@ -189,7 +194,7 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
if (!data) { if (!data) {
return filteredData; return filteredData;
} }
if (!this.filterDefinitions || !this.filterDefinitions.length){ if (!this.filterDefinitions || !this.filterDefinitions.length) {
return data; return data;
} }
data.forEach(newItem => { data.forEach(newItem => {
@ -200,7 +205,7 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
break; break;
} }
} }
if (!excluded){ if (!excluded) {
filteredData.push(newItem); filteredData.push(newItem);
} }
}); });
@ -214,27 +219,27 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
*/ */
private checkIncluded(item: V, filter: OsFilter): boolean { private checkIncluded(item: V, filter: OsFilter): boolean {
for (const option of filter.options) { for (const option of filter.options) {
if (typeof option === 'string' ){ if (typeof option === 'string') {
continue; continue;
} }
if (option.isActive) { if (option.isActive) {
if (option.condition === null ) { if (option.condition === null) {
return this.checkIncludedNegative(item, filter); return this.checkIncludedNegative(item, filter);
} }
if (item[filter.property] === undefined) { if (item[filter.property] === undefined) {
return false; return false;
} }
if (item[filter.property] instanceof BaseModel ) { if (item[filter.property] instanceof BaseModel) {
if (item[filter.property].id === option.condition){ if (item[filter.property].id === option.condition) {
return true; return true;
} }
} else if (item[filter.property] === option.condition){ } else if (item[filter.property] === option.condition) {
return true; return true;
} else if (item[filter.property].toString() === option.condition){ } else if (item[filter.property].toString() === option.condition) {
return true; return true;
} }
} }
}; }
return false; return false;
} }
@ -254,7 +259,7 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
} }
if (item[filter.property] === option.condition) { if (item[filter.property] === option.condition) {
return false; return false;
} else if (item[filter.property].toString() === option.condition){ } else if (item[filter.property].toString() === option.condition) {
return false; return false;
} }
} }
@ -271,20 +276,19 @@ export abstract class FilterListService<M extends BaseModel, V extends BaseViewM
} }
public get hasActiveFilters(): number { public get hasActiveFilters(): number {
if (!this.filterDefinitions || !this.filterDefinitions.length){ if (!this.filterDefinitions || !this.filterDefinitions.length) {
return 0; return 0;
} }
let filters = 0; let filters = 0;
for (const filter of this.filterDefinitions) { for (const filter of this.filterDefinitions) {
if (filter.count){ if (filter.count) {
filters += 1; filters += 1;
} }
}; }
return filters; return filters;
} }
public hasFilterOptions(): boolean { public hasFilterOptions(): boolean {
return (this.filterDefinitions && this.filterDefinitions.length) ? true : false; return this.filterDefinitions && this.filterDefinitions.length ? true : false;
} }
} }

View File

@ -55,7 +55,13 @@ export class HttpService {
* @param customHeader optional custom HTTP header of required * @param customHeader optional custom HTTP header of required
* @returns a promise containing a generic * @returns a promise containing a generic
*/ */
private async send<T>(path: string, method: HTTPMethod, data?: any, queryParams?: QueryParams, customHeader?: HttpHeaders): Promise<T> { private async send<T>(
path: string,
method: HTTPMethod,
data?: any,
queryParams?: QueryParams,
customHeader?: HttpHeaders
): Promise<T> {
// end early, if we are in history mode // end early, if we are in history mode
if (this.OSStatus.isInHistoryMode && method !== HTTPMethod.GET) { if (this.OSStatus.isInHistoryMode && method !== HTTPMethod.GET) {
throw this.handleError('You cannot make changes while in history mode'); throw this.handleError('You cannot make changes while in history mode');

View File

@ -8,7 +8,6 @@ import { Injectable } from '@angular/core';
providedIn: 'root' providedIn: 'root'
}) })
export class OpenSlidesStatusService { export class OpenSlidesStatusService {
/** /**
* Saves, if OpenSlides is in the history mode. * Saves, if OpenSlides is in the history mode.
*/ */
@ -37,6 +36,6 @@ export class OpenSlidesStatusService {
* Leaves the histroy mode * Leaves the histroy mode
*/ */
public leaveHistroyMode(): void { public leaveHistroyMode(): void {
this.historyMode = false; this.historyMode = false;
} }
} }

View File

@ -4,10 +4,12 @@ import { TimeTravelService } from './time-travel.service';
import { E2EImportsModule } from 'e2e-imports.module'; import { E2EImportsModule } from 'e2e-imports.module';
describe('TimeTravelService', () => { describe('TimeTravelService', () => {
beforeEach(() => TestBed.configureTestingModule({ beforeEach(() =>
imports: [E2EImportsModule], TestBed.configureTestingModule({
providers: [TimeTravelService] imports: [E2EImportsModule],
})); providers: [TimeTravelService]
})
);
it('should be created', () => { it('should be created', () => {
const service: TimeTravelService = TestBed.get(TimeTravelService); const service: TimeTravelService = TestBed.get(TimeTravelService);

View File

@ -52,7 +52,7 @@ export class TimeTravelService {
private DS: DataStoreService, private DS: DataStoreService,
private OSStatus: OpenSlidesStatusService, private OSStatus: OpenSlidesStatusService,
private OpenSlides: OpenSlidesService private OpenSlides: OpenSlidesService
) { } ) {}
/** /**
* Main entry point to set OpenSlides to another history point. * Main entry point to set OpenSlides to another history point.
@ -65,11 +65,11 @@ export class TimeTravelService {
for (const historyObject of fullDataHistory) { for (const historyObject of fullDataHistory) {
let collectionString: string; let collectionString: string;
let id: string; let id: string;
[collectionString, id] = historyObject.element_id.split(':') [collectionString, id] = historyObject.element_id.split(':');
if (historyObject.full_data) { if (historyObject.full_data) {
const targetClass = this.modelMapperService.getModelConstructor(collectionString); const targetClass = this.modelMapperService.getModelConstructor(collectionString);
await this.DS.add([new targetClass(historyObject.full_data)]) await this.DS.add([new targetClass(historyObject.full_data)]);
} else { } else {
await this.DS.remove(collectionString, [+id]); await this.DS.remove(collectionString, [+id]);
} }
@ -94,7 +94,7 @@ export class TimeTravelService {
* @returns the full history on the given date * @returns the full history on the given date
*/ */
private async getHistoryData(history: History): Promise<HistoryData[]> { private async getHistoryData(history: History): Promise<HistoryData[]> {
const historyUrl = '/core/history/' const historyUrl = '/core/history/';
const queryParams = { timestamp: Math.ceil(+history.unixtime) }; const queryParams = { timestamp: Math.ceil(+history.unixtime) };
return this.httpService.get<HistoryData[]>(environment.urlPrefix + historyUrl, null, queryParams); return this.httpService.get<HistoryData[]>(environment.urlPrefix + historyUrl, null, queryParams);
} }

View File

@ -71,11 +71,7 @@ export class WebsocketService {
* @param zone * @param zone
* @param translate * @param translate
*/ */
public constructor( public constructor(private matSnackBar: MatSnackBar, private zone: NgZone, public translate: TranslateService) {}
private matSnackBar: MatSnackBar,
private zone: NgZone,
public translate: TranslateService
) {}
/** /**
* Creates a new WebSocket connection and handles incomming events. * Creates a new WebSocket connection and handles incomming events.

View File

@ -51,7 +51,7 @@ interface ChoiceDialogData {
* it will be an array of numbers and optionally an action string for multichoice * it will be an array of numbers and optionally an action string for multichoice
* dialogs * dialogs
*/ */
export type ChoiceAnswer = undefined | { action?: string; items: number | number[]}; export type ChoiceAnswer = undefined | { action?: string; items: number | number[] };
/** /**
* A dialog with choice fields. * A dialog with choice fields.

View File

@ -5,19 +5,16 @@ describe('FilterMenuComponent', () => {
// TODO test won't work without a BaseViewModel // TODO test won't work without a BaseViewModel
// let component: FilterMenuComponent<V>; // let component: FilterMenuComponent<V>;
// let fixture: ComponentFixture<FilterMenuComponent<V>>; // let fixture: ComponentFixture<FilterMenuComponent<V>>;
// beforeEach(async(() => { // beforeEach(async(() => {
// TestBed.configureTestingModule({ // TestBed.configureTestingModule({
// declarations: [FilterMenuComponent] // declarations: [FilterMenuComponent]
// }).compileComponents(); // }).compileComponents();
// })); // }));
// beforeEach(() => { // beforeEach(() => {
// fixture = TestBed.createComponent(FilterMenuComponent); // fixture = TestBed.createComponent(FilterMenuComponent);
// component = fixture.componentInstance; // component = fixture.componentInstance;
// fixture.detectChanges(); // fixture.detectChanges();
// }); // });
// it('should create', () => { // it('should create', () => {
// expect(component).toBeTruthy(); // expect(component).toBeTruthy();
// }); // });

View File

@ -18,7 +18,6 @@ import { FilterListService, OsFilterOption } from '../../../../core/services/fil
styleUrls: ['./filter-menu.component.scss'] styleUrls: ['./filter-menu.component.scss']
}) })
export class FilterMenuComponent implements OnInit { export class FilterMenuComponent implements OnInit {
/** /**
* An event emitter to submit a desire to close this component * An event emitter to submit a desire to close this component
* TODO: Might be an easier way to do this * TODO: Might be an easier way to do this
@ -37,8 +36,7 @@ export class FilterMenuComponent implements OnInit {
* Constructor. Does nothing. * Constructor. Does nothing.
* @param service * @param service
*/ */
public constructor() { public constructor() {}
}
/** /**
* Directly closes again if no sorting is available * Directly closes again if no sorting is available
@ -53,12 +51,12 @@ export class FilterMenuComponent implements OnInit {
* Tests for escape key (to colose the sidebar) * Tests for escape key (to colose the sidebar)
* @param event * @param event
*/ */
public checkKeyEvent(event: KeyboardEvent) : void { public checkKeyEvent(event: KeyboardEvent): void {
if (event.key === 'Escape'){ if (event.key === 'Escape') {
this.dismissed.next(true) this.dismissed.next(true);
} }
} }
public isFilter(option: OsFilterOption) : boolean{ public isFilter(option: OsFilterOption): boolean {
return (typeof option === 'string') ? false : true; return typeof option === 'string' ? false : true;
} }
} }

View File

@ -3,7 +3,6 @@ import { E2EImportsModule } from 'e2e-imports.module';
import { OsSortBottomSheetComponent } from './os-sort-bottom-sheet.component'; import { OsSortBottomSheetComponent } from './os-sort-bottom-sheet.component';
describe('OsSortBottomSheetComponent', () => { describe('OsSortBottomSheetComponent', () => {
// let component: OsSortBottomSheetComponent<any>; // let component: OsSortBottomSheetComponent<any>;
let fixture: ComponentFixture<OsSortBottomSheetComponent<any>>; let fixture: ComponentFixture<OsSortBottomSheetComponent<any>>;

View File

@ -19,21 +19,21 @@ import { SortListService } from '../../../../core/services/sort-list.service';
styleUrls: ['./os-sort-bottom-sheet.component.scss'] styleUrls: ['./os-sort-bottom-sheet.component.scss']
}) })
export class OsSortBottomSheetComponent<V extends BaseViewModel> implements OnInit { export class OsSortBottomSheetComponent<V extends BaseViewModel> implements OnInit {
/** /**
* Constructor. Gets a reference to itself (for closing after interaction) * Constructor. Gets a reference to itself (for closing after interaction)
* @param data * @param data
* @param sheetRef * @param sheetRef
*/ */
public constructor( public constructor(
@Inject(MAT_BOTTOM_SHEET_DATA) public data: SortListService<V>, private sheetRef: MatBottomSheetRef ) { @Inject(MAT_BOTTOM_SHEET_DATA) public data: SortListService<V>,
} private sheetRef: MatBottomSheetRef
) {}
/** /**
* init fucntion. Closes inmediately if no sorting is available. * init fucntion. Closes inmediately if no sorting is available.
*/ */
public ngOnInit(): void { public ngOnInit(): void {
if (!this.data || !this.data.sortOptions || !this.data.sortOptions.options.length){ if (!this.data || !this.data.sortOptions || !this.data.sortOptions.options.length) {
throw new Error('No sorting available for a sorting list'); throw new Error('No sorting available for a sorting list');
} }
} }

View File

@ -9,7 +9,7 @@ describe('OsSortFilterBarComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -4,8 +4,8 @@ import { TranslateService } from '@ngx-translate/core';
import { BaseViewModel } from '../../../site/base/base-view-model'; import { BaseViewModel } from '../../../site/base/base-view-model';
import { OsSortBottomSheetComponent } from './os-sort-bottom-sheet/os-sort-bottom-sheet.component'; import { OsSortBottomSheetComponent } from './os-sort-bottom-sheet/os-sort-bottom-sheet.component';
import { FilterMenuComponent} from './filter-menu/filter-menu.component'; import { FilterMenuComponent } from './filter-menu/filter-menu.component';
import { OsSortingItem } from '../../../core/services/sort-list.service' import { OsSortingItem } from '../../../core/services/sort-list.service';
import { SortListService } from '../../../core/services/sort-list.service'; import { SortListService } from '../../../core/services/sort-list.service';
import { ViewportService } from '../../../core/services/viewport.service'; import { ViewportService } from '../../../core/services/viewport.service';
@ -29,7 +29,6 @@ import { ViewportService } from '../../../core/services/viewport.service';
styleUrls: ['./os-sort-filter-bar.component.scss'] styleUrls: ['./os-sort-filter-bar.component.scss']
}) })
export class OsSortFilterBarComponent<V extends BaseViewModel> { export class OsSortFilterBarComponent<V extends BaseViewModel> {
/** /**
* The currently active sorting service for the list view * The currently active sorting service for the list view
*/ */
@ -68,7 +67,11 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
* @param vp * @param vp
* @param bottomSheet * @param bottomSheet
*/ */
public constructor(public translate: TranslateService, public vp: ViewportService, private bottomSheet: MatBottomSheet) { public constructor(
public translate: TranslateService,
public vp: ViewportService,
private bottomSheet: MatBottomSheet
) {
this.filterMenu = new FilterMenuComponent(); this.filterMenu = new FilterMenuComponent();
} }
@ -77,9 +80,7 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
*/ */
public openSortDropDown(): void { public openSortDropDown(): void {
if (this.vp.isMobile) { if (this.vp.isMobile) {
const bottomSheetRef = this.bottomSheet.open(OsSortBottomSheetComponent, const bottomSheetRef = this.bottomSheet.open(OsSortBottomSheetComponent, { data: this.sortService });
{ data: this.sortService }
);
bottomSheetRef.afterDismissed().subscribe(result => { bottomSheetRef.afterDismissed().subscribe(result => {
if (result) { if (result) {
this.sortService.sortProperty = result; this.sortService.sortProperty = result;
@ -92,7 +93,7 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
* Listen to keypresses on the quick-search input * Listen to keypresses on the quick-search input
*/ */
public applySearch(event: KeyboardEvent, value?: string): void { public applySearch(event: KeyboardEvent, value?: string): void {
if (event.key === 'Escape' ) { if (event.key === 'Escape') {
this.searchFieldChange.emit(''); this.searchFieldChange.emit('');
this.isSearchBar = false; this.isSearchBar = false;
} else { } else {
@ -104,16 +105,16 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
* Checks if there is an active SortService present * Checks if there is an active SortService present
*/ */
public get hasSorting(): boolean { public get hasSorting(): boolean {
return (this.sortService && this.sortService.isActive); return this.sortService && this.sortService.isActive;
} }
/** /**
* Checks if there is an active FilterService present * Checks if there is an active FilterService present
*/ */
public get hasFilters(): boolean { public get hasFilters(): boolean {
if (this.filterService && this.filterService.hasFilterOptions()){ if (this.filterService && this.filterService.hasFilterOptions()) {
return true; return true;
}; }
return false; return false;
} }
@ -122,7 +123,7 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
* @param option * @param option
*/ */
public getSortIcon(option: OsSortingItem<V>): string { public getSortIcon(option: OsSortingItem<V>): string {
if (this.sortService.sortProperty !== option.property){ if (this.sortService.sortProperty !== option.property) {
return ''; return '';
} }
return this.sortService.ascending ? 'arrow_downward' : 'arrow_upward'; return this.sortService.ascending ? 'arrow_downward' : 'arrow_upward';
@ -133,7 +134,7 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
* the property is used. * the property is used.
* @param option * @param option
*/ */
public getSortLabel(option: OsSortingItem<V>) : string { public getSortLabel(option: OsSortingItem<V>): string {
if (option.label) { if (option.label) {
return option.label; return option.label;
} }
@ -146,7 +147,7 @@ export class OsSortFilterBarComponent<V extends BaseViewModel> {
* that input applied * that input applied
*/ */
public toggleSearchBar(): void { public toggleSearchBar(): void {
if (!this.isSearchBar){ if (!this.isSearchBar) {
this.isSearchBar = true; this.isSearchBar = true;
} else { } else {
this.searchFieldChange.emit(''); this.searchFieldChange.emit('');

View File

@ -78,7 +78,7 @@ export class SortingListComponent implements OnInit, OnDestroy {
if (newValues instanceof Observable) { if (newValues instanceof Observable) {
this.inputSubscription = newValues.subscribe(values => { this.inputSubscription = newValues.subscribe(values => {
this.updateArray(values); this.updateArray(values);
}) });
} else { } else {
this.inputSubscription = null; this.inputSubscription = null;
this.updateArray(newValues); this.updateArray(newValues);

View File

@ -10,7 +10,12 @@ import { BehaviorSubject } from 'rxjs';
* A test model for the sorting * A test model for the sorting
*/ */
class TestModel implements Identifiable, Displayable { class TestModel implements Identifiable, Displayable {
public constructor(public id: number, public name: string, public weight: number, public parent_id: number | null){} public constructor(
public id: number,
public name: string,
public weight: number,
public parent_id: number | null
) {}
public getTitle(): string { public getTitle(): string {
return this.name; return this.name;

View File

@ -3,11 +3,10 @@ import { Poll } from './poll';
import { AgendaBaseModel } from '../base/agenda-base-model'; import { AgendaBaseModel } from '../base/agenda-base-model';
import { SearchRepresentation } from '../../../core/services/search.service'; import { SearchRepresentation } from '../../../core/services/search.service';
export const assignmentPhase = [ export const assignmentPhase = [
{key: 0, name: 'Searching for candidates'}, { key: 0, name: 'Searching for candidates' },
{key: 1, name: 'Voting'}, { key: 1, name: 'Voting' },
{key: 2, name: 'Finished'} { key: 2, name: 'Finished' }
]; ];
/** /**

View File

@ -46,7 +46,6 @@ export abstract class AgendaBaseModel extends ProjectableBaseModel implements Ag
*/ */
public abstract formatForSearch(): SearchRepresentation; public abstract formatForSearch(): SearchRepresentation;
/** /**
* Should return the URL to the detail view. Used for the agenda, that the * Should return the URL to the detail view. Used for the agenda, that the
* user can navigate to the content object. * user can navigate to the content object.

View File

@ -1,4 +1,4 @@
import { DetailNavigable } from "./detail-navigable"; import { DetailNavigable } from './detail-navigable';
/** /**
* An Interface for all extra information needed for content objects of items. * An Interface for all extra information needed for content objects of items.

View File

@ -98,7 +98,7 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
// topicS, motionS, (media)fileS, motion blockS, commentS, personal noteS, projectorS, messageS, countdownS, ...) // topicS, motionS, (media)fileS, motion blockS, commentS, personal noteS, projectorS, messageS, countdownS, ...)
// Just categorIES need to overwrite this... // Just categorIES need to overwrite this...
} else { } else {
return this._verboseName return this._verboseName;
} }
} }

View File

@ -1,4 +1,3 @@
import { BaseModel } from '../base/base-model'; import { BaseModel } from '../base/base-model';
/** /**

View File

@ -86,7 +86,7 @@ export class Motion extends AgendaBaseModel {
* @override * @override
*/ */
public formatForSearch(): SearchRepresentation { public formatForSearch(): SearchRepresentation {
let searchValues = [this.title, this.text, this.reason] let searchValues = [this.title, this.text, this.reason];
if (this.amendment_paragraphs) { if (this.amendment_paragraphs) {
searchValues = searchValues.concat(this.amendment_paragraphs.filter(x => !!x)); searchValues = searchValues.concat(this.amendment_paragraphs.filter(x => !!x));
} }

View File

@ -62,7 +62,7 @@ export class WorkflowState extends Deserializer {
* Checks if a workflowstate has no 'next state' left, and is final * Checks if a workflowstate has no 'next state' left, and is final
*/ */
public get isFinalState(): boolean { public get isFinalState(): boolean {
if (!this.next_states_id || !this.next_states_id.length ){ if (!this.next_states_id || !this.next_states_id.length) {
return true; return true;
} }
if (this.next_states_id.length === 1 && this.next_states_id[0] === 0) { if (this.next_states_id.length === 1 && this.next_states_id[0] === 0) {

View File

@ -4,7 +4,10 @@ import { Topic } from '../../shared/models/topics/topic';
export const AgendaAppConfig: AppConfig = { export const AgendaAppConfig: AppConfig = {
name: 'agenda', name: 'agenda',
models: [{ collectionString: 'agenda/item', model: Item }, { collectionString: 'topics/topic', model: Topic, searchOrder: 1 }], models: [
{ collectionString: 'agenda/item', model: Item },
{ collectionString: 'topics/topic', model: Topic, searchOrder: 1 }
],
mainMenuEntries: [ mainMenuEntries: [
{ {
route: '/agenda', route: '/agenda',

View File

@ -12,7 +12,7 @@ import { ItemInfoDialogComponent } from './components/item-info-dialog/item-info
*/ */
@NgModule({ @NgModule({
imports: [CommonModule, AgendaRoutingModule, SharedModule], imports: [CommonModule, AgendaRoutingModule, SharedModule],
entryComponents: [ ItemInfoDialogComponent ], entryComponents: [ItemInfoDialogComponent],
declarations: [AgendaListComponent, TopicDetailComponent, ItemInfoDialogComponent] declarations: [AgendaListComponent, TopicDetailComponent, ItemInfoDialogComponent]
}) })
export class AgendaModule {} export class AgendaModule {}

View File

@ -1,5 +1,4 @@
.os-listview-table { .os-listview-table {
/** Title */ /** Title */
.mat-column-title { .mat-column-title {
padding-left: 26px; padding-left: 26px;
@ -27,7 +26,6 @@
height: $icon-size; height: $icon-size;
width: $icon-size; width: $icon-size;
} }
} }
} }

View File

@ -10,7 +10,6 @@ import { ListViewBaseComponent } from 'app/site/base/list-view-base';
import { PromptService } from '../../../../core/services/prompt.service'; import { PromptService } from '../../../../core/services/prompt.service';
import { ViewItem } from '../../models/view-item'; import { ViewItem } from '../../models/view-item';
import { AgendaCsvExportService } from '../../services/agenda-csv-export.service'; import { AgendaCsvExportService } from '../../services/agenda-csv-export.service';
import { ItemInfoDialogComponent } from '../item-info-dialog/item-info-dialog.component'; import { ItemInfoDialogComponent } from '../item-info-dialog/item-info-dialog.component';
import { ViewportService } from 'app/core/services/viewport.service'; import { ViewportService } from 'app/core/services/viewport.service';

View File

@ -73,7 +73,7 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
private itemRepo: AgendaRepositoryService, private itemRepo: AgendaRepositoryService,
private op: OperatorService private op: OperatorService
) { ) {
super(title, translate, snackBar) super(title, translate, snackBar);
this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) }); this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) });
this.getAgendaItemByUrl(); this.getAgendaItemByUrl();
} }

View File

@ -44,14 +44,14 @@ export class ViewItem extends BaseViewModel {
return ''; return '';
} }
public get verboseType() : string { public get verboseType(): string {
if (this.item && this.item.verboseType) { if (this.item && this.item.verboseType) {
return this.item.verboseType; return this.item.verboseType;
} }
return ''; return '';
} }
public get verboseCsvType() : string { public get verboseCsvType(): string {
if (this.item) { if (this.item) {
return this.item.verboseCsvType; return this.item.verboseCsvType;
} }

View File

@ -11,10 +11,7 @@ describe('AgendaCsvExportService', () => {
}); });
}); });
it('should be created', inject( it('should be created', inject([AgendaCsvExportService], (service: AgendaCsvExportService) => {
[AgendaCsvExportService], expect(service).toBeTruthy();
(service: AgendaCsvExportService) => { }));
expect(service).toBeTruthy();
}
));
}); });

View File

@ -12,7 +12,6 @@ import { ViewItem } from '../models/view-item';
providedIn: 'root' providedIn: 'root'
}) })
export class AgendaCsvExportService { export class AgendaCsvExportService {
/** /**
* Does nothing. * Does nothing.
* *
@ -27,10 +26,14 @@ export class AgendaCsvExportService {
* @param Agendas Agendas to export * @param Agendas Agendas to export
*/ */
public exportItemList(items: ViewItem[]): void { public exportItemList(items: ViewItem[]): void {
this.csvExport.export(items, this.csvExport.export(
items,
[ [
{ label: 'Title', map: viewItem => viewItem.getTitle() }, { label: 'Title', map: viewItem => viewItem.getTitle() },
{ label: 'Text', map: viewItem => viewItem.contentObject ? viewItem.contentObject.getCSVExportText() : '' }, {
label: 'Text',
map: viewItem => (viewItem.contentObject ? viewItem.contentObject.getCSVExportText() : '')
},
{ label: 'Duration', property: 'duration' }, { label: 'Duration', property: 'duration' },
{ label: 'Comment', property: 'comment' }, { label: 'Comment', property: 'comment' },
{ label: 'Item type', property: 'verboseCsvType' } { label: 'Item type', property: 'verboseCsvType' }

View File

@ -1,16 +1,15 @@
import { Injectable } from "@angular/core"; import { Injectable } from '@angular/core';
import { FilterListService, OsFilter, OsFilterOption } from "../../../core/services/filter-list.service"; import { FilterListService, OsFilter, OsFilterOption } from '../../../core/services/filter-list.service';
import { Item, itemVisibilityChoices } from "../../../shared/models/agenda/item"; import { Item, itemVisibilityChoices } from '../../../shared/models/agenda/item';
import { ViewItem } from "../models/view-item"; import { ViewItem } from '../models/view-item';
import { StorageService } from "app/core/services/storage.service"; import { StorageService } from 'app/core/services/storage.service';
import { AgendaRepositoryService } from "./agenda-repository.service"; import { AgendaRepositoryService } from './agenda-repository.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class AgendaFilterListService extends FilterListService<Item, ViewItem> { export class AgendaFilterListService extends FilterListService<Item, ViewItem> {
protected name = 'Agenda'; protected name = 'Agenda';
public filterOptions: OsFilter[] = []; public filterOptions: OsFilter[] = [];
@ -22,22 +21,22 @@ export class AgendaFilterListService extends FilterListService<Item, ViewItem> {
*/ */
public constructor(store: StorageService, repo: AgendaRepositoryService) { public constructor(store: StorageService, repo: AgendaRepositoryService) {
super(store, repo); super(store, repo);
this.filterOptions = [{ this.filterOptions = [
{
label: 'Visibility', label: 'Visibility',
property: 'type', property: 'type',
options: this.createVisibilityFilterOptions() options: this.createVisibilityFilterOptions()
}, { },
{
label: 'Hidden Status', label: 'Hidden Status',
property: 'done', property: 'done',
options: [ options: [{ label: 'Open', condition: false }, { label: 'Closed', condition: true }]
{label: 'Open', condition: false}, }
{label: 'Closed', condition: true} ];
]
}];
} }
private createVisibilityFilterOptions(): OsFilterOption[] { private createVisibilityFilterOptions(): OsFilterOption[] {
const options = []; const options = [];
itemVisibilityChoices.forEach(choice => { itemVisibilityChoices.forEach(choice => {
options.push({ options.push({
condition: choice.key as number, condition: choice.key as number,
@ -46,6 +45,4 @@ export class AgendaFilterListService extends FilterListService<Item, ViewItem> {
}); });
return options; return options;
} }
} }

View File

@ -187,7 +187,7 @@ export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
* deleted (right now) * deleted (right now)
*/ */
public delete(item: ViewItem): Promise<void> { public delete(item: ViewItem): Promise<void> {
throw new Error("Method not implemented."); throw new Error('Method not implemented.');
} }
/** /**
@ -196,7 +196,7 @@ export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
* Agenda items are created implicitly and do not have on create functions * Agenda items are created implicitly and do not have on create functions
*/ */
public async create(item: Item): Promise<Identifiable> { public async create(item: Item): Promise<Identifiable> {
throw new Error("Method not implemented."); throw new Error('Method not implemented.');
} }
/** /**

View File

@ -7,7 +7,8 @@ describe('TopicRepositoryService', () => {
beforeEach(() => beforeEach(() =>
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule] imports: [E2EImportsModule]
})); })
);
it('should be created', () => { it('should be created', () => {
const service: TopicRepositoryService = TestBed.get(TopicRepositoryService); const service: TopicRepositoryService = TestBed.get(TopicRepositoryService);

View File

@ -10,8 +10,6 @@ import { PromptService } from '../../../core/services/prompt.service';
import { ViewAssignment } from '../models/view-assignment'; import { ViewAssignment } from '../models/view-assignment';
import { AssignmentSortListService } from '../services/assignment-sort-list.service'; import { AssignmentSortListService } from '../services/assignment-sort-list.service';
/** /**
* Listview for the assignments * Listview for the assignments
* *
@ -22,7 +20,6 @@ import { AssignmentSortListService } from '../services/assignment-sort-list.serv
styleUrls: ['./assignment-list.component.scss'] styleUrls: ['./assignment-list.component.scss']
}) })
export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignment> implements OnInit { export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignment> implements OnInit {
/** /**
* Constructor. * Constructor.
* @param titleService * @param titleService

View File

@ -1,27 +1,27 @@
import { Injectable } from "@angular/core"; import { Injectable } from '@angular/core';
import { AssignmentRepositoryService } from "./assignment-repository.service";
import { Assignment, assignmentPhase } from "../../../shared/models/assignments/assignment";
import { FilterListService, OsFilter, OsFilterOption } from "../../../core/services/filter-list.service";
import { StorageService } from "app/core/services/storage.service";
import { ViewAssignment } from "../models/view-assignment";
import { AssignmentRepositoryService } from './assignment-repository.service';
import { Assignment, assignmentPhase } from '../../../shared/models/assignments/assignment';
import { FilterListService, OsFilter, OsFilterOption } from '../../../core/services/filter-list.service';
import { StorageService } from 'app/core/services/storage.service';
import { ViewAssignment } from '../models/view-assignment';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class AssignmentFilterListService extends FilterListService<Assignment, ViewAssignment> { export class AssignmentFilterListService extends FilterListService<Assignment, ViewAssignment> {
protected name = 'Assignment'; protected name = 'Assignment';
public filterOptions: OsFilter[]; public filterOptions: OsFilter[];
public constructor(store: StorageService, assignmentRepo: AssignmentRepositoryService) { public constructor(store: StorageService, assignmentRepo: AssignmentRepositoryService) {
super(store, assignmentRepo); super(store, assignmentRepo);
this.filterOptions = [{ this.filterOptions = [
property: 'phase', {
options: this.createPhaseOptions() property: 'phase',
}]; options: this.createPhaseOptions()
}
];
} }
private createPhaseOptions(): OsFilterOption[] { private createPhaseOptions(): OsFilterOption[] {

View File

@ -24,10 +24,7 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
* @param DS The DataStore * @param DS The DataStore
* @param mapperService Maps collection strings to classes * @param mapperService Maps collection strings to classes
*/ */
public constructor( public constructor(DS: DataStoreService, mapperService: CollectionStringModelMapperService) {
DS: DataStoreService,
mapperService: CollectionStringModelMapperService
) {
super(DS, mapperService, Assignment, [User, Item, Tag]); super(DS, mapperService, Assignment, [User, Item, Tag]);
} }

View File

@ -6,7 +6,6 @@ import { ViewAssignment } from '../models/view-assignment';
providedIn: 'root' providedIn: 'root'
}) })
export class AssignmentSortListService extends SortListService<ViewAssignment> { export class AssignmentSortListService extends SortListService<ViewAssignment> {
public sortOptions: OsSortingDefinition<ViewAssignment> = { public sortOptions: OsSortingDefinition<ViewAssignment> = {
sortProperty: 'assignment', sortProperty: 'assignment',
sortAscending: true, sortAscending: true,
@ -18,5 +17,4 @@ export class AssignmentSortListService extends SortListService<ViewAssignment> {
] ]
}; };
protected name = 'Assignment'; protected name = 'Assignment';
} }

View File

@ -9,7 +9,7 @@ export interface ModelEntry {
export interface SearchableModelEntry { export interface SearchableModelEntry {
collectionString: string; collectionString: string;
model: new (...args: any[]) => (BaseModel & Searchable); model: new (...args: any[]) => BaseModel & Searchable;
searchOrder: number; searchOrder: number;
} }

View File

@ -37,7 +37,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
protected DS: DataStoreService, protected DS: DataStoreService,
protected collectionStringModelMapperService: CollectionStringModelMapperService, protected collectionStringModelMapperService: CollectionStringModelMapperService,
protected baseModelCtor: ModelConstructor<M>, protected baseModelCtor: ModelConstructor<M>,
protected depsModelCtors?: ModelConstructor<BaseModel>[], protected depsModelCtors?: ModelConstructor<BaseModel>[]
) { ) {
super(); super();
this.setup(); this.setup();

View File

@ -80,7 +80,7 @@ export abstract class ListViewBaseComponent<V extends BaseViewModel> extends Bas
this.sort.sort(newSort); this.sort.sort(newSort);
} }
public onFilterData(filteredDataSource: MatTableDataSource<V>) : void { public onFilterData(filteredDataSource: MatTableDataSource<V>): void {
this.dataSource = filteredDataSource; this.dataSource = filteredDataSource;
this.dataSource.paginator = this.paginator; this.dataSource.paginator = this.paginator;
} }

View File

@ -12,7 +12,7 @@ import { DataStoreService } from 'app/core/services/data-store.service';
import { SearchService, SearchModel, SearchResult } from 'app/core/services/search.service'; import { SearchService, SearchModel, SearchResult } from 'app/core/services/search.service';
import { BaseViewComponent } from '../../../base/base-view'; import { BaseViewComponent } from '../../../base/base-view';
type SearchModelEnabled = SearchModel & { enabled: boolean; }; type SearchModelEnabled = SearchModel & { enabled: boolean };
/** /**
* Component for the full search text. * Component for the full search text.
@ -78,16 +78,16 @@ export class SearchComponent extends BaseViewComponent implements OnInit {
super(title, translate, matSnackBar); super(title, translate, matSnackBar);
this.quickSearchform = new FormGroup({ query: new FormControl([]) }); this.quickSearchform = new FormGroup({ query: new FormControl([]) });
this.registeredModels = this.searchService.getRegisteredModels().map(rm => ({...rm, enabled: true})); this.registeredModels = this.searchService.getRegisteredModels().map(rm => ({ ...rm, enabled: true }));
this.DS.changedOrDeletedObservable.pipe(auditTime(1)).subscribe(() => this.search()); this.DS.changedOrDeletedObservable.pipe(auditTime(1)).subscribe(() => this.search());
this.quickSearchSubject.pipe(debounceTime(250)).subscribe(query => this.search(query)); this.quickSearchSubject.pipe(debounceTime(250)).subscribe(query => this.search(query));
} }
/** /**
* Take the search query from the URL and does the initial search. * Take the search query from the URL and does the initial search.
*/ */
public ngOnInit(): void { public ngOnInit(): void {
super.setTitle('Search'); super.setTitle('Search');
this.query = this.activatedRoute.snapshot.queryParams.query; this.query = this.activatedRoute.snapshot.queryParams.query;
this.quickSearchform.get('query').setValue(this.query); this.quickSearchform.get('query').setValue(this.query);

View File

@ -4,9 +4,11 @@ import { DurationService } from './duration.service';
import { E2EImportsModule } from 'e2e-imports.module'; import { E2EImportsModule } from 'e2e-imports.module';
describe('DurationService', () => { describe('DurationService', () => {
beforeEach(() => TestBed.configureTestingModule({ beforeEach(() =>
imports: [E2EImportsModule] TestBed.configureTestingModule({
})); imports: [E2EImportsModule]
})
);
it('should be created', () => { it('should be created', () => {
const service: DurationService = TestBed.get(DurationService); const service: DurationService = TestBed.get(DurationService);

View File

@ -96,7 +96,7 @@ export class ViewHistory extends BaseViewModel {
* @returns the CollectionString to the model * @returns the CollectionString to the model
*/ */
public getCollectionString(): string { public getCollectionString(): string {
return this.element_id.split(":")[0] return this.element_id.split(':')[0];
} }
/** /**
@ -104,7 +104,7 @@ export class ViewHistory extends BaseViewModel {
* @returns a model id * @returns a model id
*/ */
public getModelID(): number { public getModelID(): number {
return +this.element_id.split(":")[1] return +this.element_id.split(':')[1];
} }
/** /**

View File

@ -93,15 +93,18 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
// Get the login data. Save information to the login data service. If there is an // Get the login data. Save information to the login data service. If there is an
// error, ignore it. // error, ignore it.
// TODO: This has to be caught by the offline service // TODO: This has to be caught by the offline service
this.http.get<any>(environment.urlPrefix + '/users/login/').then(response => { this.http.get<any>(environment.urlPrefix + '/users/login/').then(
if (response.info_text) { response => {
this.installationNotice = this.matSnackBar.open(response.info_text, this.translate.instant('OK'), { if (response.info_text) {
duration: 5000 this.installationNotice = this.matSnackBar.open(response.info_text, this.translate.instant('OK'), {
}); duration: 5000
} });
this.loginDataService.setPrivacyPolicy(response.privacy_policy); }
this.loginDataService.setLegalNotice(response.legal_notice); this.loginDataService.setPrivacyPolicy(response.privacy_policy);
}, () => {}); this.loginDataService.setLegalNotice(response.legal_notice);
},
() => {}
);
} }
public ngOnDestroy(): void { public ngOnDestroy(): void {

View File

@ -15,11 +15,9 @@ describe('ResetPasswordConfirmComponent', () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule],
providers: [ providers: [{ provide: MatSnackBar, useValue: spy }]
{provide: MatSnackBar, useValue: spy}
]
}).compileComponents(); }).compileComponents();
matSnackBarSpy = TestBed.get(MatSnackBar) matSnackBarSpy = TestBed.get(MatSnackBar);
})); }));
beforeEach(() => { beforeEach(() => {

View File

@ -10,7 +10,7 @@ describe('MediaUploadComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule],
declarations: [MediaUploadComponent], declarations: [MediaUploadComponent]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -27,7 +27,7 @@ interface FileData {
@Component({ @Component({
selector: 'os-media-upload', selector: 'os-media-upload',
templateUrl: './media-upload.component.html', templateUrl: './media-upload.component.html',
styleUrls: ['./media-upload.component.scss'], styleUrls: ['./media-upload.component.scss']
}) })
export class MediaUploadComponent extends BaseViewComponent implements OnInit { export class MediaUploadComponent extends BaseViewComponent implements OnInit {
/** /**
@ -168,7 +168,7 @@ export class MediaUploadComponent extends BaseViewComponent implements OnInit {
filename: file.name, filename: file.name,
title: file.name, title: file.name,
uploader_id: this.op.user.id, uploader_id: this.op.user.id,
hidden: false, hidden: false
}; };
this.uploadList.data.push(newFile); this.uploadList.data.push(newFile);

View File

@ -26,7 +26,6 @@
white-space: pre-line !important; white-space: pre-line !important;
} }
.edit-file-form { .edit-file-form {
mat-form-field + mat-form-field { mat-form-field + mat-form-field {
margin: 1em; margin: 1em;

View File

@ -16,16 +16,13 @@ import { MediafileFilterListService } from '../../services/mediafile-filter.serv
import { MediafilesSortListService } from '../../services/mediafiles-sort-list.service'; import { MediafilesSortListService } from '../../services/mediafiles-sort-list.service';
import { ViewportService } from 'app/core/services/viewport.service'; import { ViewportService } from 'app/core/services/viewport.service';
/** /**
* Lists all the uploaded files. * Lists all the uploaded files.
*/ */
@Component({ @Component({
selector: 'os-mediafile-list', selector: 'os-mediafile-list',
templateUrl: './mediafile-list.component.html', templateUrl: './mediafile-list.component.html',
styleUrls: ['./mediafile-list.component.scss'], styleUrls: ['./mediafile-list.component.scss']
}) })
export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile> implements OnInit { export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile> implements OnInit {
/** /**
@ -106,7 +103,7 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
this.fileEditForm = new FormGroup({ this.fileEditForm = new FormGroup({
title: new FormControl('', Validators.required), title: new FormControl('', Validators.required),
hidden: new FormControl(), hidden: new FormControl()
}); });
this.filterService.filter().subscribe(filteredData => { this.filterService.filter().subscribe(filteredData => {
@ -163,7 +160,7 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
} }
const updateData = new Mediafile({ const updateData = new Mediafile({
title: this.fileEditForm.value.title, title: this.fileEditForm.value.title,
hidden: this.fileEditForm.value.hidden, hidden: this.fileEditForm.value.hidden
}); });
this.repo.update(updateData, this.fileToEdit).then(() => { this.repo.update(updateData, this.fileToEdit).then(() => {
@ -269,7 +266,7 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
*/ */
public getColumnDefinition(): string[] { public getColumnDefinition(): string[] {
const columns = this.vp.isMobile ? this.displayedColumnsMobile : this.displayedColumnsDesktop; const columns = this.vp.isMobile ? this.displayedColumnsMobile : this.displayedColumnsDesktop;
if (this.isMultiSelect){ if (this.isMultiSelect) {
return ['selector'].concat(columns); return ['selector'].concat(columns);
} }
return columns; return columns;

View File

@ -5,11 +5,11 @@ import { MediaUploadComponent } from './components/media-upload/media-upload.com
const routes: Routes = [ const routes: Routes = [
{ path: '', component: MediafileListComponent }, { path: '', component: MediafileListComponent },
{ path: 'upload', component: MediaUploadComponent }, { path: 'upload', component: MediaUploadComponent }
]; ];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule], exports: [RouterModule]
}) })
export class MediafilesRoutingModule {} export class MediafilesRoutingModule {}

View File

@ -122,5 +122,4 @@ export class ViewMediafile extends BaseViewModel {
public is_hidden(): boolean { public is_hidden(): boolean {
return this._mediafile.hidden; return this._mediafile.hidden;
} }
} }

View File

@ -8,7 +8,7 @@ import { ViewMediafile } from '../models/view-mediafile';
* The structure of an image config object * The structure of an image config object
*/ */
interface ImageConfigObject { interface ImageConfigObject {
display_name: string display_name: string;
key: string; key: string;
path: string; path: string;
} }
@ -17,7 +17,7 @@ interface ImageConfigObject {
* The structure of a font config * The structure of a font config
*/ */
interface FontConfigObject { interface FontConfigObject {
display_name: string display_name: string;
default: string; default: string;
path: string; path: string;
} }
@ -26,10 +26,10 @@ interface FontConfigObject {
* Holds the required structure of the manage payload * Holds the required structure of the manage payload
*/ */
interface ManagementPayload { interface ManagementPayload {
id: number, id: number;
key?: string, key?: string;
default?: string, default?: string;
value: ImageConfigObject | FontConfigObject value: ImageConfigObject | FontConfigObject;
} }
/** /**
@ -38,7 +38,7 @@ interface ManagementPayload {
* Declaring images as logos (web, projector, pdf, ...) is handles here. * Declaring images as logos (web, projector, pdf, ...) is handles here.
*/ */
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root'
}) })
export class MediaManageService { export class MediaManageService {
/** /**
@ -61,7 +61,7 @@ export class MediaManageService {
const restPath = `rest/core/config/${action}`; const restPath = `rest/core/config/${action}`;
const config = this.getMediaConfig(action); const config = this.getMediaConfig(action);
const path = (config.path !== file.downloadUrl) ? file.downloadUrl : ''; const path = config.path !== file.downloadUrl ? file.downloadUrl : '';
// Create the payload that the server requires to manage a mediafile // Create the payload that the server requires to manage a mediafile
const payload: ManagementPayload = { const payload: ManagementPayload = {
@ -74,7 +74,7 @@ export class MediaManageService {
default: (config as FontConfigObject).default, default: (config as FontConfigObject).default,
path: path path: path
} }
} };
return this.httpService.put<void>(restPath, payload); return this.httpService.put<void>(restPath, payload);
} }

View File

@ -1,35 +1,36 @@
import { Injectable } from "@angular/core"; import { Injectable } from '@angular/core';
import { FilterListService } from "../../../core/services/filter-list.service"; import { FilterListService } from '../../../core/services/filter-list.service';
import { Mediafile } from "../../../shared/models/mediafiles/mediafile"; import { Mediafile } from '../../../shared/models/mediafiles/mediafile';
import { ViewMediafile } from "../models/view-mediafile"; import { ViewMediafile } from '../models/view-mediafile';
import { StorageService } from "app/core/services/storage.service"; import { StorageService } from 'app/core/services/storage.service';
import { MediafileRepositoryService } from "./mediafile-repository.service"; import { MediafileRepositoryService } from './mediafile-repository.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class MediafileFilterListService extends FilterListService<Mediafile, ViewMediafile> { export class MediafileFilterListService extends FilterListService<Mediafile, ViewMediafile> {
protected name = 'Mediafile'; protected name = 'Mediafile';
public filterOptions = [{ public filterOptions = [
property: 'is_hidden', label: 'Hidden', {
options: [ property: 'is_hidden',
{ condition: true, label: 'is hidden' }, label: 'Hidden',
{ condition: false, label: 'is not hidden', isActive: true } options: [
] { condition: true, label: 'is hidden' },
} { condition: false, label: 'is not hidden', isActive: true }
// , { TODO: is_pdf is not yet implemented on mediafile side ]
// property: 'is_pdf', isActive: false, label: 'PDF', }
// options: [ // , { TODO: is_pdf is not yet implemented on mediafile side
// {condition: true, label: 'is a PDF'}, // property: 'is_pdf', isActive: false, label: 'PDF',
// {condition: false, label: 'is not a PDF'} // options: [
// ] // {condition: true, label: 'is a PDF'},
// } // {condition: false, label: 'is not a PDF'}
// ]
// }
]; ];
public constructor(store: StorageService, repo: MediafileRepositoryService){ public constructor(store: StorageService, repo: MediafileRepositoryService) {
super(store, repo); super(store, repo);
} }
} }

View File

@ -15,7 +15,7 @@ import { HttpHeaders } from '@angular/common/http';
* Repository for MediaFiles * Repository for MediaFiles
*/ */
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root'
}) })
export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Mediafile> { export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Mediafile> {
/** /**
@ -29,7 +29,7 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
DS: DataStoreService, DS: DataStoreService,
mapperService: CollectionStringModelMapperService, mapperService: CollectionStringModelMapperService,
private dataSend: DataSendService, private dataSend: DataSendService,
private httpService: HttpService, private httpService: HttpService
) { ) {
super(DS, mapperService, Mediafile, [User]); super(DS, mapperService, Mediafile, [User]);
} }

View File

@ -2,12 +2,10 @@ import { Injectable } from '@angular/core';
import { SortListService, OsSortingDefinition } from '../../../core/services/sort-list.service'; import { SortListService, OsSortingDefinition } from '../../../core/services/sort-list.service';
import { ViewMediafile } from '../models/view-mediafile'; import { ViewMediafile } from '../models/view-mediafile';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class MediafilesSortListService extends SortListService<ViewMediafile> { export class MediafilesSortListService extends SortListService<ViewMediafile> {
public sortOptions: OsSortingDefinition<ViewMediafile> = { public sortOptions: OsSortingDefinition<ViewMediafile> = {
sortProperty: 'title', sortProperty: 'title',
sortAscending: true, sortAscending: true,

View File

@ -94,7 +94,7 @@ export class CategoryListComponent extends BaseViewComponent implements OnInit {
* @param viewCategory * @param viewCategory
*/ */
public keyDownFunction(event: KeyboardEvent, viewCategory?: ViewCategory): void { public keyDownFunction(event: KeyboardEvent, viewCategory?: ViewCategory): void {
if (event.key === "Enter") { if (event.key === 'Enter') {
if (viewCategory) { if (viewCategory) {
this.onSaveButton(viewCategory); this.onSaveButton(viewCategory);
} else { } else {

View File

@ -6,7 +6,6 @@ import { ViewChild, Component } from '@angular/core';
import { ViewMotion } from '../../models/view-motion'; import { ViewMotion } from '../../models/view-motion';
describe('ManageSubmittersComponent', () => { describe('ManageSubmittersComponent', () => {
@Component({ @Component({
selector: 'os-host-component', selector: 'os-host-component',
template: '<os-manage-submitters></os-manage-submitters>' template: '<os-manage-submitters></os-manage-submitters>'

View File

@ -4,7 +4,7 @@
font-size: 1em; font-size: 1em;
} }
::ng-deep .mat-dialog-content { ::ng-deep .mat-dialog-content {
overflow: visible; overflow: visible;
} }
.wide-form { .wide-form {
textarea { textarea {

View File

@ -9,13 +9,14 @@ import { MotionDetailOriginalChangeRecommendationsComponent } from '../motion-de
@Component({ @Component({
template: ` template: `
<os-motion-detail-diff <os-motion-detail-diff
[motion]="motion" [motion]="motion"
[changes]="changes" [changes]="changes"
(scrollToChange)="scrollToChange($event)" (scrollToChange)="scrollToChange($event)"
(createChangeRecommendation)="createChangeRecommendation($event)" (createChangeRecommendation)="createChangeRecommendation($event)"
> >
</os-motion-detail-diff>` </os-motion-detail-diff>
`
}) })
class TestHostComponent { class TestHostComponent {
public motion: ViewMotion; public motion: ViewMotion;

View File

@ -6,13 +6,14 @@ import { Component } from '@angular/core';
@Component({ @Component({
template: ` template: `
<os-motion-detail-original-change-recommendations <os-motion-detail-original-change-recommendations
[html]="html" [html]="html"
[changeRecommendations]="changeRecommendations" [changeRecommendations]="changeRecommendations"
(createChangeRecommendation)="createChangeRecommendation($event)" (createChangeRecommendation)="createChangeRecommendation($event)"
(gotoChangeRecommendation)="gotoChangeRecommendation($event)" (gotoChangeRecommendation)="gotoChangeRecommendation($event)"
> >
</os-motion-detail-original-change-recommendations>` </os-motion-detail-original-change-recommendations>
`
}) })
class TestHostComponent { class TestHostComponent {
public html = '<p>Test123</p>'; public html = '<p>Test123</p>';

View File

@ -21,7 +21,6 @@ import { ViewCategory } from '../../models/view-category';
import { ViewMotionBlock } from '../../models/view-motion-block'; import { ViewMotionBlock } from '../../models/view-motion-block';
import { WorkflowRepositoryService } from '../../services/workflow-repository.service'; import { WorkflowRepositoryService } from '../../services/workflow-repository.service';
/** /**
* Component that displays all the motions in a Table using DataSource. * Component that displays all the motions in a Table using DataSource.
*/ */
@ -31,7 +30,6 @@ import { WorkflowRepositoryService } from '../../services/workflow-repository.se
styleUrls: ['./motion-list.component.scss'] styleUrls: ['./motion-list.component.scss']
}) })
export class MotionListComponent extends ListViewBaseComponent<ViewMotion> implements OnInit { export class MotionListComponent extends ListViewBaseComponent<ViewMotion> implements OnInit {
/** /**
* Use for minimal width. Please note the 'selector' row for multiSelect mode, * Use for minimal width. Please note the 'selector' row for multiSelect mode,
* to be able to display an indicator for the state of selection * to be able to display an indicator for the state of selection
@ -52,7 +50,6 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
public statutesEnabled: boolean; public statutesEnabled: boolean;
public recomendationEnabled: boolean; public recomendationEnabled: boolean;
public tags: ViewTag[] = []; public tags: ViewTag[] = [];
public workflows: ViewWorkflow[] = []; public workflows: ViewWorkflow[] = [];
public categories: ViewCategory[] = []; public categories: ViewCategory[] = [];
@ -112,11 +109,11 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
this.initTable(); this.initTable();
this.configService.get('motions_statutes_enabled').subscribe(enabled => (this.statutesEnabled = enabled)); this.configService.get('motions_statutes_enabled').subscribe(enabled => (this.statutesEnabled = enabled));
this.configService.get('motions_recommendations_by').subscribe(id => (this.recomendationEnabled = !!id)); this.configService.get('motions_recommendations_by').subscribe(id => (this.recomendationEnabled = !!id));
this.motionBlockRepo.getViewModelListObservable().subscribe(mBs => this.motionBlocks = mBs); this.motionBlockRepo.getViewModelListObservable().subscribe(mBs => (this.motionBlocks = mBs));
this.categoryRepo.getViewModelListObservable().subscribe(cats => this.categories = cats); this.categoryRepo.getViewModelListObservable().subscribe(cats => (this.categories = cats));
this.tagRepo.getViewModelListObservable().subscribe(tags => this.tags = tags); this.tagRepo.getViewModelListObservable().subscribe(tags => (this.tags = tags));
this.workflowRepo.getViewModelListObservable().subscribe(wfs => this.workflows = wfs); this.workflowRepo.getViewModelListObservable().subscribe(wfs => (this.workflows = wfs));
this.filterService.filter().subscribe(filteredData => this.sortService.data = filteredData); this.filterService.filter().subscribe(filteredData => (this.sortService.data = filteredData));
this.sortService.sort().subscribe(sortedData => { this.sortService.sort().subscribe(sortedData => {
this.dataSource.data = sortedData; this.dataSource.data = sortedData;
this.checkSelection(); this.checkSelection();
@ -211,6 +208,4 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
this.raiseError(e); this.raiseError(e);
} }
} }
} }

View File

@ -34,6 +34,6 @@ export class ViewMotionBlock extends BaseViewModel {
} }
public getTitle(): string { public getTitle(): string {
return this.title return this.title;
} }
} }

View File

@ -226,7 +226,7 @@ export class ViewMotion extends BaseViewModel {
} }
public get attachments_id(): number[] { public get attachments_id(): number[] {
return this.motion ? this.motion.attachments_id : null return this.motion ? this.motion.attachments_id : null;
} }
public get attachments(): Mediafile[] { public get attachments(): Mediafile[] {

View File

@ -10,7 +10,6 @@ import { BaseViewModel } from '../../base/base-view-model';
* @ignore * @ignore
*/ */
export class ViewWorkflow extends BaseViewModel { export class ViewWorkflow extends BaseViewModel {
private _workflow: Workflow; private _workflow: Workflow;
public constructor(workflow?: Workflow) { public constructor(workflow?: Workflow) {

View File

@ -935,15 +935,14 @@ describe('DiffService', () => {
} }
)); ));
it('works with multiple inserted paragraphs', inject( it('works with multiple inserted paragraphs', inject([DiffService], (service: DiffService) => {
[DiffService], const before = '<p>This is the text before</p>',
(service: DiffService) => { after = '<p>This is the text before</p>\n<p>This is one added line</p>\n<p>Another added line</p>';
const before = '<p>This is the text before</p>', const diff = service.diff(before, after);
after = "<p>This is the text before</p>\n<p>This is one added line</p>\n<p>Another added line</p>"; expect(diff).toBe(
const diff = service.diff(before, after); '<p>This is the text before</p>\n<p class="insert">This is one added line</p>\n<p class="insert">Another added line</p>'
expect(diff).toBe("<p>This is the text before</p>\n<p class=\"insert\">This is one added line</p>\n<p class=\"insert\">Another added line</p>"); );
} }));
));
it('does not a change in a very specific case', inject([DiffService], (service: DiffService) => { it('does not a change in a very specific case', inject([DiffService], (service: DiffService) => {
// See diff._fixWrongChangeDetection // See diff._fixWrongChangeDetection
@ -965,17 +964,21 @@ describe('DiffService', () => {
})); }));
it('does not delete a paragraph before an inserted one', inject([DiffService], (service: DiffService) => { it('does not delete a paragraph before an inserted one', inject([DiffService], (service: DiffService) => {
const inHtml = '<ul class="os-split-before"><li>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</li>\n' + const inHtml =
'</ul>', '<ul class="os-split-before"><li>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</li>\n' +
outHtml = '<ul class="os-split-before">\n' + '</ul>',
outHtml =
'<ul class="os-split-before">\n' +
'<li>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</li>\n' + '<li>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</li>\n' +
'<li class="testclass">At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li>\n' + '<li class="testclass">At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li>\n' +
'</ul>'; '</ul>';
const diff = service.diff(inHtml, outHtml); const diff = service.diff(inHtml, outHtml);
expect(diff).toBe('<ul class="os-split-before">' + expect(diff).toBe(
'<li>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</li>' + '<ul class="os-split-before">' +
'<li class="testclass insert">At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li>' + '<li>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</li>' +
'</ul>'); '<li class="testclass insert">At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</li>' +
'</ul>'
);
})); }));
}); });
@ -1053,10 +1056,16 @@ describe('DiffService', () => {
)); ));
it('works with a replaced list item', inject([DiffService], (service: DiffService) => { it('works with a replaced list item', inject([DiffService], (service: DiffService) => {
const before = "<ul><li>Lorem ipsum <strong>dolor sit amet</strong>, consetetur sadipscing elitr, sed diam nonumy eirmod tempor.</li></ul>", const before =
after = "<ul>\n<li>\n<p>At vero eos et accusam et justo duo dolores et ea rebum.</p>\n</li>\n</ul>\n", '<ul><li>Lorem ipsum <strong>dolor sit amet</strong>, consetetur sadipscing elitr, sed diam nonumy eirmod tempor.</li></ul>',
expected = '<UL class="delete"><LI>' + noMarkup(1) + 'Lorem ipsum <STRONG>dolor sit amet</STRONG>, consetetur sadipscing elitr, sed diam nonumy ' + brMarkup(2) + 'eirmod tempor.</LI></UL>' + after = '<ul>\n<li>\n<p>At vero eos et accusam et justo duo dolores et ea rebum.</p>\n</li>\n</ul>\n',
"<UL class=\"insert\">\n<LI>\n<P>At vero eos et accusam et justo duo dolores et ea rebum.</P>\n</LI>\n</UL>"; expected =
'<UL class="delete"><LI>' +
noMarkup(1) +
'Lorem ipsum <STRONG>dolor sit amet</STRONG>, consetetur sadipscing elitr, sed diam nonumy ' +
brMarkup(2) +
'eirmod tempor.</LI></UL>' +
'<UL class="insert">\n<LI>\n<P>At vero eos et accusam et justo duo dolores et ea rebum.</P>\n</LI>\n</UL>';
const diff = service.diff(before, after, 80, 1); const diff = service.diff(before, after, 80, 1);
const diffNormalized = service.normalizeHtmlForDiff(diff).toLowerCase(); const diffNormalized = service.normalizeHtmlForDiff(diff).toLowerCase();
const expectedNormalized = service.normalizeHtmlForDiff(expected).toLowerCase(); const expectedNormalized = service.normalizeHtmlForDiff(expected).toLowerCase();

View File

@ -491,12 +491,12 @@ describe('LinenumberingService', () => {
expect(service.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml); expect(service.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml);
})); }));
it('does not count within .insert nodes', inject( it('does not count within .insert nodes', inject([LinenumberingService], (service: LinenumberingService) => {
[LinenumberingService], const inHtml = '<p>1234</p><ul class="insert"><li>1234</li></ul><p>1234 1234</p>';
(service: LinenumberingService) => {
const inHtml = "<p>1234</p><ul class=\"insert\"><li>1234</li></ul><p>1234 1234</p>";
const outHtml = service.insertLineNumbers(inHtml, 10); const outHtml = service.insertLineNumbers(inHtml, 10);
expect(outHtml).toBe('<p>' + noMarkup(1) + '1234</p><ul class="insert"><li>1234</li></ul><p>' + noMarkup(2) + '1234 1234</p>'); expect(outHtml).toBe(
'<p>' + noMarkup(1) + '1234</p><ul class="insert"><li>1234</li></ul><p>' + noMarkup(2) + '1234 1234</p>'
);
expect(service.stripLineNumbers(outHtml)).toBe(inHtml); expect(service.stripLineNumbers(outHtml)).toBe(inHtml);
expect(service.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml); expect(service.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml);
})); }));

View File

@ -4,10 +4,10 @@ import { LocalPermissionsService } from './local-permissions.service';
import { E2EImportsModule } from '../../../../e2e-imports.module'; import { E2EImportsModule } from '../../../../e2e-imports.module';
describe('LocalPermissionsService', () => { describe('LocalPermissionsService', () => {
beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] }));
it('should be created', () => { it('should be created', () => {
const service: LocalPermissionsService = TestBed.get(LocalPermissionsService); const service: LocalPermissionsService = TestBed.get(LocalPermissionsService);
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
}); });

View File

@ -3,20 +3,17 @@ import { OperatorService } from '../../../core/services/operator.service';
import { ViewMotion } from '../models/view-motion'; import { ViewMotion } from '../models/view-motion';
import { ConfigService } from '../../../core/services/config.service'; import { ConfigService } from '../../../core/services/config.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class LocalPermissionsService { export class LocalPermissionsService {
public configMinSupporters: number; public configMinSupporters: number;
public constructor( public constructor(private operator: OperatorService, private configService: ConfigService) {
private operator: OperatorService,
private configService: ConfigService,
) {
// load config variables // load config variables
this.configService.get('motions_min_supporters').subscribe(supporters => (this.configMinSupporters = supporters)); this.configService
.get('motions_min_supporters')
.subscribe(supporters => (this.configMinSupporters = supporters));
} }
/** /**
@ -36,13 +33,11 @@ export class LocalPermissionsService {
this.operator.hasPerms('motions.can_support') && this.operator.hasPerms('motions.can_support') &&
this.configMinSupporters > 0 && this.configMinSupporters > 0 &&
motion.state.allow_support && motion.state.allow_support &&
(motion.submitters.indexOf(this.operator.user) === -1) && motion.submitters.indexOf(this.operator.user) === -1 &&
(motion.supporters.indexOf(this.operator.user) === -1)); motion.supporters.indexOf(this.operator.user) === -1
case 'unsupport':
return (
motion.state.allow_support &&
(motion.supporters.indexOf(this.operator.user) !== -1)
); );
case 'unsupport':
return motion.state.allow_support && motion.supporters.indexOf(this.operator.user) !== -1;
default: default:
return false; return false;
} }

View File

@ -4,9 +4,11 @@ import { MotionBlockRepositoryService } from './motion-block-repository.service'
import { E2EImportsModule } from 'e2e-imports.module'; import { E2EImportsModule } from 'e2e-imports.module';
describe('MotionBlockRepositoryService', () => { describe('MotionBlockRepositoryService', () => {
beforeEach(() => TestBed.configureTestingModule({ beforeEach(() =>
imports: [E2EImportsModule] TestBed.configureTestingModule({
})); imports: [E2EImportsModule]
})
);
it('should be created', () => { it('should be created', () => {
const service: MotionBlockRepositoryService = TestBed.get(MotionBlockRepositoryService); const service: MotionBlockRepositoryService = TestBed.get(MotionBlockRepositoryService);

View File

@ -32,7 +32,7 @@ export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock
DS: DataStoreService, DS: DataStoreService,
mapperService: CollectionStringModelMapperService, mapperService: CollectionStringModelMapperService,
private dataSend: DataSendService, private dataSend: DataSendService,
private motionRepo: MotionRepositoryService, private motionRepo: MotionRepositoryService
) { ) {
super(DS, mapperService, MotionBlock); super(DS, mapperService, MotionBlock);
} }

View File

@ -11,10 +11,7 @@ describe('MotionCsvExportService', () => {
}); });
}); });
it('should be created', inject( it('should be created', inject([MotionCsvExportService], (service: MotionCsvExportService) => {
[MotionCsvExportService], expect(service).toBeTruthy();
(service: MotionCsvExportService) => { }));
expect(service).toBeTruthy();
}
));
}); });

View File

@ -12,7 +12,6 @@ import { ViewMotion } from '../models/view-motion';
providedIn: 'root' providedIn: 'root'
}) })
export class MotionCsvExportService { export class MotionCsvExportService {
/** /**
* Does nothing. * Does nothing.
* *
@ -51,11 +50,15 @@ export class MotionCsvExportService {
this.csvExport.export( this.csvExport.export(
motions, motions,
[ [
{ label: 'Called', map: motion => motion.sort_parent_id ? '' : motion.identifierOrTitle }, { label: 'Called', map: motion => (motion.sort_parent_id ? '' : motion.identifierOrTitle) },
{ label: 'Called with', map: motion => !motion.sort_parent_id ? '' : motion.identifierOrTitle }, { label: 'Called with', map: motion => (!motion.sort_parent_id ? '' : motion.identifierOrTitle) },
{ label: 'submitters', map: motion => motion.submitters.map(s => s.short_name).join(',') }, { label: 'submitters', map: motion => motion.submitters.map(s => s.short_name).join(',') },
{ property: 'title' }, { property: 'title' },
{ label: 'recommendation', map: motion => motion.recommendation ? this.translate.instant(motion.recommendation.recommendation_label) : '' }, {
label: 'recommendation',
map: motion =>
motion.recommendation ? this.translate.instant(motion.recommendation.recommendation_label) : ''
},
{ property: 'motion_block', label: 'Motion block' } { property: 'motion_block', label: 'Motion block' }
], ],
this.translate.instant('Call list') + '.csv' this.translate.instant('Call list') + '.csv'

View File

@ -1,31 +1,27 @@
import { Injectable } from "@angular/core"; import { Injectable } from '@angular/core';
import { FilterListService, OsFilter } from "../../../core/services/filter-list.service"; import { FilterListService, OsFilter } from '../../../core/services/filter-list.service';
import { Motion } from "../../../shared/models/motions/motion"; import { Motion } from '../../../shared/models/motions/motion';
import { ViewMotion } from "../models/view-motion"; import { ViewMotion } from '../models/view-motion';
import { CategoryRepositoryService } from "./category-repository.service"; import { CategoryRepositoryService } from './category-repository.service';
import { WorkflowRepositoryService } from "./workflow-repository.service"; import { WorkflowRepositoryService } from './workflow-repository.service';
import { StorageService } from "../../../core/services/storage.service"; import { StorageService } from '../../../core/services/storage.service';
import { MotionRepositoryService } from "./motion-repository.service"; import { MotionRepositoryService } from './motion-repository.service';
import { MotionBlockRepositoryService } from "./motion-block-repository.service"; import { MotionBlockRepositoryService } from './motion-block-repository.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class MotionFilterListService extends FilterListService<Motion, ViewMotion> { export class MotionFilterListService extends FilterListService<Motion, ViewMotion> {
protected name = 'Motion'; protected name = 'Motion';
/** /**
* getter for the filterOptions. Note that in this case, the options are * getter for the filterOptions. Note that in this case, the options are
* generated dynamically, as the options change with the datastore * generated dynamically, as the options change with the datastore
*/ */
public get filterOptions(): OsFilter[] { public get filterOptions(): OsFilter[] {
return [ return [this.flowFilterOptions, this.categoryFilterOptions, this.motionBlockFilterOptions].concat(
this.flowFilterOptions, this.staticFilterOptions
this.categoryFilterOptions, );
this.motionBlockFilterOptions
].concat(
this.staticFilterOptions);
} }
/** /**
@ -54,28 +50,26 @@ export class MotionFilterListService extends FilterListService<Motion, ViewMotio
label: 'Motion block', label: 'Motion block',
isActive: false, isActive: false,
options: [] options: []
} };
public commentFilterOptions = { public commentFilterOptions = {
property: 'comment', property: 'comment',
isActive: false, isActive: false,
options: [] options: []
} };
public staticFilterOptions = [ public staticFilterOptions = [
// TODO favorite (attached to user:whoamI!) // TODO favorite (attached to user:whoamI!)
// TODO personalNote (attached to user:whoamI!) // TODO personalNote (attached to user:whoamI!)
]; ];
public constructor(store: StorageService, public constructor(
store: StorageService,
private workflowRepo: WorkflowRepositoryService, private workflowRepo: WorkflowRepositoryService,
private categoryRepo: CategoryRepositoryService, private categoryRepo: CategoryRepositoryService,
private motionBlockRepo: MotionBlockRepositoryService, private motionBlockRepo: MotionBlockRepositoryService,
// private commentRepo: MotionCommentRepositoryService // private commentRepo: MotionCommentRepositoryService
motionRepo: MotionRepositoryService, motionRepo: MotionRepositoryService
) {
){
super(store, motionRepo); super(store, motionRepo);
this.subscribeWorkflows(); this.subscribeWorkflows();
this.subscribeCategories(); this.subscribeCategories();
@ -125,11 +119,11 @@ export class MotionFilterListService extends FilterListService<Motion, ViewMotio
workflows.forEach(workflow => { workflows.forEach(workflow => {
workflowOptions.push(workflow.name); workflowOptions.push(workflow.name);
workflow.states.forEach(state => { workflow.states.forEach(state => {
workflowOptions.push({ workflowOptions.push({
condition: state.name, condition: state.name,
label: state.name, label: state.name,
isActive: false isActive: false
}); });
}); });
}); });
workflowOptions.push('-'); workflowOptions.push('-');

View File

@ -89,11 +89,10 @@ export class MotionMultiselectService {
*/ */
public async setStateOfMultiple(motions: ViewMotion[]): Promise<void> { public async setStateOfMultiple(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will set the state of all selected motions to:'); const title = this.translate.instant('This will set the state of all selected motions to:');
const choices = this.workflowRepo.getWorkflowStatesForMotions(motions) const choices = this.workflowRepo.getWorkflowStatesForMotions(motions).map(workflowState => ({
.map(workflowState => ({ id: workflowState.id,
id: workflowState.id, label: workflowState.name
label: workflowState.name }));
}));
const selectedChoice = await this.choiceService.open(title, choices); const selectedChoice = await this.choiceService.open(title, choices);
if (selectedChoice) { if (selectedChoice) {
for (const motion of motions) { for (const motion of motions) {
@ -117,12 +116,11 @@ export class MotionMultiselectService {
label: workflowState.recommendation_label label: workflowState.recommendation_label
})); }));
const clearChoice = 'Delete recommendation'; const clearChoice = 'Delete recommendation';
const selectedChoice = await this.choiceService.open(title, choices, false, const selectedChoice = await this.choiceService.open(title, choices, false, null, clearChoice);
null, clearChoice);
if (selectedChoice) { if (selectedChoice) {
const requestData = motions.map(motion => ({ const requestData = motions.map(motion => ({
id: motion.id, id: motion.id,
recommendation: selectedChoice.action ? 0 : selectedChoice.items as number recommendation: selectedChoice.action ? 0 : (selectedChoice.items as number)
})); }));
await this.httpService.post('/rest/motions/motion/manage_multiple_recommendation', { await this.httpService.post('/rest/motions/motion/manage_multiple_recommendation', {
motions: requestData motions: requestData
@ -138,13 +136,19 @@ export class MotionMultiselectService {
public async setCategory(motions: ViewMotion[]): Promise<void> { public async setCategory(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will set the category of all selected motions to:'); const title = this.translate.instant('This will set the category of all selected motions to:');
const clearChoice = 'No category'; const clearChoice = 'No category';
const selectedChoice = await this.choiceService.open(title, this.categoryRepo.getViewModelList(), const selectedChoice = await this.choiceService.open(
false, null, clearChoice); title,
this.categoryRepo.getViewModelList(),
false,
null,
clearChoice
);
if (selectedChoice) { if (selectedChoice) {
for (const motion of motions) { for (const motion of motions) {
await this.repo.update( await this.repo.update(
{category_id: selectedChoice.action ? 0 : selectedChoice.items as number }, { category_id: selectedChoice.action ? 0 : (selectedChoice.items as number) },
motion); motion
);
} }
} }
} }
@ -155,10 +159,11 @@ export class MotionMultiselectService {
* @param motions The motions to add/remove the sumbitters to * @param motions The motions to add/remove the sumbitters to
*/ */
public async changeSubmitters(motions: ViewMotion[]): Promise<void> { public async changeSubmitters(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will add or remove the following submitters for all selected motions:'); const title = this.translate.instant(
'This will add or remove the following submitters for all selected motions:'
);
const choices = ['Add', 'Remove']; const choices = ['Add', 'Remove'];
const selectedChoice = await this.choiceService.open(title, const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true, choices);
this.userRepo.getViewModelList(), true, choices);
if (selectedChoice && selectedChoice.action === choices[0]) { if (selectedChoice && selectedChoice.action === choices[0]) {
const requestData = motions.map(motion => { const requestData = motions.map(motion => {
let submitterIds = [...motion.submitters_id, ...(selectedChoice.items as number[])]; let submitterIds = [...motion.submitters_id, ...(selectedChoice.items as number[])];
@ -190,8 +195,7 @@ export class MotionMultiselectService {
public async changeTags(motions: ViewMotion[]): Promise<void> { public async changeTags(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will add or remove the following tags for all selected motions:'); const title = this.translate.instant('This will add or remove the following tags for all selected motions:');
const choices = ['Add', 'Remove', 'Clear tags']; const choices = ['Add', 'Remove', 'Clear tags'];
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true, const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true, choices);
choices);
if (selectedChoice && selectedChoice.action === choices[0]) { if (selectedChoice && selectedChoice.action === choices[0]) {
const requestData = motions.map(motion => { const requestData = motions.map(motion => {
let tagIds = [...motion.tags_id, ...(selectedChoice.items as number[])]; let tagIds = [...motion.tags_id, ...(selectedChoice.items as number[])];
@ -223,7 +227,6 @@ export class MotionMultiselectService {
} }
} }
/** /**
* Opens a dialog and changes the motionBlock for all given motions. * Opens a dialog and changes the motionBlock for all given motions.
* *
@ -232,12 +235,17 @@ export class MotionMultiselectService {
public async setMotionBlock(motions: ViewMotion[]): Promise<void> { public async setMotionBlock(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will change the motion Block for all selected motions:'); const title = this.translate.instant('This will change the motion Block for all selected motions:');
const clearChoice = 'Clear motion block'; const clearChoice = 'Clear motion block';
const selectedChoice = await this.choiceService.open(title, this.motionBlockRepo.getViewModelList(), const selectedChoice = await this.choiceService.open(
false, null, clearChoice); title,
this.motionBlockRepo.getViewModelList(),
false,
null,
clearChoice
);
if (selectedChoice) { if (selectedChoice) {
for (const motion of motions) { for (const motion of motions) {
const blockId = selectedChoice.action ? null : selectedChoice.items as number; const blockId = selectedChoice.action ? null : (selectedChoice.items as number);
await this.repo.update({motion_block_id: blockId}, motion); await this.repo.update({ motion_block_id: blockId }, motion);
} }
} }
} }

View File

@ -28,7 +28,7 @@ import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragr
import { CreateMotion } from '../models/create-motion'; import { CreateMotion } from '../models/create-motion';
import { MotionBlock } from 'app/shared/models/motions/motion-block'; import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { Mediafile } from 'app/shared/models/mediafiles/mediafile'; import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
import { ConfigService } from "../../../core/services/config.service"; import { ConfigService } from '../../../core/services/config.service';
/** /**
* Repository Services for motions (and potentially categories) * Repository Services for motions (and potentially categories)
@ -44,7 +44,6 @@ import { ConfigService } from "../../../core/services/config.service";
providedIn: 'root' providedIn: 'root'
}) })
export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion> { export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion> {
// The line length; comes from the config variable motions_line_length // The line length; comes from the config variable motions_line_length
private lineLength = 90; private lineLength = 90;
@ -81,9 +80,9 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
super(DS, mapperService, Motion, [Category, User, Workflow, Item, MotionBlock, Mediafile]); super(DS, mapperService, Motion, [Category, User, Workflow, Item, MotionBlock, Mediafile]);
// load config variables // load config variables
this.configService.get('motions_line_length').subscribe(lineLength => this.lineLength = lineLength); this.configService.get('motions_line_length').subscribe(lineLength => (this.lineLength = lineLength));
this.configService.get('motions_default_line_numbering').subscribe(mode => this.defaultLineNumbering = mode); this.configService.get('motions_default_line_numbering').subscribe(mode => (this.defaultLineNumbering = mode));
this.configService.get('motions_recommendation_text_mode').subscribe(mode => this.defaultCrMode = mode); this.configService.get('motions_recommendation_text_mode').subscribe(mode => (this.defaultCrMode = mode));
} }
/** /**
@ -106,7 +105,20 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
if (workflow) { if (workflow) {
state = workflow.getStateById(motion.state_id); state = workflow.getStateById(motion.state_id);
} }
return new ViewMotion(motion, category, submitters, supporters, workflow, state, item, block, attachments, this.lineLength, this.defaultLineNumbering, this.defaultCrMode); return new ViewMotion(
motion,
category,
submitters,
supporters,
workflow,
state,
item,
block,
attachments,
this.lineLength,
this.defaultLineNumbering,
this.defaultCrMode
);
} }
/** /**
@ -220,10 +232,12 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
*/ */
public async setSubmitters(viewMotion: ViewMotion, submitters: User[]): Promise<void> { public async setSubmitters(viewMotion: ViewMotion, submitters: User[]): Promise<void> {
const requestData = { const requestData = {
motions: [{ motions: [
id: viewMotion.id, {
submitters: submitters.map(s => s.id), id: viewMotion.id,
}] submitters: submitters.map(s => s.id)
}
]
}; };
this.httpService.post('/rest/motions/motion/manage_multiple_submitters/', requestData); this.httpService.post('/rest/motions/motion/manage_multiple_submitters/', requestData);
} }

View File

@ -79,12 +79,13 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
*/ */
public getWorkflowStatesForMotions(motions: ViewMotion[]): WorkflowState[] { public getWorkflowStatesForMotions(motions: ViewMotion[]): WorkflowState[] {
let states: WorkflowState[] = []; let states: WorkflowState[] = [];
const workflowIds = motions.map(motion => motion.workflow_id).filter((value, index, self) => self.indexOf(value) === index); const workflowIds = motions
.map(motion => motion.workflow_id)
.filter((value, index, self) => self.indexOf(value) === index);
workflowIds.forEach(id => { workflowIds.forEach(id => {
const workflow = this.getViewModel(id); const workflow = this.getViewModel(id);
states = states.concat(workflow.states); states = states.concat(workflow.states);
}); });
return states; return states;
} }
} }

View File

@ -51,7 +51,6 @@ const routes: Routes = [
path: 'history', path: 'history',
loadChildren: './history/history.module#HistoryModule' loadChildren: './history/history.module#HistoryModule'
} }
], ],
canActivateChild: [AuthGuard] canActivateChild: [AuthGuard]
} }

View File

@ -141,7 +141,7 @@ export class TagListComponent extends ListViewBaseComponent<ViewTag> implements
if (event.key === 'Enter' && event.shiftKey) { if (event.key === 'Enter' && event.shiftKey) {
this.submitNewTag(); this.submitNewTag();
} }
if (event.key === "Escape") { if (event.key === 'Escape') {
this.cancelEditing(); this.cancelEditing();
} }
} }

View File

@ -198,7 +198,7 @@ export class GroupListComponent extends BaseViewComponent implements OnInit {
* Clicking escape while in #newGroupForm should toggle newGroup. * Clicking escape while in #newGroupForm should toggle newGroup.
*/ */
public keyDownFunction(event: KeyboardEvent): void { public keyDownFunction(event: KeyboardEvent): void {
if (event.key === "Escape") { if (event.key === 'Escape') {
this.newGroup = false; this.newGroup = false;
} }
} }

View File

@ -261,5 +261,4 @@ export class PasswordComponent extends BaseViewComponent implements OnInit {
this.hide_user_password = !this.hide_user_password; this.hide_user_password = !this.hide_user_password;
} }
} }
} }

View File

@ -321,7 +321,9 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
* click on the delete user button * click on the delete user button
*/ */
public async deleteUserButton(): Promise<void> { public async deleteUserButton(): Promise<void> {
const content = this.translate.instant('Do you want to delete this participant?') + `<p><strong>${this.user.full_name}</strong>`; const content =
this.translate.instant('Do you want to delete this participant?') +
`<p><strong>${this.user.full_name}</strong>`;
if (await this.promptService.open(this.translate.instant('Are you sure?'), content)) { if (await this.promptService.open(this.translate.instant('Are you sure?'), content)) {
this.repo.delete(this.user).then(() => this.router.navigate(['./users/']), this.raiseError); this.repo.delete(this.user).then(() => this.router.navigate(['./users/']), this.raiseError);
} }

View File

@ -24,7 +24,6 @@ import { UserSortListService } from '../../services/user-sort-list.service';
styleUrls: ['./user-list.component.scss'] styleUrls: ['./user-list.component.scss']
}) })
export class UserListComponent extends ListViewBaseComponent<ViewUser> implements OnInit { export class UserListComponent extends ListViewBaseComponent<ViewUser> implements OnInit {
/** /**
* /** * /**
* The usual constructor for components * The usual constructor for components
@ -71,10 +70,9 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
super.setTitle('Users'); super.setTitle('Users');
this.initTable(); this.initTable();
this.filterService.filter().subscribe(filteredData => { this.filterService.filter().subscribe(filteredData => {
this.sortService.data = filteredData; this.sortService.data = filteredData;
}); });
this.sortService.sort().subscribe(sortedData => { this.sortService.sort().subscribe(sortedData => {
this.dataSource.data = sortedData; this.dataSource.data = sortedData;
this.checkSelection(); this.checkSelection();
@ -148,8 +146,7 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
public async setGroupSelected(): Promise<void> { public async setGroupSelected(): Promise<void> {
const content = this.translate.instant('This will add or remove the following groups for all selected users:'); const content = this.translate.instant('This will add or remove the following groups for all selected users:');
const choices = ['Add group(s)', 'Remove group(s)']; const choices = ['Add group(s)', 'Remove group(s)'];
const selectedChoice = await this.choiceService.open(content, const selectedChoice = await this.choiceService.open(content, this.groupRepo.getViewModelList(), true, choices);
this.groupRepo.getViewModelList(), true, choices);
if (selectedChoice) { if (selectedChoice) {
for (const user of this.selectedRows) { for (const user of this.selectedRows) {
const newGroups = [...user.groups_id]; const newGroups = [...user.groups_id];
@ -187,7 +184,7 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
* Uses selectedRows defined via multiSelect mode. * Uses selectedRows defined via multiSelect mode.
*/ */
public async setPresentSelected(): Promise<void> { public async setPresentSelected(): Promise<void> {
const content = this.translate.instant('Set the presence status for the selected users'); const content = this.translate.instant('Set the presence status for the selected users');
const options = ['Present', 'Not present']; const options = ['Present', 'Not present'];
const selectedChoice = await this.choiceService.open(content, null, false, options); const selectedChoice = await this.choiceService.open(content, null, false, options);
if (selectedChoice) { if (selectedChoice) {
@ -203,8 +200,7 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
* Uses selectedRows defined via multiSelect mode. * Uses selectedRows defined via multiSelect mode.
*/ */
public async setCommitteeSelected(): Promise<void> { public async setCommitteeSelected(): Promise<void> {
const content = this.translate.instant( const content = this.translate.instant('Sets/unsets the committee status for the selected users');
'Sets/unsets the committee status for the selected users');
const options = ['Is committee', 'Is not committee']; const options = ['Is committee', 'Is not committee'];
const selectedChoice = await this.choiceService.open(content, null, false, options); const selectedChoice = await this.choiceService.open(content, null, false, options);
if (selectedChoice) { if (selectedChoice) {
@ -254,7 +250,5 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
public async setPresent(viewUser: ViewUser): Promise<void> { public async setPresent(viewUser: ViewUser): Promise<void> {
viewUser.user.is_present = !viewUser.user.is_present; viewUser.user.is_present = !viewUser.user.is_present;
await this.repo.update(viewUser.user, viewUser); await this.repo.update(viewUser.user, viewUser);
} }
} }

View File

@ -93,7 +93,7 @@ export class ViewUser extends BaseViewModel {
} }
public get is_last_email_send(): boolean { public get is_last_email_send(): boolean {
if (this.user && this.user.last_email_send){ if (this.user && this.user.last_email_send) {
return true; return true;
} }
return false; return false;
@ -105,7 +105,6 @@ export class ViewUser extends BaseViewModel {
this._groups = groups; this._groups = groups;
} }
/** /**
* required by BaseViewModel. Don't confuse with the users title. * required by BaseViewModel. Don't confuse with the users title.
*/ */

View File

@ -8,7 +8,8 @@ describe('GroupRepositoryService', () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule],
providers: [GroupRepositoryService] providers: [GroupRepositoryService]
})); })
);
it('should be created', inject([GroupRepositoryService], (service: GroupRepositoryService) => { it('should be created', inject([GroupRepositoryService], (service: GroupRepositoryService) => {
expect(service).toBeTruthy(); expect(service).toBeTruthy();

View File

@ -1,17 +1,16 @@
import { Injectable } from "@angular/core"; import { Injectable } from '@angular/core';
import { FilterListService, OsFilter } from "../../../core/services/filter-list.service"; import { FilterListService, OsFilter } from '../../../core/services/filter-list.service';
import { StorageService } from "../../../core/services/storage.service"; import { StorageService } from '../../../core/services/storage.service';
import { User } from "../../../shared/models/users/user"; import { User } from '../../../shared/models/users/user';
import { ViewUser } from "../models/view-user"; import { ViewUser } from '../models/view-user';
import { GroupRepositoryService } from "./group-repository.service"; import { GroupRepositoryService } from './group-repository.service';
import { UserRepositoryService } from "./user-repository.service"; import { UserRepositoryService } from './user-repository.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class UserFilterListService extends FilterListService<User, ViewUser> { export class UserFilterListService extends FilterListService<User, ViewUser> {
protected name = 'User'; protected name = 'User';
private userGroupFilterOptions = { private userGroupFilterOptions = {
@ -26,30 +25,25 @@ export class UserFilterListService extends FilterListService<User, ViewUser> {
property: 'is_present', property: 'is_present',
label: 'Presence', label: 'Presence',
isActive: false, isActive: false,
options: [ options: [{ condition: true, label: 'Is present' }, { condition: false, label: 'Is not present' }]
{ condition: true, label: 'Is present'}, },
{ condition: false, label: 'Is not present'}] {
}, {
property: 'is_active', property: 'is_active',
label: 'Active', label: 'Active',
isActive: false, isActive: false,
options: [ options: [{ condition: true, label: 'Is active' }, { condition: false, label: 'Is not active' }]
{ condition: true, label: 'Is active' }, },
{ condition: false, label: 'Is not active' }] {
}, {
property: 'is_committee', property: 'is_committee',
label: 'Committee', label: 'Committee',
isActive: false, isActive: false,
options: [ options: [{ condition: true, label: 'Is a committee' }, { condition: false, label: 'Is not a committee' }]
{ condition: true, label: 'Is a committee' }, },
{ condition: false, label: 'Is not a committee'}] {
}, {
property: 'is_last_email_send', property: 'is_last_email_send',
label: 'Last email send', label: 'Last email send',
isActive: false, isActive: false,
options: [ options: [{ condition: true, label: 'Got an email' }, { condition: false, label: "Didn't get an email" }]
{ condition: true, label: 'Got an email' },
{ condition: false, label: 'Didn\'t get an email' }]
} }
]; ];
@ -61,8 +55,7 @@ export class UserFilterListService extends FilterListService<User, ViewUser> {
return [this.userGroupFilterOptions].concat(this.staticFilterOptions); return [this.userGroupFilterOptions].concat(this.staticFilterOptions);
} }
public constructor(store: StorageService, private groupRepo: GroupRepositoryService, public constructor(store: StorageService, private groupRepo: GroupRepositoryService, repo: UserRepositoryService) {
repo: UserRepositoryService){
super(store, repo); super(store, repo);
this.subscribeGroups(); this.subscribeGroups();
} }
@ -79,6 +72,6 @@ export class UserFilterListService extends FilterListService<User, ViewUser> {
}); });
this.userGroupFilterOptions.options = groupOptions; this.userGroupFilterOptions.options = groupOptions;
this.updateFilterDefinitions(this.filterOptions); this.updateFilterDefinitions(this.filterOptions);
}) });
} }
} }

View File

@ -120,7 +120,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
public async resetPassword(user: ViewUser, password: string): Promise<void> { public async resetPassword(user: ViewUser, password: string): Promise<void> {
const path = `/rest/users/user/${user.id}/reset_password/`; const path = `/rest/users/user/${user.id}/reset_password/`;
await this.httpService.post(path, { password: password }); await this.httpService.post(path, { password: password });
await this.update({default_password: password}, user); await this.update({ default_password: password }, user);
} }
/** /**
@ -131,8 +131,8 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
*/ */
public async setNewPassword(oldPassword: string, newPassword: string): Promise<void> { public async setNewPassword(oldPassword: string, newPassword: string): Promise<void> {
await this.httpService.post(`${environment.urlPrefix}/users/setpassword/`, { await this.httpService.post(`${environment.urlPrefix}/users/setpassword/`, {
old_password: oldPassword, old_password: oldPassword,
new_password: newPassword new_password: newPassword
}); });
} }

View File

@ -6,7 +6,6 @@ import { ViewUser } from '../models/view-user';
providedIn: 'root' providedIn: 'root'
}) })
export class UserSortListService extends SortListService<ViewUser> { export class UserSortListService extends SortListService<ViewUser> {
public sortOptions: OsSortingDefinition<ViewUser> = { public sortOptions: OsSortingDefinition<ViewUser> = {
sortProperty: 'first_name', sortProperty: 'first_name',
sortAscending: true, sortAscending: true,