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

View file

@ -2,32 +2,51 @@
{% block content %} {% block content %}
{% if is_admin_dashboard_activated %} {% if is_admin_dashboard_activated %}
<table id="bill_table" class="table table-striped"> <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') %} <tbody>{% for project in projects|sort(attribute='name') %}
<tr> <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> <td><a href="{{ url_for('.list_bills', project_id=project.id) }}"
{% if project.has_bills() %} title="{{ project.name }}">{{project.name}}</a></td>
<td>{{ project.get_bills().all()[0].date }}</td> <td>{{ project.members | count }}</td>
<td>{{ project.get_bills().all()[-1].date }}</td> <td>{{ project.get_bills_unordered().count() }}</td>
{% else %} {% if project.has_bills() %}
<td></td> <td>{{ project.get_bills().all()[0].date }}</td>
<td></td> <td>{{ project.get_bills().all()[-1].date }}</td>
{% endif %} {% else %}
<td class="project-actions"> <td></td>
<a class="edit" href="{{ url_for(".edit_project", project_id=project.id) }}" title="{{ _("edit") }}">{{ _('edit') }}</a> <td></td>
<a class="delete" href="{{ url_for(".delete_project", project_id=project.id) }}" title="{{ _("delete") }}">{{ _('delete') }}</a> {% endif %}
<a class="show" href="{{ url_for(".list_bills", project_id=project.id) }}" title="{{ _("show") }}">{{ _('show') }}</a> <td class="project-actions">
</td> <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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<script language="JavaScript"> <script language="JavaScript">
$(document).ready(function() { $(document).ready(function () {
$('#bill_table').DataTable({ $('#bill_table').DataTable({
paging: true paging: true
}); });
}) })
</script> </script>
{% else %} {% else %}
<div class="alert alert-danger">{{ _("The Dashboard is currently deactivated.") }}</div> <div class="alert alert-danger">{{ _("The Dashboard is currently deactivated.") }}</div>

View file

@ -888,10 +888,19 @@ def dashboard():
return render_template( return render_template(
"dashboard.html", "dashboard.html",
projects=Project.query.all(), projects=Project.query.all(),
delete_project_form=DestructiveActionProjectForm,
is_admin_dashboard_activated=is_admin_dashboard_activated, 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") @main.route("/favicon.ico")
def favicon(): def favicon():
return send_from_directory( return send_from_directory(