atr/templates/project-view.html (290 lines of code) (raw):
{% extends "layouts/base.html" %}
{% block title %}
Project ~ ATR
{% endblock title %}
{% block description %}
Information regarding an Apache Project.
{% endblock description %}
{% block content %}
<div class="row">
<div class="col-md">
<h1>{{ project.display_name }}</h1>
</div>
{% if project.is_retired %}
<div class="col-sm-auto">
<span class="badge text-bg-secondary">retired</span>
</div>
{% endif %}
</div>
<div class="card mb-4">
<div class="card-header bg-light">
<h3 class="mb-2">Project label</h3>
</div>
<div class="card-body">
<code class="fs-6">{{ project.name }}</code>
</div>
</div>
{% set is_admin = is_admin_fn(current_user.uid) %}
{% set is_committee_member = is_committee_member_fn(project.committee, current_user.uid) %}
<div class="card mb-4">
<div class="card-header bg-light">
<h3 class="mb-2">PMC</h3>
</div>
<div class="card-body">
<div class="d-flex flex-wrap gap-3 small mb-1">
<a href="{{ as_url(routes.committees.view, name=project.committee.name) }}">{{ project.committee.display_name }}</a>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header bg-light">
<h3 class="mb-2">Description</h3>
</div>
<div class="card-body">
<div class="d-flex flex-wrap gap-3 small mb-1">
<span class="fs-6">{{ project.description }}</span>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header bg-light">
<h3 class="mb-2">Signing keys</h3>
</div>
<div class="card-body">
<div class="row row-cols-1 g-4">
{% for key in project.public_signing_keys %}
<div class="col">
<div class="card h-100 border">
<div class="card-body">
<table class="table mb-0">
<tbody>
<tr>
<th class="border-0">Fingerprint</th>
<td class="text-break border-0">{{ key.fingerprint }}</td>
</tr>
<tr>
<th class="border-0">Type</th>
<td class="text-break border-0">{{ algorithms[key.algorithm] }} ({{ key.length }} bits)</td>
</tr>
<tr>
<th class="border-0">Created</th>
<td class="text-break border-0">{{ key.created.strftime("%Y-%m-%d %H:%M:%S") }}</td>
</tr>
<tr>
<th class="border-0">Expires</th>
<td class="text-break border-0">
{% if key.expires %}
{% set days_until_expiry = (key.expires - now).days %}
{% if days_until_expiry < 0 %}
<span class="text-danger fw-bold">
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
<span class="badge bg-danger text-white ms-2">Expired</span>
</span>
{% elif days_until_expiry <= 30 %}
<span class="text-warning fw-bold">
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
<span class="badge bg-warning text-dark ms-2">Expires in {{ days_until_expiry }} days</span>
</span>
{% else %}
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
{% endif %}
{% else %}
Never
{% endif %}
</td>
</tr>
<tr>
<th class="border-0">User ID</th>
<td class="text-break border-0">{{ key.declared_uid or 'Not specified' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<h3 class="mb-0">Release policy</h3>
{% if not project.is_retired %}
{% if project.release_policy and (is_committee_member or is_admin) %}
<div>
<a class="btn btn-primary btn-sm"
href="{{ as_url(routes.projects.release_policy_edit, project_name=project.name) }}"><i class="bi bi-pencil-square"></i></a>
</div>
{% elif (is_committee_member or is_admin) %}
<div>
<a class="btn btn-primary btn-sm"
href="{{ as_url(routes.projects.release_policy_add, project_name=project.name) }}"><i class="bi bi-plus"></i></a>
</div>
{% endif %}
{% endif %}
</div>
<div class="card-body">
{% if project.release_policy %}
{% set rp = project.release_policy %}
<div class="card h-100 border">
<div class="card-body">
<table class="table mb-0">
<tbody>
<tr>
<th class="border-0 w-25">Email</th>
<td class="text-break border-0">
<a href="mailto:{{ vp.mailto_addresses[0] }}">{{ vp.mailto_addresses[0] }}</a>
</td>
</tr>
<tr>
<th class="border-0">Manual vote process</th>
<td class="text-break border-0">{{ vp.manual_vote }}</td>
</tr>
<tr>
<th class="border-0">Minimum voting period</th>
<td class="text-break border-0">{{ vp.min_hours }}h</td>
</tr>
<tr>
<th class="border-0">Release checklist</th>
<td class="text-break border-0">
{% if vp.release_checklist|length > 0 %}
<textarea readonly class="form-control font-monospace" rows="10">{{ vp.release_checklist }}</textarea>
{% else %}
None
{% endif %}
</td>
</tr>
<tr>
<th class="border-0">Start vote template</th>
<td class="text-break border-0">
{% if vp.start_vote_template|length > 0 %}
<textarea readonly class="form-control font-monospace" rows="10">{{ vp.start_vote_template }}</textarea>
{% else %}
None
{% endif %}
</td>
</tr>
<tr>
<th class="border-0">Announce release template</th>
<td class="text-break border-0">
{% if vp.announce_release_template|length > 0 %}
<textarea readonly class="form-control font-monospace" rows="10">{{ vp.announce_release_template }}</textarea>
{% else %}
None
{% endif %}
</td>
</tr>
<tr>
<th class="border-0">Pause for RM</th>
<td class="text-break border-0">{{ vp.pause_for_rm }}</td>
</tr>
</tbody>
</table>
</div>
</div>
{% else %}
<div>None defined.</div>
{% endif %}
</div>
</div>
{% if (is_committee_member or is_admin) %}
{% if candidate_drafts|length > 0 %}
<h2>Draft candidate releases</h2>
<div class="d-flex flex-wrap gap-2 mb-4">
{% for draft in candidate_drafts %}
<a href="{{ as_url(routes.draft.view, project_name=project.name, version_name=draft.version) }}"
class="btn btn-sm btn-outline-secondary py-2 px-3"
title="View draft {{ project.name }} {{ draft.version }}">
{{ project.name }} {{ draft.version }}
{% set file_count = number_of_release_files(draft) %}
<span class="badge bg-secondary ms-2">{{ file_count }}
{% if file_count == 1 %}
file
{% else %}
files
{% endif %}
</span>
</a>
{% endfor %}
</div>
{% endif %}
{% if candidates|length > 0 %}
<h2>Candidate releases</h2>
<div class="d-flex flex-wrap gap-2 mb-4">
{% for candidate in candidates %}
<a href="{{ as_url(routes.candidate.view, project_name=project.name, version_name=candidate.version) }}"
class="btn btn-sm btn-outline-info py-2 px-3"
title="View candidate {{ project.name }} {{ candidate.version }}">
{{ project.name }} {{ candidate.version }}
{% set file_count = number_of_release_files(candidate) %}
<span class="badge bg-info ms-2">{{ file_count }}
{% if file_count == 1 %}
file
{% else %}
files
{% endif %}
</span>
</a>
{% endfor %}
</div>
{% endif %}
{% if previews|length > 0 %}
<h2>Preview releases</h2>
<div class="d-flex flex-wrap gap-2 mb-4">
{% for preview in previews %}
<a href="{{ as_url(routes.preview.view, project_name=project.name, version_name=preview.version) }}"
class="btn btn-sm btn-outline-warning py-2 px-3"
title="View preview {{ project.name }} {{ preview.version }}">
{{ project.name }} {{ preview.version }}
{% set file_count = number_of_release_files(preview) %}
<span class="badge bg-warning ms-2">{{ file_count }}
{% if file_count == 1 %}
file
{% else %}
files
{% endif %}
</span> </a>
{% endfor %}
</div>
{% endif %}
{% if full_releases|length > 0 %}
<h2>Full releases</h2>
<div class="d-flex flex-wrap gap-2 mb-4">
{% for release in full_releases %}
<a href="{{ as_url(routes.release.view, project_name=project.name, version_name=release.version) }}"
class="btn btn-sm btn-outline-success py-2 px-3"
title="View release {{ project.name }} {{ release.version }}">
{{ project.name }} {{ release.version }}
{% set file_count = number_of_release_files(release) %}
<span class="badge bg-success ms-2">{{ file_count }}
{% if file_count == 1 %}
file
{% else %}
files
{% endif %}
</span>
</a>
{% endfor %}
</div>
{% endif %}
{% endif %}
{% if project.created_by == current_user.uid %}
<h2>Actions</h2>
<div class="mt-3">
<form method="post"
action="{{ as_url(routes.projects.delete) }}"
class="d-inline-block m-0"
onsubmit="return confirm('Are you sure you want to delete the project \'{{ project.display_name }}\'? This cannot be undone.');">
<input type="hidden" name="project_name" value="{{ project.name }}" />
<button type="submit"
class="btn btn-sm btn-outline-danger"
title="Delete {{ project.display_name }}">
<i class="bi bi-trash"></i> Delete project
</button>
</form>
</div>
{% endif %}
{% endblock content %}
{% block javascripts %}
{{ super() }}
{% endblock javascripts %}