Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1769] Replace deprecated djchoices with native Django choices #846

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ tinycss2
Django>=3.2.11<4.0
django-admin-index
django-axes
django-choices
django-filer
django-hijack
django-ordered-model
Expand Down
7 changes: 2 additions & 5 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,8 @@ django-better-admin-arrayfield==1.4.2
# via
# -r requirements/base.in
# mozilla-django-oidc-db
django-choices==1.7.2
# via
# -r requirements/base.in
# mail-editor
django-choices==2.0.0
# via mail-editor
django-ckeditor==6.2.0
# via mail-editor
django-classy-tags==4.0.0
Expand Down Expand Up @@ -474,7 +472,6 @@ six==1.16.0
# via
# click-repl
# django-appdata
# django-choices
# django-elasticsearch-dsl
# elasticsearch-dsl
# furl
Expand Down
3 changes: 1 addition & 2 deletions requirements/ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ django-better-admin-arrayfield==1.4.2
# -c requirements/base.txt
# -r requirements/base.txt
# mozilla-django-oidc-db
django-choices==1.7.2
django-choices==2.0.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
Expand Down Expand Up @@ -843,7 +843,6 @@ six==1.16.0
# -r requirements/base.txt
# click-repl
# django-appdata
# django-choices
# django-elasticsearch-dsl
# elasticsearch-dsl
# furl
Expand Down
3 changes: 1 addition & 2 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ django-better-admin-arrayfield==1.4.2
# -c requirements/ci.txt
# -r requirements/ci.txt
# mozilla-django-oidc-db
django-choices==1.7.2
django-choices==2.0.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
Expand Down Expand Up @@ -936,7 +936,6 @@ six==1.16.0
# -r requirements/ci.txt
# click-repl
# django-appdata
# django-choices
# django-elasticsearch-dsl
# elasticsearch-dsl
# furl
Expand Down
70 changes: 41 additions & 29 deletions src/open_inwoner/accounts/choices.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,55 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from djchoices import ChoiceItem, DjangoChoices


class LoginTypeChoices(DjangoChoices):
default = ChoiceItem("default", _("E-mail en Wachtwoord"))
digid = ChoiceItem("digid", _("DigiD"))
eherkenning = ChoiceItem("eherkenning", _("eHerkenning"))
oidc = ChoiceItem("oidc", _("OpenId connect"))
class LoginTypeChoices(models.TextChoices):
default = "default", _("E-mail en Wachtwoord")
digid = "digid", _("DigiD")
eherkenning = "eherkenning", _("eHerkenning")
oidc = "oidc", _("OpenId connect")


# Created because of a filter that needs to happen. This way the form can take the empty choice and the modal is still filled.
class AllEmptyChoice(DjangoChoices):
empty = ChoiceItem("", _("Alle"))
class AllEmptyChoice(models.TextChoices):
empty = "", _("Alle")


class ContactTypeChoices(DjangoChoices):
contact = ChoiceItem("contact", _("Contactpersoon"))
begeleider = ChoiceItem("begeleider", _("Begeleider"))
organization = ChoiceItem("organization", _("Organisatie"))
class ContactTypeChoices(models.TextChoices):
contact = "contact", _("Contactpersoon")
begeleider = "begeleider", _("Begeleider")
organization = "organization", _("Organisatie")


class EmptyContactTypeChoices(AllEmptyChoice, ContactTypeChoices):
pass
class EmptyContactTypeChoices(models.TextChoices):
stevenbal marked this conversation as resolved.
Show resolved Hide resolved
empty = "", _("Alle")
contact = "contact", _("Contactpersoon")
begeleider = "begeleider", _("Begeleider")
organization = "organization", _("Organisatie")


# Created because of a filter that needs to happen. This way the form can take the empty choice and the modal is still filled.
class StatusEmptyChoice(DjangoChoices):
empty = ChoiceItem("", _("Status"))
class StatusEmptyChoice(models.TextChoices):
empty = "", _("Status")


class StatusChoices(DjangoChoices):
open = ChoiceItem("open", _("Open"), icon="format_list_bulleted")
approval = ChoiceItem("approval", _("Accordering"), icon="question_mark")
closed = ChoiceItem("closed", _("Afgerond"), icon="check")
class StatusChoices(models.TextChoices):
open = "open", _("Open")
approval = "approval", _("Accordering")
closed = "closed", _("Afgerond")

# note the icons are names from Material Symbols and Icons - Google Fonts
@staticmethod
def get_icon_mapping():
return {
"open": "format_list_bulleted",
"approval": "question_mark",
"closed": "check",
}

@classmethod
def get_icon(cls, status, default="label"):
def get_icon(cls, status: str, default="label"):
if status in cls.values:
return cls.get_choice(status).icon
icon_mapping = cls.get_icon_mapping()
return icon_mapping[status]
else:
return default

Expand All @@ -49,10 +58,13 @@ def choices_with_icons(cls):
return [(value, label, cls.get_icon(value)) for value, label in cls.choices]


class EmptyStatusChoices(StatusEmptyChoice, StatusChoices):
pass
class EmptyStatusChoices(models.TextChoices):
pi-sigma marked this conversation as resolved.
Show resolved Hide resolved
empty = "", _("Status")
open = "open", _("Open")
approval = "approval", _("Accordering")
closed = "closed", _("Afgerond")


class TypeChoices(DjangoChoices):
incidental = ChoiceItem("incidental", _("Incidentieel"))
recurring = ChoiceItem("recurring", _("Terugkerend"))
class TypeChoices(models.TextChoices):
incidental = "incidental", _("Incidentieel")
recurring = "recurring", _("Terugkerend")
2 changes: 1 addition & 1 deletion src/open_inwoner/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ def get_contact_message_url(self) -> str:
return f"{url}#messages-last"

def get_contact_type_display(self) -> str:
choice = ContactTypeChoices.get_choice(self.contact_type)
choice = getattr(ContactTypeChoices, self.contact_type)
return choice.label

def get_contact_email(self):
Expand Down
27 changes: 11 additions & 16 deletions src/open_inwoner/accounts/tests/test_action_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,17 +354,16 @@ def test_action_status(self, mock_solo):
dropdown_button = action_element.locator(".actions__status-selector")
dropdown_content = action_element.locator(".dropdown__content")

# check state
expect(dropdown_button).to_contain_text(
str(StatusChoices.labels[StatusChoices.open])
)

# grab buttonss
status_closed_button = dropdown_content.get_by_role(
"button", name=str(StatusChoices.labels[StatusChoices.closed])
)
# labels are lazy; force evaluation by casting to `str`
approval_label = str(StatusChoices.approval.label)
closed_label = str(StatusChoices.closed.label)
stevenbal marked this conversation as resolved.
Show resolved Hide resolved
open_label = str(StatusChoices.open.label)

expect(dropdown_button).to_contain_text(open_label)
# grab buttons
status_closed_button = dropdown_content.get_by_role("button", name=closed_label)
status_approval_button = dropdown_content.get_by_role(
"button", name=str(StatusChoices.labels[StatusChoices.approval])
"button", name=approval_label
)
expect(status_approval_button).to_be_visible(visible=False)

Expand All @@ -376,9 +375,7 @@ def test_action_status(self, mock_solo):
status_approval_button.click()

# status should change to approval
expect(dropdown_button).to_contain_text(
str(StatusChoices.labels[StatusChoices.approval])
)
expect(dropdown_button).to_contain_text(approval_label)

# dropdown widget is closed
expect(status_approval_button).to_be_visible(visible=False)
Expand All @@ -395,9 +392,7 @@ def test_action_status(self, mock_solo):
status_closed_button.click()

# status should change
expect(dropdown_button).to_contain_text(
str(StatusChoices.labels[StatusChoices.closed])
)
expect(dropdown_button).to_contain_text(closed_label)

# dropdown widget is closed
expect(status_closed_button).to_be_visible(visible=False)
Expand Down
25 changes: 12 additions & 13 deletions src/open_inwoner/cms/extensions/constants.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from djchoices import ChoiceItem, DjangoChoices

class IndicatorChoices(models.TextChoices):
plan_new_contacts = "plan_new_contacts", _("Plans > new contacts")
inbox_new_messages = "inbox_new_messages", _("Inbox > new messages")

class IndicatorChoices(DjangoChoices):
plan_new_contacts = ChoiceItem("plan_new_contacts", _("Plans > new contacts"))
inbox_new_messages = ChoiceItem("inbox_new_messages", _("Inbox > new messages"))


class Icons(DjangoChoices):
person = ChoiceItem("person", _("Home"))
description = ChoiceItem("description", _("Products"))
inbox = ChoiceItem("inbox", _("Inbox"))
inventory_2 = ChoiceItem("inventory_2", _("Cases"))
group = ChoiceItem("group", _("Collaborate"))
help_outline = ChoiceItem("help_outline", _("Help"))
euro_outline = ChoiceItem("euro_outline", _("Benefits"))
class Icons(models.TextChoices):
person = "person", _("Home")
description = "description", _("Products")
inbox = "inbox", _("Inbox")
inventory_2 = "inventory_2", _("Cases")
group = "group", _("Collaborate")
help_outline = "help_outline", _("Help")
euro_outline = "euro_outline", _("Benefits")
1 change: 1 addition & 0 deletions src/open_inwoner/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
"open_inwoner.cms.footer",
"open_inwoner.cms.plugins",
"open_inwoner.cms.benefits",
"djchoices",
]

MIDDLEWARE = [
Expand Down
15 changes: 7 additions & 8 deletions src/open_inwoner/configurations/choices.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _

from djchoices import ChoiceItem, DjangoChoices

class ColorTypeChoices(models.TextChoices):
light = "#FFFFFF", _("light")
dark = "#4B4B4B", _("dark")

class ColorTypeChoices(DjangoChoices):
light = ChoiceItem("#FFFFFF", _("light"))
dark = ChoiceItem("#4B4B4B", _("dark"))


class OpenIDDisplayChoices(DjangoChoices):
admin = ChoiceItem("admin", _("Admin"))
regular = ChoiceItem("regular", _("Regular user"))
class OpenIDDisplayChoices(models.TextChoices):
admin = "admin", _("Admin")
regular = "regular", _("Regular user")
2 changes: 1 addition & 1 deletion src/open_inwoner/configurations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ class SiteConfiguration(SingletonModel):
openid_display = models.CharField(
verbose_name=_("Show option to login via OpenId"),
max_length=24,
choices=OpenIDDisplayChoices,
choices=OpenIDDisplayChoices.choices,
default=OpenIDDisplayChoices.admin,
validators=[validate_oidc_config],
help_text=_("Only selected groups will see the option to login via OpenId."),
Expand Down
98 changes: 46 additions & 52 deletions src/open_inwoner/custom_csp/constants.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,49 @@
from djchoices import ChoiceItem, DjangoChoices
from django.db import models


class CSPDirective(DjangoChoices):
class CSPDirective(models.TextChoices):
# via https://django-csp.readthedocs.io/en/latest/configuration.html
DEFAULT_SRC = ChoiceItem("default-src", label="default-src")
SCRIPT_SRC = ChoiceItem("script-src", label="script-src")
SCRIPT_SRC_ATTR = ChoiceItem("script-src-attr", label="script-src-attr")
SCRIPT_SRC_ELEM = ChoiceItem("script-src-elem", label="script-src-elem")
IMG_SRC = ChoiceItem("img-src", label="img-src")
OBJECT_SRC = ChoiceItem("object-src", label="object-src")
PREFETCH_SRC = ChoiceItem("prefetch-src", label="prefetch-src")
MEDIA_SRC = ChoiceItem("media-src", label="media-src")
FRAME_SRC = ChoiceItem("frame-src", label="frame-src")
FONT_SRC = ChoiceItem("font-src", label="font-src")
CONNECT_SRC = ChoiceItem("connect-src", label="connect-src")
STYLE_SRC = ChoiceItem("style-src", label="style-src")
STYLE_SRC_ATTR = ChoiceItem("style-src-attr", label="style-src-attr")
STYLE_SRC_ELEM = ChoiceItem("style-src-elem", label="style-src-elem")
BASE_URI = ChoiceItem(
"base-uri", label="base-uri"
) # Note: This doesn’t use default-src as a fall-back.
CHILD_SRC = ChoiceItem(
"child-src", label="child-src"
) # Note: Deprecated in CSP v3. Use frame-src and worker-src instead.
FRAME_ANCESTORS = ChoiceItem(
"frame-ancestors", label="frame-ancestors"
) # Note: This doesn’t use default-src as a fall-back.
NAVIGATE_TO = ChoiceItem(
"navigate-to", label="navigate-to"
) # Note: This doesn’t use default-src as a fall-back.
FORM_ACTION = ChoiceItem(
"form-action", label="form-action"
) # Note: This doesn’t use default-src as a fall-back.
SANDBOX = ChoiceItem(
"sandbox", label="sandbox"
) # Note: This doesn’t use default-src as a fall-back.
REPORT_URI = ChoiceItem(
"report-uri", label="report-uri"
) # Each URI can be a full or relative URI. None Note: This doesn’t use default-src as a fall-back.
REPORT_TO = ChoiceItem(
"report-to", label="report-to"
) # A string describing a reporting group. None Note: This doesn’t use default-src as a fall-back. See Section 1.2: https://w3c.github.io/reporting/#group
MANIFEST_SRC = ChoiceItem("manifest-src", label="manifest-src")
WORKER_SRC = ChoiceItem("worker-src", label="worker-src")
PLUGIN_TYPES = ChoiceItem(
"plugin-types", label="plugin-types"
) # Note: This doesn’t use default-src as a fall-back.
REQUIRE_SRI_FOR = ChoiceItem(
"require-sri-for", label="require-sri-for"
) # Valid values: script, style, or both. See: require-sri-for-known-tokens Note: This doesn’t use default-src as a fall-back.

# CSP_UPGRADE_INSECURE_REQUESTS # Include upgrade-insecure-requests directive. A boolean. False See: upgrade-insecure-requests
# CSP_BLOCK_ALL_MIXED_CONTENT # Include block-all-mixed-content directive. A boolean. False See: block-all-mixed-content
# CSP_INCLUDE_NONCE_IN # Include dynamically generated nonce in all listed directives, e.g. CSP_INCLUDE_NONCE_IN=['script-src'] will add 'nonce-<b64-value>' to the script-src directive.
DEFAULT_SRC = "default-src", "default-src"
SCRIPT_SRC = "script-src", "script-src"
SCRIPT_SRC_ATTR = "script-src-attr", "script-src-attr"
SCRIPT_SRC_ELEM = "script-src-elem", "script-src-elem"
IMG_SRC = "img-src", "img-src"
OBJECT_SRC = "object-src", "object-src"
PREFETCH_SRC = "prefetch-src", "prefetch-src"
MEDIA_SRC = "media-src", "media-src"
FRAME_SRC = "frame-src", "frame-src"
FONT_SRC = "font-src", "font-src"
CONNECT_SRC = "connect-src", "connect-src"
STYLE_SRC = "style-src", "style-src"
STYLE_SRC_ATTR = "style-src-attr", "style-src-attr"
STYLE_SRC_ELEM = "style-src-elem", "style-src-elem"
BASE_URI = (
"base-uri",
"base-uri",
) # Note: This doesn’t use default-src as a fall-back
CHILD_SRC = (
"child-src",
"child-src",
) # Note: This doesn’t use default-src as a fall-back
FRAME_ANCESTORS = (
"frame-ancestors",
"frame-ancestors",
) # Note: This doesn’t use default-src as a fall-back
NAVIGATE_TO = (
"navigate-to",
"navigate-to",
) # Note: This doesn’t use default-src as a fall-back
FORM_ACTION = (
"form-action",
"form-action",
) # Note: This doesn’t use default-src as a fall-back
SANDBOX = "sandbox", "sandbox" # Note: This doesn’t use default-src as a fall-back
REPORT_URI = (
"report-uri",
"report-uri",
) # Note: This doesn’t use default-src as a fall-back
REPORT_TO = "report-to", "report-to"
MANIFEST_SRC = "manifest-src", "manifest-src"
WORKER_SRC = "worker-src", "worker-src"
PLUGIN_TYPES = "plugin-types", "plugin-types"
REQUIRE_SRI_FOR = "require-sri-for", "require-sri-for"
Loading
Loading