Rework config defaults.
- move motion setttings from settings.py to config that user can change it while runtime - show help text for config fields as tooltip
This commit is contained in:
parent
ba5782a655
commit
f981106524
@ -100,9 +100,6 @@ _('Topics');
|
||||
_('General');
|
||||
_('Workflow of new motions');
|
||||
_('Workflow of new statute amendments');
|
||||
_('Numbered per category');
|
||||
_('Serially numbered');
|
||||
_('Set it manually');
|
||||
_('Motion preamble');
|
||||
_('The assembly may decide:');
|
||||
_('Default line numbering');
|
||||
@ -113,8 +110,9 @@ _('Reason required for creating new motion');
|
||||
_('Hide reason on projector');
|
||||
_('Hide meta information box on projector');
|
||||
_('Hide recommendation on projector');
|
||||
_('Show the sequential number for a motion');
|
||||
_('In motion list, motion detail and PDF.');
|
||||
_('Stop submitting new motions by non-staff users');
|
||||
_('Allow to disable versioning');
|
||||
_('Name of recommender');
|
||||
_(
|
||||
'Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.'
|
||||
@ -122,7 +120,15 @@ _(
|
||||
_('Name of recommender for statute amendments');
|
||||
_('Will be displayed as label before selected recommendation in statute amendments.');
|
||||
_('Default text version for change recommendations');
|
||||
_('Show the sequential number for a motion');
|
||||
_('Sort motions by');
|
||||
// subgroup Numbering
|
||||
_('Numbered per category');
|
||||
_('Serially numbered');
|
||||
_('Set it manually');
|
||||
_('Number of minimal digits for identifier');
|
||||
_('Uses leading zeros to sort motions correctly by identifier.');
|
||||
_('Allow blank in identifier');
|
||||
_("Blank between prefix and number, e.g. 'A 001'.");
|
||||
// subgroup Amendments
|
||||
_('Amendments');
|
||||
_('Activate statute amendments');
|
||||
@ -135,6 +141,7 @@ _('How to create new amendments');
|
||||
_('Empty text field');
|
||||
_('Edit the whole motion text');
|
||||
_('Paragraph-based, Diff-enabled');
|
||||
_('Allow amendments of amendments');
|
||||
// subgroup Supporters
|
||||
_('Supporters');
|
||||
_('Number of (minimum) required supporters for a motion');
|
||||
@ -165,7 +172,6 @@ _('Title for PDF documents of motions');
|
||||
_('Preamble text for PDF documents of motions');
|
||||
_('Show submitters and recommendation in table of contents');
|
||||
_('Show checkbox to record decision');
|
||||
_('Sort motions by');
|
||||
// misc motion strings
|
||||
_('Amendment');
|
||||
_('Statute amendment for');
|
||||
@ -340,7 +346,7 @@ _('Sender name');
|
||||
_('The sender address is defined in the OpenSlides server settings and should modified by administrator only.');
|
||||
_('Reply address');
|
||||
_('Email subject');
|
||||
_('Your login for {event_name}');
|
||||
_('OpenSlides access data');
|
||||
_('You can use {event_name} and {username} as placeholder.');
|
||||
_('Email body');
|
||||
_(
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
<!-- required for all kinds of input -->
|
||||
<mat-label>{{ configItem.label | translate }}</mat-label>
|
||||
<mat-hint *ngIf="configItem.helpText">{{ configItem.helpText | translate }}</mat-hint>
|
||||
<span matSuffix>
|
||||
<mat-icon pull="right" class="text-success" *ngIf="updateSuccessIcon">check_circle</mat-icon>
|
||||
</span>
|
||||
@ -44,9 +43,6 @@
|
||||
<div class="config-form-group" *ngIf="isExcludedType(configItem.inputType)">
|
||||
<div *ngIf="configItem.inputType === 'boolean'">
|
||||
<mat-checkbox formControlName="value">{{ configItem.label | translate }}</mat-checkbox>
|
||||
<mat-hint class="hint" *ngIf="configItem.helpText && !error">
|
||||
{{ configItem.helpText | translate }}
|
||||
</mat-hint>
|
||||
<div class="error" *ngIf="error">{{ error }}</div>
|
||||
</div>
|
||||
|
||||
@ -60,9 +56,6 @@
|
||||
placeholder="{{ configItem.label | translate }}"
|
||||
[value]="translatedValue"
|
||||
></textarea>
|
||||
<mat-hint class="hint" *ngIf="configItem.helpText && !error">
|
||||
{{ configItem.helpText | translate }}
|
||||
</mat-hint>
|
||||
<span matSuffix>
|
||||
<mat-icon pull="right" class="text-success" *ngIf="updateSuccessIcon">
|
||||
check_circle
|
||||
@ -79,7 +72,6 @@
|
||||
/>
|
||||
|
||||
<mat-label>{{ configItem.label | translate }}</mat-label>
|
||||
<mat-hint *ngIf="configItem.helpText">{{ configItem.helpText | translate }}</mat-hint>
|
||||
<span matSuffix>
|
||||
<mat-icon pull="right" class="text-success" *ngIf="updateSuccessIcon">check_circle</mat-icon>
|
||||
</span>
|
||||
@ -105,6 +97,11 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="reset-button">
|
||||
<button mat-icon-button *ngIf="configItem.helpText" matTooltip="{{ configItem.helpText | translate }}">
|
||||
<mat-icon>help_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="reset-button">
|
||||
<button mat-icon-button *ngIf="hasDefault()" matTooltip="{{ 'Reset' | translate }}" (click)="onResetButton()">
|
||||
<mat-icon>replay</mat-icon>
|
||||
|
@ -1,14 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { ConstantsService } from 'app/core/core-services/constants.service';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { ViewMotion } from '../models/view-motion';
|
||||
|
||||
interface Settings {
|
||||
MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS: boolean;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@ -17,11 +12,7 @@ export class LocalPermissionsService {
|
||||
private amendmentEnabled: boolean;
|
||||
private amendmentOfAmendment: boolean;
|
||||
|
||||
public constructor(
|
||||
private operator: OperatorService,
|
||||
private configService: ConfigService,
|
||||
private constants: ConstantsService
|
||||
) {
|
||||
public constructor(private operator: OperatorService, private configService: ConfigService) {
|
||||
// load config variables
|
||||
this.configService
|
||||
.get<number>('motions_min_supporters')
|
||||
@ -29,9 +20,9 @@ export class LocalPermissionsService {
|
||||
this.configService
|
||||
.get<boolean>('motions_amendments_enabled')
|
||||
.subscribe(enabled => (this.amendmentEnabled = enabled));
|
||||
this.constants
|
||||
.get<Settings>('Settings')
|
||||
.subscribe(settings => (this.amendmentOfAmendment = settings.MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS));
|
||||
this.configService
|
||||
.get<boolean>('motions_amendments_of_amendments')
|
||||
.subscribe(enabled => (this.amendmentOfAmendment = enabled));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,14 +9,30 @@ def get_config_variables():
|
||||
|
||||
It has to be evaluated during app loading (see apps.py).
|
||||
"""
|
||||
|
||||
# General
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_start_event_date_time",
|
||||
default_value=None,
|
||||
input_type="datetimepicker",
|
||||
label="Begin of event",
|
||||
help_text="Input format: DD.MM.YYYY HH:MM",
|
||||
weight=200,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
# Numbering
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_enable_numbering",
|
||||
label="Enable numbering for agenda items",
|
||||
input_type="boolean",
|
||||
default_value=True,
|
||||
weight=200,
|
||||
weight=205,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
subgroup="Numbering",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -24,28 +40,12 @@ def get_config_variables():
|
||||
default_value="",
|
||||
label="Numbering prefix for agenda items",
|
||||
help_text="This prefix will be set if you run the automatic agenda numbering.",
|
||||
weight=210,
|
||||
weight=206,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
subgroup="Numbering",
|
||||
validators=(MaxLengthValidator(20),),
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_item_creation",
|
||||
label="Add to agenda",
|
||||
default_value="always",
|
||||
input_type="choice",
|
||||
choices=(
|
||||
{"value": "always", "display_name": "Always"},
|
||||
{"value": "never", "display_name": "Never"},
|
||||
{"value": "default_yes", "display_name": "Ask, default yes"},
|
||||
{"value": "default_no", "display_name": "Ask, default no"},
|
||||
),
|
||||
weight=212,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_numeral_system",
|
||||
default_value="arabic",
|
||||
@ -55,30 +55,27 @@ def get_config_variables():
|
||||
{"value": "arabic", "display_name": "Arabic"},
|
||||
{"value": "roman", "display_name": "Roman"},
|
||||
),
|
||||
weight=215,
|
||||
weight=207,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
subgroup="Numbering",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_start_event_date_time",
|
||||
default_value=None,
|
||||
input_type="datetimepicker",
|
||||
label="Begin of event",
|
||||
help_text="Input format: DD.MM.YYYY HH:MM",
|
||||
weight=220,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
)
|
||||
# Visibility
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_hide_internal_items_on_projector",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Hide internal items when projecting subitems",
|
||||
weight=225,
|
||||
name="agenda_item_creation",
|
||||
label="Add to agenda",
|
||||
default_value="default_yes",
|
||||
input_type="choice",
|
||||
choices=(
|
||||
{"value": "always", "display_name": "Always"},
|
||||
{"value": "never", "display_name": "Never"},
|
||||
{"value": "default_yes", "display_name": "Ask, default yes"},
|
||||
{"value": "default_no", "display_name": "Ask, default no"},
|
||||
),
|
||||
weight=210,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
subgroup="Visibility",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -91,19 +88,29 @@ def get_config_variables():
|
||||
{"value": "3", "display_name": "Hidden item"},
|
||||
),
|
||||
label="Default visibility for new agenda items (except topics)",
|
||||
weight=227,
|
||||
weight=211,
|
||||
group="Agenda",
|
||||
subgroup="General",
|
||||
subgroup="Visibility",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_hide_internal_items_on_projector",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Hide internal items when projecting subitems",
|
||||
weight=212,
|
||||
group="Agenda",
|
||||
subgroup="Visibility",
|
||||
)
|
||||
|
||||
# List of speakers
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_show_last_speakers",
|
||||
default_value=1,
|
||||
default_value=0,
|
||||
input_type="integer",
|
||||
label="Number of last speakers to be shown on the projector",
|
||||
weight=230,
|
||||
weight=220,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
validators=(MinValueValidator(0),),
|
||||
@ -115,29 +122,39 @@ def get_config_variables():
|
||||
input_type="integer",
|
||||
label="Show orange countdown in the last x seconds of speaking time",
|
||||
help_text="Enter duration in seconds. Choose 0 to disable warning color.",
|
||||
weight=235,
|
||||
weight=221,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
validators=(MinValueValidator(0),),
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="projector_default_countdown",
|
||||
default_value=60,
|
||||
input_type="integer",
|
||||
label="Predefined seconds of new countdowns",
|
||||
weight=222,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_couple_countdown_and_speakers",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Couple countdown with the list of speakers",
|
||||
help_text="[Begin speech] starts the countdown, [End speech] stops the countdown.",
|
||||
weight=223,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_hide_amount_of_speakers",
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Hide the amount of speakers in subtitle of list of speakers slide",
|
||||
weight=236,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="agenda_couple_countdown_and_speakers",
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Couple countdown with the list of speakers",
|
||||
help_text="[Begin speech] starts the countdown, [End speech] stops the countdown.",
|
||||
weight=240,
|
||||
weight=224,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
)
|
||||
@ -147,7 +164,7 @@ def get_config_variables():
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Only present participants can be added to the list of speakers",
|
||||
weight=250,
|
||||
weight=225,
|
||||
group="Agenda",
|
||||
subgroup="List of speakers",
|
||||
)
|
||||
|
@ -67,16 +67,6 @@ def get_config_variables():
|
||||
subgroup="Ballot and ballot papers",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="assignments_add_candidates_to_list_of_speakers",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Put all candidates on the list of speakers",
|
||||
weight=428,
|
||||
group="Elections",
|
||||
subgroup="Ballot and ballot papers",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="assignments_pdf_ballot_papers_selection",
|
||||
default_value="CUSTOM_NUMBER",
|
||||
@ -103,12 +93,22 @@ def get_config_variables():
|
||||
default_value=8,
|
||||
input_type="integer",
|
||||
label="Custom number of ballot papers",
|
||||
weight=440,
|
||||
weight=435,
|
||||
group="Elections",
|
||||
subgroup="Ballot and ballot papers",
|
||||
validators=(MinValueValidator(1),),
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="assignments_add_candidates_to_list_of_speakers",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Put all candidates on the list of speakers",
|
||||
weight=440,
|
||||
group="Elections",
|
||||
subgroup="Ballot and ballot papers",
|
||||
)
|
||||
|
||||
# PDF
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -117,7 +117,7 @@ def get_config_variables():
|
||||
label="Title for PDF document (all elections)",
|
||||
weight=460,
|
||||
group="Elections",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -126,5 +126,5 @@ def get_config_variables():
|
||||
label="Preamble text for PDF document (all elections)",
|
||||
weight=470,
|
||||
group="Elections",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
@ -133,9 +133,6 @@ class CoreAppConfig(AppConfig):
|
||||
|
||||
# Client settings
|
||||
client_settings_keys = [
|
||||
"MOTION_IDENTIFIER_MIN_DIGITS",
|
||||
"MOTION_IDENTIFIER_WITHOUT_BLANKS",
|
||||
"MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS",
|
||||
"PRIORITIZED_GROUP_IDS",
|
||||
"PING_INTERVAL",
|
||||
"PING_TIMEOUT",
|
||||
|
@ -133,38 +133,6 @@ def get_config_variables():
|
||||
subgroup="System",
|
||||
)
|
||||
|
||||
# Projector
|
||||
|
||||
yield ConfigVariable(
|
||||
name="projector_language",
|
||||
default_value="browser",
|
||||
input_type="choice",
|
||||
label="Projector language",
|
||||
choices=(
|
||||
{"value": "browser", "display_name": "Current browser language"},
|
||||
{"value": "en", "display_name": "English"},
|
||||
{"value": "de", "display_name": "Deutsch"},
|
||||
{"value": "fr", "display_name": "Français"},
|
||||
{"value": "es", "display_name": "Español"},
|
||||
{"value": "pt", "display_name": "Português"},
|
||||
{"value": "cs", "display_name": "Čeština"},
|
||||
{"value": "ru", "display_name": "русский"},
|
||||
),
|
||||
weight=150,
|
||||
group="General",
|
||||
subgroup="Projector",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="projector_default_countdown",
|
||||
default_value=60,
|
||||
input_type="integer",
|
||||
label="Predefined seconds of new countdowns",
|
||||
weight=152,
|
||||
group="General",
|
||||
subgroup="Projector",
|
||||
)
|
||||
|
||||
# General export settings
|
||||
|
||||
yield ConfigVariable(
|
||||
|
@ -49,21 +49,6 @@ def get_config_variables():
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_identifier",
|
||||
default_value="per_category",
|
||||
input_type="choice",
|
||||
label="Identifier",
|
||||
choices=(
|
||||
{"value": "per_category", "display_name": "Numbered per category"},
|
||||
{"value": "serially_numbered", "display_name": "Serially numbered"},
|
||||
{"value": "manually", "display_name": "Set it manually"},
|
||||
),
|
||||
weight=315,
|
||||
group="Motions",
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_preamble",
|
||||
default_value="The assembly may decide:",
|
||||
@ -75,7 +60,7 @@ def get_config_variables():
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_default_line_numbering",
|
||||
default_value="none",
|
||||
default_value="outside",
|
||||
input_type="choice",
|
||||
label="Default line numbering",
|
||||
choices=(
|
||||
@ -90,7 +75,7 @@ def get_config_variables():
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_line_length",
|
||||
default_value=90,
|
||||
default_value=85,
|
||||
input_type="integer",
|
||||
label="Line length",
|
||||
help_text="The maximum number of characters per line. Relevant when line numbering is enabled. Min: 40",
|
||||
@ -140,6 +125,17 @@ def get_config_variables():
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_show_sequential_numbers",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Show the sequential number for a motion",
|
||||
help_text="In motion list, motion detail and PDF.",
|
||||
weight=328,
|
||||
group="Motions",
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_recommendations_by",
|
||||
default_value="",
|
||||
@ -162,7 +158,7 @@ def get_config_variables():
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_recommendation_text_mode",
|
||||
default_value="original",
|
||||
default_value="diff",
|
||||
input_type="choice",
|
||||
label="Default text version for change recommendations",
|
||||
choices=(
|
||||
@ -190,14 +186,43 @@ def get_config_variables():
|
||||
subgroup="General",
|
||||
)
|
||||
|
||||
# Numbering
|
||||
yield ConfigVariable(
|
||||
name="motions_show_sequential_numbers",
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Show the sequential number for a motion",
|
||||
weight=336,
|
||||
name="motions_identifier",
|
||||
default_value="per_category",
|
||||
input_type="choice",
|
||||
label="Identifier",
|
||||
choices=(
|
||||
{"value": "per_category", "display_name": "Numbered per category"},
|
||||
{"value": "serially_numbered", "display_name": "Serially numbered"},
|
||||
{"value": "manually", "display_name": "Set it manually"},
|
||||
),
|
||||
weight=340,
|
||||
group="Motions",
|
||||
subgroup="General",
|
||||
subgroup="Numbering",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_identifier_min_digits",
|
||||
default_value=1,
|
||||
input_type="integer",
|
||||
label="Number of minimal digits for identifier",
|
||||
help_text="Uses leading zeros to sort motions correctly by identifier.",
|
||||
weight=342,
|
||||
group="Motions",
|
||||
subgroup="Numbering",
|
||||
validators=(MinValueValidator(1),),
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_identifier_with_blank",
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Allow blank in identifier",
|
||||
help_text="Blank between prefix and number, e.g. 'A 001'.",
|
||||
weight=344,
|
||||
group="Motions",
|
||||
subgroup="Numbering",
|
||||
)
|
||||
|
||||
# Amendments
|
||||
@ -207,7 +232,7 @@ def get_config_variables():
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Activate statute amendments",
|
||||
weight=338,
|
||||
weight=350,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
@ -217,17 +242,17 @@ def get_config_variables():
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Activate amendments",
|
||||
weight=339,
|
||||
weight=351,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_amendments_main_table",
|
||||
default_value=False,
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Show amendments together with motions",
|
||||
weight=340,
|
||||
weight=352,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
@ -236,14 +261,14 @@ def get_config_variables():
|
||||
name="motions_amendments_prefix",
|
||||
default_value="-",
|
||||
label="Prefix for the identifier for amendments",
|
||||
weight=341,
|
||||
weight=353,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_amendments_text_mode",
|
||||
default_value="freestyle",
|
||||
default_value="paragraph",
|
||||
input_type="choice",
|
||||
label="How to create new amendments",
|
||||
choices=(
|
||||
@ -251,17 +276,27 @@ def get_config_variables():
|
||||
{"value": "fulltext", "display_name": "Edit the whole motion text"},
|
||||
{"value": "paragraph", "display_name": "Paragraph-based, Diff-enabled"},
|
||||
),
|
||||
weight=342,
|
||||
weight=354,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_amendments_multiple_paragraphs",
|
||||
default_value=False,
|
||||
default_value=True,
|
||||
input_type="boolean",
|
||||
label="Amendments can change multiple paragraphs",
|
||||
weight=343,
|
||||
weight=355,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
name="motions_amendments_of_amendments",
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Allow amendments of amendments",
|
||||
weight=356,
|
||||
group="Motions",
|
||||
subgroup="Amendments",
|
||||
)
|
||||
@ -274,7 +309,7 @@ def get_config_variables():
|
||||
input_type="integer",
|
||||
label="Number of (minimum) required supporters for a motion",
|
||||
help_text="Choose 0 to disable the supporting system.",
|
||||
weight=345,
|
||||
weight=360,
|
||||
group="Motions",
|
||||
subgroup="Supporters",
|
||||
validators=(MinValueValidator(0),),
|
||||
@ -285,7 +320,7 @@ def get_config_variables():
|
||||
default_value=False,
|
||||
input_type="boolean",
|
||||
label="Remove all supporters of a motion if a submitter edits his motion in early state",
|
||||
weight=350,
|
||||
weight=361,
|
||||
group="Motions",
|
||||
subgroup="Supporters",
|
||||
)
|
||||
@ -304,7 +339,7 @@ def get_config_variables():
|
||||
{"value": "CAST", "display_name": "All casted ballots"},
|
||||
{"value": "DISABLED", "display_name": "Disabled (no percents)"},
|
||||
),
|
||||
weight=355,
|
||||
weight=370,
|
||||
group="Motions",
|
||||
subgroup="Voting and ballot papers",
|
||||
)
|
||||
@ -317,7 +352,7 @@ def get_config_variables():
|
||||
choices=majorityMethods,
|
||||
label="Required majority",
|
||||
help_text="Default method to check whether a motion has reached the required majority.",
|
||||
weight=357,
|
||||
weight=372,
|
||||
group="Motions",
|
||||
subgroup="Voting and ballot papers",
|
||||
)
|
||||
@ -338,7 +373,7 @@ def get_config_variables():
|
||||
"display_name": "Use the following custom number",
|
||||
},
|
||||
),
|
||||
weight=360,
|
||||
weight=374,
|
||||
group="Motions",
|
||||
subgroup="Voting and ballot papers",
|
||||
)
|
||||
@ -348,7 +383,7 @@ def get_config_variables():
|
||||
default_value=8,
|
||||
input_type="integer",
|
||||
label="Custom number of ballot papers",
|
||||
weight=365,
|
||||
weight=376,
|
||||
group="Motions",
|
||||
subgroup="Voting and ballot papers",
|
||||
validators=(MinValueValidator(1),),
|
||||
@ -360,7 +395,7 @@ def get_config_variables():
|
||||
name="motions_export_title",
|
||||
default_value="Motions",
|
||||
label="Title for PDF documents of motions",
|
||||
weight=370,
|
||||
weight=380,
|
||||
group="Motions",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
@ -369,7 +404,7 @@ def get_config_variables():
|
||||
name="motions_export_preamble",
|
||||
default_value="",
|
||||
label="Preamble text for PDF documents of motions",
|
||||
weight=375,
|
||||
weight=382,
|
||||
group="Motions",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
@ -379,7 +414,7 @@ def get_config_variables():
|
||||
default_value=False,
|
||||
label="Show submitters and recommendation in table of contents",
|
||||
input_type="boolean",
|
||||
weight=378,
|
||||
weight=384,
|
||||
group="Motions",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
@ -389,7 +424,7 @@ def get_config_variables():
|
||||
default_value=False,
|
||||
label="Show checkbox to record decision",
|
||||
input_type="boolean",
|
||||
weight=379,
|
||||
weight=386,
|
||||
group="Motions",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
@ -332,26 +332,23 @@ class Motion(RESTModelMixin, AgendaItemWithListOfSpeakersMixin, models.Model):
|
||||
# Do not set an identifier.
|
||||
return
|
||||
|
||||
# If MOTION_IDENTIFIER_WITHOUT_BLANKS is set, don't use blanks when building identifier.
|
||||
without_blank = (
|
||||
hasattr(settings, "MOTION_IDENTIFIER_WITHOUT_BLANKS")
|
||||
and settings.MOTION_IDENTIFIER_WITHOUT_BLANKS
|
||||
)
|
||||
# If config 'motions_identifier_with_blank' is set, use blanks when building identifier.
|
||||
with_blank = config["motions_identifier_with_blank"]
|
||||
|
||||
# Build prefix.
|
||||
if self.is_amendment():
|
||||
parent_identifier = self.parent.identifier or ""
|
||||
if without_blank:
|
||||
prefix = f"{parent_identifier}{config['motions_amendments_prefix']}"
|
||||
if with_blank:
|
||||
prefix = f"{parent_identifier} {config['motions_amendments_prefix']}"
|
||||
else:
|
||||
prefix = f"{parent_identifier} {config['motions_amendments_prefix']} "
|
||||
prefix = f"{parent_identifier}{config['motions_amendments_prefix']}"
|
||||
elif self.category is None or not self.category.prefix:
|
||||
prefix = ""
|
||||
else:
|
||||
if without_blank:
|
||||
prefix = self.category.prefix
|
||||
else:
|
||||
if with_blank:
|
||||
prefix = f"{self.category.prefix} "
|
||||
else:
|
||||
prefix = self.category.prefix
|
||||
self._identifier_prefix = prefix
|
||||
|
||||
# Use the already assigned identifier_number, if the motion has one.
|
||||
@ -402,20 +399,16 @@ class Motion(RESTModelMixin, AgendaItemWithListOfSpeakersMixin, models.Model):
|
||||
def extend_identifier_number(cls, number):
|
||||
"""
|
||||
Returns the number used in the set_identifier method with leading
|
||||
zero charaters according to the settings value
|
||||
MOTION_IDENTIFIER_MIN_DIGITS.
|
||||
zero charaters according to the config value.
|
||||
"""
|
||||
result = str(number)
|
||||
if (
|
||||
hasattr(settings, "MOTION_IDENTIFIER_MIN_DIGITS")
|
||||
and settings.MOTION_IDENTIFIER_MIN_DIGITS
|
||||
):
|
||||
if not isinstance(settings.MOTION_IDENTIFIER_MIN_DIGITS, int):
|
||||
if config["motions_identifier_min_digits"]:
|
||||
if not isinstance(config["motions_identifier_min_digits"], int):
|
||||
raise ImproperlyConfigured(
|
||||
"Settings value MOTION_IDENTIFIER_MIN_DIGITS must be an integer."
|
||||
"Config value 'motions_identifier_min_digits' must be an integer."
|
||||
)
|
||||
result = (
|
||||
"0" * (settings.MOTION_IDENTIFIER_MIN_DIGITS - len(str(number)))
|
||||
"0" * (config["motions_identifier_min_digits"] - len(str(number)))
|
||||
+ result
|
||||
)
|
||||
return result
|
||||
|
@ -44,7 +44,7 @@ def get_config_variables():
|
||||
label="Title for access data and welcome PDF",
|
||||
weight=520,
|
||||
group="Participants",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -53,7 +53,7 @@ def get_config_variables():
|
||||
label="Help text for access data and welcome PDF",
|
||||
weight=530,
|
||||
group="Participants",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
# TODO: Use Django's URLValidator here.
|
||||
@ -64,7 +64,7 @@ def get_config_variables():
|
||||
help_text="Used for QRCode in PDF of access data.",
|
||||
weight=540,
|
||||
group="Participants",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -74,7 +74,7 @@ def get_config_variables():
|
||||
help_text="Used for WLAN QRCode in PDF of access data.",
|
||||
weight=550,
|
||||
group="Participants",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -84,7 +84,7 @@ def get_config_variables():
|
||||
help_text="Used for WLAN QRCode in PDF of access data.",
|
||||
weight=560,
|
||||
group="Participants",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
yield ConfigVariable(
|
||||
@ -101,7 +101,7 @@ def get_config_variables():
|
||||
),
|
||||
weight=570,
|
||||
group="Participants",
|
||||
subgroup="PDF",
|
||||
subgroup="PDF export",
|
||||
)
|
||||
|
||||
# Email
|
||||
@ -129,7 +129,7 @@ def get_config_variables():
|
||||
|
||||
yield ConfigVariable(
|
||||
name="users_email_subject",
|
||||
default_value="Your login for {event_name}",
|
||||
default_value="OpenSlides access data",
|
||||
input_type="string",
|
||||
label="Email subject",
|
||||
help_text="You can use {event_name} and {username} as placeholder.",
|
||||
|
@ -170,10 +170,3 @@ LOGGING = {
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# Customization of OpenSlides apps
|
||||
|
||||
MOTION_IDENTIFIER_MIN_DIGITS = 1
|
||||
MOTION_IDENTIFIER_WITHOUT_BLANKS = False
|
||||
MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS = True
|
||||
|
@ -1,7 +1,6 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
@ -295,7 +294,7 @@ class CreateMotion(TestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
motion = Motion.objects.get()
|
||||
self.assertEqual(motion.category, category)
|
||||
self.assertEqual(motion.identifier, "TEST_PREFIX_la0eadaewuec3seoxeiN 1")
|
||||
self.assertEqual(motion.identifier, "TEST_PREFIX_la0eadaewuec3seoxeiN1")
|
||||
|
||||
def test_with_submitters(self):
|
||||
submitter_1 = get_user_model().objects.create_user(
|
||||
@ -1942,24 +1941,25 @@ class NumberMotionsInCategories(TestCase):
|
||||
|
||||
def test_with_blanks(self):
|
||||
config["motions_amendments_prefix"] = "-X"
|
||||
settings.MOTION_IDENTIFIER_WITHOUT_BLANKS = True
|
||||
settings.MOTION_IDENTIFIER_MIN_DIGITS = 3
|
||||
config["motions_identifier_with_blank"] = False
|
||||
config["motions_identifier_min_digits"] = 3
|
||||
response = self.client.post(reverse("category-numbering", args=[self.A.pk]))
|
||||
settings.MOTION_IDENTIFIER_WITHOUT_BLANKS = False
|
||||
settings.MOTION_IDENTIFIER_MIN_DIGITS = 1
|
||||
config["motions_identifier_with_blank"] = True
|
||||
config["motions_identifier_min_digits"] = 1
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(Motion.objects.get(pk=self.M1.pk).identifier, "test_A001")
|
||||
self.assertEqual(Motion.objects.get(pk=self.M3.pk).identifier, "test_A002")
|
||||
self.assertEqual(Motion.objects.get(pk=self.M2.pk).identifier, "test_C003")
|
||||
self.assertEqual(Motion.objects.get(pk=self.M1.pk).identifier, "test_A 001")
|
||||
self.assertEqual(Motion.objects.get(pk=self.M3.pk).identifier, "test_A 002")
|
||||
self.assertEqual(Motion.objects.get(pk=self.M2.pk).identifier, "test_C 003")
|
||||
self.assertEqual(
|
||||
Motion.objects.get(pk=self.M2_A1.pk).identifier, "test_C003-X002"
|
||||
Motion.objects.get(pk=self.M2_A1.pk).identifier, "test_C 003 -X 002"
|
||||
)
|
||||
self.assertEqual(
|
||||
Motion.objects.get(pk=self.M2_A1_A1.pk).identifier, "test_C003-X002-X001"
|
||||
Motion.objects.get(pk=self.M2_A1_A1.pk).identifier,
|
||||
"test_C 003 -X 002 -X 001",
|
||||
)
|
||||
self.assertEqual(
|
||||
Motion.objects.get(pk=self.M2_A2.pk).identifier, "test_C003-X001"
|
||||
Motion.objects.get(pk=self.M2_A2.pk).identifier, "test_C 003 -X 001"
|
||||
)
|
||||
|
||||
def test_existing_identifier_no_category(self):
|
||||
|
@ -89,13 +89,14 @@ class ModelTest(TestCase):
|
||||
parent + a suffix.
|
||||
"""
|
||||
config["motions_amendments_enabled"] = True
|
||||
config["motions_identifier_with_blank"] = False
|
||||
self.motion.identifier = "Parent identifier"
|
||||
self.motion.save()
|
||||
motion = Motion(parent=self.motion)
|
||||
|
||||
motion.set_identifier()
|
||||
|
||||
self.assertEqual(motion.identifier, "Parent identifier - 1")
|
||||
self.assertEqual(motion.identifier, "Parent identifier-1")
|
||||
|
||||
def test_set_identifier_second_amendment(self):
|
||||
"""
|
||||
@ -110,4 +111,4 @@ class ModelTest(TestCase):
|
||||
|
||||
motion.set_identifier()
|
||||
|
||||
self.assertEqual(motion.identifier, "Parent identifier - 2")
|
||||
self.assertEqual(motion.identifier, "Parent identifier-2")
|
||||
|
@ -60,11 +60,6 @@ STATICFILES_DIRS.insert(0, os.path.join(OPENSLIDES_USER_DATA_PATH, "static")) #
|
||||
MEDIA_ROOT = os.path.join(OPENSLIDES_USER_DATA_PATH, "")
|
||||
|
||||
|
||||
# Customization of OpenSlides apps
|
||||
|
||||
MOTION_IDENTIFIER_MIN_DIGITS = 1
|
||||
|
||||
|
||||
# Special settings only for testing
|
||||
|
||||
# Use a faster password hasher.
|
||||
|
@ -295,8 +295,8 @@ async def test_motion_slide(all_data):
|
||||
"show_meta_box": True,
|
||||
"reason": "",
|
||||
"submitter": ["Administrator"],
|
||||
"line_length": 90,
|
||||
"line_numbering_mode": "none",
|
||||
"line_length": 85,
|
||||
"line_numbering_mode": "outside",
|
||||
"preamble": "The assembly may decide:",
|
||||
"recommendation_referencing_motions": None,
|
||||
}
|
||||
@ -321,8 +321,8 @@ async def test_amendment_slide(all_data):
|
||||
"show_meta_box": True,
|
||||
"reason": "",
|
||||
"submitter": ["Administrator"],
|
||||
"line_length": 90,
|
||||
"line_numbering_mode": "none",
|
||||
"line_length": 85,
|
||||
"line_numbering_mode": "outside",
|
||||
"preamble": "The assembly may decide:",
|
||||
"recommendation_referencing_motions": None,
|
||||
}
|
||||
@ -347,8 +347,8 @@ async def test_statute_amendment_slide(all_data):
|
||||
"show_meta_box": True,
|
||||
"reason": "",
|
||||
"submitter": ["Administrator"],
|
||||
"line_length": 90,
|
||||
"line_numbering_mode": "none",
|
||||
"line_length": 85,
|
||||
"line_numbering_mode": "outside",
|
||||
"preamble": "The assembly may decide:",
|
||||
"recommendation_referencing_motions": None,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user