Aug 19 - Update code to catch up with ST
This commit is contained in:
@@ -19,6 +19,30 @@
|
||||
|
||||
{% block content %}
|
||||
{% set can_edit = current_user.role in ['admin','director','auditor'] or issue.assigned_to == current_user.id %}
|
||||
{# The filtered list URL this page was opened from (phase: filter
|
||||
preservation). Threaded into every action so an update or delete
|
||||
returns to the same filtered page, and used by the Back button. #}
|
||||
{% set back_url = request.args.get('next') or url_for('issues.index') %}
|
||||
{# Is the viewer OUR staff? Drives the internal-only chrome on this page: the
|
||||
"comments are visible to everyone" warning and the per-comment
|
||||
"Customer visible" / "Staff only" badges. Both are instructions about how WE
|
||||
work and must never reach a customer account.
|
||||
|
||||
Written as an explicit ALLOWLIST of our own roles, deliberately:
|
||||
|
||||
* It FAILS CLOSED. The obvious form, `not current_user.is_customer_account`,
|
||||
fails OPEN — if the attribute is missing for any reason (a process still
|
||||
running an older models/user.py after a template-only reload, say) Jinja
|
||||
yields Undefined, `not Undefined` is true, and the internal text is shown
|
||||
to exactly the people it must be hidden from. An allowlist of literal role
|
||||
strings can only ever be true for a role we listed.
|
||||
* `external_inspector` is absent ON PURPOSE. This is NOT the rule-87 case:
|
||||
rule 87 is about capability/scoping, where a Customer Inspector must
|
||||
behave exactly like our own inspector. Here the question is "does this
|
||||
person work for us?", which is the one place the two genuinely differ.
|
||||
Do not "fix" this by adding external_inspector to the list. #}
|
||||
{% set viewer_is_our_staff = current_user.role in
|
||||
['admin', 'director', 'project_manager', 'auditor', 'inspector'] %}
|
||||
|
||||
<div class="row">
|
||||
{# ══════════════════════════════════ LEFT COLUMN ══════════════════════════════════ #}
|
||||
@@ -187,6 +211,7 @@
|
||||
<strong>Awaiting director verification.</strong>
|
||||
{% if current_user.role in ['admin','director','auditor'] %}
|
||||
<form method="POST" action="{{ url_for('issues.verify', issue_id=issue.id) }}" class="mt-2">
|
||||
<input type="hidden" name="next" value="{{ back_url }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-2">
|
||||
<input type="text" name="verification_note" class="form-control form-control-sm"
|
||||
@@ -230,8 +255,12 @@
|
||||
{% else %}
|
||||
<span class="badge bg-secondary" style="font-size:.65rem;">{{ c.author.role|replace('_',' ')|title }}</span>
|
||||
{% endif %}
|
||||
{# Visibility indicator — staff only #}
|
||||
{% if current_user.role != 'customer' %}
|
||||
{# Visibility indicator — OUR staff only, and only while the
|
||||
per-comment flag still decides anything. While comments_open
|
||||
is set EVERY comment reaches the customer, so a "Staff only"
|
||||
badge would be a lie; it is suppressed rather than shown
|
||||
incorrectly. #}
|
||||
{% if viewer_is_our_staff and not comments_open %}
|
||||
{% if c.is_customer_visible %}
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success"
|
||||
style="font-size:.6rem;" title="Customer can see this comment">
|
||||
@@ -274,6 +303,7 @@
|
||||
<div class="card-body">
|
||||
<p class="fw-semibold small mb-2">Add Comment</p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="next" value="{{ back_url }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="status" value="{{ issue.status }}">
|
||||
<input type="hidden" name="assigned_to" value="{{ issue.assigned_to or 0 }}">
|
||||
@@ -281,6 +311,25 @@
|
||||
<textarea name="update_notes" class="form-control" rows="3"
|
||||
placeholder="Write a comment…" required></textarea>
|
||||
</div>
|
||||
{# While comments_open is set, every comment reaches the customer, so
|
||||
the "Share with customer" tick decides nothing. Saying so plainly
|
||||
matters: a staff member must not write something they believe is
|
||||
private. The checkbox is still posted and recorded, so turning the
|
||||
setting off restores its meaning immediately.
|
||||
|
||||
OUR STAFF ONLY. `can_edit` is also true for a Customer Inspector
|
||||
assigned to the issue, and this banner is an internal-process
|
||||
warning ("do not post internal-only notes") — showing it to a
|
||||
customer account exposes how we work and reads as nonsense to
|
||||
them, since nothing they write was ever private. #}
|
||||
{% if comments_open and viewer_is_our_staff %}
|
||||
<div class="alert alert-warning py-2 px-3 small mb-2">
|
||||
<i class="bi bi-eye me-1"></i>
|
||||
<strong>Comments are currently visible to everyone,</strong> including
|
||||
the customer, regardless of the tick below. Do not post internal-only
|
||||
notes here.
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div class="form-check form-check-inline mb-0">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
@@ -301,6 +350,7 @@
|
||||
<div class="card-body">
|
||||
<p class="fw-semibold small mb-2">Add Comment</p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="next" value="{{ back_url }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-2">
|
||||
<textarea name="update_notes" class="form-control" rows="3"
|
||||
@@ -369,6 +419,7 @@
|
||||
<div class="card-header bg-light"><h6 class="mb-0">Update Issue</h6></div>
|
||||
<div class="card-body">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="next" value="{{ back_url }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-3">
|
||||
{{ form.status.label(class="form-label fw-semibold") }}
|
||||
@@ -545,7 +596,7 @@
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mt-2">
|
||||
<a href="{{ url_for('issues.index') }}" class="btn btn-outline-secondary btn-sm">
|
||||
<a href="{{ back_url }}" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> Back to Issues
|
||||
</a>
|
||||
<a href="{{ url_for('issues.export_pdf', issue_id=issue.id) }}" class="btn btn-outline-primary btn-sm">
|
||||
@@ -583,6 +634,7 @@
|
||||
<i class="bi bi-x-circle"></i> Cancel
|
||||
</button>
|
||||
<form method="POST" action="{{ url_for('issues.delete', issue_id=issue.id) }}" class="d-inline">
|
||||
<input type="hidden" name="next" value="{{ back_url }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="bi bi-trash-fill"></i> Delete Permanently
|
||||
|
||||
Reference in New Issue
Block a user