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> {
|
): Promise<ChoiceAnswer> {
|
||||||
const dialogRef = this.dialog.open(ChoiceDialogComponent, {
|
const dialogRef = this.dialog.open(ChoiceDialogComponent, {
|
||||||
minWidth: '250px',
|
minWidth: '250px',
|
||||||
maxHeight:'90vh',
|
maxHeight: '90vh',
|
||||||
data: { title: title, choices: choices, multiSelect: multiSelect }
|
data: { title: title, choices: choices, multiSelect: multiSelect }
|
||||||
});
|
});
|
||||||
return dialogRef.afterClosed().toPromise();
|
return dialogRef.afterClosed().toPromise();
|
||||||
|
@ -28,5 +28,5 @@
|
|||||||
<span translate>Ok</span>
|
<span translate>Ok</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-button (click)="closeDialog(false)"><span translate>Cancel</span></button>
|
<button mat-button (click)="closeDialog(false)"><span translate>Cancel</span></button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,5 +13,5 @@ mat-radio-group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scrollmenu-outer {
|
.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 })[];
|
export type ChoiceDialogOptions = (Identifiable & Displayable)[] | (Identifiable & { label: string })[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All data needed for this dialog
|
||||||
|
*/
|
||||||
interface ChoiceDialogData {
|
interface ChoiceDialogData {
|
||||||
|
/**
|
||||||
|
* A title to display
|
||||||
|
*/
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The choices to display
|
||||||
|
*/
|
||||||
choices: ChoiceDialogOptions;
|
choices: ChoiceDialogOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select, if this should be a multiselect choice
|
||||||
|
*/
|
||||||
multiSelect: boolean;
|
multiSelect: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +64,12 @@ export class ChoiceDialogComponent {
|
|||||||
@Inject(MAT_DIALOG_DATA) public data: ChoiceDialogData
|
@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 {
|
public getChoiceTitle(choice: ChoiceDialogOption): string {
|
||||||
if ('label' in choice) {
|
if ('label' in choice) {
|
||||||
return choice.label;
|
return choice.label;
|
||||||
|
@ -23,7 +23,6 @@ export class Motion extends AgendaBaseModel {
|
|||||||
public motion_block_id: number;
|
public motion_block_id: number;
|
||||||
public origin: string;
|
public origin: string;
|
||||||
public submitters: MotionSubmitter[];
|
public submitters: MotionSubmitter[];
|
||||||
public submitters_id: number[];
|
|
||||||
public supporters_id: number[];
|
public supporters_id: number[];
|
||||||
public comments: MotionComment[];
|
public comments: MotionComment[];
|
||||||
public workflow_id: number;
|
public workflow_id: number;
|
||||||
|
@ -9,8 +9,8 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
||||||
import { ViewMotion } from '../../models/view-motion';
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
import { LinenumberingService } from '../../services/linenumbering.service';
|
import { LinenumberingService } from '../../services/linenumbering.service';
|
||||||
import { Motion } from '../../../../shared/models/motions/motion';
|
|
||||||
import { BaseViewComponent } from '../../../base/base-view';
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { CreateMotion } from '../../models/create-motion';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the single paragraphs from the base motion.
|
* Describes the single paragraphs from the base motion.
|
||||||
@ -167,10 +167,10 @@ export class AmendmentCreateWizardComponent extends BaseViewComponent {
|
|||||||
amendment_paragraphs: amendedParagraphs
|
amendment_paragraphs: amendedParagraphs
|
||||||
};
|
};
|
||||||
|
|
||||||
const fromForm = new Motion();
|
const motion = new CreateMotion();
|
||||||
fromForm.deserialize(newMotionValues);
|
motion.deserialize(newMotionValues);
|
||||||
|
|
||||||
const response = await this.repo.create(fromForm);
|
const response = await this.repo.create(motion);
|
||||||
this.router.navigate(['./motions/' + response.id]);
|
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 { Workflow } from 'app/shared/models/motions/workflow';
|
||||||
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
||||||
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
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
|
* Component for the motion detail view
|
||||||
@ -260,28 +262,10 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// load config variables
|
// load config variables
|
||||||
this.configService.get('motions_statutes_enabled').subscribe(
|
this.configService.get('motions_statutes_enabled').subscribe(enabled => (this.statutesEnabled = enabled));
|
||||||
(enabled: boolean): void => {
|
this.configService.get('motions_min_supporters').subscribe(supporters => (this.minSupporters = supporters));
|
||||||
this.statutesEnabled = enabled;
|
this.configService.get('motions_preamble').subscribe(preamble => (this.preamble = preamble));
|
||||||
}
|
this.configService.get('motions_amendments_enabled').subscribe(enabled => (this.amendmentsEnabled = 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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -327,8 +311,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
// creates a new motion
|
// creates a new motion
|
||||||
this.newMotion = true;
|
this.newMotion = true;
|
||||||
this.editMotion = true;
|
this.editMotion = true;
|
||||||
this.motion = new ViewMotion();
|
this.motion = new ViewCreateMotion();
|
||||||
this.motionCopy = new ViewMotion();
|
this.motionCopy = new ViewCreateMotion();
|
||||||
} else {
|
} else {
|
||||||
// load existing motion
|
// load existing motion
|
||||||
this.route.params.subscribe(params => {
|
this.route.params.subscribe(params => {
|
||||||
@ -393,7 +377,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
state_id: [''],
|
state_id: [''],
|
||||||
recommendation_id: [''],
|
recommendation_id: [''],
|
||||||
submitters_id: [],
|
submitters_id: [],
|
||||||
supporters_id: [],
|
supporters_id: [[]],
|
||||||
workflow_id: [],
|
workflow_id: [],
|
||||||
origin: ['']
|
origin: ['']
|
||||||
});
|
});
|
||||||
@ -419,45 +403,65 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a motion. Calls the "patchValues" function in the MotionObject
|
* 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
|
||||||
* 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
|
|
||||||
*
|
*
|
||||||
|
* @param motionValues valus for the new motion
|
||||||
|
* @param ctor The motion constructor, so different motion types can be created.
|
||||||
*/
|
*/
|
||||||
public async saveMotion(): Promise<void> {
|
private prepareMotionForSave<T extends Motion>(motionValues: any, ctor: new (...args: any[]) => T): T {
|
||||||
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
const motion = new ctor();
|
||||||
|
|
||||||
const fromForm = new Motion();
|
|
||||||
if (this.motion.isParagraphBasedAmendment()) {
|
if (this.motion.isParagraphBasedAmendment()) {
|
||||||
fromForm.amendment_paragraphs = this.motion.amendment_paragraphs.map(
|
motion.amendment_paragraphs = this.motion.amendment_paragraphs.map(
|
||||||
(para: string): string => {
|
(paragraph: string): string => {
|
||||||
if (para === null) {
|
if (paragraph === null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} 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 {
|
try {
|
||||||
if (this.newMotion) {
|
const response = await this.repo.create(motion);
|
||||||
const response = await this.repo.create(fromForm);
|
this.router.navigate(['./motions/' + response.id]);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.raiseError(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.
|
* get the formated motion text from the repository.
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +42,10 @@
|
|||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<div class="innerTable">
|
<div class="innerTable">
|
||||||
<span class="motion-list-title">{{ motion.title }}</span> <br />
|
<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 -->
|
<!-- state -->
|
||||||
<mat-basic-chip
|
<mat-basic-chip
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
@ -135,37 +138,37 @@
|
|||||||
<mat-icon>sort</mat-icon>
|
<mat-icon>sort</mat-icon>
|
||||||
<span translate>Move to agenda item</span>
|
<span translate>Move to agenda item</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item (click)="multiselectService.setStatus(selectedRows); toggleMultiSelect()">
|
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setStatus(selectedRows))">
|
||||||
<mat-icon>label</mat-icon>
|
<mat-icon>label</mat-icon>
|
||||||
<span translate>Set status</span>
|
<span translate>Set status</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item (click)="multiselectService.setRecommendation(selectedRows); toggleMultiSelect()">
|
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setRecommendation(selectedRows))">
|
||||||
<mat-icon>report</mat-icon>
|
<mat-icon>report</mat-icon>
|
||||||
<!-- TODO: better icon -->
|
<!-- TODO: better icon -->
|
||||||
<span translate>Set recommendation</span>
|
<span translate>Set recommendation</span>
|
||||||
</button>
|
</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>
|
<mat-icon>device_hub</mat-icon>
|
||||||
<!-- TODO: icon -->
|
<!-- TODO: icon -->
|
||||||
<span translate>Set categories</span>
|
<span translate>Set categories</span>
|
||||||
</button>
|
</button>
|
||||||
<mat-divider></mat-divider>
|
<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>
|
<mat-icon>person_add</mat-icon>
|
||||||
<!-- TODO: icon -->
|
<!-- TODO: icon -->
|
||||||
<span translate>Add submitters</span>
|
<span translate>Add submitters</span>
|
||||||
</button>
|
</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>
|
<mat-icon>person_outline</mat-icon>
|
||||||
<!-- TODO: icon -->
|
<!-- TODO: icon -->
|
||||||
<span translate>remove submitters</span>
|
<span translate>remove submitters</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item (click)="multiselectService.addTags(selectedRows); toggleMultiSelect()">
|
<button mat-menu-item (click)="multiselectWrapper(multiselectService.addTags(selectedRows))">
|
||||||
<mat-icon>bookmarks</mat-icon>
|
<mat-icon>bookmarks</mat-icon>
|
||||||
<!-- TODO: icon -->
|
<!-- TODO: icon -->
|
||||||
<span translate>Add tags</span>
|
<span translate>Add tags</span>
|
||||||
</button>
|
</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>
|
<mat-icon>bookmark_border</mat-icon>
|
||||||
<!-- TODO: icon -->
|
<!-- TODO: icon -->
|
||||||
<span translate>Remove tags</span>
|
<span translate>Remove tags</span>
|
||||||
|
@ -172,4 +172,13 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
}
|
}
|
||||||
return this.columnsToDisplayMinWidth;
|
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
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ViewMotion extends BaseViewModel {
|
export class ViewMotion extends BaseViewModel {
|
||||||
private _motion: Motion;
|
protected _motion: Motion;
|
||||||
private _category: Category;
|
protected _category: Category;
|
||||||
private _submitters: User[];
|
protected _submitters: User[];
|
||||||
private _supporters: User[];
|
protected _supporters: User[];
|
||||||
private _workflow: Workflow;
|
protected _workflow: Workflow;
|
||||||
private _state: WorkflowState;
|
protected _state: WorkflowState;
|
||||||
private _item: Item;
|
protected _item: Item;
|
||||||
private _block: MotionBlock;
|
protected _block: MotionBlock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the LineNumberingMode Mode.
|
* Indicates the LineNumberingMode Mode.
|
||||||
@ -126,7 +126,7 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get submitters_id(): number[] {
|
public get submitters_id(): number[] {
|
||||||
return this.motion ? this.motion.submitters_id : null;
|
return this.motion ? this.motion.submitterIds : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get supporters(): User[] {
|
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;
|
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 {
|
public get recommendation_id(): number {
|
||||||
return this.motion && this.motion.recommendation_id ? this.motion.recommendation_id : null;
|
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);
|
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 {
|
public get item(): Item {
|
||||||
return this._item;
|
return this._item;
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,8 @@ import { BaseViewModel } from '../../base/base-view-model';
|
|||||||
export class ViewWorkflow extends BaseViewModel {
|
export class ViewWorkflow extends BaseViewModel {
|
||||||
private _workflow: Workflow;
|
private _workflow: Workflow;
|
||||||
|
|
||||||
public constructor(workflow?: Workflow, id?: number, name?: string) {
|
public constructor(workflow?: Workflow) {
|
||||||
super();
|
super();
|
||||||
if (!workflow) {
|
|
||||||
workflow = new Workflow();
|
|
||||||
workflow.id = id;
|
|
||||||
workflow.name = name;
|
|
||||||
}
|
|
||||||
this._workflow = workflow;
|
this._workflow = workflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import { UserRepositoryService } from 'app/site/users/services/user-repository.s
|
|||||||
import { WorkflowRepositoryService } from './workflow-repository.service';
|
import { WorkflowRepositoryService } from './workflow-repository.service';
|
||||||
import { CategoryRepositoryService } from './category-repository.service';
|
import { CategoryRepositoryService } from './category-repository.service';
|
||||||
import { TagRepositoryService } from 'app/site/tags/services/tag-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.
|
* Contains all multiselect actions for the motion list view.
|
||||||
@ -38,7 +39,8 @@ export class MotionMultiselectService {
|
|||||||
private userRepo: UserRepositoryService,
|
private userRepo: UserRepositoryService,
|
||||||
private workflowRepo: WorkflowRepositoryService,
|
private workflowRepo: WorkflowRepositoryService,
|
||||||
private categoryRepo: CategoryRepositoryService,
|
private categoryRepo: CategoryRepositoryService,
|
||||||
private tagRepo: TagRepositoryService
|
private tagRepo: TagRepositoryService,
|
||||||
|
private httpService: HttpService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,11 +90,16 @@ export class MotionMultiselectService {
|
|||||||
id: workflowState.id,
|
id: workflowState.id,
|
||||||
label: workflowState.recommendation_label
|
label: workflowState.recommendation_label
|
||||||
}));
|
}));
|
||||||
|
choices.push({ id: 0, label: 'Delete recommendation' });
|
||||||
const selectedChoice = await this.choiceService.open(title, choices);
|
const selectedChoice = await this.choiceService.open(title, choices);
|
||||||
if (selectedChoice) {
|
if (typeof selectedChoice === 'number') {
|
||||||
for (const motion of motions) {
|
const requestData = motions.map(motion => ({
|
||||||
await this.repo.setRecommendation(motion, selectedChoice as number);
|
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 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);
|
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true);
|
||||||
if (selectedChoice) {
|
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 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);
|
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true);
|
||||||
if (selectedChoice) {
|
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 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);
|
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true);
|
||||||
if (selectedChoice) {
|
if (selectedChoice) {
|
||||||
for (const motion of motions) {
|
const requestData = motions.map(motion => {
|
||||||
let tagIds = [...motion.tags_id, ...(selectedChoice as number[])];
|
let tagIds = [...motion.tags_id, ...(selectedChoice as number[])];
|
||||||
tagIds = tagIds.filter((id, index, self) => self.indexOf(id) === index);
|
tagIds = tagIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
||||||
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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,11 +190,15 @@ export class MotionMultiselectService {
|
|||||||
const title = this.translate.instant('This will remove the following tags from all selected motions:');
|
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);
|
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true);
|
||||||
if (selectedChoice) {
|
if (selectedChoice) {
|
||||||
for (const motion of motions) {
|
const requestData = motions.map(motion => {
|
||||||
const tagIdsToRemove = selectedChoice as number[];
|
const tagIdsToRemove = selectedChoice as number[];
|
||||||
const tagIds = motion.tags_id.filter(id => !tagIdsToRemove.includes(id));
|
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 { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
import { TreeService } from 'app/core/services/tree.service';
|
import { TreeService } from 'app/core/services/tree.service';
|
||||||
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
||||||
|
import { CreateMotion } from '../models/create-motion';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for motions (and potentially categories)
|
* 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 update the form data containing the updated values
|
||||||
* @param viewMotion The View Motion. If not present, a new motion will be created
|
* @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> {
|
public async create(motion: CreateMotion): Promise<Identifiable> {
|
||||||
if (!motion.supporters_id) {
|
|
||||||
delete motion.supporters_id;
|
|
||||||
}
|
|
||||||
return await this.dataSend.createModel(motion);
|
return await this.dataSend.createModel(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
|||||||
* @param dataSend
|
* @param dataSend
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringModelMapperService,
|
mapperService: CollectionStringModelMapperService,
|
||||||
private dataSend: DataSendService
|
private dataSend: DataSendService
|
||||||
) {
|
) {
|
||||||
@ -61,17 +61,11 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the workflow for the ID
|
* Collects all existing states from all workflows
|
||||||
* @param workflow_id workflow ID
|
|
||||||
*/
|
*/
|
||||||
public getWorkflowByID(workflow_id: number): Workflow {
|
|
||||||
const wfList = this.DS.getAll(Workflow);
|
|
||||||
return wfList.find(workflow => workflow.id === workflow_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAllWorkflowStates(): WorkflowState[] {
|
public getAllWorkflowStates(): WorkflowState[] {
|
||||||
let states: WorkflowState[] = [];
|
let states: WorkflowState[] = [];
|
||||||
this.DS.getAll(Workflow).forEach(workflow => {
|
this.getViewModelList().forEach(workflow => {
|
||||||
states = states.concat(workflow.states);
|
states = states.concat(workflow.states);
|
||||||
});
|
});
|
||||||
return states;
|
return states;
|
||||||
|
@ -105,12 +105,25 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
return pw;
|
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> {
|
public async resetPassword(user: ViewUser, password: string): Promise<void> {
|
||||||
await this.update({ default_password: password }, user);
|
await this.update({ default_password: password }, user);
|
||||||
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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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> {
|
public async sendInvitationEmail(users: ViewUser[]): Promise<string> {
|
||||||
const user_ids = users.map(user => user.id);
|
const user_ids = users.map(user => user.id);
|
||||||
const subject = this.translate.instant(this.configService.instant('users_email_subject'));
|
const subject = this.translate.instant(this.configService.instant('users_email_subject'));
|
||||||
|
Loading…
Reference in New Issue
Block a user