Add logging preferences

This commit is contained in:
Andrew Dickinson 2020-04-10 21:50:37 -04:00
parent a8e74c98df
commit edaab5501b
6 changed files with 110 additions and 1 deletions

View file

@ -23,7 +23,7 @@ from jinja2 import Markup
import email_validator import email_validator
from ihatemoney.models import Project, Person from ihatemoney.models import Project, Person
from ihatemoney.utils import slugify, eval_arithmetic_expression from ihatemoney.utils import slugify, eval_arithmetic_expression, LoggingMode
def strip_filter(string): def strip_filter(string):
@ -89,6 +89,12 @@ 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(
_("Logging Preferences"),
choices=LoggingMode.choices(),
coerce=LoggingMode.coerce,
default=LoggingMode.default(),
)
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.
@ -100,6 +106,7 @@ class EditProjectForm(FlaskForm):
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,
) )
return project return project
@ -108,6 +115,7 @@ class EditProjectForm(FlaskForm):
project.name = self.name.data project.name = self.name.data
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
return project return project

View file

@ -7,6 +7,7 @@ from flask import g, current_app
from debts import settle from debts import settle
from sqlalchemy import orm from sqlalchemy import orm
from sqlalchemy.sql import func from sqlalchemy.sql import func
from ihatemoney.utils import LoggingMode
from itsdangerous import ( from itsdangerous import (
TimedJSONWebSignatureSerializer, TimedJSONWebSignatureSerializer,
URLSafeSerializer, URLSafeSerializer,
@ -27,6 +28,7 @@ class Project(db.Model):
name = db.Column(db.UnicodeText) name = db.Column(db.UnicodeText)
password = db.Column(db.String(128)) password = db.Column(db.String(128))
contact_email = db.Column(db.String(128)) contact_email = db.Column(db.String(128))
logging_preference = db.Column(db.Enum(LoggingMode), default=LoggingMode.default())
members = db.relationship("Person", backref="project") members = db.relationship("Person", backref="project")
query_class = ProjectQuery query_class = ProjectQuery
@ -37,6 +39,7 @@ class Project(db.Model):
"id": self.id, "id": self.id,
"name": self.name, "name": self.name,
"contact_email": self.contact_email, "contact_email": self.contact_email,
"logging_preference": self.logging_preference.value,
"members": [], "members": [],
} }

View file

@ -5,3 +5,46 @@ function selectCheckboxes(value){
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

@ -78,6 +78,26 @@
{{ input(form.name) }} {{ input(form.name) }}
{{ input(form.password) }} {{ input(form.password) }}
{{ input(form.contact_email) }} {{ input(form.contact_email) }}
<div class="form-group">
<label for="privacy_checkboxes">{{ _("Privacy Settings") }}</label>
<div id="privacy_checkboxes" class="card card-body bg-light">
<div class="controls">
<input id="logging_enabled" type="checkbox" onchange="updatePrivacySelectFromCheckBoxes()">
<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 class="actions"> <div class="actions">
<button class="btn btn-primary">{{ _("Edit the project") }}</button> <button class="btn btn-primary">{{ _("Edit the project") }}</button>
<a id="delete-project" style="color:red; margin-left:10px; cursor:pointer; ">{{ _("delete") }}</a> <a id="delete-project" style="color:red; margin-left:10px; cursor:pointer; ">{{ _("delete") }}</a>

View file

@ -2,6 +2,7 @@ import re
import os import os
import ast import ast
import operator import operator
from enum import Enum
from io import BytesIO, StringIO from io import BytesIO, StringIO
@ -257,3 +258,36 @@ def same_bill(bill1, bill2):
if bill1[a] != bill2[a]: if bill1[a] != bill2[a]:
return False return False
return True return True
class FormEnum(Enum):
"""Extend builtin Enum class to be seamlessly compatible with WTForms"""
@classmethod
def choices(cls):
return [(choice, choice.name) for choice in cls]
@classmethod
def coerce(cls, item):
"""Coerce a str or int representation into an Enum object"""
if isinstance(item, cls):
return item
# If item is not already a Enum object then it must be
# a string or int corresponding to an ID (e.g. '0' or 1)
# Either int() or cls() will correctly throw a TypeError if this
# is not the case
return cls(int(item))
def __str__(self):
return str(self.value)
class LoggingMode(FormEnum):
DISABLED = 0
ENABLED = 1
RECORD_IP = 2
@classmethod
def default(cls):
return cls.ENABLED

View file

@ -391,6 +391,7 @@ def edit_project():
return redirect(url_for(".list_bills")) return redirect(url_for(".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
edit_form.contact_email.data = g.project.contact_email edit_form.contact_email.data = g.project.contact_email
return render_template( return render_template(