Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
69bae43
New vote service (#5534)
bastianjoel May 7, 2026
540385a
Rename Ballot to PollBallot (#6161)
bastianjoel May 12, 2026
4a32f08
Scaffold all neccessary components for new vote service (#6177)
bastianjoel May 22, 2026
8f8c2d7
Poll result views (#6250)
bastianjoel Jun 1, 2026
a7393ec
Merge remote-tracking branch 'upstream/main' into feature/vote
bastianjoel Jun 1, 2026
a8b3e38
Update meta
bastianjoel Jun 1, 2026
b9dae9e
Vote detail view (#6251)
Elblinator Jun 23, 2026
e52b689
Poll pdf exports (#6268)
bastianjoel Jul 1, 2026
9de8bfb
Add new poll permissions to settings (#6347)
bastianjoel Jul 7, 2026
6c6ffae
Added frontent for maximum available yes votes (#6390)
potatojuicemachine Jul 13, 2026
ae43dca
WIP: Implement analog polls (#6373)
bastianjoel Jul 14, 2026
cfd29a2
Merge commit 'e8dcb5fe2cb35d1d860d849a06239be464d8e5b7' into feature/…
bastianjoel Jul 14, 2026
9b1c061
Merge remote-tracking branch 'upstream/main' into feature/vote
bastianjoel Jul 14, 2026
5796170
Poll defaults meeting settings (#6409)
bastianjoel Jul 16, 2026
4e81c56
Split ballot from ballot users (#6420)
bastianjoel Jul 17, 2026
a0e95be
New vote service (#6421)
bastianjoel Jul 21, 2026
e4f30ae
Merge commit 'c3aa7ee0f7959b33f0331e0632026d3e15c85fcb' into feature/…
bastianjoel Jul 22, 2026
9d27a53
Merge commit '6609b288e6d15c0864c14eb1de8283b69e99d8bb' into feature/…
bastianjoel Jul 22, 2026
ff93e0a
Merge branch 'main' into feature/vote
bastianjoel Jul 22, 2026
4204b4c
Merge branch 'main' into feature/vote
bastianjoel Jul 22, 2026
df95376
Fix merge commit
bastianjoel Jul 22, 2026
745149f
Use poll meeting setting defaults (#6418)
Elblinator Jul 22, 2026
2190221
Poll edit (#6436)
bastianjoel Jul 22, 2026
2e11f3d
Fix create forms treated as started poll edit
bastianjoel Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 8 additions & 8 deletions client/cli/generate-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { overloadJsFunctions } from '@app/infrastructure/utils/overload-js-funct

overloadJsFunctions();

const SOURCE = path.resolve(path.join(__dirname, '../meta/permission.yml'));
const SOURCE = path.resolve(path.join(__dirname, '../../meta/permission.yml'));

const BASE_PATH = path.resolve(path.join(__dirname, `..`));
const DOMAIN_DEFINITIONS_PATH = `src/app/domain/definitions`;
Expand All @@ -33,21 +33,21 @@ const FILE_TEMPLATE_PERMISSION_RELATIONS = dedent`
import { PermissionsMap } from './permission.config';
`;

const permissionMap: { [permission: string]: PermissionNode } = {};
const permissionMap: Record<string, PermissionNode> = {};

/**
* Helper class to generate the permission graph. Holds information about its permission as well as
* its parents and children.
*/
class PermissionNode {
parents: PermissionNode[] = [];
children: PermissionNode[] = [];
permission: string;
enumKey: string;
public parents: PermissionNode[] = [];
public children: PermissionNode[] = [];
public permission: string;
public enumKey: string;

public constructor(permission: string) {
this.permission = permission;
this.enumKey = permission.replace(/[_\.]([a-z])/g, (_, c) => c.toUpperCase());
this.enumKey = permission.replace(/[_.]([a-z])/g, (_, c) => c.toUpperCase());
}
}

Expand Down Expand Up @@ -137,7 +137,7 @@ function writePermissionRelations(nodes: PermissionNode[]): void {
* @returns a set of all relatives of the given node in the given direction
*/
function getPermissionRelatives(node: PermissionNode, field: PermissionNodeRelativesField): Set<PermissionNode> {
let relatives = new Set<PermissionNode>();
const relatives = new Set<PermissionNode>();
for (const relative of node[field]) {
relatives.add(relative);
relatives.update(getPermissionRelatives(relative, field));
Expand Down
22 changes: 22 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"@tsparticles/plugin-emitters": "^3.9.1",
"@tsparticles/shape-text": "^3.9.1",
"@tsparticles/slim": "^3.9.1",
"big.js": "^7.0.1",
"chart.js": "^4.5.1",
"cm-chess": "^3.6.3",
"cm-chessboard": "^8.12.14",
Expand Down Expand Up @@ -108,6 +109,7 @@
"@angular/compiler-cli": "^22.0.7",
"@angular/language-server": "^22.0.7",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/big.js": "^7.0.0",
"@types/file-saver": "^2.0.7",
"@types/jasmine": "~5.1.15",
"@types/js-yaml": "^4.0.9",
Expand Down
10 changes: 8 additions & 2 deletions client/src/app/domain/definitions/meeting-settings-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ export const meetingSettingsDefaults: Record<string, any> = {
motions_amendments_prefix: '-Ä',
motions_amendments_text_mode: 'paragraph',
motions_supporters_min_amount: 0,
poll_default_live_voting_enabled: false,
motions_enable_origin_motion_display: false,
motions_origin_motion_toggle_default: false,
assignment_poll_default_method: 'selection',
assignment_poll_add_candidates_to_list_of_speakers: false,
poll_enable_max_votes_per_option: false,
poll_default_live_voting_enabled: false,
poll_default_allow_invalid: false,
poll_default_allow_vote_split: false,
poll_projection_name_order_first: 'last_name',
poll_projection_max_columns: 6,
topic_poll_default_method: 'selection',
assignment_poll_default_method: 'selection',
users_allow_self_set_present: true,
users_enable_presence_view: false,
users_enable_vote_weight: false,
Expand Down
35 changes: 28 additions & 7 deletions client/src/app/domain/definitions/permission-relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ import { PermissionsMap } from './permission.config';

export const permissionChildren: PermissionsMap = {
'agenda_item.can_manage': [Permission.agendaItemCanSeeInternal, Permission.agendaItemCanSee],
'agenda_item.can_manage_polls': [
Permission.agendaItemCanSeePolls,
Permission.agendaItemCanSeeInternal,
Permission.agendaItemCanSee
],
'agenda_item.can_see': [],
'agenda_item.can_see_internal': [Permission.agendaItemCanSee],
'agenda_item.can_see_polls': [Permission.agendaItemCanSeeInternal, Permission.agendaItemCanSee],
'assignment.can_manage': [Permission.assignmentCanNominateOther, Permission.assignmentCanSee],
'assignment.can_manage_polls': [Permission.assignmentCanSee],
'assignment.can_manage_polls': [Permission.assignmentCanSeePolls, Permission.assignmentCanSee],
'assignment.can_nominate_other': [Permission.assignmentCanSee],
'assignment.can_nominate_self': [Permission.assignmentCanSee],
'assignment.can_see': [],
'assignment.can_see_polls': [Permission.assignmentCanSee],
'chat.can_manage': [],
'list_of_speakers.can_be_speaker': [],
'list_of_speakers.can_manage': [Permission.listOfSpeakersCanSee],
Expand All @@ -37,12 +44,12 @@ export const permissionChildren: PermissionsMap = {
Permission.motionCanForward
],
'motion.can_manage_metadata': [Permission.motionCanSee],
'motion.can_manage_polls': [Permission.motionCanSee],
'motion.can_manage_polls': [Permission.motionCanSeePolls, Permission.motionCanSee],
'motion.can_see': [],
'motion.can_see_internal': [Permission.motionCanSee],
'motion.can_see_origin': [Permission.motionCanSee],
'motion.can_see_polls': [Permission.motionCanSee],
'motion.can_support': [Permission.motionCanSee],
'poll.can_manage': [Permission.pollCanSeeProgress],
'poll.can_see_progress': [],
'projector.can_manage': [Permission.projectorCanSee],
'projector.can_see': [],
Expand All @@ -62,18 +69,31 @@ export const permissionChildren: PermissionsMap = {

export const permissionParents: PermissionsMap = {
'agenda_item.can_manage': [],
'agenda_item.can_see': [Permission.agendaItemCanSeeInternal, Permission.agendaItemCanManage],
'agenda_item.can_see_internal': [Permission.agendaItemCanManage],
'agenda_item.can_manage_polls': [],
'agenda_item.can_see': [
Permission.agendaItemCanSeeInternal,
Permission.agendaItemCanManage,
Permission.agendaItemCanSeePolls,
Permission.agendaItemCanManagePolls
],
'agenda_item.can_see_internal': [
Permission.agendaItemCanManage,
Permission.agendaItemCanSeePolls,
Permission.agendaItemCanManagePolls
],
'agenda_item.can_see_polls': [Permission.agendaItemCanManagePolls],
'assignment.can_manage': [],
'assignment.can_manage_polls': [],
'assignment.can_nominate_other': [Permission.assignmentCanManage],
'assignment.can_nominate_self': [],
'assignment.can_see': [
Permission.assignmentCanNominateOther,
Permission.assignmentCanManage,
Permission.assignmentCanSeePolls,
Permission.assignmentCanManagePolls,
Permission.assignmentCanNominateSelf
],
'assignment.can_see_polls': [Permission.assignmentCanManagePolls],
'chat.can_manage': [],
'list_of_speakers.can_be_speaker': [],
'list_of_speakers.can_manage': [],
Expand Down Expand Up @@ -101,15 +121,16 @@ export const permissionParents: PermissionsMap = {
Permission.motionCanCreate,
Permission.motionCanCreateAmendments,
Permission.motionCanForward,
Permission.motionCanSeePolls,
Permission.motionCanManagePolls,
Permission.motionCanSupport,
Permission.motionCanSeeOrigin
],
'motion.can_see_internal': [Permission.motionCanManage],
'motion.can_see_origin': [],
'motion.can_see_polls': [Permission.motionCanManagePolls],
'motion.can_support': [],
'poll.can_manage': [],
'poll.can_see_progress': [Permission.pollCanManage],
'poll.can_see_progress': [],
'projector.can_manage': [],
'projector.can_see': [Permission.projectorCanManage],
'tag.can_manage': [],
Expand Down
22 changes: 21 additions & 1 deletion client/src/app/domain/definitions/permission.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,12 @@ Optional combination of requests to speak with presence status is possible. ( >
{
display_name: _(`Can manage polls`),
help_text: _(`Can create, change, start/stop and delete polls.`),
value: Permission.pollCanManage
value: Permission.agendaItemCanManagePolls
},
{
display_name: _(`Can see ballot results`),
help_text: _(`Can see unpublished ballot results.`),
value: Permission.agendaItemCanSeePolls
},
{
display_name: _(`Can see moderation notes`),
Expand Down Expand Up @@ -180,6 +185,11 @@ Further requirements:
display_name: _(`Can manage motion polls`),
help_text: _(`Can create, modify, start/stop and delete votings.`),
value: Permission.motionCanManagePolls
},
{
display_name: _(`Can see ballot results`),
help_text: _(`Can see unpublished ballot results.`),
value: Permission.motionCanSeePolls
}
]
},
Expand Down Expand Up @@ -208,6 +218,11 @@ Note: The right to vote is defined directly in the ballot.`
help_text: _(`Can create, modify, start/stop and delete ballots.`),
value: Permission.assignmentCanManagePolls
},
{
display_name: _(`Can see ballot results`),
help_text: _(`Can see unpublished ballot results.`),
value: Permission.assignmentCanSeePolls
},
{
display_name: _(`Can nominate another participant`),
help_text: _(`Can nominate other participants as candidates.
Expand Down Expand Up @@ -335,6 +350,11 @@ Note: For privacy reasons, it is recommended to limit the rights to view the His

Note: The chat menu item becomes visible to all participants, except admins, as soon as a chat has been created.`),
value: Permission.chatCanManage
},
{
display_name: _(`Can see poll progress`),
help_text: _(`Can see the progress bar of a running poll.`),
value: Permission.pollCanSeeProgress
}
]
}
Expand Down
5 changes: 4 additions & 1 deletion client/src/app/domain/definitions/permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
*/
export enum Permission {
agendaItemCanManage = `agenda_item.can_manage`,
agendaItemCanManagePolls = `agenda_item.can_manage_polls`,
agendaItemCanSee = `agenda_item.can_see`,
agendaItemCanSeeInternal = `agenda_item.can_see_internal`,
agendaItemCanSeePolls = `agenda_item.can_see_polls`,
assignmentCanManage = `assignment.can_manage`,
assignmentCanManagePolls = `assignment.can_manage_polls`,
assignmentCanNominateOther = `assignment.can_nominate_other`,
assignmentCanNominateSelf = `assignment.can_nominate_self`,
assignmentCanSee = `assignment.can_see`,
assignmentCanSeePolls = `assignment.can_see_polls`,
chatCanManage = `chat.can_manage`,
listOfSpeakersCanBeSpeaker = `list_of_speakers.can_be_speaker`,
listOfSpeakersCanManage = `list_of_speakers.can_manage`,
Expand All @@ -35,8 +38,8 @@ export enum Permission {
motionCanSee = `motion.can_see`,
motionCanSeeInternal = `motion.can_see_internal`,
motionCanSeeOrigin = `motion.can_see_origin`,
motionCanSeePolls = `motion.can_see_polls`,
motionCanSupport = `motion.can_support`,
pollCanManage = `poll.can_manage`,
pollCanSeeProgress = `poll.can_see_progress`,
projectorCanManage = `projector.can_manage`,
projectorCanSee = `projector.can_see`,
Expand Down
5 changes: 4 additions & 1 deletion client/src/app/domain/models/meeting-users/meeting-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ export class MeetingUser extends BaseDecimalModel<MeetingUser> {
public motion_supporter_ids!: Id[]; // (motion_supporter/meeting_user_id);
public submitted_motion_ids!: Id[]; // (motion_submitter/meeting_user_id)[];
public assignment_candidate_ids!: Id[]; // (assignment_candidate/meeting_user_id)[];
public vote_delegated_vote_ids!: Id[];
public vote_delegations_from_ids!: Id[]; // meeting_user/vote_delegated_to_id;
public acting_ballot_ids: Id[];
public represented_ballot_ids: Id[];
public chat_message_ids!: Id[]; // (chat_message/meeting_user_id)[];
public structure_level_ids!: Id[]; // structure_level/meeting_user_ids

Expand All @@ -52,6 +53,8 @@ export class MeetingUser extends BaseDecimalModel<MeetingUser> {
`assignment_candidate_ids`,
`vote_delegated_to_id`,
`vote_delegations_from_ids`,
`acting_ballot_ids`,
`represented_ballot_ids`,
`chat_message_ids`,
`group_ids`,
`structure_level_ids`
Expand Down
37 changes: 37 additions & 0 deletions client/src/app/domain/models/meetings/meeting-poll-default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { HasMeetingId } from '../../interfaces';
import { BaseModel } from '../base/base-model';
import { PollVisibility } from '../poll';
import { BaseOnehundredPercentBase } from '../poll/poll-config-types';

export class MeetingPollDefault extends BaseModel<MeetingPollDefault> {
public static COLLECTION = `meeting_poll_default`;

public sort_result_by_votes: boolean;

public allow_abstain!: boolean;
public allow_nota: boolean;
public visibility: PollVisibility;
public strike_out: boolean;
public onehundred_percent_base: BaseOnehundredPercentBase;
public group_ids: number[];
public display_chart: string;

public constructor(input?: Partial<MeetingPollDefault>) {
super(MeetingPollDefault.COLLECTION, input);
}

public static readonly REQUESTABLE_FIELDS: (keyof MeetingPollDefault)[] = [
`id`,
`sort_result_by_votes`,
`visibility`,
`allow_abstain`,
`allow_nota`,
`strike_out`,
`onehundred_percent_base`,
`group_ids`,
`display_chart`,
`meeting_id`
];
}

export interface MeetingPollDefault extends HasMeetingId {}
10 changes: 0 additions & 10 deletions client/src/app/domain/models/meetings/meeting.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@ import { PROJECTIONDEFAULTS, ProjectiondefaultValue } from '../projector/project

export type ExportCsvEncoding = `utf-8` | `iso-8859-15`;

/**
* Server side ballot choice definitions.
* Server-defined methods to determine the number of ballots to print
* Options are:
* - NUMBER_OF_DELEGATES Amount of users belonging to the predefined 'delegates' group (group id 2)
* - NUMBER_OF_ALL_PARTICIPANTS The amount of all registered users
* - CUSTOM_NUMBER a given number of ballots
*/
export type BallotPaperSelection = `NUMBER_OF_DELEGATES` | `NUMBER_OF_ALL_PARTICIPANTS` | `CUSTOM_NUMBER`;

export type ViewMeetingMediafileUsageKey = `logo_${LogoPlace}` | `font_${FontPlace}`;

export type MeetingMediafileUsageIdKey = `${ViewMeetingMediafileUsageKey}_id`;
Expand Down
Loading
Loading