Fix project deletion in the dashboard. Fixes #1094

This was broken due to changes introduced to make project deletion more
secure. Rather than doing some complicated if branches, I decided this
dashboard stuff is probably better handled with separate routes instead.

So I've reintroduced a way to delete the projects without specifying the
project code (otherwise it's not possible for admins to do it).
This commit is contained in:
Alexis Métaireau 2022-12-11 14:22:20 +01:00 committed by Glandos
parent 97a0aea2ba
commit 43289b8dd2
3 changed files with 128 additions and 59 deletions

View file

@ -14,18 +14,22 @@ body {
.navbar-brand span {
color: #abe128;
}
.navbar h1 {
font-size: 1rem;
margin: 0;
padding: 0;
}
.navbar .secondary-nav {
text-align: right;
flex-direction: row-reverse;
}
.secondary-nav .nav-link {
padding: 0.5rem 1rem;
}
.navbar-brand {
font-family: "Lobster", arial, serif;
font-size: 1.5rem;
@ -51,7 +55,8 @@ body {
font-size: 2.4em;
}
#header .tryout, #header .showcase {
#header .tryout,
#header .showcase {
color: #fff;
background-color: #414141;
border-color: #414141;
@ -59,7 +64,8 @@ body {
margin-right: 3px;
}
#header .tryout:hover, #header .showcase:hover {
#header .tryout:hover,
#header .showcase:hover {
background-color: #606060;
border-color: #606060;
cursor: pointer;
@ -91,9 +97,11 @@ body {
.balance tr td {
font-weight: bold;
}
.positive {
color: green;
}
.negative {
color: red;
}
@ -113,6 +121,7 @@ body {
flex: 1 1 auto;
overflow-y: auto;
}
.identifier {
margin-bottom: 15px;
}
@ -143,10 +152,12 @@ body {
.home-container {
background: linear-gradient(150deg, #abe128 0%, #43ca61 100%);
}
.home {
padding-top: 1em;
padding-bottom: 3em;
}
#authentication-form legend {
text-align: right;
}
@ -160,11 +171,13 @@ body {
margin-bottom: 20px;
margin-left: 25px;
}
@media (max-width: 450px) {
.home .card {
min-width: unset;
}
}
/* Other */
#bills {
@ -174,6 +187,7 @@ body {
.empty-bill {
margin-top: 5rem !important;
}
.empty-bill .card {
border: 0;
}
@ -250,12 +264,15 @@ footer .footer-right a {
border-radius: 50%;
opacity: 0.5;
}
@-moz-document url-prefix() {
/** Firefox style fix **/
footer .footer-right a {
padding-top: 2px;
}
}
footer .footer-right a:hover {
opacity: 1;
}
@ -268,6 +285,7 @@ footer .footer-left {
/* If you don't want the footer to be responsive, remove these media queries */
@media (max-width: 600px) {
footer .footer-left,
footer .footer-right {
text-align: center;
@ -298,9 +316,9 @@ footer .footer-left {
position: relative;
}
.bill-actions > form > .delete,
.bill-actions > .edit,
.bill-actions > .show {
.bill-actions>form>.delete,
.bill-actions>.edit,
.bill-actions>.show {
font-size: 0px;
display: block;
width: 16px;
@ -316,15 +334,15 @@ footer .footer-left {
height: calc(100% - 8px);
}
.bill-actions > form > .delete {
.bill-actions>form>.delete {
background: url("../images/delete.png") no-repeat right;
}
.bill-actions > .edit {
.bill-actions>.edit {
background: url("../images/edit.png") no-repeat right;
}
.bill-actions > .show {
.bill-actions>.show {
background: url("../images/show.png") no-repeat right;
}
@ -344,6 +362,7 @@ footer .footer-left {
}
@media (min-width: 768px) {
.split_bills,
#table_overflow.statistics {
/* The table is shifted to left, so add the spacer width on the right to match */
@ -351,9 +370,9 @@ footer .footer-left {
}
}
.project-actions > .delete,
.project-actions > .edit,
.project-actions > .show {
.project-actions>form>.delete,
.project-actions>.edit,
.project-actions>.show {
font-size: 0px;
display: block;
width: 16px;
@ -363,21 +382,22 @@ footer .footer-left {
float: left;
}
.project-actions > .delete {
.project-actions>form>.delete {
background: url("../images/delete.png") no-repeat right;
border: 0px !important;
}
.project-actions > .edit {
.project-actions>.edit {
background: url("../images/edit.png") no-repeat right;
}
.project-actions > .show {
.project-actions>.show {
background: url("../images/show.png") no-repeat right;
}
.history_icon > .delete,
.history_icon > .add,
.history_icon > .edit {
.history_icon>.delete,
.history_icon>.add,
.history_icon>.edit {
font-size: 0px;
display: block;
width: 16px;
@ -388,15 +408,15 @@ footer .footer-left {
float: left;
}
.history_icon > .delete {
.history_icon>.delete {
background: url("../images/delete.png") no-repeat right;
}
.history_icon > .edit {
.history_icon>.edit {
background: url("../images/edit.png") no-repeat right;
}
.history_icon > .add {
.history_icon>.add {
background: url("../images/add.png") no-repeat right;
}
@ -461,7 +481,7 @@ tr.payer_line .balance-name {
margin-top: 30px;
}
#bill-form > fieldset {
#bill-form>fieldset {
margin-top: 10px;
}
@ -485,64 +505,81 @@ tr:hover .extra-info {
/* Fluid Offsets for Boostrap */
.row-fluid > [class*="span"]:not([class*="offset"]):first-child {
.row-fluid>[class*="span"]:not([class*="offset"]):first-child {
margin-left: 0;
}
.row-fluid > .offset12 {
.row-fluid>.offset12 {
margin-left: 100%;
}
.row-fluid > .offset11 {
.row-fluid>.offset11 {
margin-left: 93.5%;
}
.row-fluid > .offset10 {
.row-fluid>.offset10 {
margin-left: 85%;
}
.row-fluid > .offset9 {
.row-fluid>.offset9 {
margin-left: 76.5%;
}
.row-fluid > .offset8 {
.row-fluid>.offset8 {
margin-left: 68%;
}
.row-fluid > .offset7 {
.row-fluid>.offset7 {
margin-left: 59.5%;
}
.row-fluid > .offset6 {
.row-fluid>.offset6 {
margin-left: 51%;
}
.row-fluid > .offset5 {
.row-fluid>.offset5 {
margin-left: 42.5%;
}
.row-fluid > .offset4 {
.row-fluid>.offset4 {
margin-left: 34%;
}
.row-fluid > .offset3 {
.row-fluid>.offset3 {
margin-left: 25.5%;
}
.row-fluid > .offset2 {
.row-fluid>.offset2 {
margin-left: 17%;
}
.row-fluid > .offset1 {
.row-fluid>.offset1 {
margin-left: 8.5%;
}
.globe-europe svg {
fill: rgba(255, 255, 255, 0.5);
}
.navbar-nav .dropdown-toggle:hover .globe-europe svg {
fill: rgba(255, 255, 255, 0.75);
}
.navbar-dark .navbar-nav .show > .nav-link svg {
.navbar-dark .navbar-nav .show>.nav-link svg {
fill: white;
}
.navbar-dark .dropdown-menu {
max-height: 50vh;
overflow-y: auto;
}
.icon svg {
display: inline-block;
border-bottom: 0.2em solid transparent;
height: 1.2em;
width: 1.2em; /* protection for IE11 */
width: 1.2em;
/* protection for IE11 */
}
.icon.high svg {
@ -558,9 +595,11 @@ tr:hover .extra-info {
.icon.before-text svg {
margin-right: 3px;
}
footer .icon svg {
fill: white;
}
.icon.icon-white {
fill: white;
}
@ -568,7 +607,8 @@ footer .icon svg {
.icon.icon-red {
fill: #dc3545;
}
.btn:hover .icon.icon-red {
.btn:hover .icon.icon-red {
fill: white !important;
}
@ -592,6 +632,7 @@ footer .icon svg {
margin-top: 1em;
margin-bottom: 3em;
}
.edit-project .custom-file {
margin-bottom: 2em;
}
}

View file

@ -2,34 +2,53 @@
{% block content %}
{% if is_admin_dashboard_activated %}
<table id="bill_table" class="table table-striped">
<thead><tr><th>{{ _("Project") }}</th><th>{{ _("Number of participants") }}</th><th>{{ _("Number of bills") }}</th><th>{{_("Newest bill")}}</th><th>{{_("Oldest bill")}}</th><th>{{_("Actions")}}</th></tr></thead>
<thead>
<tr>
<th>{{ _("Project") }}</th>
<th>{{ _("Number of participants") }}</th>
<th>{{ _("Number of bills") }}</th>
<th>{{_("Newest bill")}}</th>
<th>{{_("Oldest bill")}}</th>
<th>{{_("Actions")}}</th>
</tr>
</thead>
<tbody>{% for project in projects|sort(attribute='name') %}
<tr>
<td><a href="{{ url_for(".list_bills", project_id=project.id) }}" title="{{ project.name }}">{{ project.name }}</a></td><td>{{ project.members | count }}</td><td>{{ project.get_bills_unordered().count() }}</td>
{% if project.has_bills() %}
<td>{{ project.get_bills().all()[0].date }}</td>
<td>{{ project.get_bills().all()[-1].date }}</td>
{% else %}
<td></td>
<td></td>
{% endif %}
<td class="project-actions">
<a class="edit" href="{{ url_for(".edit_project", project_id=project.id) }}" title="{{ _("edit") }}">{{ _('edit') }}</a>
<a class="delete" href="{{ url_for(".delete_project", project_id=project.id) }}" title="{{ _("delete") }}">{{ _('delete') }}</a>
<a class="show" href="{{ url_for(".list_bills", project_id=project.id) }}" title="{{ _("show") }}">{{ _('show') }}</a>
</td>
<td><a href="{{ url_for('.list_bills', project_id=project.id) }}"
title="{{ project.name }}">{{project.name}}</a></td>
<td>{{ project.members | count }}</td>
<td>{{ project.get_bills_unordered().count() }}</td>
{% if project.has_bills() %}
<td>{{ project.get_bills().all()[0].date }}</td>
<td>{{ project.get_bills().all()[-1].date }}</td>
{% else %}
<td></td>
<td></td>
{% endif %}
<td class="project-actions">
<a class="edit" href="{{ url_for('.edit_project', project_id=project.id) }}" title=" {{ _('edit')}}">{{
_('edit') }}</a>
<form id="delete-project" class="form-horizontal" action="{{ url_for('.dashboard_delete_project',
project_id=project.id) }}" method="post">
<button class="delete">{{ _("Delete project") }}</button>
</form>
<a class="show" href="{{ url_for('.list_bills', project_id=project.id) }}" title="{{ _(" show") }}">{{
_('show') }}</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<script language="JavaScript">
$(document).ready(function() {
$('#bill_table').DataTable({
paging: true
});
})
$(document).ready(function () {
$('#bill_table').DataTable({
paging: true
});
})
</script>
{% else %}
<div class="alert alert-danger">{{ _("The Dashboard is currently deactivated.") }}</div>
{% endif %}
{% endblock %}
{% endblock %}

View file

@ -888,10 +888,19 @@ def dashboard():
return render_template(
"dashboard.html",
projects=Project.query.all(),
delete_project_form=DestructiveActionProjectForm,
is_admin_dashboard_activated=is_admin_dashboard_activated,
)
@main.route("/dashboard/<project_id>/delete", methods=["POST"])
@requires_admin()
def dashboard_delete_project():
g.project.remove_project()
flash(_("Project successfully deleted"))
return redirect(request.headers.get("Referer") or url_for(".home"))
@main.route("/favicon.ico")
def favicon():
return send_from_directory(