Replace JavaScript-based dependent checkboxes with WTForms BooleanFields

This commit is contained in:
Andrew Dickinson 2020-04-17 16:16:32 -04:00
parent 114c151501
commit 0b206daab2
5 changed files with 73 additions and 77 deletions

View file

@ -1,7 +1,7 @@
from flask_wtf.form import FlaskForm from flask_wtf.form import FlaskForm
from wtforms.fields.core import SelectField, SelectMultipleField from wtforms.fields.core import SelectField, SelectMultipleField
from wtforms.fields.html5 import DateField, DecimalField, URLField from wtforms.fields.html5 import DateField, DecimalField, URLField
from wtforms.fields.simple import PasswordField, SubmitField, StringField from wtforms.fields.simple import PasswordField, SubmitField, StringField, BooleanField
from wtforms.validators import ( from wtforms.validators import (
Email, Email,
DataRequired, DataRequired,
@ -89,25 +89,28 @@ class EditProjectForm(FlaskForm):
name = StringField(_("Project name"), validators=[DataRequired()]) name = StringField(_("Project name"), validators=[DataRequired()])
password = StringField(_("Private code"), validators=[DataRequired()]) password = StringField(_("Private code"), validators=[DataRequired()])
contact_email = StringField(_("Email"), validators=[DataRequired(), Email()]) contact_email = StringField(_("Email"), validators=[DataRequired(), Email()])
logging_preferences = SelectField( project_history = BooleanField(_("Enable project history"))
_("Logging Preferences"), ip_recording = BooleanField(_("Use IP tracking for project history"))
choices=LoggingMode.choices(),
coerce=LoggingMode.coerce,
default=LoggingMode.default(),
validators=[DataRequired()],
)
def save(self): def save(self):
"""Create a new project with the information given by this form. """Create a new project with the information given by this form.
Returns the created instance Returns the created instance
""" """
if not self.project_history.data:
new_logging_preference = LoggingMode.DISABLED
else:
if self.ip_recording.data:
new_logging_preference = LoggingMode.RECORD_IP
else:
new_logging_preference = LoggingMode.ENABLED
project = Project( project = Project(
name=self.name.data, name=self.name.data,
id=self.id.data, id=self.id.data,
password=generate_password_hash(self.password.data), password=generate_password_hash(self.password.data),
contact_email=self.contact_email.data, contact_email=self.contact_email.data,
logging_preference=self.logging_preferences.data, logging_preference=new_logging_preference,
) )
return project return project
@ -120,7 +123,16 @@ class EditProjectForm(FlaskForm):
project.password = generate_password_hash(self.password.data) project.password = generate_password_hash(self.password.data)
project.contact_email = self.contact_email.data project.contact_email = self.contact_email.data
project.logging_preference = self.logging_preferences.data
if not self.project_history.data:
new_logging_preference = LoggingMode.DISABLED
else:
if self.ip_recording.data:
new_logging_preference = LoggingMode.RECORD_IP
else:
new_logging_preference = LoggingMode.ENABLED
project.logging_preference = new_logging_preference
return project return project
@ -139,6 +151,14 @@ class ProjectForm(EditProjectForm):
password = PasswordField(_("Private code"), validators=[DataRequired()]) password = PasswordField(_("Private code"), validators=[DataRequired()])
submit = SubmitField(_("Create the project")) submit = SubmitField(_("Create the project"))
def save(self):
# WTForms Boolean Fields don't insert the default value when the
# request doesn't include any value the way that other fields do,
# so we'll manually do it here
self.project_history.data = LoggingMode.default() != LoggingMode.DISABLED
self.ip_recording.data = LoggingMode.default() == LoggingMode.RECORD_IP
return super().save()
def validate_id(form, field): def validate_id(form, field):
form.id.data = slugify(field.data) form.id.data = slugify(field.data)
if (form.id.data == "dashboard") or Project.query.get(form.id.data): if (form.id.data == "dashboard") or Project.query.get(form.id.data):

View file

@ -4,47 +4,4 @@ function selectCheckboxes(value){
for(var i = 0; i < els.length; i++){ for(var i = 0; i < els.length; i++){
els[i].checked = value; els[i].checked = value;
} }
}
function updateCheckBoxesFromPrivacySelect() {
var history_checkbox = document.getElementById('logging_enabled');
var record_ip_checkbox = document.getElementById('record_ip');
var record_ip_checkbox_text = document.getElementById("record_ip_label");
var select_input = document.getElementById("logging_preferences");
if (select_input.selectedIndex === 0) {
history_checkbox.checked = false;
record_ip_checkbox.checked = false;
record_ip_checkbox.disabled = true;
record_ip_checkbox_text.classList.add("text-muted");
} else if (select_input.selectedIndex === 1 || select_input.selectedIndex === 2) {
history_checkbox.checked = true;
record_ip_checkbox.disabled = false;
record_ip_checkbox_text.classList.remove("text-muted");
if (select_input.selectedIndex === 2) {
record_ip_checkbox.checked = true
}
}
}
function updatePrivacySelectFromCheckBoxes() {
var history_checkbox = document.getElementById('logging_enabled');
var record_ip_checkbox = document.getElementById('record_ip');
var record_ip_checkbox_text = document.getElementById("record_ip_label");
var select_input = document.getElementById("logging_preferences");
if (!history_checkbox.checked) {
record_ip_checkbox.checked = false;
record_ip_checkbox.disabled = true;
record_ip_checkbox_text.classList.add("text-muted");
select_input.selectedIndex = 0
} else {
record_ip_checkbox.disabled = false;
record_ip_checkbox_text.classList.remove("text-muted");
if (record_ip_checkbox.checked){
select_input.selectedIndex = 2
} else {
select_input.selectedIndex = 1
}
}
} }

View file

@ -20,6 +20,16 @@
</div> </div>
{% endmacro %} {% endmacro %}
{% macro checkbox(field) %}
<div class="controls{% if inline %} col-9{% endif %}">
{{ field(id=field.name) }}
<label for="{{ field.name }}">{{ field.label() }}</label>
{% if field.description %}
<small id="{{field.name}}_description"" class="form-text text-muted">{{ field.description }}</small>
{% endif %}
</div>
{% endmacro %}
{% macro submit(field, cancel=False, home=False) -%} {% macro submit(field, cancel=False, home=False) -%}
<div class="actions"> <div class="actions">
<button type="submit" class="btn btn-primary">{{ field.name }}</button> <button type="submit" class="btn btn-primary">{{ field.name }}</button>
@ -81,20 +91,8 @@
<div class="form-group"> <div class="form-group">
<label for="privacy_checkboxes">{{ _("Privacy Settings") }}</label> <label for="privacy_checkboxes">{{ _("Privacy Settings") }}</label>
<div id="privacy_checkboxes" class="card card-body bg-light"> <div id="privacy_checkboxes" class="card card-body bg-light">
<div class="controls"> {{ checkbox(form.project_history) }}
<input id="logging_enabled" type="checkbox" onchange="updatePrivacySelectFromCheckBoxes()"> {{ checkbox(form.ip_recording) }}
<label for="logging_enabled">{{ _("Enable Project History") }}</label>
</div>
<div class="controls">
<div class="ml-4">
<input id="record_ip" type="checkbox" onchange="updatePrivacySelectFromCheckBoxes()">
<label id="record_ip_label" for="record_ip">{{ _("Record IP Adressses") }}</label>
</div>
</div>
</div>
<div class="d-none">
{{ form.logging_preferences }}
<script>updateCheckBoxesFromPrivacySelect()</script>
</div> </div>
</div> </div>

View file

@ -2276,9 +2276,13 @@ class HistoryTestCase(IhatemoneyTestCase):
"name": "demo", "name": "demo",
"contact_email": "demo@notmyidea.org", "contact_email": "demo@notmyidea.org",
"password": "demo", "password": "demo",
"logging_preferences": logging_preference.value,
} }
if logging_preference != LoggingMode.DISABLED:
new_data["project_history"] = "y"
if logging_preference == LoggingMode.RECORD_IP:
new_data["ip_recording"] = "y"
# Disable History # Disable History
resp = self.client.post("/demo/edit", data=new_data, follow_redirects=True) resp = self.client.post("/demo/edit", data=new_data, follow_redirects=True)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@ -2286,11 +2290,17 @@ class HistoryTestCase(IhatemoneyTestCase):
resp = self.client.get("/demo/edit") resp = self.client.get("/demo/edit")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn( if logging_preference == LoggingMode.DISABLED:
'<option selected value="%i">%s</option>' self.assertIn('<input id="project_history"', resp.data.decode("utf-8"))
% (logging_preference.value, logging_preference.name), else:
resp.data.decode("utf-8"), self.assertIn(
) '<input checked id="project_history"', resp.data.decode("utf-8")
)
if logging_preference == LoggingMode.RECORD_IP:
self.assertIn('<input checked id="ip_recording"', resp.data.decode("utf-8"))
else:
self.assertIn('<input id="ip_recording"', resp.data.decode("utf-8"))
def assert_empty_history_logging_disabled(self): def assert_empty_history_logging_disabled(self):
resp = self.client.get("/demo/history") resp = self.client.get("/demo/history")
@ -2319,6 +2329,7 @@ class HistoryTestCase(IhatemoneyTestCase):
"name": "demo2", "name": "demo2",
"contact_email": "demo2@notmyidea.org", "contact_email": "demo2@notmyidea.org",
"password": "123456", "password": "123456",
"project_history": "y",
} }
resp = self.client.post("/demo/edit", data=new_data, follow_redirects=True) resp = self.client.post("/demo/edit", data=new_data, follow_redirects=True)
@ -2354,7 +2365,8 @@ class HistoryTestCase(IhatemoneyTestCase):
resp = self.client.get("/demo/edit") resp = self.client.get("/demo/edit")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn( self.assertIn(
'<option selected value="1">ENABLED</option>', resp.data.decode("utf-8") '<input checked id="project_history" name="project_history" type="checkbox" value="y">',
resp.data.decode("utf-8"),
) )
self.change_privacy_to(LoggingMode.DISABLED) self.change_privacy_to(LoggingMode.DISABLED)
@ -2415,10 +2427,14 @@ class HistoryTestCase(IhatemoneyTestCase):
"name": "demo2", "name": "demo2",
"contact_email": "demo2@notmyidea.org", "contact_email": "demo2@notmyidea.org",
"password": "123456", "password": "123456",
"logging_preferences": logging_mode.value,
# Keep privacy settings where they were
} }
# Keep privacy settings where they were
if logging_mode != LoggingMode.DISABLED:
new_data["project_history"] = "y"
if logging_mode == LoggingMode.RECORD_IP:
new_data["ip_recording"] = "y"
resp = self.client.post("/demo/edit", data=new_data, follow_redirects=True) resp = self.client.post("/demo/edit", data=new_data, follow_redirects=True)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)

View file

@ -406,7 +406,12 @@ def edit_project():
return redirect(url_for("main.list_bills")) return redirect(url_for("main.list_bills"))
else: else:
edit_form.name.data = g.project.name edit_form.name.data = g.project.name
edit_form.logging_preferences.data = g.project.logging_preference
if g.project.logging_preference != LoggingMode.DISABLED:
edit_form.project_history.data = True
if g.project.logging_preference == LoggingMode.RECORD_IP:
edit_form.ip_recording.data = True
edit_form.contact_email.data = g.project.contact_email edit_form.contact_email.data = g.project.contact_email
return render_template( return render_template(