init
This commit is contained in:
25
templates/base/footer_content.tmpl
Normal file
25
templates/base/footer_content.tmpl
Normal file
@ -0,0 +1,25 @@
|
||||
<footer>
|
||||
<div class="ui container">
|
||||
<div class="ui left">
|
||||
{{.i18n.Tr "powered_by" "Laurel"}} - {{if ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}:
|
||||
<strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}:
|
||||
<strong>{{call .TmplLoadTimes}}</strong>{{end}}
|
||||
</div>
|
||||
<div class="ui right links">
|
||||
<div class="ui language bottom floating slide up dropdown link item">
|
||||
{{svg "octicon-globe"}}
|
||||
<div class="text">{{.LangName}}</div>
|
||||
<div class="menu language-menu">
|
||||
{{range .AllLangs}}
|
||||
<a lang="{{.Lang}}" data-url="{{AppSubUrl}}/?lang={{.Lang}}"
|
||||
class="item {{if eq $.Lang .Lang}}active selected{{end}}">{{.Name}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<a target="_blank" rel="noopener noreferrer"
|
||||
href="https://proglang.informatik.uni-freiburg.de">Department of Programming Languages</a>
|
||||
<a target="_blank" rel="noopener noreferrer"
|
||||
href="https://proglang.informatik.uni-freiburg.de/imprint.html">Imprint</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
75
templates/base/head_navbar.tmpl
Normal file
75
templates/base/head_navbar.tmpl
Normal file
@ -0,0 +1,75 @@
|
||||
<div class="ui container" id="navbar">
|
||||
<div class="item brand" style="justify-content: space-between;">
|
||||
<a href="{{AppSubUrl}}/">
|
||||
<img class="ui mini image" width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg">
|
||||
</a>
|
||||
<div class="ui basic icon button mobile-only" id="navbar-expand-toggle">
|
||||
<i class="sidebar icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if and .IsSigned .MustChangePassword}}
|
||||
{{/* No links */}}
|
||||
{{else if .IsSigned}}
|
||||
<a class="item {{if .PageIsDashboard}}active{{end}}" href="{{AppSubUrl}}/">Home</a>
|
||||
{{if .IsAdmin}}
|
||||
<div class="divider"></div>
|
||||
<a class="item" id="admin">Admin</a>
|
||||
{{end}}
|
||||
<a class="item" href="http://chat.localhost">Chat</a>
|
||||
{{end}}
|
||||
|
||||
{{template "custom/extra_links" .}}
|
||||
|
||||
{{if .IsSigned}}
|
||||
<div class="right stackable menu">
|
||||
<div class="ui dropdown jump item poping up" tabindex="-1"
|
||||
data-content="{{.i18n.Tr "user_profile_and_more"}}" data-variation="tiny inverted">
|
||||
<span class="text">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tiny"}}
|
||||
<span class="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span>
|
||||
<span class="mobile-only">{{.SignedUser.Name}}</span>
|
||||
<span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu" tabindex="-1">
|
||||
<div class="ui header">
|
||||
{{.i18n.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="{{if .PageIsUserSettings}}active{{end}} item" href="{{AppSubUrl}}/user/settings">
|
||||
{{svg "octicon-tools"}}
|
||||
{{.i18n.Tr "your_settings"}}<!-- Your settings -->
|
||||
</a>
|
||||
{{if .IsAdmin}}
|
||||
<div class="divider"></div>
|
||||
<a class="{{if .PageIsAdmin}}active{{end}} item" href="{{AppSubUrl}}/admin">
|
||||
{{svg "octicon-server"}}
|
||||
{{.i18n.Tr "admin_panel"}}<!-- Admin Panel -->
|
||||
</a>
|
||||
{{end}}
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout"
|
||||
data-redirect="http://courses.docker.localhost/auth/logout?redirect={{AppUrl}}">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{.i18n.Tr "sign_out"}}<!-- Sign Out -->
|
||||
</a>
|
||||
</div><!-- end content ctx.AvatarUtils.Avatar menu -->
|
||||
</div><!-- end dropdown ctx.AvatarUtils.Avatar menu -->
|
||||
</div><!-- end signed user right menu -->
|
||||
{{else}}
|
||||
<div class="right stackable menu">
|
||||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow"
|
||||
href="http://courses.localhost/auth/login?redirect={{AppUrl}}user/oauth2/uni">
|
||||
{{svg "octicon-sign-in"}} {{.i18n.Tr "sign_in"}}
|
||||
</a>
|
||||
</div><!-- end anonymous right menu -->
|
||||
{{end}}
|
||||
</div>
|
||||
<script>
|
||||
const COURSES_URL = "http://courses.localhost"
|
||||
const BUILD_URL = "http://build.localhost"
|
||||
{{if .IsAdmin}}
|
||||
document.getElementById("admin").setAttribute("href", COURSES_URL + "/admin")
|
||||
{{end}}
|
||||
</script>
|
58
templates/explore/repo_list.tmpl
Normal file
58
templates/explore/repo_list.tmpl
Normal file
@ -0,0 +1,58 @@
|
||||
<div class="ui repository list">
|
||||
{{range .Repos}}
|
||||
<div class="item">
|
||||
<div class="ui header df ac">
|
||||
<div class="repo-title">
|
||||
{{$avatar := (ctx.AvatarUtils .Repo 32 "mr-3")}}
|
||||
{{if $avatar}}
|
||||
{{$avatar}}
|
||||
{{end}}
|
||||
<a class="name" href="{{.Link}}">
|
||||
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}
|
||||
</a>
|
||||
<div class="labels df ac fw">
|
||||
{{if .IsArchived}}
|
||||
<span class="ui basic label">{{$.i18n.Tr "repo.desc.archived"}}</span>
|
||||
{{end}}
|
||||
{{if .IsTemplate}}
|
||||
{{if .IsPrivate}}
|
||||
<span class="ui basic label">{{$.i18n.Tr "repo.desc.private_template"}}</span>
|
||||
{{else}}
|
||||
{{if .Owner.Visibility.IsPrivate}}
|
||||
<span class="ui basic label">{{$.i18n.Tr "repo.desc.internal_template"}}</span>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
{{if .Owner.Visibility.IsPrivate}}
|
||||
<span class="ui basic label">{{$.i18n.Tr "repo.desc.internal"}}</span>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
||||
{{if .IsFork}}
|
||||
{{svg "octicon-repo-forked"}}
|
||||
{{else if .IsMirror}}
|
||||
{{svg "octicon-mirror"}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="description">
|
||||
{{if .DescriptionHTML}}<p>{{.DescriptionHTML}}</p>{{end}}
|
||||
{{if .Topics }}
|
||||
<div class="ui tags">
|
||||
{{range .Topics}}
|
||||
{{if ne . "" }}<a href="{{AppSubUrl}}/explore/repos?q={{.}}&topic=1">
|
||||
<div class="ui small label topic">{{.}}</div>
|
||||
</a>{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<p class="time">{{$.i18n.Tr "org.repo_updated"}} {{TimeSinceUnix .UpdatedUnix $.i18n.Lang}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div>
|
||||
{{$.i18n.Tr "explore.repo_no_results"}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
17
templates/home.tmpl
Normal file
17
templates/home.tmpl
Normal file
@ -0,0 +1,17 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content home">
|
||||
<div class="ui stackable middle very relaxed page grid">
|
||||
<div class="sixteen wide center aligned centered column">
|
||||
<div>
|
||||
<img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.svg"/>
|
||||
</div>
|
||||
<div class="hero">
|
||||
<h1 class="ui icon header title">
|
||||
Progλang Git
|
||||
</h1>
|
||||
<h2>Student Code Submission</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
95
templates/org/home.tmpl
Normal file
95
templates/org/home.tmpl
Normal file
@ -0,0 +1,95 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content organization profile">
|
||||
<div class="ui container df">
|
||||
{{ctx.AvatarUtils .Org 140 "org-avatar"}}
|
||||
<div id="org-info">
|
||||
<div class="ui header">
|
||||
{{.Org.DisplayName}}
|
||||
<span class="org-visibility">
|
||||
{{if .Org.Visibility.IsLimited}}
|
||||
<div class="ui large basic horizontal label">{{.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
|
||||
{{if .Org.Visibility.IsPrivate}}
|
||||
<div class="ui large basic horizontal label">{{.i18n.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}
|
||||
</span>
|
||||
{{if .IsOrganizationOwner}}<a class="middle text grey"
|
||||
href="{{.OrgLink}}/settings">{{svg "octicon-gear" 16 "mb-3"}}</a>{{end}}
|
||||
</div>
|
||||
{{if $.RenderedDescription}}<p class="render-content markup">{{$.RenderedDescription|Str2html}}</p>{{end}}
|
||||
<div class="text grey meta">
|
||||
{{if .Org.Location}}
|
||||
<div class="item">{{svg "octicon-location"}} <span>{{.Org.Location}}</span></div>{{end}}
|
||||
{{if .Org.Website}}
|
||||
<div class="item">{{svg "octicon-link"}} <a target="_blank" rel="noopener noreferrer"
|
||||
href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<div class="ui container">
|
||||
<div class="ui mobile reversed stackable grid">
|
||||
<div class="ui eleven wide column">
|
||||
{{template "explore/repo_search" .}}
|
||||
{{template "explore/repo_list" .}}
|
||||
{{template "base/paginate" .}}
|
||||
</div>
|
||||
{{ if .IsAdmin }}
|
||||
<div class="ui five wide column">
|
||||
<h4 class="ui top attached header df">
|
||||
<strong class="f1">{{.i18n.Tr "org.people"}}</strong>
|
||||
{{if .IsOrganizationMember}}
|
||||
<div class="ui">
|
||||
<a class="text grey dif ac"
|
||||
href="{{.OrgLink}}/members"><span>{{.Org.NumMembers}}</span> {{svg "octicon-chevron-right"}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</h4>
|
||||
<div class="ui attached segment members">
|
||||
{{$isMember := .IsOrganizationMember}}
|
||||
{{range .Members}}
|
||||
{{if or $isMember (.IsPublicMember $.Org.ID)}}
|
||||
<a href="{{.HomeLink}}" title="{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}">
|
||||
{{ctx.AvatarUtils .}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .IsOrganizationMember}}
|
||||
<div class="ui top attached header df">
|
||||
<strong class="f1">{{.i18n.Tr "org.teams"}}</strong>
|
||||
<div class="ui">
|
||||
<a class="text grey dif ac"
|
||||
href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui attached table segment teams">
|
||||
{{range .Teams}}
|
||||
<div class="item">
|
||||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong
|
||||
class="team-name">{{.Name}}</strong></a>
|
||||
<p class="text grey">
|
||||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}
|
||||
</a> ·
|
||||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .IsOrganizationOwner}}
|
||||
<div class="ui bottom attached segment">
|
||||
<a class="ui blue small button"
|
||||
href="{{.OrgLink}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
27
templates/repo/clone_buttons.tmpl
Normal file
27
templates/repo/clone_buttons.tmpl
Normal 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}}
|
151
templates/repo/course_view_list.tmpl
Normal file
151
templates/repo/course_view_list.tmpl
Normal 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>
|
69
templates/repo/header.tmpl
Normal file
69
templates/repo/header.tmpl
Normal 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
124
templates/repo/home.tmpl
Normal 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"}}
|
||||
{{.i18n.Tr "repo.download_zip"}}</a>
|
||||
<a class="item archive-link"
|
||||
data-url="{{$.RepoLink}}/archive/{{$.BranchName}}.tar.gz">{{svg "octicon-file-zip"}}
|
||||
{{.i18n.Tr "repo.download_tar"}}</a>
|
||||
<a class="item archive-link"
|
||||
data-url="{{$.RepoLink}}/archive/{{$.BranchName}}.bundle">{{svg "octicon-package"}}
|
||||
{{.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>
|
38
templates/repo/sub_menu.tmpl
Normal file
38
templates/repo/sub_menu.tmpl
Normal 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 }}"> </div>
|
||||
{{end}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
183
templates/user/dashboard/courses.tmpl
Normal file
183
templates/user/dashboard/courses.tmpl
Normal file
@ -0,0 +1,183 @@
|
||||
<div class="ui container">
|
||||
<div class="ui container" style="margin-bottom: 2vh; display: none" id="info">
|
||||
<h4 class="ui top attached header">
|
||||
Information
|
||||
</h4>
|
||||
<div class="ui attached segment" id="info_content">
|
||||
Currently there are no courses available, check back later.
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui container" style="margin-bottom: 2vh; display: none" id="my_courses">
|
||||
<h4 class="ui top attached header">
|
||||
Your Courses
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<div class="ui attached table segment">
|
||||
<table class="ui very basic striped table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>UID</th>
|
||||
<th>Name</th>
|
||||
<th>Website</th>
|
||||
<th>Role</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="my_courses_table">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui container" style="margin-bottom: 2vh; display: none" id="courses">
|
||||
<h4 class="ui top attached header">
|
||||
Available Courses
|
||||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<div class="ui attached table segment">
|
||||
<table class="ui very basic striped table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>UID</th>
|
||||
<th>Name</th>
|
||||
<th>Website</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="courses_table"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
fetch(COURSES_URL + "/courses/list", {
|
||||
referrerPolicy: "origin",
|
||||
credentials: "include",
|
||||
redirect: 'follow',
|
||||
}).then(res => res.text()).then(res => {
|
||||
for (let [name, info] of Object.entries(JSON.parse(res))) {
|
||||
if (info["role"] !== null && info["role"] !== "admin" && (!info["restricted"] || info["role"] !== "student")) {
|
||||
const tr = document.createElement("tr")
|
||||
|
||||
const uid = document.createElement("td")
|
||||
const link = document.createElement("a")
|
||||
link.href = "/" + name
|
||||
link.innerText = name
|
||||
uid.appendChild(link)
|
||||
tr.appendChild(uid)
|
||||
|
||||
const display = document.createElement("td")
|
||||
display.innerText = info["display_name"]
|
||||
tr.appendChild(display)
|
||||
|
||||
const website = document.createElement("td")
|
||||
const link2 = document.createElement("a")
|
||||
link2.href = info["website"]
|
||||
link2.innerText = info["website"]
|
||||
website.appendChild(link2)
|
||||
tr.appendChild(website)
|
||||
|
||||
const role = document.createElement("td")
|
||||
role.innerText = info["role"]
|
||||
tr.appendChild(role)
|
||||
|
||||
const repo = document.createElement("td")
|
||||
|
||||
const form2 = document.createElement("form")
|
||||
if (info["role"] === "student") {
|
||||
form2.action = "/" + name + "/{{.SignedUser.Name}}"
|
||||
} else {
|
||||
form2.action = "/" + name
|
||||
}
|
||||
form2.method = "GET"
|
||||
|
||||
|
||||
const btn2 = document.createElement("button")
|
||||
btn2.type = "submit"
|
||||
btn2.classList.add("ui", "button", "green")
|
||||
btn2.innerText = "OPEN"
|
||||
form2.appendChild(btn2)
|
||||
|
||||
repo.appendChild(form2)
|
||||
tr.appendChild(repo)
|
||||
|
||||
document.getElementById("my_courses_table").appendChild(tr)
|
||||
} else if (info["open"] || info["role"] === "admin") {
|
||||
const tr = document.createElement("tr")
|
||||
|
||||
const uid = document.createElement("td")
|
||||
uid.innerText = name
|
||||
tr.appendChild(uid)
|
||||
|
||||
const display = document.createElement("td")
|
||||
display.innerText = info["display_name"]
|
||||
tr.appendChild(display)
|
||||
|
||||
const website = document.createElement("td")
|
||||
const link = document.createElement("a")
|
||||
link.href = info["website"]
|
||||
link.innerText = info["website"]
|
||||
website.appendChild(link)
|
||||
tr.appendChild(website)
|
||||
|
||||
|
||||
const join = document.createElement("td")
|
||||
|
||||
const form = document.createElement("form")
|
||||
if (info["role"] !== "admin") {
|
||||
form.method = "POST"
|
||||
form.action = COURSES_URL + "/courses/join"
|
||||
const hidden = document.createElement("input")
|
||||
hidden.type = "hidden"
|
||||
hidden.name = "course"
|
||||
hidden.value = name
|
||||
form.appendChild(hidden)
|
||||
} else {
|
||||
form.method = "GET"
|
||||
form.action = "/" + name
|
||||
}
|
||||
const btn = document.createElement("button")
|
||||
btn.type = "submit"
|
||||
btn.classList.add("ui", "button", "red")
|
||||
if (info["role"] !== "admin") {
|
||||
btn.innerText = "JOIN"
|
||||
btn.id = name
|
||||
btn.onsubmit = function () {
|
||||
document.getElementById(name).disabled = true
|
||||
}
|
||||
} else {
|
||||
btn.innerText = "OPEN"
|
||||
}
|
||||
form.appendChild(btn)
|
||||
|
||||
join.appendChild(form)
|
||||
tr.appendChild(join)
|
||||
|
||||
document.getElementById("courses_table").appendChild(tr)
|
||||
}
|
||||
}
|
||||
let my_c = document.getElementById("my_courses_table").childElementCount
|
||||
let c = document.getElementById("courses_table").childElementCount
|
||||
if (my_c !== 0) {
|
||||
document.getElementById("my_courses").style.removeProperty("display")
|
||||
}
|
||||
if (c !== 0) {
|
||||
document.getElementById("courses").style.removeProperty("display")
|
||||
}
|
||||
if (my_c === 0 && c === 0) {
|
||||
document.getElementById("info").style.removeProperty("display")
|
||||
}
|
||||
}).catch(_ => {
|
||||
{{if .IsAdmin}}
|
||||
document.getElementById("info").style.removeProperty("display")
|
||||
document.getElementById("info_content").innerText = "you are admin. if you are non-oauth-user -> no courses, otherwise re-login to gitea."
|
||||
{{else}}
|
||||
const form = document.createElement("form")
|
||||
form.action = "/user/logout"
|
||||
form.method = "POST"
|
||||
document.body.appendChild(form);
|
||||
form.submit()
|
||||
{{end}}
|
||||
});
|
||||
</script>
|
15
templates/user/dashboard/dashboard.tmpl
Normal file
15
templates/user/dashboard/dashboard.tmpl
Normal file
@ -0,0 +1,15 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content dashboard feeds">
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<div class="ui stackable grid">
|
||||
<div class="ui row">
|
||||
<div class="ui container ten wide column">
|
||||
{{template "user/dashboard/courses" .}}
|
||||
</div>
|
||||
{{template "user/dashboard/named_feeds" .}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
11
templates/user/dashboard/named_feeds.tmpl
Normal file
11
templates/user/dashboard/named_feeds.tmpl
Normal file
@ -0,0 +1,11 @@
|
||||
<div class="ui container six wide column">
|
||||
<h4 class="ui top attached header">
|
||||
Feed
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{template "user/dashboard/feeds" .}}
|
||||
{{ $length := len .Feeds }} {{ if eq $length 0 }}
|
||||
Nothing to see here, at least yet.
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
78
templates/user/profile.tmpl
Normal file
78
templates/user/profile.tmpl
Normal file
@ -0,0 +1,78 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content user profile">
|
||||
<div class="ui container">
|
||||
<div class="ui stackable middle very relaxed page grid">
|
||||
<div class="ui seven wide center aligned centered column">
|
||||
<div class="ui card">
|
||||
{{if eq .SignedUserName .Owner.Name}}
|
||||
<a class="image poping up" href="{{AppSubUrl}}/user/settings" id="profile-avatar"
|
||||
data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny"
|
||||
data-position="bottom center">
|
||||
{{ctx.AvatarUtils.Owner 290}}
|
||||
</a>
|
||||
{{else}}
|
||||
<span class="image" id="profile-avatar">
|
||||
{{ctx.AvatarUtils.Owner 290}}
|
||||
</span>
|
||||
{{end}}
|
||||
<div class="content word-break profile-avatar-name">
|
||||
{{if .Owner.FullName}}<span class="header text center">{{.Owner.FullName}}</span>{{end}}
|
||||
<span class="username text center">{{.Owner.Name}}</span>
|
||||
</div>
|
||||
<div class="extra content word-break">
|
||||
<ul>
|
||||
{{if .Owner.Location}}
|
||||
<li>{{svg "octicon-location"}} {{.Owner.Location}}</li>
|
||||
{{end}}
|
||||
{{if .ShowUserEmail }}
|
||||
<li>
|
||||
{{svg "octicon-mail"}}
|
||||
<a href="mailto:{{.Owner.Email}}" rel="nofollow">{{.Owner.Email}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{if .Owner.Website}}
|
||||
<li>
|
||||
{{svg "octicon-link"}}
|
||||
<a target="_blank" rel="noopener noreferrer me"
|
||||
href="{{.Owner.Website}}">{{.Owner.Website}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{if $.RenderedDescription}}
|
||||
<li>
|
||||
<div class="render-content markup">{{$.RenderedDescription|Str2html}}</div>
|
||||
</li>
|
||||
{{end}}
|
||||
{{range .OpenIDs}}
|
||||
{{if .Show}}
|
||||
<li>
|
||||
{{svg "fontawesome-openid"}}
|
||||
<a target="_blank" rel="noopener noreferrer" href="{{.URI}}">{{.URI}}</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<li>{{svg "octicon-clock"}} {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li>
|
||||
{{if .Orgs}}
|
||||
<li>
|
||||
<ul class="user-orgs">
|
||||
{{range .Orgs}}
|
||||
{{if or (.Visibility.IsPublic) ($.IsAdmin)}}
|
||||
<li>
|
||||
<a class="poping up" href="{{.HomeLink}}" data-content="{{.Name}}"
|
||||
data-position="top center" data-variation="tiny inverted">
|
||||
{{ctx.AvatarUtils .}}
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</ul>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
124
templates/user/settings/account.tmpl
Normal file
124
templates/user/settings/account.tmpl
Normal file
@ -0,0 +1,124 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content user settings account">
|
||||
{{template "user/settings/navbar" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<h4 class="ui top attached header">
|
||||
{{.i18n.Tr "settings.manage_emails"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="ui email list">
|
||||
{{range .Emails}}
|
||||
<div class="item">
|
||||
{{if not .IsPrimary}}
|
||||
<div class="right floated content">
|
||||
<button class="ui red tiny button delete-button" data-modal-id="delete-email" data-url="{{AppSubUrl}}/user/settings/account/email/delete" data-id="{{.ID}}">
|
||||
{{$.i18n.Tr "settings.delete_email"}}
|
||||
</button>
|
||||
</div>
|
||||
{{if .CanBePrimary}}
|
||||
<div class="right floated content">
|
||||
<form action="{{AppSubUrl}}/user/settings/account/email" method="post">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input name="_method" type="hidden" value="PRIMARY">
|
||||
<input name="id" type="hidden" value="{{.ID}}">
|
||||
<button class="ui blue tiny button">{{$.i18n.Tr "settings.primary_email"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{if not .IsActivated}}
|
||||
<div class="right floated content">
|
||||
<form action="{{AppSubUrl}}/user/settings/account/email" method="post">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input name="_method" type="hidden" value="SENDACTIVATION">
|
||||
<input name="id" type="hidden" value="{{.ID}}">
|
||||
{{if $.ActivationsPending}}
|
||||
<button disabled class="ui blue tiny button">{{$.i18n.Tr "settings.activations_pending"}}</button>
|
||||
{{else}}
|
||||
<button class="ui blue tiny button">{{$.i18n.Tr "settings.activate_email"}}</button>
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="content">
|
||||
<strong>{{.Email}}</strong>
|
||||
{{if .IsPrimary}}
|
||||
<div class="ui blue label">{{$.i18n.Tr "settings.primary"}}</div>
|
||||
{{end}}
|
||||
{{if .IsActivated}}
|
||||
<div class="ui green label">{{$.i18n.Tr "settings.activated"}}</div>
|
||||
{{else}}
|
||||
<div class="ui label">{{$.i18n.Tr "settings.requires_activation"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui attached bottom segment">
|
||||
<form class="ui form" action="{{AppSubUrl}}/user/settings/account/email" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="required field {{if .Err_Email}}error{{end}}">
|
||||
<label for="email">{{.i18n.Tr "settings.add_new_email"}}</label>
|
||||
<input id="email" name="email" type="email" required {{if not .CanAddEmails}}disabled{{end}}>
|
||||
</div>
|
||||
<button class="ui green button" {{if not .CanAddEmails}}disabled{{end}}>
|
||||
{{.i18n.Tr "settings.add_email"}}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<h4 class="ui top attached header">
|
||||
{{.i18n.Tr "settings.manage_themes"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="ui email list">
|
||||
<div class="item">
|
||||
{{.i18n.Tr "settings.theme_desc"}}
|
||||
</div>
|
||||
|
||||
<form class="ui form" action="{{.Link}}/theme" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="field">
|
||||
<label for="ui">{{.i18n.Tr "settings.ui"}}</label>
|
||||
<div class="ui selection dropdown" id="ui">
|
||||
<input name="theme" type="hidden" value="{{.SignedUser.Theme}}">
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="text">
|
||||
{{range $i,$a := .AllThemes}}
|
||||
{{if eq $.SignedUser.Theme $a}}{{$a}}{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="menu">
|
||||
{{range $i,$a := .AllThemes}}
|
||||
<div class="item{{if eq $.SignedUser.Theme $a}} active selected{{end}}" data-value="{{$a}}">
|
||||
{{$a}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.i18n.Tr "settings.update_theme"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui small basic delete modal" id="delete-email">
|
||||
<div class="ui icon header">
|
||||
{{svg "octicon-trash"}}
|
||||
{{.i18n.Tr "settings.email_deletion"}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>{{.i18n.Tr "settings.email_deletion_desc"}}</p>
|
||||
</div>
|
||||
{{template "base/delete_modal_actions" .}}
|
||||
</div>
|
||||
|
||||
|
||||
{{template "base/footer" .}}
|
12
templates/user/settings/keys.tmpl
Normal file
12
templates/user/settings/keys.tmpl
Normal file
@ -0,0 +1,12 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content user settings sshkeys">
|
||||
{{template "user/settings/navbar" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
{{template "user/settings/keys_ssh" .}}
|
||||
{{template "user/settings/keys_principal" .}}
|
||||
{{template "user/settings/keys_gpg" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "base/footer" .}}
|
13
templates/user/settings/navbar.tmpl
Normal file
13
templates/user/settings/navbar.tmpl
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
|
||||
<div class="new-menu-inner">
|
||||
<a class="{{if .PageIsSettingsProfile}}active{{end}} item" href="{{AppSubUrl}}/user/settings">
|
||||
Profile
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsAccount}}active{{end}} item" href="{{AppSubUrl}}/user/settings/account">
|
||||
Account
|
||||
</a>
|
||||
<a class="{{if .PageIsSettingsKeys}}active{{end}} item" href="{{AppSubUrl}}/user/settings/keys">
|
||||
Keys
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
102
templates/user/settings/profile.tmpl
Normal file
102
templates/user/settings/profile.tmpl
Normal file
@ -0,0 +1,102 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content user settings profile">
|
||||
{{template "user/settings/navbar" .}}
|
||||
<div class="ui container">
|
||||
{{template "base/alert" .}}
|
||||
<h4 class="ui top attached header">
|
||||
{{.i18n.Tr "settings.public_profile"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<p>{{.i18n.Tr "settings.profile_desc"}}</p>
|
||||
<form class="ui form" action="{{.Link}}" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="field {{if .Err_FullName}}error{{end}}">
|
||||
<label for="full_name">{{.i18n.Tr "settings.full_name"}}</label>
|
||||
<input id="full_name" name="full_name" value="{{.SignedUser.FullName}}">
|
||||
</div>
|
||||
<div class="field {{if .Err_Email}}error{{end}}">
|
||||
<label for="email">{{.i18n.Tr "email"}}</label>
|
||||
<p>{{.SignedUser.Email}}</p>
|
||||
</div>
|
||||
<div class="inline field">
|
||||
<div class="ui checkbox" id="keep-email-private">
|
||||
<label class="poping up" data-content="{{.i18n.Tr "settings.keep_email_private_popup"}}"><strong>{{.i18n.Tr "settings.keep_email_private"}}</strong></label>
|
||||
<input name="keep_email_private" type="checkbox" {{if .SignedUser.KeepEmailPrivate}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field {{if .Err_Description}}error{{end}}">
|
||||
<label for="description">{{$.i18n.Tr "user.user_bio"}}</label>
|
||||
<textarea id="description" name="description" rows="2" placeholder="{{.i18n.Tr "settings.biography_placeholder"}}">{{.SignedUser.Description}}</textarea>
|
||||
</div>
|
||||
<div class="field {{if .Err_Website}}error{{end}}">
|
||||
<label for="website">{{.i18n.Tr "settings.website"}}</label>
|
||||
<input id="website" name="website" type="url" value="{{.SignedUser.Website}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="location">{{.i18n.Tr "settings.location"}}</label>
|
||||
<input id="location" name="location" value="{{.SignedUser.Location}}">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="language">{{.i18n.Tr "settings.language"}}</label>
|
||||
<div class="ui language selection dropdown" id="language">
|
||||
<input name="language" type="hidden" value="{{.SignedUser.Language}}">
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="text">{{range .AllLangs}}{{if eq $.SignedUser.Language .Lang}}{{.Name}}{{end}}{{end}}</div>
|
||||
<div class="menu">
|
||||
{{range .AllLangs}}
|
||||
<div class="item{{if eq $.SignedUser.Language .Lang}} active selected{{end}}" data-value="{{.Lang}}">{{.Name}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<!-- private block -->
|
||||
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.i18n.Tr "settings.update_profile"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h4 class="ui top attached header">
|
||||
{{.i18n.Tr "settings.avatar"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
||||
{{.CsrfTokenHtml}}
|
||||
{{if not .DisableGravatar}}
|
||||
<div class="inline field">
|
||||
<div class="ui radio checkbox">
|
||||
<input name="source" value="lookup" type="radio" {{if not .SignedUser.UseCustomAvatar}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "settings.lookup_avatar_by_mail"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field {{if .Err_Gravatar}}error{{end}}">
|
||||
<label for="gravatar">Avatar {{.i18n.Tr "email"}}</label>
|
||||
<input id="gravatar" name="gravatar" value="{{.SignedUser.AvatarEmail}}" />
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="inline field">
|
||||
<div class="ui radio checkbox">
|
||||
<input name="source" value="local" type="radio" {{if .SignedUser.UseCustomAvatar}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "settings.enable_custom_avatar"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inline field">
|
||||
<label for="avatar">{{.i18n.Tr "settings.choose_new_avatar"}}</label>
|
||||
<input name="avatar" type="file" >
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.i18n.Tr "settings.update_avatar"}}</button>
|
||||
<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.i18n.Tr "settings.delete_current_avatar"}}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
Reference in New Issue
Block a user