//! Templates for the public git source browser, issues, and repo settings. use std::collections::HashMap; use std::sync::Arc; use askama::Template; use crate::auth::SessionUser; use crate::git; use crate::routes::git::annotations_view::PersonalAnnotation; use crate::routes::git::notes_view::{AnnotationRow, CommitNote, NamespaceSummary}; use crate::types::{Item, Project, Version}; use super::super::CsrfTokenOption; /// An item paired with its versions, for release display on the git repo page. pub struct ReleaseItem { pub item: Item, pub versions: Vec, } /// Repository overview: file tree at HEAD + README. #[derive(Template)] #[template(path = "pages/git/repo.html")] pub struct GitRepoTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub description: Option, pub current_ref: String, pub refs: Vec, pub tree_items: Vec, pub readme_html: Option, pub host_url: Arc, /// Hostname for SSH clone URLs (e.g., "git.makenot.work"). Hidden when `None`. pub git_ssh_host: Option, /// Linked project, if this repo is associated with a public project. pub linked_project: Option, /// Public items with versions from the linked project (releases). pub release_items: Vec, /// Number of open issues for the nav bar badge. pub open_issue_count: i64, /// Whether this repository has `refs/replace/*` mappings, which links the /// replace page from the overview. Almost always false, and shown here /// rather than in the nav so no repository grows a permanently empty tab. pub has_replacements: bool, /// Whether the current viewer is the repo owner (for settings link). pub is_owner: bool, /// Whether the viewer has muted this repo's issue notifications. `None` /// when logged out, which hides the control: there is nothing to mute for /// somebody we could not mail anyway. pub issues_muted: Option, /// Repository id, for the mute form's hidden field. pub repo_id: String, pub active_tab: &'static str, } /// Subdirectory listing with breadcrumb navigation. #[derive(Template)] #[template(path = "pages/git/tree.html")] pub struct GitTreeTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub path: String, pub parent_path: String, /// Whether we're in a subdirectory (for ".." link and path joining). pub in_subdir: bool, pub breadcrumbs: Vec, pub tree_items: Vec, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// File viewer with syntax highlighting and line numbers. #[derive(Template)] #[template(path = "pages/git/file.html")] pub struct GitFileTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub file_path: String, pub filename: String, pub breadcrumbs: Vec, pub file_size: String, pub line_count: usize, pub is_binary: bool, pub highlighted_lines: Vec, /// Notes on the blob this view is showing. A note on a file annotates the /// object, so it follows the content rather than the path. pub notes: Vec, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// Commit log with pagination. #[derive(Template)] #[template(path = "pages/git/commits.html")] pub struct GitCommitsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub commits: Vec, /// Commits on this page that carry a note, mapped to how many namespaces /// annotate them. One batched lookup per page, never one per row. pub annotated: HashMap, pub page: usize, pub has_more: bool, pub open_issue_count: i64, /// Whether the current viewer is the repo owner (for settings link). pub is_owner: bool, pub active_tab: &'static str, } /// Commit detail page with inline diffs. #[derive(Template)] #[template(path = "pages/git/commit.html")] pub struct GitCommitDetailTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub detail: git::CommitDetail, /// Signature state. `Unsigned` for almost every commit, and rendered as /// nothing rather than as a warning: an unsigned commit is normal. pub signature: git::signing::SignatureStatus, /// Notes on this commit, one entry per `refs/notes/*` namespace that has /// one. Empty for the ordinary unannotated commit. pub notes: Vec, /// Whether this reader may add, edit or remove a note here: the repo owner /// or a collaborator who can push. False for everyone signed out. pub can_write_notes: bool, /// The note just saved was merged with somebody else's concurrent edit, so /// what is on screen is not what was typed. Set from the redirect that /// follows a write, since the page cannot work it out from the repository. pub notes_merged: bool, /// Whether this reader may write a personal annotation: signed in, in good /// standing, not a sandbox account. Unrelated to `can_write_notes`, which /// asks about pushing to THIS repository. pub can_annotate: bool, /// The reader's own annotation on this commit, as written, for the form to /// load. Empty when there is none. pub annotation_source: String, /// Every annotation the signed-in reader has on this commit, from their own /// annotation repository, rendered. Usually the one they wrote here, and /// more when the same commit is served by a fork they also read. Empty for /// everyone signed out, and never populated for anybody but the reader: /// nobody else sees these. pub personal_annotations: Vec, /// The annotation just saved was merged with a concurrent edit of the same /// note from another session. pub annotation_merged: bool, pub diff_files: Vec, pub total_files: usize, pub total_additions: usize, pub total_deletions: usize, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// Every annotation this account has written, newest first. /// /// The only page an orphan can be reached from: its target has no commit page /// anywhere any more, so nothing else links to it. #[derive(Template)] #[template(path = "pages/git/annotations.html")] pub struct GitAnnotationsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub annotations: Vec, /// 1-based page. pub page: usize, pub has_more: bool, /// Every annotation the account has, orphans included, since they are /// shown and nothing collects them. pub total: i64, } /// The repo's Notes tab: every `refs/notes/*` namespace with its count, and a /// feed of the annotations in the selected one. #[derive(Template)] #[template(path = "pages/git/notes.html")] pub struct GitNotesTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub namespaces: Vec, /// Namespace the feed belongs to. `None` for a repository with no notes. pub selected: Option, pub rows: Vec, /// Notes in the selected namespace, which exceeds `rows.len()` when the feed /// was capped. pub total: usize, /// The search these rows answer, echoed back into the box. pub search: String, pub commits_only: bool, /// 1-based page of the feed. Named apart from `page` because Askama would /// otherwise shadow nothing usefully and the template reads better for it. pub page_number: usize, pub has_more: bool, /// Whether the rows came from the Postgres index. False means the /// repository was walked, which cannot search or page, so the controls are /// not offered. pub from_index: bool, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// The repo's Tags tab: every tag, with the annotation an annotated tag /// carries. The message on an annotated tag is a release note the author /// already wrote and no forge shows. #[derive(Template)] #[template(path = "pages/git/tags.html")] pub struct GitTagsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub tags: Vec, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// The repository's `refs/replace/*` mappings. /// /// Reachable by URL on every repository and linked from the overview only when /// there is something to see, because the overwhelming majority of repositories /// have no replacements and a permanently empty tab is noise. #[derive(Template)] #[template(path = "pages/git/replace.html")] pub struct GitReplaceTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub replacements: Vec, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// Blame view for a single file. #[derive(Template)] #[template(path = "pages/git/blame.html")] pub struct GitBlameTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub file_path: String, pub filename: String, pub breadcrumbs: Vec, pub blame_lines: Vec, /// Commits on this page that carry a note, mapped to how many namespaces /// annotate them. Asked once per distinct commit, not once per line. pub annotated: HashMap, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } /// Per-file commit history with breadcrumb context. #[derive(Template)] #[template(path = "pages/git/file_log.html")] pub struct GitFileLogTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub refs: Vec, pub file_path: String, pub filename: String, pub breadcrumbs: Vec, pub commits: Vec, /// Commits on this page that carry a note, mapped to how many namespaces /// annotate them. One batched lookup per page, never one per row. pub annotated: HashMap, pub page: usize, pub has_more: bool, pub open_issue_count: i64, pub is_owner: bool, pub active_tab: &'static str, } // Git Issues /// Issue list page with status tabs and search (read-only). #[derive(Template)] #[template(path = "pages/git/issues.html")] pub struct GitIssueListTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub issues: Vec, pub open_count: i64, pub closed_count: i64, pub current_status: String, pub search_query: String, pub current_page: i64, pub total_pages: i64, /// Whether the current viewer is the repo owner (for settings link). pub is_owner: bool, /// Email address for submitting new issues. pub email_address: String, } /// Issue detail page with comments (read-only). #[derive(Template)] #[template(path = "pages/git/issue.html")] pub struct GitIssueDetailTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub issue: crate::db::DbIssue, pub author_username: String, pub comments: Vec, pub is_owner: bool, pub open_issue_count: i64, /// Email address for submitting new issues. pub email_address: String, } /// Repository settings page (owner only). #[derive(Template)] #[template(path = "pages/git/settings.html")] pub struct GitRepoSettingsTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub owner: String, pub repo_name: String, pub current_ref: String, pub repo: crate::db::DbGitRepo, pub open_issue_count: i64, /// Owner's projects for the link dropdown. pub projects: Vec, /// ID of the currently linked project as a string (for dropdown comparison). pub linked_project_id: String, }