From 7cf36f0ebdcfa13d4bf684a9f4a7fa9a6638e8b8 Mon Sep 17 00:00:00 2001 From: GabrielMeyer Date: Thu, 4 Mar 2021 12:06:04 +0100 Subject: [PATCH] Update motion and assignment polls to OS4 --- .../core/actions/assignment-poll-action.ts | 74 ---- .../app/core/actions/motion-poll-action.ts | 57 --- client/src/app/core/actions/poll-action.ts | 159 ++++++++ .../core-services/active-meeting.service.ts | 6 + .../core/core-services/app-load.service.ts | 2 + .../app/core/core-services/auth.service.ts | 9 +- .../app/core/core-services/key-transforms.ts | 5 + .../model-request-builder.service.ts | 10 +- .../core/core-services/operator.service.ts | 18 +- client/src/app/core/definitions/key-types.ts | 3 + client/src/app/core/definitions/relations.ts | 10 +- .../pdf-services/base-poll-pdf-service.ts | 4 +- ...signment-option-repository.service.spec.ts | 14 - .../assignment-option-repository.service.ts | 36 -- ...assignment-poll-repository.service.spec.ts | 14 - .../assignment-poll-repository.service.ts | 68 ---- ...assignment-vote-repository.service.spec.ts | 14 - .../assignment-vote-repository.service.ts | 40 -- .../core/repositories/base-poll-repository.ts | 64 ---- .../base-repository-with-active-meeting.ts | 8 +- .../meeting-settings-definition.ts | 22 +- .../motion-option-repository.service.spec.ts | 14 - .../motion-option-repository.service.ts | 33 -- .../motion-poll-repository.service.spec.ts | 14 - .../motions/motion-poll-repository.service.ts | 54 --- .../motions/motion-repository.service.ts | 3 +- .../motion-vote-repository.service.spec.ts | 14 - .../polls/option-repository.service.ts | 37 ++ .../polls/poll-repository.service.spec.ts | 16 + .../polls/poll-repository.service.ts | 355 ++++++++++++++++++ .../vote-repository.service.ts} | 19 +- client/src/app/core/repositories/relations.ts | 167 +++----- .../users/user-repository.service.ts | 5 +- .../ui-services/base-poll-dialog.service.ts | 91 +++-- .../core/ui-services/voting-banner.service.ts | 33 +- .../app/core/ui-services/voting.service.ts | 10 +- ...ignment-poll-detail-content.component.html | 2 +- ...ssignment-poll-detail-content.component.ts | 20 +- .../components/base-search-value-selector.ts | 2 +- .../components/charts/charts.component.ts | 10 +- .../check-input/check-input.component.ts | 4 +- .../motion-poll-detail-content.component.scss | 5 + .../motion-poll-detail-content.component.ts | 13 +- .../search-value-selector.component.html | 6 - .../directives/only-number.directive.ts | 18 +- .../app/shared/directives/perms.directive.ts | 1 - .../app/shared/directives/swipe.directive.ts | 36 +- .../models/assignments/assignment-option.ts | 14 - .../models/assignments/assignment-poll.ts | 91 ----- .../models/assignments/assignment-vote.ts | 9 - .../shared/models/base/base-decimal-model.ts | 28 +- .../shared/models/event-management/meeting.ts | 18 +- .../shared/models/motions/motion-option.ts | 9 - .../app/shared/models/motions/motion-poll.ts | 36 -- .../app/shared/models/motions/motion-vote.ts | 12 - .../src/app/shared/models/poll/base-option.ts | 18 - client/src/app/shared/models/poll/option.ts | 25 ++ .../app/shared/models/poll/poll-constants.ts | 181 +++++++++ .../models/poll/{base-poll.ts => poll.ts} | 120 +++--- .../src/app/shared/models/poll/view-option.ts | 22 ++ .../src/app/shared/models/poll/view-poll.ts | 144 +++++++ .../src/app/shared/models/poll/view-vote.ts | 22 ++ .../models/poll/{base-vote.ts => vote.ts} | 13 +- client/src/app/shared/models/users/user.ts | 17 +- .../shared/pipes/parse-poll-number.pipe.ts | 4 +- client/src/app/shared/utils/to-decimal.ts | 31 +- .../site/assignments/assignments.config.ts | 24 -- .../assignment-detail.component.html | 6 +- .../assignment-detail.component.ts | 39 +- .../models/view-assignment-option.ts | 18 - .../models/view-assignment-poll.ts | 71 ---- .../models/view-assignment-vote.ts | 14 - .../assignments/models/view-assignment.ts | 6 +- .../assignment-poll-detail.component.html | 7 +- .../assignment-poll-detail.component.ts | 134 ++++--- .../assignment-poll-dialog.component.html | 43 +-- .../assignment-poll-dialog.component.ts | 173 ++------- .../assignment-poll-meta-info.component.html | 2 +- .../assignment-poll-meta-info.component.ts | 18 +- .../assignment-poll-vote.component.html | 16 +- .../assignment-poll-vote.component.ts | 70 ++-- .../assignment-poll.component.html | 2 +- .../assignment-poll.component.ts | 34 +- .../assignment-poll-dialog.service.ts | 10 +- .../services/assignment-poll-pdf.service.ts | 24 +- .../services/assignment-poll.service.ts | 188 +++++----- .../services/assignment-pdf.service.ts | 10 +- .../site/base/components/base.component.ts | 8 + .../poll-collection.component.ts | 37 +- .../event-management/models/view-meeting.ts | 18 +- .../site/motions/models/view-motion-option.ts | 14 - .../site/motions/models/view-motion-poll.ts | 67 ---- .../site/motions/models/view-motion-vote.ts | 14 - .../app/site/motions/models/view-motion.ts | 7 +- .../motion-manage-polls.component.html | 15 + .../motion-manage-polls.component.scss | 0 .../motion-manage-polls.component.spec.ts | 24 ++ .../motion-manage-polls.component.ts | 43 +++ .../motion-meta-data.component.html | 14 +- .../motion-meta-data.component.ts | 16 - .../motion-detail/motion-detail.module.ts | 4 +- .../motion-poll-detail.component.html | 4 +- .../motion-poll-detail.component.ts | 35 +- .../motion-poll-dialog.component.html | 34 +- .../motion-poll-dialog.component.ts | 69 +--- .../motion-poll-vote.component.html | 24 +- .../motion-poll-vote.component.ts | 53 +-- .../motion-poll/motion-poll.component.html | 28 +- .../motion-poll/motion-poll.component.ts | 32 +- client/src/app/site/motions/motions.config.ts | 14 +- .../motions/services/motion-pdf.service.ts | 25 +- .../services/motion-poll-dialog.service.ts | 10 +- .../services/motion-poll-pdf.service.ts | 8 +- .../motions/services/motion-poll.service.ts | 89 +++-- .../components/base-poll-detail.component.ts | 185 ++++----- .../components/base-poll-dialog.component.ts | 203 +++++++++- .../components/base-poll-vote.component.ts | 27 +- .../polls/components/base-poll.component.ts | 102 +++-- .../poll-form/poll-form.component.html | 2 +- .../poll-form/poll-form.component.ts | 79 ++-- .../poll-list/poll-list.component.html | 10 +- .../poll-list/poll-list.component.ts | 8 +- .../poll-progress/poll-progress.component.ts | 9 +- .../app/site/polls/models/base-view-option.ts | 26 -- .../app/site/polls/models/base-view-poll.ts | 142 ------- .../app/site/polls/models/base-view-vote.ts | 20 - .../app/site/polls/models/has-view-polls.ts | 4 +- client/src/app/site/polls/polls.config.ts | 31 ++ .../services/poll-filter-list.service.ts | 10 +- .../poll-list-observable.service.spec.ts | 18 - .../services/poll-list-observable.service.ts | 51 --- .../app/site/polls/services/poll.service.ts | 104 +++-- client/src/app/site/site.component.html | 8 +- client/src/app/site/site.component.ts | 46 +-- .../user-list/user-list.component.html | 2 +- .../user-list/user-list.component.ts | 5 +- .../src/app/site/users/models/view-group.ts | 6 +- client/src/app/site/users/models/view-user.ts | 19 +- .../assignment-poll-slide-data.ts | 10 +- .../assignment-poll-slide.component.ts | 6 +- .../motion-poll/motion-poll-slide-data.ts | 10 +- .../motion-poll-slide.component.ts | 6 +- .../app/slides/polls/base-poll-slide-data.ts | 6 +- .../slides/polls/base-poll-slide.component.ts | 2 +- 144 files changed, 2563 insertions(+), 2642 deletions(-) delete mode 100644 client/src/app/core/actions/assignment-poll-action.ts delete mode 100644 client/src/app/core/actions/motion-poll-action.ts create mode 100644 client/src/app/core/actions/poll-action.ts delete mode 100644 client/src/app/core/repositories/assignments/assignment-option-repository.service.spec.ts delete mode 100644 client/src/app/core/repositories/assignments/assignment-option-repository.service.ts delete mode 100644 client/src/app/core/repositories/assignments/assignment-poll-repository.service.spec.ts delete mode 100644 client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts delete mode 100644 client/src/app/core/repositories/assignments/assignment-vote-repository.service.spec.ts delete mode 100644 client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts delete mode 100644 client/src/app/core/repositories/base-poll-repository.ts delete mode 100644 client/src/app/core/repositories/motions/motion-option-repository.service.spec.ts delete mode 100644 client/src/app/core/repositories/motions/motion-option-repository.service.ts delete mode 100644 client/src/app/core/repositories/motions/motion-poll-repository.service.spec.ts delete mode 100644 client/src/app/core/repositories/motions/motion-poll-repository.service.ts delete mode 100644 client/src/app/core/repositories/motions/motion-vote-repository.service.spec.ts create mode 100644 client/src/app/core/repositories/polls/option-repository.service.ts create mode 100644 client/src/app/core/repositories/polls/poll-repository.service.spec.ts create mode 100644 client/src/app/core/repositories/polls/poll-repository.service.ts rename client/src/app/core/repositories/{motions/motion-vote-repository.service.ts => polls/vote-repository.service.ts} (50%) delete mode 100644 client/src/app/shared/models/assignments/assignment-option.ts delete mode 100644 client/src/app/shared/models/assignments/assignment-poll.ts delete mode 100644 client/src/app/shared/models/assignments/assignment-vote.ts delete mode 100644 client/src/app/shared/models/motions/motion-option.ts delete mode 100644 client/src/app/shared/models/motions/motion-poll.ts delete mode 100644 client/src/app/shared/models/motions/motion-vote.ts delete mode 100644 client/src/app/shared/models/poll/base-option.ts create mode 100644 client/src/app/shared/models/poll/option.ts create mode 100644 client/src/app/shared/models/poll/poll-constants.ts rename client/src/app/shared/models/poll/{base-poll.ts => poll.ts} (50%) create mode 100644 client/src/app/shared/models/poll/view-option.ts create mode 100644 client/src/app/shared/models/poll/view-poll.ts create mode 100644 client/src/app/shared/models/poll/view-vote.ts rename client/src/app/shared/models/poll/{base-vote.ts => vote.ts} (80%) delete mode 100644 client/src/app/site/assignments/models/view-assignment-option.ts delete mode 100644 client/src/app/site/assignments/models/view-assignment-poll.ts delete mode 100644 client/src/app/site/assignments/models/view-assignment-vote.ts delete mode 100644 client/src/app/site/motions/models/view-motion-option.ts delete mode 100644 client/src/app/site/motions/models/view-motion-poll.ts delete mode 100644 client/src/app/site/motions/models/view-motion-vote.ts create mode 100644 client/src/app/site/motions/modules/motion-detail/components/motion-manage-polls/motion-manage-polls.component.html create mode 100644 client/src/app/site/motions/modules/motion-detail/components/motion-manage-polls/motion-manage-polls.component.scss create mode 100644 client/src/app/site/motions/modules/motion-detail/components/motion-manage-polls/motion-manage-polls.component.spec.ts create mode 100644 client/src/app/site/motions/modules/motion-detail/components/motion-manage-polls/motion-manage-polls.component.ts delete mode 100644 client/src/app/site/polls/models/base-view-option.ts delete mode 100644 client/src/app/site/polls/models/base-view-poll.ts delete mode 100644 client/src/app/site/polls/models/base-view-vote.ts create mode 100644 client/src/app/site/polls/polls.config.ts delete mode 100644 client/src/app/site/polls/services/poll-list-observable.service.spec.ts delete mode 100644 client/src/app/site/polls/services/poll-list-observable.service.ts diff --git a/client/src/app/core/actions/assignment-poll-action.ts b/client/src/app/core/actions/assignment-poll-action.ts deleted file mode 100644 index d12df22be5..0000000000 --- a/client/src/app/core/actions/assignment-poll-action.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { AssignmentAnalogVoteData } from 'app/core/repositories/assignments/assignment-poll-repository.service'; -import { Identifiable } from 'app/shared/models/base/identifiable'; -import { Id } from '../definitions/key-types'; - -export namespace AssignmentPollAction { - type GlobalYPollMethod = { [key: number]: number } | 'N' | 'A'; - interface PartialPayload { - // Optional, every state - description?: string; - onehundred_percent_base?: string; - majority_method?: string; - } - interface PartialCreatePayload { - // Optional, only if state == created - pollmethod?: string; - type?: string; - } - interface CreatePayload extends PartialPayload, PartialCreatePayload { - // Required - assignment_id: Id; - title: string; - - // Optional - votes_amount?: number; - allow_multiple_votes_per_candidate?: boolean; - global_abstain?: boolean; - global_no?: boolean; - } - interface CanPublishImmediately { - // Only for type==analog, optional: votes can be directly given - votes?: AssignmentAnalogVoteData; - publish_immediately?: boolean; - } - interface NonAnalogPayload { - // Only for non analog types - entitled_group_ids?: Id[]; - } - interface UpdateCreatedPayload extends UpdatePayload, PartialCreatePayload {} - - export interface UpdatePayload extends Identifiable, PartialPayload { - title?: string; - } - export interface CreateAnalogPollPayload extends CreatePayload, CanPublishImmediately {} - export interface CreateNonAnalogPollPayload extends CreatePayload, NonAnalogPayload {} - export interface UpdateCreatedAnalogPollPayload extends UpdateCreatedPayload, CanPublishImmediately {} - export interface UpdateCreatedNonAnalogPollPayload extends UpdateCreatedPayload, NonAnalogPayload {} - - export interface StartPayload extends Identifiable {} - export interface StopPayload extends Identifiable {} - export interface PublishPayload extends Identifiable {} - export interface PseudoanonymizePayload extends Identifiable {} - export interface ResetPayload extends Identifiable {} - - export interface VoteNonAnalogPollWithGlobalPayload extends Identifiable { - value: GlobalYPollMethod; - } - export interface VoteNonAnalogPollNonGlobalPayload extends Identifiable { - value: { [key: number]: PM }; - } - export interface VoteAnalogPollPayload extends Identifiable { - options: { - [key: number]: { - Y: number; - N?: number; - A?: number; - }; - }; - votesvalid?: number; - votesinvalid?: number; - votescast?: number; - global_no?: number; - global_abstain?: number; - } -} diff --git a/client/src/app/core/actions/motion-poll-action.ts b/client/src/app/core/actions/motion-poll-action.ts deleted file mode 100644 index 94927c7fe4..0000000000 --- a/client/src/app/core/actions/motion-poll-action.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Identifiable } from 'app/shared/models/base/identifiable'; -import { Id } from '../definitions/key-types'; -import { MotionAnalogVoteData } from '../repositories/motions/motion-poll-repository.service'; - -export namespace MotionPollAction { - interface UserIdentifier { - user_id?: Id; - } - interface PartialPayload { - // Optional, every state - onehundred_percent_base?: string; - majority_method?: string; - } - interface PartialCreatePayload { - // Optional - pollmethod?: string; - type?: string; - } - interface CanPublishImmediately { - // Only for type==analog, optional: votes can be directly given - votes?: MotionAnalogVoteData; - publish_immediately?: boolean; - } - interface NonAnalogPayload { - // Only for non analog types - entitled_group_ids?: Id[]; - } - interface CreatePayload extends PartialPayload, PartialCreatePayload { - // Required - motion_id: Id; - title: string; - } - interface UpdatePayload extends Identifiable, PartialPayload { - // Optional, every state - title?: string; - } - - export interface CreateAnalogPollPayload extends CreatePayload, CanPublishImmediately {} - export interface CreateNonAnalogPollPayload extends CreatePayload, NonAnalogPayload {} - export interface UpdateCreatedAnalogPollPayload extends UpdatePayload, PartialCreatePayload {} - export interface UpdateCreatedNonAnalogPollPayload extends UpdateCreatedAnalogPollPayload, NonAnalogPayload {} - export interface UpdateAnalogPollPayload extends UpdatePayload, CanPublishImmediately {} - - export interface StartPayload extends Identifiable {} - export interface StopPayload extends Identifiable {} - export interface PublishPayload extends Identifiable {} - export interface PseudoanonymizePayload extends Identifiable {} - export interface ResetPayload extends Identifiable {} - export interface VoteNonAnalogPollPayload extends Identifiable, UserIdentifier { - value: PM; - } - export type VoteAnalogPollPayload = { - [K in keyof MotionAnalogVoteData]; - } & - Identifiable & - UserIdentifier; -} diff --git a/client/src/app/core/actions/poll-action.ts b/client/src/app/core/actions/poll-action.ts new file mode 100644 index 0000000000..198682f02f --- /dev/null +++ b/client/src/app/core/actions/poll-action.ts @@ -0,0 +1,159 @@ +import { HasMeetingId } from 'app/shared/models/base/has-meeting-id'; +import { Identifiable } from 'app/shared/models/base/identifiable'; +import { Decimal, Fqid, Id } from '../definitions/key-types'; + +export namespace PollAction { + export const CREATE = 'poll.create'; + export const UPDATE = 'poll.update'; + export const DELETE = 'poll.delete'; + + export const PUBLISH = 'poll.publish'; + export const RESET = 'poll.reset'; + export const START = 'poll.start'; + export const STOP = 'poll.stop'; + export const ANONYMIZE = 'poll.anonymize'; + export const VOTE = 'poll.vote'; + export const UPDATE_OPTION = 'option.update'; + + interface UserIdentifiable { + user_id: Id; + } + + type YNA = 'Y' | 'N' | 'A'; + + interface Option { + // Exactly one of text and content_object_id must be given + text?: string; + content_object_id?: Fqid; + } + + export interface AnalogOption extends Option { + // Only for type==analog, optional votes can be given + Y?: Decimal; // Y, YN, YNA mode + N?: Decimal; // N, YN mode + A?: Decimal; // YNA mode + } + + export interface ElectronicOption extends Option {} + + interface AnalogVotesPayload { + votesvalid?: Decimal; + votesinvalid?: Decimal; + votescast?: Decimal; + } + + interface AnalogGlobalAmountPayload { + // Only for type==analog, optional votes can be given + amount_global_yes?: Decimal; + amount_global_no?: Decimal; + amount_global_abstain?: Decimal; + } + + interface PartialCreatePayload extends HasMeetingId { + // Required + title: string; + type: string; + pollmethod: string; + + options: Option[]; // must have at least one entry. + + // Optional + content_object_id?: Fqid; + description?: string; + min_votes_amount?: number; + max_votes_amount?: number; + global_yes?: boolean; + global_no?: boolean; + global_abstain?: boolean; + onehundred_percent_base?: string; + majority_method?: string; + } + + interface PartialUpdatePayload { + // Optional, every state + title?: string; + description?: string; + onehundred_percent_base?: string; + majority_method?: string; + } + + interface PartialUpdateCreatedPollPayload { + // Optional, only if state == created + pollmethod?: string; + min_votes_amount?: number; + max_votes_amount?: number; + allow_multiple_votes_per_candidate?: boolean; + } + + interface PartialUpdateAnalogPayload extends AnalogVotesPayload, AnalogGlobalAmountPayload { + // type==analog, every state + } + export interface CreateAnalogPollPayload + extends PartialCreatePayload, + AnalogVotesPayload, + AnalogGlobalAmountPayload { + options: AnalogOption[]; + + // Only for type==analog + publish_immediately?: boolean; + } + + export interface CreateElectronicPollPayload extends PartialCreatePayload { + options: ElectronicOption[]; + // Only for non analog types + entitled_group_ids?: Id[]; + } + + export interface UpdateAnalogPollPayload + extends Identifiable, + PartialUpdatePayload, + PartialUpdateCreatedPollPayload, + PartialUpdateAnalogPayload { + // Only for type==analog + publish_immediately?: boolean; + } + + export interface UpdateElectronicPollPayload + extends Identifiable, + PartialUpdatePayload, + PartialUpdateCreatedPollPayload { + // Optional, only if state == created, only for non analog types + entitled_group_ids: Id[]; + } + + export interface UpdateOtherStateAnalogPollPayload + extends Identifiable, + PartialUpdatePayload, + PartialUpdateAnalogPayload { + publish_immediately?: boolean; + } + + export interface DeletePollPayload extends Identifiable {} + + export interface ResetPollPayload extends Identifiable {} + export interface StartPollPayload extends Identifiable {} + export interface StopPollPayload extends Identifiable {} + export interface PublishPollPayload extends Identifiable {} + export interface AnonymizePollPayload extends Identifiable {} + + /** + * Only for non-analog polls + */ + export interface YNVotePayload extends Identifiable, UserIdentifiable { + value: { [option_id: number]: number } | YNA; + } + + /** + * Only for non-analog polls + */ + export interface YNAVotePayload extends Identifiable, UserIdentifiable { + value: { [option_id: number]: YNA } | YNA; + } + + export interface OptionUpdatePayload extends Identifiable { + Y?: number; + N?: number; + A?: number; + publish_immediately?: boolean; + } +} diff --git a/client/src/app/core/core-services/active-meeting.service.ts b/client/src/app/core/core-services/active-meeting.service.ts index fc15ca189c..8a8759872a 100644 --- a/client/src/app/core/core-services/active-meeting.service.ts +++ b/client/src/app/core/core-services/active-meeting.service.ts @@ -86,6 +86,12 @@ export class ActiveMeetingService { { idField: 'preview_projection_ids', follow: [{ idField: 'content_object_id' }] } // {idField: 'used_as_default_$_in_meeting_id'} // TODO ] + }, + { + // needed for the voting-banner + idField: 'poll_ids', + fieldset: 'list', + follow: [{ idField: 'content_object_id' }] } ], fieldset: 'settings', diff --git a/client/src/app/core/core-services/app-load.service.ts b/client/src/app/core/core-services/app-load.service.ts index d9856903de..c2b2815554 100644 --- a/client/src/app/core/core-services/app-load.service.ts +++ b/client/src/app/core/core-services/app-load.service.ts @@ -12,6 +12,7 @@ import { HistoryAppConfig } from 'app/site/history/history.config'; import { MediafileAppConfig } from 'app/site/mediafiles/mediafile.config'; import { SettingsAppConfig } from 'app/site/meeting-settings/meeting-settings.config'; import { MotionsAppConfig } from 'app/site/motions/motions.config'; +import { PollsAppConfig } from 'app/site/polls/polls.config'; import { ProjectorAppConfig } from 'app/site/projector/projector.config'; import { TagAppConfig } from 'app/site/tags/tag.config'; import { TopicsAppConfig } from 'app/site/topics/topics.config'; @@ -32,6 +33,7 @@ const appConfigs: AppConfig[] = [ AgendaAppConfig, AssignmentsAppConfig, MotionsAppConfig, + PollsAppConfig, MediafileAppConfig, TagAppConfig, UsersAppConfig, diff --git a/client/src/app/core/core-services/auth.service.ts b/client/src/app/core/core-services/auth.service.ts index 3a00204245..c24bd7529e 100644 --- a/client/src/app/core/core-services/auth.service.ts +++ b/client/src/app/core/core-services/auth.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { EventEmitter, Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { environment } from 'environments/environment'; @@ -48,6 +48,11 @@ export class AuthService { return this.authTokenSubject.getValue(); } + /** + * "Pings" every time when a user logs out. + */ + public readonly onLogout = new EventEmitter(); + public constructor( private http: HttpService, private lifecycleService: LifecycleService, @@ -101,7 +106,7 @@ export class AuthService { if (response.success) { this.authTokenService.setRawAccessToken(null); } - this.router.navigate(['/']); + this.onLogout.emit(); this.lifecycleService.bootup(); } diff --git a/client/src/app/core/core-services/key-transforms.ts b/client/src/app/core/core-services/key-transforms.ts index 9fd65c4b7c..bcfff047ec 100644 --- a/client/src/app/core/core-services/key-transforms.ts +++ b/client/src/app/core/core-services/key-transforms.ts @@ -30,3 +30,8 @@ export function collectionIdFieldFromFqfield(fqfield: Fqfield): [Collection, Id, export function collectionFromFqid(fqid: Fqid): Collection { return collectionIdFromFqid(fqid)[0]; } + +// E.g. (group_$_ids, 4) -> group_$4_ids +export function fillTemplateValueInTemplateField(field: Field, value: string): Field { + return field.replace('$', '$' + value); +} diff --git a/client/src/app/core/core-services/model-request-builder.service.ts b/client/src/app/core/core-services/model-request-builder.service.ts index 5472d73d52..e8e51ec657 100644 --- a/client/src/app/core/core-services/model-request-builder.service.ts +++ b/client/src/app/core/core-services/model-request-builder.service.ts @@ -12,6 +12,7 @@ import { } from './autoupdate.service'; import { CollectionMapperService } from './collection-mapper.service'; import { Deferred } from '../promises/deferred'; +import { fillTemplateValueInTemplateField } from './key-transforms'; import { Collection, Field, Id } from '../definitions/key-types'; import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded'; import { RelationManagerService } from './relation-manager.service'; @@ -166,16 +167,11 @@ export class ModelRequestBuilderService implements OnAfterAppsLoaded { }; } else { // Specific structured field - fields[this.fillTemplateValueInTempalteField(f.templateIdField, f.templateValue)] = null; + fields[fillTemplateValueInTemplateField(f.templateIdField, f.templateValue)] = null; } } } - // E.g. (group_$_ids, 4) -> group_$4_ids - private fillTemplateValueInTempalteField(field: Field, value: string): Field { - return field.replace('$', '$' + value); - } - private addFollowedRelations(collection: Collection, followList: FollowList, fields: Fields): void { for (const entry of followList) { let follow: Follow; @@ -199,7 +195,7 @@ export class ModelRequestBuilderService implements OnAfterAppsLoaded { effectiveIdField = queryIdField = follow.idField; } else { queryIdField = follow.idField.templateIdField; - effectiveIdField = this.fillTemplateValueInTempalteField(queryIdField, follow.idField.templateValue); + effectiveIdField = fillTemplateValueInTemplateField(queryIdField, follow.idField.templateValue); } const isSpecificStructuredField = queryIdField !== effectiveIdField; diff --git a/client/src/app/core/core-services/operator.service.ts b/client/src/app/core/core-services/operator.service.ts index 4712838e80..f637e84ea0 100644 --- a/client/src/app/core/core-services/operator.service.ts +++ b/client/src/app/core/core-services/operator.service.ts @@ -1,4 +1,5 @@ import { EventEmitter, Injectable } from '@angular/core'; +import { Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; import { take } from 'rxjs/operators'; @@ -122,10 +123,12 @@ export class OperatorService { private autoupdateService: AutoupdateService, private activeMeetingService: ActiveMeetingService, private userRepo: UserRepositoryService, - private groupRepo: GroupRepositoryService + private groupRepo: GroupRepositoryService, + private router: Router ) { this._loaded = this.operatorUpdatedEvent.pipe(take(1)).toPromise(); + this.authService.onLogout.subscribe(() => this.navigateOnLogout()); this.authService.authTokenObservable.subscribe(token => { const id = token ? token.userId : null; if (id !== this._lastUserId) { @@ -320,4 +323,17 @@ export class OperatorService { } return groupIds.some(id => this.groupIds.includes(id)); } + + /** + * Checks if guests are enabled. If they are not enabled, then a user has to be navigated + * to the login-page. + * This behaviour prevents a non-redirect on the startpage. + */ + private navigateOnLogout(): void { + if (this.anonymousEnabled) { + this.router.navigate(['/']); + } else { + this.router.navigate(['/login']); + } + } } diff --git a/client/src/app/core/definitions/key-types.ts b/client/src/app/core/definitions/key-types.ts index 009b8b6506..379a01d1f5 100644 --- a/client/src/app/core/definitions/key-types.ts +++ b/client/src/app/core/definitions/key-types.ts @@ -5,5 +5,8 @@ export type Collection = string; export type Field = string; export type Id = number; export type UnsafeHtml = string; +/** + * A string with exactly six digits after comma. + */ export type Decimal = string; export type Base64Encoded = string; diff --git a/client/src/app/core/definitions/relations.ts b/client/src/app/core/definitions/relations.ts index bff962575c..4b718359c0 100644 --- a/client/src/app/core/definitions/relations.ts +++ b/client/src/app/core/definitions/relations.ts @@ -153,14 +153,14 @@ export function makeGenericO2O(args: { ]; } -export function makeGenericO2M(args: { +export function makeGenericO2M(args: { OViewModel: ViewModelConstructor; - MPossibleViewModels: ViewModelConstructor[]; + MPossibleViewModels: ViewModelConstructor[]; OViewModelField: keyof V & string; - MPossibleViewModelsField: keyof I & string; + MPossibleViewModelsField: string; OViewModelIdField?: keyof V & string; - MPossibleViewModelsIdField?: keyof I & string; - OViewModelOrder?: keyof I & string; + MPossibleViewModelsIdField?: string; + OViewModelOrder?: string; }): Relation[] { return [ // viewModel -> possible view models diff --git a/client/src/app/core/pdf-services/base-poll-pdf-service.ts b/client/src/app/core/pdf-services/base-poll-pdf-service.ts index 7d0dda29ab..1b6d142954 100644 --- a/client/src/app/core/pdf-services/base-poll-pdf-service.ts +++ b/client/src/app/core/pdf-services/base-poll-pdf-service.ts @@ -1,7 +1,7 @@ import { ActiveMeetingIdService } from '../core-services/active-meeting-id.service'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { BallotPaperSelection } from 'app/shared/models/event-management/meeting'; -import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; +import { ViewPoll } from 'app/shared/models/poll/view-poll'; import { MediaManageService } from '../ui-services/media-manage.service'; import { MeetingSettingsService } from '../ui-services/meeting-settings.service'; @@ -14,7 +14,7 @@ export interface AbstractPollData { title: string; subtitle?: string; sheetend: number; // should reflect the vertical size of one ballot on the paper - poll?: ViewAssignmentPoll; // TODO ugly workaround because assignment poll needs the poll on ballot level + poll?: ViewPoll; } export abstract class PollPdfService { diff --git a/client/src/app/core/repositories/assignments/assignment-option-repository.service.spec.ts b/client/src/app/core/repositories/assignments/assignment-option-repository.service.spec.ts deleted file mode 100644 index c0991b1d5a..0000000000 --- a/client/src/app/core/repositories/assignments/assignment-option-repository.service.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { E2EImportsModule } from 'e2e-imports.module'; - -import { AssignmentOptionRepositoryService } from './assignment-option-repository.service'; - -describe('AssignmentOptionRepositoryService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: AssignmentOptionRepositoryService = TestBed.inject(AssignmentOptionRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/assignments/assignment-option-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-option-repository.service.ts deleted file mode 100644 index 7ea6d5c241..0000000000 --- a/client/src/app/core/repositories/assignments/assignment-option-repository.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { Fieldsets } from 'app/core/core-services/model-request-builder.service'; -import { AssignmentOption } from 'app/shared/models/assignments/assignment-option'; -import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignment-option'; -import { BaseRepositoryWithActiveMeeting } from '../base-repository-with-active-meeting'; -import { RepositoryServiceCollector } from '../repository-service-collector'; - -/** - * Repository Service for Options. - * - * Documentation partially provided in {@link BaseRepository} - */ -@Injectable({ - providedIn: 'root' -}) -export class AssignmentOptionRepositoryService extends BaseRepositoryWithActiveMeeting< - ViewAssignmentOption, - AssignmentOption -> { - public constructor(repositoryServiceCollector: RepositoryServiceCollector) { - super(repositoryServiceCollector, AssignmentOption); - } - - public getTitle = (viewAssignmentOption: ViewAssignmentOption) => { - return 'Option'; - }; - - public getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Options' : 'Option'); - }; - - public getFieldsets(): Fieldsets { - return {}; // TODO - } -} diff --git a/client/src/app/core/repositories/assignments/assignment-poll-repository.service.spec.ts b/client/src/app/core/repositories/assignments/assignment-poll-repository.service.spec.ts deleted file mode 100644 index fa54853028..0000000000 --- a/client/src/app/core/repositories/assignments/assignment-poll-repository.service.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { E2EImportsModule } from 'e2e-imports.module'; - -import { AssignmentPollRepositoryService } from './assignment-poll-repository.service'; - -describe('AssignmentPollRepositoryService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: AssignmentPollRepositoryService = TestBed.inject(AssignmentPollRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts deleted file mode 100644 index e331596b9b..0000000000 --- a/client/src/app/core/repositories/assignments/assignment-poll-repository.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { HttpService } from 'app/core/core-services/http.service'; -import { DEFAULT_FIELDSET, Fieldsets } from 'app/core/core-services/model-request-builder.service'; -import { BasePollRepository } from 'app/core/repositories/base-poll-repository'; -import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll'; -import { UserVote } from 'app/shared/models/poll/base-vote'; -import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; -import { RepositoryServiceCollector } from '../repository-service-collector'; - -export interface AssignmentAnalogVoteData { - options: { - [key: number]: { - Y: number; - N?: number; - A?: number; - }; - }; - votesvalid?: number; - votesinvalid?: number; - votescast?: number; - global_yes?: number; - global_no?: number; - global_abstain?: number; -} - -export interface VotingData { - votes: Object; - global?: GlobalVote; -} - -export type GlobalVote = 'A' | 'N'; - -/** - * Repository Service for Assignments. - * - * Documentation partially provided in {@link BaseRepository} - */ -@Injectable({ - providedIn: 'root' -}) -export class AssignmentPollRepositoryService extends BasePollRepository { - public constructor(repositoryServiceCollector: RepositoryServiceCollector, http: HttpService) { - super(repositoryServiceCollector, AssignmentPoll, http); - } - - public getTitle = (viewAssignmentPoll: ViewAssignmentPoll) => { - return viewAssignmentPoll.title; - }; - - public getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Polls' : 'Poll'); - }; - - public getFieldsets(): Fieldsets { - return { - [DEFAULT_FIELDSET]: [] // TODO - }; - } - - public vote(data: VotingData, poll_id: number, userId?: number): Promise { - const requestData: UserVote = { - data: data.global ?? data.votes, - user_id: userId ?? undefined - }; - return this.http.post(`/rest/assignments/assignment-poll/${poll_id}/vote/`, requestData); - } -} diff --git a/client/src/app/core/repositories/assignments/assignment-vote-repository.service.spec.ts b/client/src/app/core/repositories/assignments/assignment-vote-repository.service.spec.ts deleted file mode 100644 index 5e7cf13693..0000000000 --- a/client/src/app/core/repositories/assignments/assignment-vote-repository.service.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { E2EImportsModule } from 'e2e-imports.module'; - -import { AssignmentVoteRepositoryService } from './assignment-vote-repository.service'; - -describe('AssignmentVoteRepositoryService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: AssignmentVoteRepositoryService = TestBed.inject(AssignmentVoteRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts b/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts deleted file mode 100644 index 47e914b8fe..0000000000 --- a/client/src/app/core/repositories/assignments/assignment-vote-repository.service.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { Fieldsets } from 'app/core/core-services/model-request-builder.service'; -import { AssignmentVote } from 'app/shared/models/assignments/assignment-vote'; -import { ViewAssignmentVote } from 'app/site/assignments/models/view-assignment-vote'; -import { BaseRepositoryWithActiveMeeting } from '../base-repository-with-active-meeting'; -import { RepositoryServiceCollector } from '../repository-service-collector'; - -/** - * Repository Service for Assignments. - * - * Documentation partially provided in {@link BaseRepository} - */ -@Injectable({ - providedIn: 'root' -}) -export class AssignmentVoteRepositoryService extends BaseRepositoryWithActiveMeeting< - ViewAssignmentVote, - AssignmentVote -> { - public constructor(repositoryServiceCollector: RepositoryServiceCollector) { - super(repositoryServiceCollector, AssignmentVote); - } - - public getTitle = (viewAssignmentVote: ViewAssignmentVote) => { - return 'Vote'; - }; - - public getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Votes' : 'Vote'); - }; - - public getFieldsets(): Fieldsets { - return {}; // TODO - } - - public getVotesForUser(pollId: number, userId: number): ViewAssignmentVote[] { - return this.getViewModelList().filter(vote => vote.option.poll_id === pollId && vote.user_id === userId); - } -} diff --git a/client/src/app/core/repositories/base-poll-repository.ts b/client/src/app/core/repositories/base-poll-repository.ts deleted file mode 100644 index 9be8572022..0000000000 --- a/client/src/app/core/repositories/base-poll-repository.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { HttpService } from 'app/core/core-services/http.service'; -import { RepositoryServiceCollector } from 'app/core/repositories/repository-service-collector'; -import { VotingService } from 'app/core/ui-services/voting.service'; -import { ModelConstructor } from 'app/shared/models/base/base-model'; -import { BasePoll, PollState } from 'app/shared/models/poll/base-poll'; -import { BaseRepositoryWithActiveMeeting } from './base-repository-with-active-meeting'; -import { BaseViewPoll } from '../../site/polls/models/base-view-poll'; - -export type PollMethodYNA = 'Y' | 'N' | 'A'; -export type PollMethodYN = 'Y' | 'N'; - -export abstract class BasePollRepository< - V extends BaseViewPoll = any, - M extends BasePoll = any -> extends BaseRepositoryWithActiveMeeting { - // just passing everything to superclass - public constructor( - repositoryServiceCollector: RepositoryServiceCollector, - protected baseModelCtor: ModelConstructor, - protected http: HttpService - ) { - super(repositoryServiceCollector, baseModelCtor); - } - - /** - * overwrites the view model creation to insert the `canBeVotedFor` property - * @param model the model - */ - protected createViewModel(model: M): V { - const viewModel = super.createViewModel(model); - viewModel.canBeVotedFor = () => viewModel.isStarted; - return viewModel; - } - - public async changePollState(poll: BasePoll): Promise { - const path = this.restPath(poll); - switch (poll.state) { - case PollState.Created: - // return this.http.post(`${path}/start/`); - case PollState.Started: - // return this.http.post(`${path}/stop/`); - case PollState.Finished: - // return this.http.post(`${path}/publish/`); - case PollState.Published: - // return this.resetPoll(poll); - } - } - - private restPath(poll: BasePoll): string { - return `/rest/${poll.collection}/${poll.id}`; - } - - public async resetPoll(poll: BasePoll): Promise { - // return this.http.post(`${this.restPath(poll)}/reset/`); - } - - public async pseudoanonymize(poll: BasePoll): Promise { - // return this.http.post(`${this.restPath(poll)}/pseudoanonymize/`); - } - - public async refresh(poll: BasePoll): Promise { - // return this.http.post(`${this.restPath(poll)}/refresh/`); - } -} diff --git a/client/src/app/core/repositories/base-repository-with-active-meeting.ts b/client/src/app/core/repositories/base-repository-with-active-meeting.ts index 248394e64d..bd35299495 100644 --- a/client/src/app/core/repositories/base-repository-with-active-meeting.ts +++ b/client/src/app/core/repositories/base-repository-with-active-meeting.ts @@ -1,8 +1,8 @@ import { ActiveMeetingIdService } from '../core-services/active-meeting-id.service'; import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model'; import { BaseRepository } from './base-repository'; -import { BaseViewModel, ViewModelConstructor } from '../../site/base/base-view-model'; -import { Identifiable } from '../../shared/models/base/identifiable'; +import { BaseViewModel } from '../../site/base/base-view-model'; +import { Id } from '../definitions/key-types'; import { RepositoryServiceCollector } from './repository-service-collector'; /** @@ -13,6 +13,10 @@ export abstract class BaseRepositoryWithActiveMeeting< V extends BaseViewModel, M extends BaseModel > extends BaseRepository { + protected get activeMeetingId(): Id | null { + return this.activeMeetingIdService.meetingId; + } + protected get activeMeetingIdService(): ActiveMeetingIdService { return this.fullRepositoryServiceCollector.activeMeetingIdService; } diff --git a/client/src/app/core/repositories/event-management/meeting-settings-definition.ts b/client/src/app/core/repositories/event-management/meeting-settings-definition.ts index 979d03fd6f..3d6b0ff519 100644 --- a/client/src/app/core/repositories/event-management/meeting-settings-definition.ts +++ b/client/src/app/core/repositories/event-management/meeting-settings-definition.ts @@ -3,12 +3,16 @@ import { ValidatorFn, Validators } from '@angular/forms'; import dedent from 'ts-dedent'; import { AgendaItemType } from 'app/shared/models/agenda/agenda-item'; -import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll'; import { Settings } from 'app/shared/models/event-management/meeting'; import { MotionWorkflow } from 'app/shared/models/motions/motion-workflow'; -import { PercentBase } from 'app/shared/models/poll/base-poll'; -import { AssignmentPollMethodVerbose } from 'app/site/assignments/models/view-assignment-poll'; -import { MajorityMethodVerbose, PercentBaseVerbose, PollTypeVerbose } from 'app/site/polls/models/base-view-poll'; +import { PollPercentBase } from 'app/shared/models/poll/poll-constants'; +import { + AssignmentPollMethodVerbose, + MajorityMethodVerbose, + PollMethod, + PollPercentBaseVerbose, + PollTypeVerbose +} from 'app/shared/models/poll/poll-constants'; export type SettingsType = | 'string' @@ -615,9 +619,9 @@ export const meetingSettings: SettingsGroup[] = [ { key: 'motion_poll_default_100_percent_base', label: 'Default 100 % base of a voting result', - default: PercentBase.YNA, + default: PollPercentBase.YNA, type: 'choice', - choices: switchKeyValue(PercentBaseVerbose) + choices: switchKeyValue(PollPercentBaseVerbose) }, { key: 'motion_poll_default_majority_method', @@ -688,7 +692,7 @@ export const meetingSettings: SettingsGroup[] = [ { key: 'assignment_poll_default_method', label: 'Default election method', - default: AssignmentPollMethod.Y, + default: PollMethod.Y, type: 'choice', choices: switchKeyValue(AssignmentPollMethodVerbose) }, @@ -701,9 +705,9 @@ export const meetingSettings: SettingsGroup[] = [ { key: 'assignment_poll_default_100_percent_base', label: 'Default 100 % base of an election result', - default: PercentBase.Valid, + default: PollPercentBase.Valid, type: 'choice', - choices: switchKeyValue(PercentBaseVerbose) + choices: switchKeyValue(PollPercentBaseVerbose) }, { key: 'assignment_poll_default_majority_method', diff --git a/client/src/app/core/repositories/motions/motion-option-repository.service.spec.ts b/client/src/app/core/repositories/motions/motion-option-repository.service.spec.ts deleted file mode 100644 index a71dda10f0..0000000000 --- a/client/src/app/core/repositories/motions/motion-option-repository.service.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { E2EImportsModule } from 'e2e-imports.module'; - -import { MotionOptionRepositoryService } from './motion-option-repository.service'; - -describe('MotionOptionRepositoryService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: MotionOptionRepositoryService = TestBed.inject(MotionOptionRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/motions/motion-option-repository.service.ts b/client/src/app/core/repositories/motions/motion-option-repository.service.ts deleted file mode 100644 index 96603da1d0..0000000000 --- a/client/src/app/core/repositories/motions/motion-option-repository.service.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { Fieldsets } from 'app/core/core-services/model-request-builder.service'; -import { MotionOption } from 'app/shared/models/motions/motion-option'; -import { ViewMotionOption } from 'app/site/motions/models/view-motion-option'; -import { BaseRepositoryWithActiveMeeting } from '../base-repository-with-active-meeting'; -import { RepositoryServiceCollector } from '../repository-service-collector'; - -/** - * Repository Service for Options. - * - * Documentation partially provided in {@link BaseRepository} - */ -@Injectable({ - providedIn: 'root' -}) -export class MotionOptionRepositoryService extends BaseRepositoryWithActiveMeeting { - public constructor(repositoryServiceCollector: RepositoryServiceCollector) { - super(repositoryServiceCollector, MotionOption); - } - - public getTitle = (viewMotionOption: ViewMotionOption) => { - return 'Option'; - }; - - public getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Options' : 'Option'); - }; - - public getFieldsets(): Fieldsets { - return {}; // TODO - } -} diff --git a/client/src/app/core/repositories/motions/motion-poll-repository.service.spec.ts b/client/src/app/core/repositories/motions/motion-poll-repository.service.spec.ts deleted file mode 100644 index 84a45986df..0000000000 --- a/client/src/app/core/repositories/motions/motion-poll-repository.service.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { E2EImportsModule } from 'e2e-imports.module'; - -import { MotionPollRepositoryService } from './motion-poll-repository.service'; - -describe('MotionPollRepositoryService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: MotionPollRepositoryService = TestBed.inject(MotionPollRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/motions/motion-poll-repository.service.ts b/client/src/app/core/repositories/motions/motion-poll-repository.service.ts deleted file mode 100644 index d2188f226b..0000000000 --- a/client/src/app/core/repositories/motions/motion-poll-repository.service.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { HttpService } from 'app/core/core-services/http.service'; -import { DEFAULT_FIELDSET, Fieldsets } from 'app/core/core-services/model-request-builder.service'; -import { BasePollRepository } from 'app/core/repositories/base-poll-repository'; -import { MotionPoll } from 'app/shared/models/motions/motion-poll'; -import { UserVote, VoteValue } from 'app/shared/models/poll/base-vote'; -import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll'; -import { RepositoryServiceCollector } from '../repository-service-collector'; - -export interface MotionAnalogVoteData { - Y: number; - N: number; - A?: number; // Only if pollmethod is YNA - votesvalid?: number; - votesinvalid?: number; - votescast?: number; -} - -/** - * Repository Service for Assignments. - * - * Documentation partially provided in {@link BaseRepository} - */ -@Injectable({ - providedIn: 'root' -}) -export class MotionPollRepositoryService extends BasePollRepository { - public constructor(repositoryServiceCollector: RepositoryServiceCollector, http: HttpService) { - super(repositoryServiceCollector, MotionPoll, http); - } - - public getTitle = (viewMotionPoll: ViewMotionPoll) => { - return viewMotionPoll.title; - }; - - public getVerboseName = (plural: boolean = false) => { - return this.translate.instant(plural ? 'Polls' : 'Poll'); - }; - - public getFieldsets(): Fieldsets { - return { - [DEFAULT_FIELDSET]: [] // TODO - }; - } - - public vote(vote: VoteValue, poll_id: number, userId?: number): Promise { - const requestData: UserVote = { - data: vote, - user_id: userId ?? undefined - }; - return this.http.post(`/rest/motions/motion-poll/${poll_id}/vote/`, requestData); - } -} diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 17ebcaa686..390210b6ed 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -177,7 +177,8 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo 'state_extension', 'recommendation_extension', 'agenda_item_id', // for add/remove from agenda, - 'amendment_paragraph_$' + 'amendment_paragraph_$', + 'poll_ids' ]); const amendmentFields: (keyof Motion)[] = listFields.concat(['lead_motion_id', 'amendment_ids']); const callListFields: (keyof Motion)[] = titleFields.concat(['sort_weight', 'sort_parent_id']); diff --git a/client/src/app/core/repositories/motions/motion-vote-repository.service.spec.ts b/client/src/app/core/repositories/motions/motion-vote-repository.service.spec.ts deleted file mode 100644 index b9ab4de0a6..0000000000 --- a/client/src/app/core/repositories/motions/motion-vote-repository.service.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { E2EImportsModule } from 'e2e-imports.module'; - -import { MotionVoteRepositoryService } from './motion-vote-repository.service'; - -describe('MotionVoteRepositoryService', () => { - beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] })); - - it('should be created', () => { - const service: MotionVoteRepositoryService = TestBed.inject(MotionVoteRepositoryService); - expect(service).toBeTruthy(); - }); -}); diff --git a/client/src/app/core/repositories/polls/option-repository.service.ts b/client/src/app/core/repositories/polls/option-repository.service.ts new file mode 100644 index 0000000000..e67c3e456c --- /dev/null +++ b/client/src/app/core/repositories/polls/option-repository.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; + +import { PollAction } from 'app/core/actions/poll-action'; +import { DEFAULT_FIELDSET, Fieldsets } from 'app/core/core-services/model-request-builder.service'; +import { Option } from 'app/shared/models/poll/option'; +import { ViewOption } from 'app/shared/models/poll/view-option'; +import { BaseRepositoryWithActiveMeeting } from '../base-repository-with-active-meeting'; +import { RepositoryServiceCollector } from '../repository-service-collector'; + +/** + * Repository Service for Options. + * + * Documentation partially provided in {@link BaseRepository} + */ +@Injectable({ + providedIn: 'root' +}) +export class OptionRepositoryService extends BaseRepositoryWithActiveMeeting { + public constructor(repositoryServiceCollector: RepositoryServiceCollector) { + super(repositoryServiceCollector, Option); + } + + public getTitle = (_viewOption: ViewOption) => { + return 'Option'; + }; + + public getVerboseName = (plural: boolean = false) => { + return this.translate.instant(plural ? 'Options' : 'Option'); + }; + + public getFieldsets(): Fieldsets