This commit is contained in:
2024-07-16 15:07:01 +02:00
commit 2ebd0bee3a
18 changed files with 1217 additions and 0 deletions

View File

@ -0,0 +1,27 @@
<button class="ui basic clone button no-transition" id="repo-clone-ssh"
data-link="{{if $.PageIsWiki}}{{$.WikiCloneLink.SSH}}{{else}}{{$.CloneLink.SSH}}{{end}}">
SSH
</button>
<input id="repo-clone-url" value="{{if $.PageIsWiki}}{{$.WikiCloneLink.SSH}}{{else}}{{$.CloneLink.SSH}}{{end}}"
readonly>
<button class="ui basic icon button poping up" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}"
data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}"
data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny"
data-clipboard-target="#repo-clone-url">
Copy
</button>
{{if not (and $.DisableHTTP $.DisableSSH)}}
<script defer>
const isSSH = true
const sshButton = document.getElementById('repo-clone-ssh');
const input = document.getElementById('repo-clone-url');
if (input) input.value = sshButton.dataset.link;
if (sshButton) sshButton.classList['add']('primary');
setTimeout(() => {
if (sshButton) sshButton.classList.remove('no-transition');
}, 100);
</script>
{{end}}

View File

@ -0,0 +1,151 @@
<table id="repo-files-table" class="ui single line table">
<thead>
<tr class="commit-list">
<th>
{{if .LatestCommitUser}}
{{ctx.AvatarUtils .LatestCommitUser 24}}
{{if .LatestCommitUser.FullName}}
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
{{else}}
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a>
{{end}}
{{else}}
{{if .LatestCommit.Author}}
{{ctx.AvatarUtils.AvatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24}}
<strong>{{.LatestCommit.Author.Name}}</strong>
{{end}}
{{end}}
</th>
<th>{{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }}
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses "root" $}}
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span
class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
{{if IsMultilineCommitMessage .LatestCommit.Message}}
<button class="basic compact mini ui icon button commit-button"><i
class="ellipsis horizontal icon"></i></button>
<pre class="commit-body"
style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
{{end}}
</span>
</th>
<th id="total"></th>
<th class="text grey right age">{{if .LatestCommit.Author}}{{TimeSince .LatestCommit.Author.When $.Lang}}{{end}}</th>
</tr>
</thead>
<tbody>
{{if .HasParentPath}}
<tr class="has-parent">
<td colspan="3">{{svg "octicon-reply"}}<a href="{{$.BranchLink}}{{.ParentPath}}">..</a></td>
</tr>
{{end}}
{{range $item := .Files}}
{{$entry := $item.Entry}}
{{$commit := $item.Commit}}
{{$subModuleFile := $item.SubModuleFile}}
<tr>
<td class="name four wide">
<span class="truncate">
{{if $entry.IsSubModule}}
{{svg "octicon-file-submodule"}}
{{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}}
{{if $refURL}}
<a href="{{$refURL}}">{{$entry.Name}}</a><span class="at">@</span><a
href="{{$refURL}}/commit/{{$subModuleFile.RefID}}">{{ShortSha $subModuleFile.RefID}}</a>
{{else}}
{{$entry.Name}}<span class="at">@</span>{{ShortSha $subModuleFile.RefID}}
{{end}}
{{else}}
{{if $entry.IsDir}}
{{$subJumpablePathName := $entry.GetSubJumpablePathName}}
{{svg "octicon-file-directory-fill"}}
<a class="muted" href="{{$.TreeLink}}/{{PathEscapeSegments $subJumpablePathName}}" title="{{$subJumpablePathName}}">
{{$subJumpablePathFields := StringUtils.Split $subJumpablePathName "/"}}
{{$subJumpablePathFieldLast := (Eval (len $subJumpablePathFields) "-" 1)}}
{{if eq $subJumpablePathFieldLast 0}}
{{$subJumpablePathName}}
{{else}}
{{$subJumpablePathPrefixes := slice $subJumpablePathFields 0 $subJumpablePathFieldLast}}
<span class="text light-2">{{StringUtils.Join $subJumpablePathPrefixes "/"}}</span>/{{index $subJumpablePathFields $subJumpablePathFieldLast}}
{{end}}
</a>
{{else}}
{{svg (printf "octicon-%s" (EntryIcon $entry))}}
<a href="{{$.TreeLink}}/{{$entry.Name}}"
title="{{$entry.Name}}">{{$entry.Name}}</a>
{{end}}
{{end}}
</span>
</td>
<td class="message five wide">
<span class="truncate">
<a href="{{$.RepoLink}}/commit/{{$commit.ID}}"
title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a>
</span>
</td>
<td class="message four wide center centered">
<span style="display: none;" class="ui basic label" id="{{$entry.Name}}">
</span>
</td>
<td class="text right age three wide">{{TimeSince $commit.Committer.When $.Lang}}</td>
</tr>
{{end}}
</tbody>
</table>
{{if .ReadmeExist}}
{{template "repo/view_file" .}}
{{end}}
<script>
if (!("{{.Name}}" === "tutors" || "{{.Name}}" === "template")) {
fetch(COURSES_URL + "/courses/{{$.Repository.Owner.Name}}/{{$.Repository.Name}}/exercises", {
referrerPolicy: "origin",
credentials: "include",
redirect: 'follow'
}).then(res => {
if (!res.ok) {
if (res.status === 401) {
const form = document.createElement("form")
form.action = "/user/logout"
form.method = "POST"
document.body.appendChild(form);
form.submit()
}
} else {
res.text().then(res => {
let json = JSON.parse(res)
let total = document.getElementById("total")
total.innerText = json["total"] + " / " + json["max_total"] + " (" + json["percentage"] + "%)"
for (let [name, info] of Object.entries(json["exercises"])) {
let el = document.getElementById(name)
if (el !== null) {
if (info["finished"]) {
if (info["points"] !== null) {
el.innerText = info["points"].toString() + " / " + info["max_points"].toString()
el.classList.add("black")
} else {
el.innerText = "grading in process"
el.classList.add("black")
}
} else {
el.innerText = info["message"]
if (info["important_message"]) {
el.classList.add("red")
}
}
el.style.removeProperty("display")
}
}
})
}
}).catch(_ => {
{{if .IsAdmin}}
{{else}}
const form = document.createElement("form")
form.action = "/user/logout"
form.method = "POST"
document.body.appendChild(form);
form.submit()
{{end}}
})
}
</script>

View File

@ -0,0 +1,69 @@
<div class="header-wrapper">
{{with .Repository}}
<div class="ui container">
<div class="repo-header">
<div class="repo-title-wrap df fc mb-4">
<div class="repo-title">
<a href="{{AppSubUrl}}/{{.Owner.Name}}" id="repo_owner">{{.Owner.FullName}}</a>
<div class="mx-2" style="display: none" id="slash">/</div>
<a href="{{$.RepoLink}}" id="repo_name" style="display: none">{{.Name}}</a>
</div>
</div>
<div class="ui labeled button mb-4" data-position="top center" data-variation="tiny" tabindex="0">
<a class="ui compact small basic button disabled" id="tutor">
Tutor
</a>
<a class="ui basic label disabled" id="tutor_name">
Not found
</a>
</div>
</div>
</div><!-- end grid -->
</div><!-- end container -->
<div class="ui tabs divider"></div>
<script>
function no_course_repo() {
document.getElementById("slash").style.removeProperty("display")
document.getElementById("repo_name").style.removeProperty("display")
document.getElementById("repo_owner").innerText = "{{.Owner.Name}}"
document.getElementById("tutor").style.display = "none"
document.getElementById("tutor_name").style.display = "none"
setTimeout(function () {
document.getElementById("builds").style.display = "none"
document.getElementById("chat").style.display = "none"
}, 1)
}
if ("{{.Name}}" === "tutors" || "{{.Name}}" === "template") {
no_course_repo()
} else {
fetch(COURSES_URL + "/courses/{{.Owner.Name}}/{{.Name}}/tutor", {
referrerPolicy: "origin",
credentials: "include"
}).then(res => {
if (!res.ok) {
if (res.status === 401) {
const form = document.createElement("form")
form.action = "/user/logout"
form.method = "POST"
document.body.appendChild(form);
form.submit()
} else if (res.status === 500) {
no_course_repo()
}
} else {
res.text().then(res => {
let tutor = JSON.parse(res)
let el = document.getElementById("tutor_name")
el.innerText = tutor["name"]
el.setAttribute("href", "/" + tutor["username"])
el.classList.remove("disabled")
})
}
}).catch(_ => {
no_course_repo()
})
}
</script>
{{end}}

124
templates/repo/home.tmpl Normal file
View File

@ -0,0 +1,124 @@
{{template "base/head" .}}
{{ $n := len .TreeNames}}
{{$l := Eval $n "-" 1}}
<div class="page-content repository file list {{if .IsBlame}}blame{{end}}">
{{template "repo/header" .}}
<div class="ui container {{if .IsBlame}}fluid padded{{end}}">
{{template "base/alert" .}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui repo-topic-edit grid form" id="topic_edit" style="display:none">
<div class="fourteen wide column">
<div class="field">
<div class="ui fluid multiple search selection dropdown">
<input type="hidden" name="topics"
value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Eval $i "+" 1) (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
<div class="ui small label topic transition visible" data-value="{{.Name}}"
style="display: inline-block !important; cursor: default;">{{.Name}}{{svg "octicon-x" 16 "delete icon ml-3 mt-1"}}</div>
{{end}}
<div class="text"></div>
</div>
</div>
</div>
</div>
{{end}}
{{if .Repository.IsArchived}}
<div class="ui warning message">
{{.i18n.Tr "repo.archive.title"}}
</div>
{{end}}
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
{{if eq $n 0}}
<div class="left fitted item mr-0">
<div class="ui tiny primary buttons">
{{if .Permission.IsAdmin}}
<a class="ui button" href="{{.RepoLink}}/settings">
{{.i18n.Tr "repo.settings"}}
</a>
{{end}}
<a class="ui button" target="_blank" id="builds">
Builds
</a>
<a class="ui button" href="{{.RepoLink}}/commits/branch/master">
Commits
</a>
</div>
</div>
{{else}}
<div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section"
href="{{.RepoLink}}/src/{{.BranchNameSubURL}}"
title="{{.Repository.Name}}">{{StringUtils.EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span
class="divider">/</span>{{if eq $i $l}}<span class="active section"
title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}
<span class="section"><a href="{{$.BranchLink}}/{{$p}}"
title="{{$v}}">{{StringUtils.EllipsisString $v 30}}</a></span>{{end}}{{end}}</span>
</div>
{{end}}
<!-- If home page, show new PR. If not, show breadcrumb -->
<div class="right fitted item mr-0" id="file-buttons">
<div class="ui tiny primary buttons">
{{if .Repository.CanEnableEditor}}
{{if .CanAddFile}}
<a href="{{.RepoLink}}/_new/{{.BranchName}}/{{.TreePath}}"
class="ui button">
{{.i18n.Tr "repo.editor.new_file"}}
</a>
{{end}}
{{if .CanUploadFile}}
<a href="{{.RepoLink}}/_upload/{{.BranchName}}/{{.TreePath}}"
class="ui button">
{{.i18n.Tr "repo.editor.upload_file"}}
</a>
{{end}}
{{end}}
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }}
<a href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath}}"
class="ui button">
{{.i18n.Tr "repo.file_history"}}
</a>
{{end}}
</div>
</div>
<div class="fitted item">
<!-- Only show clone panel in repository home page -->
{{if eq $n 0}}
<div class="ui action tiny input" id="clone-panel">
{{template "repo/clone_buttons" .}}
<button id="download-btn" class="ui basic jump dropdown icon button poping up"
data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted"
data-position="top right">
{{svg "octicon-download"}}
<div class="menu">
<a class="item archive-link"
data-url="{{$.RepoLink}}/archive/{{$.BranchName}}.zip">{{svg "octicon-file-zip"}}
&nbsp;{{.i18n.Tr "repo.download_zip"}}</a>
<a class="item archive-link"
data-url="{{$.RepoLink}}/archive/{{$.BranchName}}.tar.gz">{{svg "octicon-file-zip"}}
&nbsp;{{.i18n.Tr "repo.download_tar"}}</a>
<a class="item archive-link"
data-url="{{$.RepoLink}}/archive/{{$.BranchName}}.bundle">{{svg "octicon-package"}}
&nbsp;{{.i18n.Tr "repo.download_bundle"}}</a>
</div>
</button>
</div>
{{end}}
</div>
</div>
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
{{template "repo/blame" .}}
{{else}}
{{if eq $n 0}}
{{template "repo/course_view_list" .}}
{{else}}
{{template "repo/view_list" .}}
{{end}}
{{end}}
</div>
</div>
{{template "base/footer" .}}
<script>
document.getElementById("builds").setAttribute("href", BUILD_URL + "/builds/{{ .Repository.Owner.Name }}/{{.Repository.Name}}")
</script>

View File

@ -0,0 +1,38 @@
<div class="ui segments repository-summary{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) .LanguageStats }} repository-summary-language-stats{{end}} mt-3">
<div class="ui segment sub-menu repository-menu">
<div class="ui two horizontal center link list">
{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo)}}
<div class="item{{if .PageIsCommits}} active{{end}}">
<a class="ui" href="{{.RepoLink}}/commits{{if .IsViewBranch}}/branch{{else if .IsViewTag}}/tag{{else if .IsViewCommit}}/commit{{end}}/{{.BranchName}}">{{svg "octicon-history"}} <b>{{.CommitsCount}}</b> {{.i18n.Tr (ctx.Locale.TrN .i18n.Lang .CommitsCount "repo.commit" "repo.commits") }}</a>
</div>
<div class="item">
<span class="ui">{{svg "octicon-database"}} <b>{{FileSize .Repository.Size}}</b></span>
</div>
{{end}}
</div>
</div>
{{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo) .LanguageStats }}
<div class="ui segment sub-menu language-stats-details" style="display: none">
<div class="ui horizontal center link list">
{{range .LanguageStats}}
<div class="item df ac jc">
<i class="color-icon mr-3" style="background-color: {{ .Color }}"></i>
<span class="bold mr-3">
{{if eq .Language "other" }}
{{ $.i18n.Tr "repo.language_other" }}
{{else}}
{{ .Language }}
{{end}}
</span>
{{ .Percentage }}%
</div>
{{end}}
</div>
</div>
<a class="ui segment language-stats">
{{range .LanguageStats}}
<div class="bar" style="width: {{ .Percentage }}%; background-color: {{ .Color }}">&nbsp;</div>
{{end}}
</a>
{{end}}
</div>