stages/prompt: add static and separator elements
This commit is contained in:
parent
e58ac7ae90
commit
ef2cdf27b3
|
@ -1,12 +1,15 @@
|
||||||
"""Prompt forms"""
|
"""Prompt forms"""
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from guardian.shortcuts import get_anonymous_user
|
from guardian.shortcuts import get_anonymous_user
|
||||||
|
|
||||||
|
from passbook.core.models import User
|
||||||
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
from passbook.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
from passbook.policies.engine import PolicyEngine
|
from passbook.policies.engine import PolicyEngine
|
||||||
from passbook.stages.prompt.models import Prompt, PromptStage
|
from passbook.stages.prompt.models import FieldTypes, Prompt, PromptStage
|
||||||
|
|
||||||
|
|
||||||
class PromptStageForm(forms.ModelForm):
|
class PromptStageForm(forms.ModelForm):
|
||||||
|
@ -57,6 +60,14 @@ class PromptForm(forms.Form):
|
||||||
for field in fields:
|
for field in fields:
|
||||||
field: Prompt
|
field: Prompt
|
||||||
self.fields[field.field_key] = field.field
|
self.fields[field.field_key] = field.field
|
||||||
|
# Special handling for fields with username type
|
||||||
|
# these check for existing users with the same username
|
||||||
|
if field.type == FieldTypes.USERNAME:
|
||||||
|
setattr(
|
||||||
|
self,
|
||||||
|
f"clean_{field.field_key}",
|
||||||
|
username_field_cleaner_generator(field),
|
||||||
|
)
|
||||||
self.field_order = sorted(fields, key=lambda x: x.order)
|
self.field_order = sorted(fields, key=lambda x: x.order)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -68,3 +79,15 @@ class PromptForm(forms.Form):
|
||||||
result = engine.result
|
result = engine.result
|
||||||
if not result.passing:
|
if not result.passing:
|
||||||
raise forms.ValidationError(list(result.messages))
|
raise forms.ValidationError(list(result.messages))
|
||||||
|
|
||||||
|
|
||||||
|
def username_field_cleaner_generator(field: Prompt) -> Callable:
|
||||||
|
"""Return a `clean_` method for `field`. Clean method checks if username is taken already."""
|
||||||
|
|
||||||
|
def username_field_cleaner(self: PromptForm):
|
||||||
|
"""Check for duplicate usernames"""
|
||||||
|
username = self.cleaned_data.get(field.field_key)
|
||||||
|
if User.objects.filter(username=username).exists():
|
||||||
|
raise forms.ValidationError("Username is already taken.")
|
||||||
|
|
||||||
|
return username_field_cleaner
|
||||||
|
|
|
@ -7,25 +7,34 @@ from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from passbook.flows.models import Stage
|
from passbook.flows.models import Stage
|
||||||
from passbook.policies.models import PolicyBindingModel
|
from passbook.policies.models import PolicyBindingModel
|
||||||
|
from passbook.stages.prompt.widgets import HorizontalRuleWidget, StaticTextWidget
|
||||||
|
|
||||||
|
|
||||||
class FieldTypes(models.TextChoices):
|
class FieldTypes(models.TextChoices):
|
||||||
"""Field types an Prompt can be"""
|
"""Field types an Prompt can be"""
|
||||||
|
|
||||||
# Simple text field
|
# Simple text field
|
||||||
TEXT = "text"
|
TEXT = "text", _("Text: Simple Text input")
|
||||||
# Same as text, but has autocomplete for password managers
|
# Same as text, but has autocomplete for password managers
|
||||||
USERNAME = "username"
|
USERNAME = (
|
||||||
EMAIL = "email"
|
"username",
|
||||||
|
_(
|
||||||
|
(
|
||||||
|
"Username: Same as Text input, but checks for "
|
||||||
|
"duplicate and prevents duplicate usernames."
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
EMAIL = "email", _("Email: Text field with Email type.")
|
||||||
PASSWORD = "password" # noqa # nosec
|
PASSWORD = "password" # noqa # nosec
|
||||||
NUMBER = "number"
|
NUMBER = "number"
|
||||||
CHECKBOX = "checkbox"
|
CHECKBOX = "checkbox"
|
||||||
DATE = "data"
|
DATE = "data"
|
||||||
DATE_TIME = "data-time"
|
DATE_TIME = "data-time"
|
||||||
|
|
||||||
SEPARATOR = "separator"
|
SEPARATOR = "separator", _("Separator: Static Separator Line")
|
||||||
HIDDEN = "hidden"
|
HIDDEN = "hidden", _("Hidden: Hidden field, can be used to insert data into form.")
|
||||||
STATIC = "static"
|
STATIC = "static", _("Static: Static value, displayed as-is.")
|
||||||
|
|
||||||
|
|
||||||
class Prompt(models.Model):
|
class Prompt(models.Model):
|
||||||
|
@ -74,9 +83,16 @@ class Prompt(models.Model):
|
||||||
field_class = forms.DateInput
|
field_class = forms.DateInput
|
||||||
if self.type == FieldTypes.DATE_TIME:
|
if self.type == FieldTypes.DATE_TIME:
|
||||||
field_class = forms.DateTimeInput
|
field_class = forms.DateTimeInput
|
||||||
|
if self.type == FieldTypes.STATIC:
|
||||||
|
widget = StaticTextWidget(attrs=attrs)
|
||||||
|
kwargs["initial"] = self.placeholder
|
||||||
|
kwargs["required"] = False
|
||||||
|
kwargs["label"] = ""
|
||||||
|
if self.type == FieldTypes.SEPARATOR:
|
||||||
|
widget = HorizontalRuleWidget(attrs=attrs)
|
||||||
|
kwargs["required"] = False
|
||||||
|
kwargs["label"] = ""
|
||||||
|
|
||||||
# TODO: Implement static
|
|
||||||
# TODO: Implement separator
|
|
||||||
kwargs["widget"] = widget
|
kwargs["widget"] = widget
|
||||||
return field_class(**kwargs)
|
return field_class(**kwargs)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
"""Prompt Widgets"""
|
||||||
|
from django import forms
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
|
||||||
|
class StaticTextWidget(forms.widgets.Widget):
|
||||||
|
"""Widget to render static text"""
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
|
return mark_safe(f"<p>{value}</p>") # nosec
|
||||||
|
|
||||||
|
|
||||||
|
class HorizontalRuleWidget(forms.widgets.Widget):
|
||||||
|
"""Widget, which renders an <hr> element"""
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
|
return mark_safe("<hr>") # nosec
|
Reference in New Issue