mirror of
https://github.com/spiral-project/ihatemoney.git
synced 2025-05-06 13:01:50 +02:00
Add logging preferences
This commit is contained in:
parent
a8e74c98df
commit
edaab5501b
6 changed files with 110 additions and 1 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue