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,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>

View 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" .}}

View 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>

View 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" .}}

View 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" .}}

View 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" .}}

View 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>

View 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" .}}