rework motion submitters to clear confusion about creation and normal update.
Also docs and cleanup
This commit is contained in:
parent
1de2161ded
commit
212bce1c08
@ -36,7 +36,7 @@ export class ChoiceService extends OpenSlidesComponent {
|
||||
): Promise<ChoiceAnswer> {
|
||||
const dialogRef = this.dialog.open(ChoiceDialogComponent, {
|
||||
minWidth: '250px',
|
||||
maxHeight:'90vh',
|
||||
maxHeight: '90vh',
|
||||
data: { title: title, choices: choices, multiSelect: multiSelect }
|
||||
});
|
||||
return dialogRef.afterClosed().toPromise();
|
||||
|
@ -28,5 +28,5 @@
|
||||
<span translate>Ok</span>
|
||||
</button>
|
||||
<button mat-button (click)="closeDialog(false)"><span translate>Cancel</span></button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
||||
|
@ -13,5 +13,5 @@ mat-radio-group {
|
||||
}
|
||||
|
||||
.scrollmenu-outer {
|
||||
max-height:inherit;
|
||||
}
|
||||
max-height: inherit;
|
||||
}
|
||||
|
@ -14,9 +14,23 @@ type ChoiceDialogOption = (Identifiable & Displayable) | (Identifiable & { label
|
||||
*/
|
||||
export type ChoiceDialogOptions = (Identifiable & Displayable)[] | (Identifiable & { label: string })[];
|
||||
|
||||
/**
|
||||
* All data needed for this dialog
|
||||
*/
|
||||
interface ChoiceDialogData {
|
||||
/**
|
||||
* A title to display
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* The choices to display
|
||||
*/
|
||||
choices: ChoiceDialogOptions;
|
||||
|
||||
/**
|
||||
* Select, if this should be a multiselect choice
|
||||
*/
|
||||
multiSelect: boolean;
|
||||
}
|
||||
|
||||
@ -50,6 +64,12 @@ export class ChoiceDialogComponent {
|
||||
@Inject(MAT_DIALOG_DATA) public data: ChoiceDialogData
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the title from a choice. Maybe saved in a label property or using getTitle().
|
||||
*
|
||||
* @param choice The choice
|
||||
* @return the title
|
||||
*/
|
||||
public getChoiceTitle(choice: ChoiceDialogOption): string {
|
||||
if ('label' in choice) {
|
||||
return choice.label;
|
||||
|
@ -23,7 +23,6 @@ export class Motion extends AgendaBaseModel {
|
||||
public motion_block_id: number;
|
||||
public origin: string;
|
||||
public submitters: MotionSubmitter[];
|
||||
public submitters_id: number[];
|
||||
public supporters_id: number[];
|
||||
public comments: MotionComment[];
|
||||
public workflow_id: number;
|
||||
|
@ -9,8 +9,8 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
||||
import { ViewMotion } from '../../models/view-motion';
|
||||
import { LinenumberingService } from '../../services/linenumbering.service';
|
||||
import { Motion } from '../../../../shared/models/motions/motion';
|
||||
import { BaseViewComponent } from '../../../base/base-view';
|
||||
import { CreateMotion } from '../../models/create-motion';
|
||||
|
||||
/**
|
||||
* Describes the single paragraphs from the base motion.
|
||||
@ -167,10 +167,10 @@ export class AmendmentCreateWizardComponent extends BaseViewComponent {
|
||||
amendment_paragraphs: amendedParagraphs
|
||||
};
|
||||
|
||||
const fromForm = new Motion();
|
||||
fromForm.deserialize(newMotionValues);
|
||||
const motion = new CreateMotion();
|
||||
motion.deserialize(newMotionValues);
|
||||
|
||||
const response = await this.repo.create(fromForm);
|
||||
const response = await this.repo.create(motion);
|
||||
this.router.navigate(['./motions/' + response.id]);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ import { ConfigService } from '../../../../core/services/config.service';
|
||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
||||
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
||||
import { ViewCreateMotion } from '../../models/view-create-motion';
|
||||
import { CreateMotion } from '../../models/create-motion';
|
||||
|
||||
/**
|
||||
* Component for the motion detail view
|
||||
@ -260,28 +262,10 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
// load config variables
|
||||
this.configService.get('motions_statutes_enabled').subscribe(
|
||||
(enabled: boolean): void => {
|
||||
this.statutesEnabled = enabled;
|
||||
}
|
||||
);
|
||||
this.configService.get('motions_min_supporters').subscribe(
|
||||
(supporters: number): void => {
|
||||
this.minSupporters = supporters;
|
||||
}
|
||||
);
|
||||
|
||||
this.configService.get('motions_preamble').subscribe(
|
||||
(preamble: string): void => {
|
||||
this.preamble = preamble;
|
||||
}
|
||||
);
|
||||
|
||||
this.configService.get('motions_amendments_enabled').subscribe(
|
||||
(enabled: boolean): void => {
|
||||
this.amendmentsEnabled = enabled;
|
||||
}
|
||||
);
|
||||
this.configService.get('motions_statutes_enabled').subscribe(enabled => (this.statutesEnabled = enabled));
|
||||
this.configService.get('motions_min_supporters').subscribe(supporters => (this.minSupporters = supporters));
|
||||
this.configService.get('motions_preamble').subscribe(preamble => (this.preamble = preamble));
|
||||
this.configService.get('motions_amendments_enabled').subscribe(enabled => (this.amendmentsEnabled = enabled));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -327,8 +311,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
// creates a new motion
|
||||
this.newMotion = true;
|
||||
this.editMotion = true;
|
||||
this.motion = new ViewMotion();
|
||||
this.motionCopy = new ViewMotion();
|
||||
this.motion = new ViewCreateMotion();
|
||||
this.motionCopy = new ViewCreateMotion();
|
||||
} else {
|
||||
// load existing motion
|
||||
this.route.params.subscribe(params => {
|
||||
@ -393,7 +377,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
state_id: [''],
|
||||
recommendation_id: [''],
|
||||
submitters_id: [],
|
||||
supporters_id: [],
|
||||
supporters_id: [[]],
|
||||
workflow_id: [],
|
||||
origin: ['']
|
||||
});
|
||||
@ -419,45 +403,65 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a motion. Calls the "patchValues" function in the MotionObject
|
||||
*
|
||||
* http:post the motion to the server.
|
||||
* The AutoUpdate-Service should see a change once it arrives and show it
|
||||
* in the list view automatically
|
||||
* Before updating or creating, the motions needs to be prepared for paragraph based amendments.
|
||||
* A motion of type T is created, prepared and deserialized from the given motionValues
|
||||
*
|
||||
* @param motionValues valus for the new motion
|
||||
* @param ctor The motion constructor, so different motion types can be created.
|
||||
*/
|
||||
public async saveMotion(): Promise<void> {
|
||||
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
||||
|
||||
const fromForm = new Motion();
|
||||
private prepareMotionForSave<T extends Motion>(motionValues: any, ctor: new (...args: any[]) => T): T {
|
||||
const motion = new ctor();
|
||||
if (this.motion.isParagraphBasedAmendment()) {
|
||||
fromForm.amendment_paragraphs = this.motion.amendment_paragraphs.map(
|
||||
(para: string): string => {
|
||||
if (para === null) {
|
||||
motion.amendment_paragraphs = this.motion.amendment_paragraphs.map(
|
||||
(paragraph: string): string => {
|
||||
if (paragraph === null) {
|
||||
return null;
|
||||
} else {
|
||||
return newMotionValues.text;
|
||||
return motionValues.text;
|
||||
}
|
||||
}
|
||||
);
|
||||
newMotionValues.text = '';
|
||||
motionValues.text = '';
|
||||
}
|
||||
fromForm.deserialize(newMotionValues);
|
||||
motion.deserialize(motionValues);
|
||||
return motion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a motion. Calls the "patchValues" function in the MotionObject
|
||||
*/
|
||||
public async createMotion(): Promise<void> {
|
||||
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
||||
const motion = this.prepareMotionForSave(newMotionValues, CreateMotion);
|
||||
|
||||
try {
|
||||
if (this.newMotion) {
|
||||
const response = await this.repo.create(fromForm);
|
||||
this.router.navigate(['./motions/' + response.id]);
|
||||
} else {
|
||||
await this.repo.update(fromForm, this.motionCopy);
|
||||
// if the motion was successfully updated, change the edit mode.
|
||||
this.editMotion = false;
|
||||
}
|
||||
const response = await this.repo.create(motion);
|
||||
this.router.navigate(['./motions/' + response.id]);
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a motion. Calls the "patchValues" function in the MotionObject
|
||||
*/
|
||||
public async updateMotion(): Promise<void> {
|
||||
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
||||
const motion = this.prepareMotionForSave(newMotionValues, Motion);
|
||||
this.repo.update(motion, this.motionCopy).then(() => (this.editMotion = false), this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the ui are no distinct buttons for update or create. This is decided here.
|
||||
*/
|
||||
public saveMotion(): void {
|
||||
if (this.newMotion) {
|
||||
this.createMotion();
|
||||
} else {
|
||||
this.updateMotion();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the formated motion text from the repository.
|
||||
*/
|
||||
|
@ -42,7 +42,10 @@
|
||||
<mat-cell *matCellDef="let motion">
|
||||
<div class="innerTable">
|
||||
<span class="motion-list-title">{{ motion.title }}</span> <br />
|
||||
<span class="motion-list-from"> <span translate>by</span> {{ motion.submitters }} </span> <br />
|
||||
<span class="motion-list-from" *ngIf="motion.submitters.length">
|
||||
<span translate>by</span> {{ motion.submitters }}
|
||||
</span>
|
||||
<br *ngIf="motion.submitters.length" />
|
||||
<!-- state -->
|
||||
<mat-basic-chip
|
||||
[ngClass]="{
|
||||
@ -135,37 +138,37 @@
|
||||
<mat-icon>sort</mat-icon>
|
||||
<span translate>Move to agenda item</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="multiselectService.setStatus(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setStatus(selectedRows))">
|
||||
<mat-icon>label</mat-icon>
|
||||
<span translate>Set status</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="multiselectService.setRecommendation(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setRecommendation(selectedRows))">
|
||||
<mat-icon>report</mat-icon>
|
||||
<!-- TODO: better icon -->
|
||||
<span translate>Set recommendation</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="multiselectService.setCategory(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setCategory(selectedRows))">
|
||||
<mat-icon>device_hub</mat-icon>
|
||||
<!-- TODO: icon -->
|
||||
<span translate>Set categories</span>
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)="multiselectService.addSubmitters(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.addSubmitters(selectedRows))">
|
||||
<mat-icon>person_add</mat-icon>
|
||||
<!-- TODO: icon -->
|
||||
<span translate>Add submitters</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="multiselectService.removeSubmitters(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.removeSubmitters(selectedRows))">
|
||||
<mat-icon>person_outline</mat-icon>
|
||||
<!-- TODO: icon -->
|
||||
<span translate>remove submitters</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="multiselectService.addTags(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.addTags(selectedRows))">
|
||||
<mat-icon>bookmarks</mat-icon>
|
||||
<!-- TODO: icon -->
|
||||
<span translate>Add tags</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="multiselectService.removeTags(selectedRows); toggleMultiSelect()">
|
||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.removeTags(selectedRows))">
|
||||
<mat-icon>bookmark_border</mat-icon>
|
||||
<!-- TODO: icon -->
|
||||
<span translate>Remove tags</span>
|
||||
|
@ -172,4 +172,13 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
}
|
||||
return this.columnsToDisplayMinWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps multiselect actions to close the multiselect mode or throw an error if one happens.
|
||||
*
|
||||
* @param multiselectPromise The promise returned by multiselect actions.
|
||||
*/
|
||||
public multiselectWrapper(multiselectPromise: Promise<void>): void {
|
||||
multiselectPromise.then(() => this.toggleMultiSelect());
|
||||
}
|
||||
}
|
||||
|
13
client/src/app/site/motions/models/create-motion.ts
Normal file
13
client/src/app/site/motions/models/create-motion.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Motion } from 'app/shared/models/motions/motion';
|
||||
|
||||
/**
|
||||
* Representation of Motion during creation. The submitters_id is added to send this information
|
||||
* as an array of user ids to the server.
|
||||
*/
|
||||
export class CreateMotion extends Motion {
|
||||
public submitters_id: number[];
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(input);
|
||||
}
|
||||
}
|
62
client/src/app/site/motions/models/view-create-motion.ts
Normal file
62
client/src/app/site/motions/models/view-create-motion.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { Category } from '../../../shared/models/motions/category';
|
||||
import { User } from '../../../shared/models/users/user';
|
||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { ViewMotion } from './view-motion';
|
||||
import { CreateMotion } from './create-motion';
|
||||
|
||||
/**
|
||||
* Create motion class for the View. Its different to ViewMotion in fact that the submitter handling is different
|
||||
* on motion creation.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewCreateMotion extends ViewMotion {
|
||||
protected _motion: CreateMotion;
|
||||
|
||||
public get motion(): CreateMotion {
|
||||
return this._motion;
|
||||
}
|
||||
|
||||
public get submitters(): User[] {
|
||||
return this._submitters;
|
||||
}
|
||||
|
||||
public get submitters_id(): number[] {
|
||||
return this.motion ? this.motion.submitters_id : null;
|
||||
}
|
||||
|
||||
public set submitters(users: User[]) {
|
||||
this._submitters = users;
|
||||
this._motion.submitters_id = users.map(user => user.id);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
motion?: CreateMotion,
|
||||
category?: Category,
|
||||
submitters?: User[],
|
||||
supporters?: User[],
|
||||
workflow?: Workflow,
|
||||
state?: WorkflowState,
|
||||
item?: Item,
|
||||
block?: MotionBlock
|
||||
) {
|
||||
super(motion, category, submitters, supporters, workflow, state, item, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate this motion into a copy of itself
|
||||
*/
|
||||
public copy(): ViewCreateMotion {
|
||||
return new ViewCreateMotion(
|
||||
this._motion,
|
||||
this._category,
|
||||
this._submitters,
|
||||
this._supporters,
|
||||
this._workflow,
|
||||
this._state
|
||||
);
|
||||
}
|
||||
}
|
@ -31,14 +31,14 @@ export enum ChangeRecoMode {
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewMotion extends BaseViewModel {
|
||||
private _motion: Motion;
|
||||
private _category: Category;
|
||||
private _submitters: User[];
|
||||
private _supporters: User[];
|
||||
private _workflow: Workflow;
|
||||
private _state: WorkflowState;
|
||||
private _item: Item;
|
||||
private _block: MotionBlock;
|
||||
protected _motion: Motion;
|
||||
protected _category: Category;
|
||||
protected _submitters: User[];
|
||||
protected _supporters: User[];
|
||||
protected _workflow: Workflow;
|
||||
protected _state: WorkflowState;
|
||||
protected _item: Item;
|
||||
protected _block: MotionBlock;
|
||||
|
||||
/**
|
||||
* Indicates the LineNumberingMode Mode.
|
||||
@ -126,7 +126,7 @@ export class ViewMotion extends BaseViewModel {
|
||||
}
|
||||
|
||||
public get submitters_id(): number[] {
|
||||
return this.motion ? this.motion.submitters_id : null;
|
||||
return this.motion ? this.motion.submitterIds : null;
|
||||
}
|
||||
|
||||
public get supporters(): User[] {
|
||||
@ -153,10 +153,6 @@ export class ViewMotion extends BaseViewModel {
|
||||
return this.motion && this.motion.state_id ? this.motion.state_id : null;
|
||||
}
|
||||
|
||||
public get possibleStates(): WorkflowState[] {
|
||||
return this.workflow ? this.workflow.states : null;
|
||||
}
|
||||
|
||||
public get recommendation_id(): number {
|
||||
return this.motion && this.motion.recommendation_id ? this.motion.recommendation_id : null;
|
||||
}
|
||||
@ -188,11 +184,6 @@ export class ViewMotion extends BaseViewModel {
|
||||
this._motion.supporters_id = users.map(user => user.id);
|
||||
}
|
||||
|
||||
public set submitters(users: User[]) {
|
||||
this._submitters = users;
|
||||
this._motion.submitters_id = users.map(user => user.id);
|
||||
}
|
||||
|
||||
public get item(): Item {
|
||||
return this._item;
|
||||
}
|
||||
|
@ -12,13 +12,8 @@ import { BaseViewModel } from '../../base/base-view-model';
|
||||
export class ViewWorkflow extends BaseViewModel {
|
||||
private _workflow: Workflow;
|
||||
|
||||
public constructor(workflow?: Workflow, id?: number, name?: string) {
|
||||
public constructor(workflow?: Workflow) {
|
||||
super();
|
||||
if (!workflow) {
|
||||
workflow = new Workflow();
|
||||
workflow.id = id;
|
||||
workflow.name = name;
|
||||
}
|
||||
this._workflow = workflow;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { UserRepositoryService } from 'app/site/users/services/user-repository.s
|
||||
import { WorkflowRepositoryService } from './workflow-repository.service';
|
||||
import { CategoryRepositoryService } from './category-repository.service';
|
||||
import { TagRepositoryService } from 'app/site/tags/services/tag-repository.service';
|
||||
import { HttpService } from 'app/core/services/http.service';
|
||||
|
||||
/**
|
||||
* Contains all multiselect actions for the motion list view.
|
||||
@ -38,7 +39,8 @@ export class MotionMultiselectService {
|
||||
private userRepo: UserRepositoryService,
|
||||
private workflowRepo: WorkflowRepositoryService,
|
||||
private categoryRepo: CategoryRepositoryService,
|
||||
private tagRepo: TagRepositoryService
|
||||
private tagRepo: TagRepositoryService,
|
||||
private httpService: HttpService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -88,11 +90,16 @@ export class MotionMultiselectService {
|
||||
id: workflowState.id,
|
||||
label: workflowState.recommendation_label
|
||||
}));
|
||||
choices.push({ id: 0, label: 'Delete recommendation' });
|
||||
const selectedChoice = await this.choiceService.open(title, choices);
|
||||
if (selectedChoice) {
|
||||
for (const motion of motions) {
|
||||
await this.repo.setRecommendation(motion, selectedChoice as number);
|
||||
}
|
||||
if (typeof selectedChoice === 'number') {
|
||||
const requestData = motions.map(motion => ({
|
||||
id: motion.id,
|
||||
recommendation: selectedChoice
|
||||
}));
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_recommendation', {
|
||||
motions: requestData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +127,15 @@ export class MotionMultiselectService {
|
||||
const title = this.translate.instant('This will add the following submitters of all selected motions:');
|
||||
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true);
|
||||
if (selectedChoice) {
|
||||
throw new Error('Not implemented on the server');
|
||||
const requestData = motions.map(motion => {
|
||||
let submitterIds = [...motion.submitters_id, ...(selectedChoice as number[])];
|
||||
submitterIds = submitterIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
||||
return {
|
||||
id: motion.id,
|
||||
submitters: submitterIds
|
||||
};
|
||||
});
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_submitters', { motions: requestData });
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +148,15 @@ export class MotionMultiselectService {
|
||||
const title = this.translate.instant('This will remove the following submitters from all selected motions:');
|
||||
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true);
|
||||
if (selectedChoice) {
|
||||
throw new Error('Not implemented on the server');
|
||||
const requestData = motions.map(motion => {
|
||||
const submitterIdsToRemove = selectedChoice as number[];
|
||||
const submitterIds = motion.submitters_id.filter(id => !submitterIdsToRemove.includes(id));
|
||||
return {
|
||||
id: motion.id,
|
||||
submitters: submitterIds
|
||||
};
|
||||
});
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_submitters', { motions: requestData });
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,11 +169,15 @@ export class MotionMultiselectService {
|
||||
const title = this.translate.instant('This will add the following tags to all selected motions:');
|
||||
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true);
|
||||
if (selectedChoice) {
|
||||
for (const motion of motions) {
|
||||
const requestData = motions.map(motion => {
|
||||
let tagIds = [...motion.tags_id, ...(selectedChoice as number[])];
|
||||
tagIds = tagIds.filter((id, index, self) => self.indexOf(id) === index);
|
||||
await this.repo.update({ tags_id: tagIds }, motion);
|
||||
}
|
||||
tagIds = tagIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
||||
return {
|
||||
id: motion.id,
|
||||
tags: tagIds
|
||||
};
|
||||
});
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,11 +190,15 @@ export class MotionMultiselectService {
|
||||
const title = this.translate.instant('This will remove the following tags from all selected motions:');
|
||||
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true);
|
||||
if (selectedChoice) {
|
||||
for (const motion of motions) {
|
||||
const requestData = motions.map(motion => {
|
||||
const tagIdsToRemove = selectedChoice as number[];
|
||||
const tagIds = motion.tags_id.filter(id => !tagIdsToRemove.includes(id));
|
||||
await this.repo.update({ tags_id: tagIds }, motion);
|
||||
}
|
||||
return {
|
||||
id: motion.id,
|
||||
tags: tagIds
|
||||
};
|
||||
});
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import { Item } from 'app/shared/models/agenda/item';
|
||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||
import { TreeService } from 'app/core/services/tree.service';
|
||||
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
||||
import { CreateMotion } from '../models/create-motion';
|
||||
|
||||
/**
|
||||
* Repository Services for motions (and potentially categories)
|
||||
@ -109,12 +110,8 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
*
|
||||
* @param update the form data containing the updated values
|
||||
* @param viewMotion The View Motion. If not present, a new motion will be created
|
||||
* TODO: Remove the viewMotion and make it actually distignuishable from save()
|
||||
*/
|
||||
public async create(motion: Motion): Promise<Identifiable> {
|
||||
if (!motion.supporters_id) {
|
||||
delete motion.supporters_id;
|
||||
}
|
||||
public async create(motion: CreateMotion): Promise<Identifiable> {
|
||||
return await this.dataSend.createModel(motion);
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
||||
* @param dataSend
|
||||
*/
|
||||
public constructor(
|
||||
protected DS: DataStoreService,
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringModelMapperService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
@ -61,17 +61,11 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workflow for the ID
|
||||
* @param workflow_id workflow ID
|
||||
* Collects all existing states from all workflows
|
||||
*/
|
||||
public getWorkflowByID(workflow_id: number): Workflow {
|
||||
const wfList = this.DS.getAll(Workflow);
|
||||
return wfList.find(workflow => workflow.id === workflow_id);
|
||||
}
|
||||
|
||||
public getAllWorkflowStates(): WorkflowState[] {
|
||||
let states: WorkflowState[] = [];
|
||||
this.DS.getAll(Workflow).forEach(workflow => {
|
||||
this.getViewModelList().forEach(workflow => {
|
||||
states = states.concat(workflow.states);
|
||||
});
|
||||
return states;
|
||||
|
@ -105,12 +105,25 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
return pw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the default password and sets the real password.
|
||||
*
|
||||
* @param user The user to update
|
||||
* @param password The password to set
|
||||
*/
|
||||
public async resetPassword(user: ViewUser, password: string): Promise<void> {
|
||||
await this.update({ default_password: password }, user);
|
||||
const path = `/rest/users/user/${user.id}/reset_password/`;
|
||||
await this.httpService.post(path, { password: password });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends invitation emails to all given users. Returns a prepared string to show the user.
|
||||
* This string should always be shown, becuase even in success cases, some users may not get
|
||||
* an email and the user should be notified about this.
|
||||
*
|
||||
* @param users All affected users
|
||||
*/
|
||||
public async sendInvitationEmail(users: ViewUser[]): Promise<string> {
|
||||
const user_ids = users.map(user => user.id);
|
||||
const subject = this.translate.instant(this.configService.instant('users_email_subject'));
|
||||
|
Loading…
Reference in New Issue
Block a user