diff options
Diffstat (limited to '')
72 files changed, 3532 insertions, 0 deletions
diff --git a/templates/user/auth/activate.tmpl b/templates/user/auth/activate.tmpl new file mode 100644 index 0000000..9ae811b --- /dev/null +++ b/templates/user/auth/activate.tmpl @@ -0,0 +1,61 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user activate"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form ignore-dirty tw-max-w-2xl tw-m-auto" action="{{AppSubUrl}}/user/activate" method="post"> + {{.CsrfTokenHtml}} + <h2 class="ui top attached header"> + {{ctx.Locale.Tr "auth.active_your_account"}} + </h2> + <div class="ui attached segment"> + {{template "base/alert" .}} + {{if .IsActivatePage}} + {{if .ServiceNotEnabled}} + <p class="center">{{ctx.Locale.Tr "auth.disable_register_mail"}}</p> + {{else if .ResendLimited}} + <p class="center">{{ctx.Locale.Tr "auth.resent_limit_prompt"}}</p> + {{else}} + <p>{{ctx.Locale.Tr "auth.confirmation_mail_sent_prompt" .SignedUser.Email .ActiveCodeLives}}</p> + {{end}} + {{else}} + {{if .NeedsPassword}} + <div class="required field"> + <label for="password">{{ctx.Locale.Tr "password"}}</label> + <input id="password" name="password" type="password" autocomplete="off" required> + </div> + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "install.confirm_password"}}</button> + </div> + <input id="code" name="code" type="hidden" value="{{.Code}}"> + {{else if .IsSendRegisterMail}} + <p>{{ctx.Locale.Tr "auth.confirmation_mail_sent_prompt" .Email .ActiveCodeLives}}</p> + {{else if .IsCodeInvalid}} + <p>{{ctx.Locale.Tr "auth.invalid_code"}}</p> + {{else if .IsPasswordInvalid}} + <p>{{ctx.Locale.Tr "auth.invalid_password"}}</p> + {{else if .ManualActivationOnly}} + <p class="center">{{ctx.Locale.Tr "auth.manual_activation_only"}}</p> + {{else}} + <p>{{ctx.Locale.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email}}</p> + <div class="divider"></div> + <details class="inline field"> + <summary>{{ctx.Locale.Tr "auth.change_unconfirmed_email_summary"}}</summary> + + <p>{{ctx.Locale.Tr "auth.change_unconfirmed_email"}}</p> + <div class="inline field"> + <label for="email">{{ctx.Locale.Tr "email"}}</label> + <input id="email" name="email" type="email" autocomplete="on"> + </div> + </details> + + <div class="text"> + <button class="ui primary button">{{ctx.Locale.Tr "auth.resend_mail"}}</button> + </div> + {{end}} + {{end}} + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/captcha.tmpl b/templates/user/auth/captcha.tmpl new file mode 100644 index 0000000..03e3607 --- /dev/null +++ b/templates/user/auth/captcha.tmpl @@ -0,0 +1,30 @@ +{{if .EnableCaptcha}}{{if eq .CaptchaType "image"}} + <div class="inline field tw-text-center"> + <input type="hidden" name="img-captcha-id" value="{{.Captcha}}"> + <img style="transform: scaleX(-1)" onclick="this.src=`{{AppSubUrl}}/captcha/{{.Captcha}}.png?reload=${Date.now()}`" class="captcha-img" src="{{AppSubUrl}}/captcha/{{.Captcha}}.png"> + </div> + <div class="required field {{if .Err_Captcha}}error{{end}}"> + <label for="captcha">{{ctx.Locale.Tr "captcha"}}</label> + <input id="captcha" name="img-captcha-response" autocomplete="off"> + </div> +{{else if eq .CaptchaType "recaptcha"}} + <div class="inline field tw-text-center required"> + <div id="captcha" data-captcha-type="g-recaptcha" class="g-recaptcha-style" data-sitekey="{{.RecaptchaSitekey}}"></div> + </div> + <script src='{{URLJoin .RecaptchaURL "api.js"}}'></script> +{{else if eq .CaptchaType "hcaptcha"}} + <div class="inline field tw-text-center required"> + <div id="captcha" data-captcha-type="h-captcha" class="h-captcha-style" data-sitekey="{{.HcaptchaSitekey}}"></div> + </div> + <script src='https://hcaptcha.com/1/api.js'></script> +{{else if eq .CaptchaType "mcaptcha"}} + <div class="inline field tw-text-center"> + <div class="m-captcha-style" id="mcaptcha__widget-container"></div> + <div id="captcha" data-captcha-type="m-captcha" data-sitekey="{{.McaptchaSitekey}}" data-instance-url="{{.McaptchaURL}}"></div> + </div> +{{else if eq .CaptchaType "cfturnstile"}} + <div class="inline field tw-text-center"> + <div id="captcha" data-captcha-type="cf-turnstile" data-sitekey="{{.CfTurnstileSitekey}}"></div> + </div> + <script src='https://challenges.cloudflare.com/turnstile/v0/api.js'></script> +{{end}}{{end}} diff --git a/templates/user/auth/change_passwd.tmpl b/templates/user/auth/change_passwd.tmpl new file mode 100644 index 0000000..e05f46f --- /dev/null +++ b/templates/user/auth/change_passwd.tmpl @@ -0,0 +1,7 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin{{if .LinkAccountMode}} icon{{end}}"> + <div class="ui container"> + {{template "user/auth/change_passwd_inner" .}} + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/change_passwd_inner.tmpl b/templates/user/auth/change_passwd_inner.tmpl new file mode 100644 index 0000000..601f036 --- /dev/null +++ b/templates/user/auth/change_passwd_inner.tmpl @@ -0,0 +1,22 @@ + {{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}} + {{template "base/alert" .}} + {{end}} + <h4 class="ui top attached header center"> + {{ctx.Locale.Tr "settings.update_password"}} + </h4> + <div class="ui attached segment"> + <form class="ui form tw-max-w-2xl tw-m-auto" action="{{.ChangePasscodeLink}}" method="post"> + {{.CsrfTokenHtml}} + <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> + <label for="password">{{ctx.Locale.Tr "password"}}</label> + <input id="password" name="password" type="password" value="{{.password}}" autocomplete="new-password" required> + </div> + <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> + <label for="retype">{{ctx.Locale.Tr "re_type"}}</label> + <input id="retype" name="retype" type="password" autocomplete="new-password" required> + </div> + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "settings.update_password"}}</button> + </div> + </form> + </div> diff --git a/templates/user/auth/finalize_openid.tmpl b/templates/user/auth/finalize_openid.tmpl new file mode 100644 index 0000000..f84f860 --- /dev/null +++ b/templates/user/auth/finalize_openid.tmpl @@ -0,0 +1,47 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin"> + <div class="ui container"> + <div class="ui grid"> + {{template "user/auth/finalize_openid_navbar" .}} + <div class="twelve wide column content"> + {{template "base/alert" .}} + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "auth.login_userpass"}} + </h4> + <div class="ui attached segment"> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="required inline field {{if .Err_UserName}}error{{end}}"> + <label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label> + <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required> + </div> + <div class="required inline field {{if .Err_Password}}error{{end}}"> + <label for="password">{{ctx.Locale.Tr "password"}}</label> + <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> + </div> + <div class="inline field"> + <label></label> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "auth.remember_me"}}</label> + <input name="remember" type="checkbox"> + </div> + </div> + + <div class="inline field"> + <label></label> + <button class="ui primary button">{{ctx.Locale.Tr "sign_in"}}</button> + <a href="{{AppSubUrl}}/user/forget_password">{{ctx.Locale.Tr "auth.forget_password"}}</a> + </div> + {{if .ShowRegistrationButton}} + <div class="inline field"> + <label></label> + <a href="{{AppSubUrl}}/user/sign_up">{{ctx.Locale.Tr "auth.sign_up_button"}}</a> + </div> + {{end}} + </form> + </div> + </div> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/forgot_passwd.tmpl b/templates/user/auth/forgot_passwd.tmpl new file mode 100644 index 0000000..55bcf63 --- /dev/null +++ b/templates/user/auth/forgot_passwd.tmpl @@ -0,0 +1,39 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user forgot password"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form ignore-dirty" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <h2 class="ui top attached header"> + {{ctx.Locale.Tr "auth.forgot_password_title"}} + </h2> + <div class="ui attached segment"> + {{template "base/alert" .}} + {{if .IsResetSent}} + <p>{{ctx.Locale.Tr "auth.reset_password_mail_sent_prompt" .Email .ResetPwdCodeLives}}</p> + {{else if .IsResetRequest}} + <div class="required field {{if .Err_Email}}error{{end}}"> + <label for="email">{{ctx.Locale.Tr "email"}}</label> + <input id="email" name="email" type="email" value="{{.Email}}" autofocus required> + </div> + <div class="divider"></div> + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "auth.send_reset_mail"}}</button> + </div> + {{else if .IsResetDisable}} + <p class="center"> + {{if $.IsAdmin}} + {{ctx.Locale.Tr "auth.disable_forgot_password_mail_admin"}} + {{else}} + {{ctx.Locale.Tr "auth.disable_forgot_password_mail"}} + {{end}} + </p> + {{else if .ResendLimited}} + <p class="center">{{ctx.Locale.Tr "auth.resent_limit_prompt"}}</p> + {{end}} + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/grant.tmpl b/templates/user/auth/grant.tmpl new file mode 100644 index 0000000..1a1b72b --- /dev/null +++ b/templates/user/auth/grant.tmpl @@ -0,0 +1,34 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content ui one column stackable center aligned page grid oauth2-authorize-application-box"> + <div class="column seven wide"> + <div class="ui middle centered raised segments"> + <h3 class="ui top attached header"> + {{ctx.Locale.Tr "auth.authorize_title" .Application.Name}} + </h3> + <div class="ui attached segment"> + {{template "base/alert" .}} + <p> + <b>{{ctx.Locale.Tr "auth.authorize_application_description"}}</b><br> + {{ctx.Locale.Tr "auth.authorize_application_created_by" .ApplicationCreatorLinkHTML}} + </p> + <p>With scopes: {{.Scope}}.</p> + </div> + <div class="ui attached segment"> + <p>{{ctx.Locale.Tr "auth.authorize_redirect_notice" .ApplicationRedirectDomainHTML}}</p> + </div> + <div class="ui attached segment"> + <form method="post" action="{{AppSubUrl}}/login/oauth/grant"> + {{.CsrfTokenHtml}} + <input type="hidden" name="client_id" value="{{.Application.ClientID}}"> + <input type="hidden" name="state" value="{{.State}}"> + <input type="hidden" name="scope" value="{{.Scope}}"> + <input type="hidden" name="nonce" value="{{.Nonce}}"> + <input type="hidden" name="redirect_uri" value="{{.RedirectURI}}"> + <button type="submit" id="authorize-app" name="granted" value="true" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button> + <button type="submit" name="granted" value="false" class="ui basic primary inline button">{{ctx.Locale.Tr "cancel"}}</button> + </form> + </div> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/grant_error.tmpl b/templates/user/auth/grant_error.tmpl new file mode 100644 index 0000000..b2e0779 --- /dev/null +++ b/templates/user/auth/grant_error.tmpl @@ -0,0 +1,16 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content ui one column stackable center aligned page grid oauth2-authorize-application-box {{if .IsRepo}}repository{{end}}"> + {{if .IsRepo}}{{template "repo/header" .}}{{end}} + <div class="column seven wide"> + <div class="ui middle centered raised segments"> + <h1 class="ui top attached header"> + {{ctx.Locale.Tr "auth.authorization_failed"}} + </h1> + <h3 class="ui attached segment">{{.Error.ErrorDescription}}</h3> + <div class="ui attached segment"> + <p>{{ctx.Locale.Tr "auth.authorization_failed_desc"}}</p> + </div> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/link_account.tmpl b/templates/user/auth/link_account.tmpl new file mode 100644 index 0000000..e8bb3d4 --- /dev/null +++ b/templates/user/auth/link_account.tmpl @@ -0,0 +1,34 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user link-account"> + <overflow-menu class="ui secondary pointing tabular top attached borderless menu secondary-nav"> + <div class="overflow-menu-items tw-justify-center"> + <!-- TODO handle .ShowRegistrationButton once other login bugs are fixed --> + {{if not .AllowOnlyInternalRegistration}} + <a class="item {{if not .user_exists}}active{{end}}" + data-tab="auth-link-signup-tab"> + {{ctx.Locale.Tr "auth.oauth_signup_tab"}} + </a> + {{end}} + <a class="item {{if .user_exists}}active{{end}}" + data-tab="auth-link-signin-tab"> + {{ctx.Locale.Tr "auth.oauth_signin_tab"}} + </a> + </div> + </overflow-menu> + <div class="ui middle very relaxed page grid"> + <div class="column tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto"> + <div class="ui tab {{if not .user_exists}}active{{end}}" + data-tab="auth-link-signup-tab"> + {{template "user/auth/signup_inner" .}} + </div> + <div class="ui tab {{if .user_exists}}active{{end}}" + data-tab="auth-link-signin-tab"> + <div class="ui user signin container icon"> + {{template "user/auth/signin_inner" .}} + </div> + </div> + </div> + </div> +</div> + +{{template "base/footer" .}} diff --git a/templates/user/auth/oauth_container.tmpl b/templates/user/auth/oauth_container.tmpl new file mode 100644 index 0000000..bb6a10d --- /dev/null +++ b/templates/user/auth/oauth_container.tmpl @@ -0,0 +1,29 @@ +{{if or .OAuth2Providers .EnableOpenIDSignIn}} +<div class="divider divider-text"> + {{ctx.Locale.Tr "sign_in_or"}} +</div> +<div id="oauth2-login-navigator" class="tw-py-1"> + <div class="tw-flex tw-flex-col tw-justify-center"> + <div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2"> + {{range $provider := .OAuth2Providers}} + <a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}"> + {{$provider.IconHTML 28}} + {{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}} + </a> + {{end}} + {{if .EnableOpenIDSignIn}} + <a class="openid ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" href="{{AppSubUrl}}/user/login/openid"> + {{svg "fontawesome-openid" 28 "tw-mr-2"}} + {{ctx.Locale.Tr "auth.sign_in_openid"}} + </a> + {{end}} + {{if .EnableSSPI}} + <a class="ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full" rel="nofollow" href="{{AppSubUrl}}/user/login?auth_with_sspi=1"> + {{svg "fontawesome-windows"}} + SSPI + </a> + {{end}} + </div> + </div> +</div> +{{end}} diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl new file mode 100644 index 0000000..54bb4a7 --- /dev/null +++ b/templates/user/auth/oidc_wellknown.tmpl @@ -0,0 +1,49 @@ +{ + "issuer": "{{AppUrl | JSEscape}}", + "authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize", + "token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token", + "jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys", + "userinfo_endpoint": "{{AppUrl | JSEscape}}login/oauth/userinfo", + "introspection_endpoint": "{{AppUrl | JSEscape}}login/oauth/introspect", + "response_types_supported": [ + "code", + "id_token" + ], + "id_token_signing_alg_values_supported": [ + "{{.SigningKey.SigningMethod.Alg | JSEscape}}" + ], + "subject_types_supported": [ + "public" + ], + "scopes_supported": [ + "openid", + "profile", + "email", + "groups" + ], + "claims_supported": [ + "aud", + "exp", + "iat", + "iss", + "sub", + "name", + "preferred_username", + "profile", + "picture", + "website", + "locale", + "updated_at", + "email", + "email_verified", + "groups" + ], + "code_challenge_methods_supported": [ + "plain", + "S256" + ], + "grant_types_supported": [ + "authorization_code", + "refresh_token" + ] +} diff --git a/templates/user/auth/prohibit_login.tmpl b/templates/user/auth/prohibit_login.tmpl new file mode 100644 index 0000000..962ddfa --- /dev/null +++ b/templates/user/auth/prohibit_login.tmpl @@ -0,0 +1,16 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user activate"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form tw-max-w-2xl tw-m-auto"> + <h2 class="ui top attached header"> + {{ctx.Locale.Tr "auth.prohibit_login"}} + </h2> + <div class="ui attached segment"> + <p>{{ctx.Locale.Tr "auth.prohibit_login_desc"}}</p> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/reset_passwd.tmpl b/templates/user/auth/reset_passwd.tmpl new file mode 100644 index 0000000..f8303fe --- /dev/null +++ b/templates/user/auth/reset_passwd.tmpl @@ -0,0 +1,65 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user reset password"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form ignore-dirty" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <input name="code" type="hidden" value="{{.Code}}"> + <h2 class="ui top attached header"> + {{ctx.Locale.Tr "auth.reset_password"}} + </h2> + <div class="ui attached segment"> + {{template "base/alert" .}} + {{if .user_email}} + <div class="inline field"> + <label for="user_name">{{ctx.Locale.Tr "email"}}</label> + <input id="user_name" type="text" value="{{.user_email}}" disabled> + </div> + {{end}} + {{if .IsResetForm}} + <div class="required field {{if .Err_Password}}error{{end}}"> + <label for="password">{{ctx.Locale.Tr "settings.new_password"}}</label> + <input id="password" name="password" type="password" value="{{.password}}" autocomplete="new-password" autofocus required> + </div> + {{if not .user_signed_in}} + <div class="inline field"> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "auth.remember_me"}}</label> + <input name="remember" type="checkbox"> + </div> + </div> + {{end}} + {{if .has_two_factor}} + <h4 class="ui dividing header"> + {{ctx.Locale.Tr "twofa"}} + </h4> + <div class="ui warning visible message">{{ctx.Locale.Tr "settings.twofa_is_enrolled"}}</div> + {{if .scratch_code}} + <div class="required inline field {{if .Err_Token}}error{{end}}"> + <label for="token">{{ctx.Locale.Tr "auth.scratch_code"}}</label> + <input id="token" name="token" type="text" autocomplete="off" autofocus required> + </div> + <input type="hidden" name="scratch_code" value="true"> + {{else}} + <div class="required field {{if .Err_Passcode}}error{{end}}"> + <label for="passcode">{{ctx.Locale.Tr "passcode"}}</label> + <input id="passcode" name="passcode" type="number" autocomplete="off" autofocus required> + </div> + {{end}} + {{end}} + <div class="divider"></div> + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "auth.reset_password_helper"}}</button> + {{if and .has_two_factor (not .scratch_code)}} + <a href="?code={{.Code}}&scratch_code=true">{{ctx.Locale.Tr "auth.use_scratch_code"}}</a> + {{end}} + </div> + {{else}} + <p class="center">{{ctx.Locale.Tr "auth.invalid_code_forgot_password" (printf "%s/user/forgot_password" AppSubUrl)}}</p> + {{end}} + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/signin.tmpl b/templates/user/auth/signin.tmpl new file mode 100644 index 0000000..54cc82d --- /dev/null +++ b/templates/user/auth/signin.tmpl @@ -0,0 +1,9 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin{{if .LinkAccountMode}} icon{{end}}"> + <div class="ui middle very relaxed page grid"> + <div class="column tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto"> + {{template "user/auth/signin_inner" .}} + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl new file mode 100644 index 0000000..d4ba664 --- /dev/null +++ b/templates/user/auth/signin_inner.tmpl @@ -0,0 +1,62 @@ +<div class="ui container fluid"> + {{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}} + {{template "base/alert" .}} + {{end}} + <h4 class="ui top attached header center"> + {{if .LinkAccountMode}} + {{ctx.Locale.Tr "auth.oauth_signin_title"}} + {{else}} + {{ctx.Locale.Tr "auth.login_userpass"}} + {{end}} + </h4> + <div class="ui attached segment"> + <form class="ui form" action="{{.SignInLink}}" method="post"> + {{.CsrfTokenHtml}} + <div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> + <label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label> + <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required> + </div> + {{if or (not .DisablePassword) .LinkAccountMode}} + <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}} form-field-content-aside-label"> + <label for="password">{{ctx.Locale.Tr "password"}}</label> + <a href="{{AppSubUrl}}/user/forgot_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a> + <input id="password" name="password" type="password" value="{{.password}}" autocomplete="current-password" required> + </div> + {{end}} + {{if not .LinkAccountMode}} + <div class="inline field"> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "auth.remember_me"}}</label> + <input name="remember" type="checkbox"> + </div> + </div> + {{end}} + + {{template "user/auth/captcha" .}} + + <div class="field"> + <button class="ui primary button tw-w-full"> + {{if .LinkAccountMode}} + {{ctx.Locale.Tr "auth.oauth_signin_submit"}} + {{else}} + {{ctx.Locale.Tr "sign_in"}} + {{end}} + </button> + </div> + </form> + + {{template "user/auth/oauth_container" .}} + </div> +</div> + +<div class="ui container fluid"> + {{template "user/auth/webauthn_error" .}} + + {{if .ShowRegistrationButton}} + <div class="ui attached segment header top tw-max-w-2xl tw-m-auto tw-flex tw-flex-col tw-items-center"> + <div class="field"> + {{ctx.Locale.Tr "auth.hint_register" (printf "%s/user/sign_up" AppSubUrl)}} + </div> + </div> + {{end}} +</div> diff --git a/templates/user/auth/signin_openid.tmpl b/templates/user/auth/signin_openid.tmpl new file mode 100644 index 0000000..20c7bdc --- /dev/null +++ b/templates/user/auth/signin_openid.tmpl @@ -0,0 +1,51 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin openid"> + <div class="ui middle very relaxed page grid"> + <div class="column tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto"> + <a href="{{AppSubUrl}}/user/login" class="tw-mx-auto"> + <img width="100" height="100" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}"> + </a> + + <div class="ui container fluid"> + {{template "base/alert" .}} + <h4 class="ui top attached header center"> + {{svg "fontawesome-openid"}} + OpenID + </h4> + <div class="ui attached segment"> + <form class="ui form tw-m-auto" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="inline field"> + {{ctx.Locale.Tr "auth.openid_signin_desc"}} + </div> + <div class="required field {{if .Err_OpenID}}error{{end}}"> + <label for="openid"> + {{svg "fontawesome-openid"}} + OpenID URI + </label> + <input id="openid" name="openid" value="{{.openid}}" autofocus required> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "auth.remember_me"}}</label> + <input name="remember" type="checkbox"> + </div> + </div> + <div class="inline field"> + <button class="ui primary button tw-w-full">{{ctx.Locale.Tr "sign_in"}}</button> + </div> + </form> + </div> + </div> + + <div class="ui container fluid"> + {{template "user/auth/webauthn_error" .}} + + <div class="ui attached segment header top tw-flex tw-flex-col tw-items-center"> + <a href="{{AppSubUrl}}/user/login">{{ctx.Locale.Tr "auth.back_to_sign_in"}}</a> + </div> + </div> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/signup.tmpl b/templates/user/auth/signup.tmpl new file mode 100644 index 0000000..1ce3934 --- /dev/null +++ b/templates/user/auth/signup.tmpl @@ -0,0 +1,9 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin{{if .LinkAccountMode}} icon{{end}}"> + <div class="ui middle very relaxed page grid"> + <div class="column tw-flex tw-flex-col tw-gap-4 tw-max-w-2xl tw-m-auto"> + {{template "user/auth/signup_inner" .}} + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl new file mode 100644 index 0000000..6c5ac67 --- /dev/null +++ b/templates/user/auth/signup_inner.tmpl @@ -0,0 +1,64 @@ +<div class="ui container fluid{{if .LinkAccountMode}} icon{{end}}"> + <h4 class="ui top attached header center"> + {{if .LinkAccountMode}} + {{ctx.Locale.Tr "auth.oauth_signup_title"}} + {{else}} + {{ctx.Locale.Tr "sign_up"}} + {{end}} + </h4> + <div class="ui attached segment"> + <form class="ui form" action="{{.SignUpLink}}" method="post"> + {{.CsrfTokenHtml}} + {{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}} + {{template "base/alert" .}} + {{end}} + {{if .DisableRegistration}} + <p>{{ctx.Locale.Tr "auth.disable_register_prompt"}}</p> + {{else}} + <div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> + <label for="user_name">{{ctx.Locale.Tr "username"}}</label> + <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required> + </div> + <div class="required field {{if .Err_Email}}error{{end}}"> + <label for="email">{{ctx.Locale.Tr "email"}}</label> + <input id="email" name="email" type="email" value="{{.email}}" required> + </div> + + {{if not .DisablePassword}} + <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> + <label for="password">{{ctx.Locale.Tr "password"}}</label> + <input id="password" name="password" type="password" value="{{.password}}" autocomplete="new-password" required> + </div> + <div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> + <label for="retype">{{ctx.Locale.Tr "re_type"}}</label> + <input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="new-password" required> + </div> + {{end}} + + {{template "user/auth/captcha" .}} + + <div class="inline field"> + <button class="ui primary button tw-w-full"> + {{if .LinkAccountMode}} + {{ctx.Locale.Tr "auth.oauth_signup_submit"}} + {{else}} + {{ctx.Locale.Tr "auth.create_new_account"}} + {{end}} + </button> + </div> + {{end}} + + {{template "user/auth/oauth_container" .}} + </form> + </div> +</div> + +{{if not .LinkAccountMode}} +<div class="ui container fluid"> + <div class="ui attached segment header top tw-flex tw-flex-col tw-items-center"> + <div class="field"> + {{ctx.Locale.Tr "auth.hint_login" (printf "%s/user/login" AppSubUrl)}} + </div> + </div> +</div> +{{end}} diff --git a/templates/user/auth/signup_openid_connect.tmpl b/templates/user/auth/signup_openid_connect.tmpl new file mode 100644 index 0000000..e4b7936 --- /dev/null +++ b/templates/user/auth/signup_openid_connect.tmpl @@ -0,0 +1,36 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signup"> + {{template "user/auth/signup_openid_navbar" .}} + <div class="ui container"> + {{template "base/alert" .}} + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "auth.openid_connect_title"}} + </h4> + <div class="ui attached segment"> + <p> + {{ctx.Locale.Tr "auth.openid_connect_desc"}} + </p> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="required inline field {{if .Err_UserName}}error{{end}}"> + <label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label> + <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required> + </div> + <div class="required inline field {{if .Err_Password}}error{{end}}"> + <label for="password">{{ctx.Locale.Tr "password"}}</label> + <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> + </div> + <div class="inline field"> + <label for="openid">OpenID URI</label> + <input id="openid" value="{{.OpenID}}" readonly> + </div> + <div class="inline field"> + <label></label> + <button class="ui primary button">{{ctx.Locale.Tr "auth.openid_connect_submit"}}</button> + <a href="{{AppSubUrl}}/user/forgot_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/signup_openid_navbar.tmpl b/templates/user/auth/signup_openid_navbar.tmpl new file mode 100644 index 0000000..89068dd --- /dev/null +++ b/templates/user/auth/signup_openid_navbar.tmpl @@ -0,0 +1,12 @@ +<overflow-menu class="ui secondary pointing tabular top attached borderless menu secondary-nav"> + <div class="overflow-menu-items tw-justify-center"> + <a class="{{if .PageIsOpenIDConnect}}active {{end}}item" href="{{AppSubUrl}}/user/openid/connect"> + {{ctx.Locale.Tr "auth.openid_connect_title"}} + </a> + {{if and .EnableOpenIDSignUp (not .AllowOnlyInternalRegistration)}} + <a class="{{if .PageIsOpenIDRegister}}active {{end}}item" href="{{AppSubUrl}}/user/openid/register"> + {{ctx.Locale.Tr "auth.openid_register_title"}} + </a> + {{end}} + </div> +</overflow-menu> diff --git a/templates/user/auth/signup_openid_register.tmpl b/templates/user/auth/signup_openid_register.tmpl new file mode 100644 index 0000000..c017a0e --- /dev/null +++ b/templates/user/auth/signup_openid_register.tmpl @@ -0,0 +1,37 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signup"> + {{template "user/auth/signup_openid_navbar" .}} + <div class="ui container"> + {{template "base/alert" .}} + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "auth.openid_register_title"}} + </h4> + <div class="ui attached segment"> + <p class="tw-max-w-2xl tw-mx-auto"> + {{ctx.Locale.Tr "auth.openid_register_desc"}} + </p> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="required field {{if .Err_UserName}}error{{end}}"> + <label for="user_name">{{ctx.Locale.Tr "username"}}</label> + <input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required> + </div> + <div class="required field {{if .Err_Email}}error{{end}}"> + <label for="email">{{ctx.Locale.Tr "email"}}</label> + <input id="email" name="email" type="email" value="{{.email}}" required> + </div> + + {{template "user/auth/captcha" .}} + + <div class="field"> + <label for="openid">OpenID URI</label> + <input id="openid" value="{{.OpenID}}" readonly> + </div> + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "auth.create_new_account"}}</button> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/twofa.tmpl b/templates/user/auth/twofa.tmpl new file mode 100644 index 0000000..d245239 --- /dev/null +++ b/templates/user/auth/twofa.tmpl @@ -0,0 +1,26 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form tw-max-w-2xl tw-m-auto" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <h3 class="ui top attached header"> + {{ctx.Locale.Tr "twofa"}} + </h3> + <div class="ui attached segment"> + {{template "base/alert" .}} + <div class="required field"> + <label for="passcode">{{ctx.Locale.Tr "passcode"}}</label> + <input id="passcode" name="passcode" type="text" autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" autofocus required> + </div> + + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "auth.verify"}}</button> + <a href="{{AppSubUrl}}/user/two_factor/scratch">{{ctx.Locale.Tr "auth.use_scratch_code"}}</a> + </div> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/twofa_scratch.tmpl b/templates/user/auth/twofa_scratch.tmpl new file mode 100644 index 0000000..23ad77f --- /dev/null +++ b/templates/user/auth/twofa_scratch.tmpl @@ -0,0 +1,25 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin"> + <div class="ui middle very relaxed page grid"> + <div class="column"> + <form class="ui form tw-max-w-2xl tw-m-auto" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <h3 class="ui top attached header"> + {{ctx.Locale.Tr "twofa_scratch"}} + </h3> + <div class="ui attached segment"> + {{template "base/alert" .}} + <div class="required field"> + <label for="token">{{ctx.Locale.Tr "auth.scratch_code"}}</label> + <input id="token" name="token" type="text" autocomplete="off" autofocus required> + </div> + + <div class="inline field"> + <button class="ui primary button">{{ctx.Locale.Tr "auth.verify"}}</button> + </div> + </div> + </form> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/webauthn.tmpl b/templates/user/auth/webauthn.tmpl new file mode 100644 index 0000000..1b84765 --- /dev/null +++ b/templates/user/auth/webauthn.tmpl @@ -0,0 +1,25 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user signin webauthn-prompt"> + <div class="ui page grid"> + <div class="column center aligned"> + {{template "user/auth/webauthn_error" .}} + <h3 class="ui top attached header">{{ctx.Locale.Tr "twofa"}}</h3> + <div class="ui attached segment"> + {{svg "octicon-key" 56}} + <h3>{{ctx.Locale.Tr "webauthn_insert_key"}}</h3> + {{template "base/alert" .}} + <p>{{ctx.Locale.Tr "webauthn_sign_in"}}</p> + </div> + <div class="ui attached segment tw-flex tw-items-center tw-justify-center tw-gap-1 tw-py-2"> + <div class="is-loading tw-w-[40px] tw-h-[40px]"></div> + {{ctx.Locale.Tr "webauthn_press_button"}} + </div> + {{if .HasTwoFactor}} + <div class="ui attached segment"> + <a href="{{AppSubUrl}}/user/two_factor">{{ctx.Locale.Tr "webauthn_use_twofa"}}</a> + </div> + {{end}} + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/auth/webauthn_error.tmpl b/templates/user/auth/webauthn_error.tmpl new file mode 100644 index 0000000..511ff7c --- /dev/null +++ b/templates/user/auth/webauthn_error.tmpl @@ -0,0 +1,13 @@ +<div id="webauthn-error" class="ui negative message tw-hidden"> + <div class="header">{{ctx.Locale.Tr "webauthn_error"}}</div> + <div id="webauthn-error-msg" class="tw-pt-2"></div> + <div class="tw-hidden"> + <div data-webauthn-error-msg="browser">{{ctx.Locale.Tr "webauthn_unsupported_browser"}}</div> + <div data-webauthn-error-msg="unknown">{{ctx.Locale.Tr "webauthn_error_unknown"}}</div> + <div data-webauthn-error-msg="insecure">{{ctx.Locale.Tr "webauthn_error_insecure"}}</div> + <div data-webauthn-error-msg="unable-to-process">{{ctx.Locale.Tr "webauthn_error_unable_to_process"}}</div> + <div data-webauthn-error-msg="duplicated">{{ctx.Locale.Tr "webauthn_error_duplicated"}}</div> + <div data-webauthn-error-msg="empty">{{ctx.Locale.Tr "webauthn_error_empty"}}</div> + <div data-webauthn-error-msg="timeout">{{ctx.Locale.Tr "webauthn_error_timeout"}}</div> + </div> +</div> diff --git a/templates/user/code.tmpl b/templates/user/code.tmpl new file mode 100644 index 0000000..ff6c69d --- /dev/null +++ b/templates/user/code.tmpl @@ -0,0 +1,24 @@ +{{template "base/head" .}} +{{if .ContextUser.IsOrganization}} + <div role="main" aria-label="{{.Title}}" class="page-content organization code"> + {{template "org/header" .}} + <div class="ui container"> + {{template "shared/search/code/search" .}} + </div> + </div> +{{else}} + <div role="main" aria-label="{{.Title}}" class="page-content user profile"> + <div class="ui container"> + <div class="ui stackable grid"> + <div class="ui four wide column"> + {{template "shared/user/profile_big_avatar" .}} + </div> + <div class="ui twelve wide column"> + {{template "user/overview/header" .}} + {{template "shared/search/code/search" .}} + </div> + </div> + </div> + </div> +{{end}} +{{template "base/footer" .}} diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl new file mode 100644 index 0000000..5dc46dc --- /dev/null +++ b/templates/user/dashboard/dashboard.tmpl @@ -0,0 +1,13 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content dashboard feeds"> + {{template "user/dashboard/navbar" .}} + <div class="ui container flex-container"> + <div class="flex-container-main"> + {{template "base/alert" .}} + {{template "user/heatmap" .}} + {{template "user/dashboard/feeds" .}} + </div> + {{template "user/dashboard/repolist" .}} + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl new file mode 100644 index 0000000..60aa194 --- /dev/null +++ b/templates/user/dashboard/feeds.tmpl @@ -0,0 +1,127 @@ +<div id="activity-feed" class="flex-list"> + {{range .Feeds}} + <div class="flex-item"> + <div class="flex-item-leading"> + {{ctx.AvatarUtils.AvatarByAction .}} + </div> + <div class="flex-item-main tw-gap-2"> + <div> + {{if gt .ActUser.ID 0}} + <a href="{{AppSubUrl}}/{{(.GetActUserName ctx) | PathEscape}}" title="{{.GetActDisplayNameTitle ctx}}">{{.GetActDisplayName ctx}}</a> + {{else}} + {{.ShortActUserName ctx}} + {{end}} + {{if .GetOpType.InActions "create_repo"}} + {{ctx.Locale.Tr "action.create_repo" (.GetRepoLink ctx) (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "rename_repo"}} + {{ctx.Locale.Tr "action.rename_repo" .GetContent (.GetRepoLink ctx) (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "commit_repo"}} + {{if .Content}} + {{ctx.Locale.Tr "action.commit_repo" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else}} + {{ctx.Locale.Tr "action.create_branch" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{end}} + {{else if .GetOpType.InActions "create_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.create_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "create_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.create_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "transfer_repo"}} + {{ctx.Locale.Tr "action.transfer_repo" .GetContent (.GetRepoLink ctx) (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "push_tag"}} + {{ctx.Locale.Tr "action.push_tag" (.GetRepoLink ctx) (.GetRefLink ctx) .GetTag (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "comment_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.comment_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "merge_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.merge_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "close_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.close_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "reopen_issue"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.reopen_issue" (printf "%s/issues/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "close_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.close_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "reopen_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.reopen_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "delete_tag"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.delete_tag" (.GetRepoLink ctx) .GetTag (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "delete_branch"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.delete_branch" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "mirror_sync_push"}} + {{ctx.Locale.Tr "action.mirror_sync_push" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "mirror_sync_create"}} + {{ctx.Locale.Tr "action.mirror_sync_create" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "mirror_sync_delete"}} + {{ctx.Locale.Tr "action.mirror_sync_delete" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "approve_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.approve_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "reject_pull_request"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.reject_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "comment_pull"}} + {{$index := index .GetIssueInfos 0}} + {{ctx.Locale.Tr "action.comment_pull" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} + {{else if .GetOpType.InActions "publish_release"}} + {{$linkText := .Content | RenderEmoji $.Context}} + {{ctx.Locale.Tr "action.publish_release" (.GetRepoLink ctx) (printf "%s/releases/tag/%s" (.GetRepoLink ctx) .GetTag) (.ShortRepoPath ctx) $linkText}} + {{else if .GetOpType.InActions "review_dismissed"}} + {{$index := index .GetIssueInfos 0}} + {{$reviewer := index .GetIssueInfos 1}} + {{ctx.Locale.Tr "action.review_dismissed" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx) $reviewer}} + {{end}} + {{TimeSince .GetCreate ctx.Locale}} + </div> + {{if .GetOpType.InActions "commit_repo" "mirror_sync_push"}} + {{$push := ActionContent2Commits .}} + {{$repoLink := (.GetRepoLink ctx)}} + {{$repo := .Repo}} + <div class="tw-flex tw-flex-col tw-gap-1"> + {{range $push.Commits}} + {{$commitLink := printf "%s/commit/%s" $repoLink .Sha1}} + <div class="flex-text-block"> + <img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16"> + <a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a> + <span class="text truncate"> + {{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}} + </span> + </div> + {{end}} + </div> + {{if and (gt $push.Len 1) $push.CompareURL}} + <a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{ctx.Locale.Tr "action.compare_commits" $push.Len}} »</a> + {{end}} + {{else if .GetOpType.InActions "create_issue"}} + <span class="text truncate issue title">{{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}}</span> + {{else if .GetOpType.InActions "create_pull_request"}} + <span class="text truncate issue title">{{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}}</span> + {{else if .GetOpType.InActions "comment_issue" "approve_pull_request" "reject_pull_request" "comment_pull"}} + <a href="{{.GetCommentLink ctx}}" class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</a> + {{$comment := index .GetIssueInfos 1}} + {{if $comment}} + <div class="markup tw-text-14">{{RenderMarkdownToHtml ctx $comment}}</div> + {{end}} + {{else if .GetOpType.InActions "merge_pull_request"}} + <div class="flex-item-body text black">{{index .GetIssueInfos 1}}</div> + {{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}} + <span class="text truncate issue title">{{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}}</span> + {{else if .GetOpType.InActions "pull_review_dismissed"}} + <div class="flex-item-body text black">{{ctx.Locale.Tr "action.review_dismissed_reason"}}</div> + <div class="flex-item-body text black">{{index .GetIssueInfos 2 | RenderEmoji $.Context}}</div> + {{end}} + </div> + <div class="flex-item-trailing"> + {{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32 "text grey tw-mr-1"}} + </div> + </div> + {{end}} + {{template "base/paginate" .}} +</div> diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl new file mode 100644 index 0000000..09d282b --- /dev/null +++ b/templates/user/dashboard/issues.tmpl @@ -0,0 +1,91 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content dashboard issues"> + {{template "user/dashboard/navbar" .}} + <div class="ui container"> + {{template "base/alert" .}} + <div class="list-header"> + <div class="small-menu-items ui compact tiny menu list-header-toggle"> + <a class="item{{if not .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=open&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + {{svg "octicon-issue-opened" 16 "tw-mr-2"}} + {{ctx.Locale.PrettyNumber .IssueStats.OpenCount}} {{ctx.Locale.Tr "repo.issues.open_title"}} + </a> + <a class="item{{if .IsShowClosed}} active{{end}}" href="?type={{$.ViewType}}&sort={{$.SortType}}&state=closed&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + {{svg "octicon-issue-closed" 16 "tw-mr-2"}} + {{ctx.Locale.PrettyNumber .IssueStats.ClosedCount}} {{ctx.Locale.Tr "repo.issues.closed_title"}} + </a> + </div> + <form class="list-header-search ui form ignore-dirty"> + <div class="ui small search fluid action input"> + <input type="hidden" name="type" value="{{$.ViewType}}"> + <input type="hidden" name="sort" value="{{$.SortType}}"> + <input type="hidden" name="state" value="{{$.State}}"> + {{if .PageIsPulls}} + {{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr "search.pull_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}} + {{else}} + {{template "shared/search/combo_fuzzy" dict "Value" $.Keyword "IsFuzzy" $.IsFuzzy "Placeholder" (ctx.Locale.Tr "search.issue_kind") "Tooltip" (ctx.Locale.Tr "explore.go_to")}} + {{end}} + </div> + </form> + <div class="ui secondary menu tw-mt-0"> + <!-- Label --> + {{if .PageIsOrgIssues}} + {{template "shared/label_filter" .}} + {{end}} + <!-- Type --> + <div class="list-header ui dropdown type jump item"> + <span class="text tw-whitespace-nowrap"> + {{ctx.Locale.Tr "repo.issues.filter_type"}} + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + </span> + <div class="ui menu"> + <a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="?type=created_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + <div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.CreateCount}}</div> + {{ctx.Locale.Tr "repo.issues.filter_type.created_by_you"}} + </a> + <a class="{{if eq .ViewType "your_repositories"}}active{{end}} item" href="?type=your_repositories&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + <div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.YourRepositoriesCount}}</div> + {{ctx.Locale.Tr "home.issues.in_your_repos"}} + </a> + <a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="?type=assigned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + <div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.AssignCount}}</div> + {{ctx.Locale.Tr "repo.issues.filter_type.assigned_to_you"}} + </a> + {{if .PageIsPulls}} + <a class="{{if eq .ViewType "review_requested"}}active{{end}} item" href="?type=review_requested&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + <div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.ReviewRequestedCount}}</div> + {{ctx.Locale.Tr "repo.issues.filter_type.review_requested"}} + </a> + <a class="{{if eq .ViewType "reviewed_by"}}active{{end}} item" href="?type=reviewed_by&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + <div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.ReviewedCount}}</div> + {{ctx.Locale.Tr "repo.issues.filter_type.reviewed_by_you"}} + </a> + {{end}} + <a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="?type=mentioned&sort={{$.SortType}}&state={{.State}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}"> + <div class="ui circular mini label tw-ml-0">{{CountFmt .IssueStats.MentionCount}}</div> + {{ctx.Locale.Tr "repo.issues.filter_type.mentioning_you"}} + </a> + </div> + </div> + <!-- Sort --> + <div class="list-header-sort ui dropdown type jump item"> + <span class="text tw-whitespace-nowrap"> + {{ctx.Locale.Tr "repo.issues.filter_sort"}} + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + </span> + <div class="menu"> + <a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a> + <a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a> + <a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a> + <a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a> + <a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a> + <a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a> + <a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a> + <a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&q={{$.Keyword}}&fuzzy={{.IsFuzzy}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a> + </div> + </div> + </div> + </div> + {{template "shared/issuelist" dict "." . "listType" "dashboard"}} + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/dashboard/milestones.tmpl b/templates/user/dashboard/milestones.tmpl new file mode 100644 index 0000000..fe8e246 --- /dev/null +++ b/templates/user/dashboard/milestones.tmpl @@ -0,0 +1,155 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content dashboard issues repository milestones"> + {{template "user/dashboard/navbar" .}} + <div class="ui container"> + <div class="flex-container"> + <div class="flex-container-nav"> + <div class="ui secondary vertical filter menu tw-bg-transparent"> + <div class="item"> + {{ctx.Locale.Tr "home.issues.in_your_repos"}} + <strong>{{.Total}}</strong> + </div> + <div class="divider"></div> + {{range .Repos}} + {{with $Repo := .}} + <a class="{{range $.RepoIDs}}{{if eq . $Repo.ID}}active{{end}}{{end}} repo name item" href="?repos=[ + {{- with $include := true -}} + {{- range $.RepoIDs -}} + {{- if eq . $Repo.ID -}} + {{$include = false}} + {{- else -}} + {{.}}%2C + {{- end -}} + {{- end -}} + {{- if eq $include true -}} + {{$Repo.ID}}%2C + {{- end -}} + {{- end -}} + ]&sort={{$.SortType}}&state={{$.State}}&q={{$.Keyword}}" title="{{.FullName}}"> + <span class="text truncate">{{$Repo.FullName}}</span> + <div class="ui {{if $.IsShowClosed}}red{{else}}green{{end}} label">{{index $.Counts $Repo.ID}}</div> + </a> + {{end}} + {{end}} + </div> + </div> + <div class="flex-container-main content"> + <div class="list-header"> + <div class="small-menu-items ui compact tiny menu list-header-toggle"> + <a class="item{{if not .IsShowClosed}} active{{end}}" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=open&q={{$.Keyword}}"> + {{svg "octicon-milestone" 16 "tw-mr-2"}} + {{ctx.Locale.PrettyNumber .MilestoneStats.OpenCount}} {{ctx.Locale.Tr "repo.issues.open_title"}} + </a> + <a class="item{{if .IsShowClosed}} active{{end}}" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state=closed&q={{$.Keyword}}"> + {{svg "octicon-check" 16 "tw-mr-2"}} + {{ctx.Locale.PrettyNumber .MilestoneStats.ClosedCount}} {{ctx.Locale.Tr "repo.issues.closed_title"}} + </a> + </div> + <form class="list-header-search ui form ignore-dirty"> + <input type="hidden" name="type" value="{{$.ViewType}}"> + <input type="hidden" name="repos" value="[{{range $.RepoIDs}}{{.}},{{end}}]"> + <input type="hidden" name="sort" value="{{$.SortType}}"> + <input type="hidden" name="state" value="{{$.State}}"> + {{template "shared/search/combo" dict "Value" $.Keyword}} + </form> + <!-- Sort --> + <div class="list-header-sort ui dropdown type jump item"> + <span class="text"> + {{ctx.Locale.Tr "repo.issues.filter_sort"}} + </span> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="menu"> + <a class="{{if or (eq .SortType "closestduedate") (not .SortType)}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=closestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.earliest_due_data"}}</a> + <a class="{{if eq .SortType "furthestduedate"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=furthestduedate&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.latest_due_date"}}</a> + <a class="{{if eq .SortType "leastcomplete"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_complete"}}</a> + <a class="{{if eq .SortType "mostcomplete"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostcomplete&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_complete"}}</a> + <a class="{{if eq .SortType "mostissues"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=mostissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.most_issues"}}</a> + <a class="{{if eq .SortType "leastissues"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=leastissues&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.least_issues"}}</a> + <a class="{{if eq .SortType "name"}}active {{end}}item" href="?repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort=name&state={{$.State}}&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.milestones.filter_sort.name"}}</a> + </div> + </div> + </div> + <div class="milestone-list"> + {{range .Milestones}} + <li class="milestone-card"> + <div class="milestone-header"> + <h3 class="flex-text-block tw-m-0"> + <span class="ui large label"> + {{.Repo.FullName}} + </span> + {{svg "octicon-milestone" 16}} + <a class="muted" href="{{.Repo.Link}}/milestone/{{.ID}}">{{.Name}}</a> + </h3> + <div class="tw-flex tw-items-center"> + <span class="tw-mr-2">{{.Completeness}}%</span> + <progress value="{{.Completeness}}" max="100"></progress> + </div> + </div> + <div class="milestone-toolbar"> + <div class="group"> + <div class="flex-text-block"> + {{svg "octicon-issue-opened" 14}} + {{ctx.Locale.PrettyNumber .NumOpenIssues}} {{ctx.Locale.Tr "repo.issues.open_title"}} + </div> + <div class="flex-text-block"> + {{svg "octicon-check" 14}} + {{ctx.Locale.PrettyNumber .NumClosedIssues}} {{ctx.Locale.Tr "repo.issues.closed_title"}} + </div> + {{if .TotalTrackedTime}} + <div class="flex-text-block"> + {{svg "octicon-clock"}} + {{.TotalTrackedTime|Sec2Time}} + </div> + {{end}} + {{if .UpdatedUnix}} + <div class="flex-text-block"> + {{svg "octicon-clock"}} + {{ctx.Locale.Tr "repo.milestones.update_ago" (TimeSinceUnix .UpdatedUnix ctx.Locale)}} + </div> + {{end}} + <div class="flex-text-block"> + {{if .IsClosed}} + {{$closedDate:= TimeSinceUnix .ClosedDateUnix ctx.Locale}} + {{svg "octicon-clock" 14}} + {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}} + {{else}} + {{if .DeadlineString}} + <span{{if .IsOverdue}} class="text red"{{end}}> + {{svg "octicon-calendar" 14}} + {{DateTime "short" .DeadlineString}} + </span> + {{else}} + {{svg "octicon-calendar" 14}} + {{ctx.Locale.Tr "repo.milestones.no_due_date"}} + {{end}} + {{end}} + </div> + </div> + {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} + <div class="group"> + <a class="flex-text-inline" href="{{$.Link}}/{{.ID}}/edit">{{svg "octicon-pencil" 14}}{{ctx.Locale.Tr "repo.issues.label_edit"}}</a> + {{if .IsClosed}} + <a class="link-action flex-text-inline" href data-url="{{$.Link}}/{{.ID}}/open">{{svg "octicon-check" 14}}{{ctx.Locale.Tr "repo.milestones.open"}}</a> + {{else}} + <a class="link-action flex-text-inline" href data-url="{{$.Link}}/{{.ID}}/close">{{svg "octicon-x" 14}}{{ctx.Locale.Tr "repo.milestones.close"}}</a> + {{end}} + <a class="delete-button flex-text-inline" href="#" data-url="{{$.RepoLink}}/milestones/delete" data-id="{{.ID}}">{{svg "octicon-trash" 14}}{{ctx.Locale.Tr "repo.issues.label_delete"}}</a> + </div> + {{end}} + </div> + {{if .Content}} + <div class="markup content"> + {{.RenderedContent}} + </div> + {{end}} + </li> + {{end}} + + {{template "base/paginate" .}} + </div> + + </div> + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/dashboard/navbar.tmpl b/templates/user/dashboard/navbar.tmpl new file mode 100644 index 0000000..1fa356f --- /dev/null +++ b/templates/user/dashboard/navbar.tmpl @@ -0,0 +1,102 @@ +<div class="secondary-nav tw-border-b tw-border-b-secondary"> + <div class="ui secondary stackable menu"> + <div class="item"> + <div class="ui floating dropdown jump"> + <span class="text truncated-item-container"> + {{ctx.AvatarUtils.Avatar .ContextUser}} + <span class="truncated-item-name">{{.ContextUser.ShortName 40}}</span> + <span class="org-visibility"> + {{if .ContextUser.Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}} + {{if .ContextUser.Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}} + </span> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + </span> + <div class="context user overflow menu"> + <div class="ui header"> + {{ctx.Locale.Tr "home.switch_dashboard_context"}} + </div> + <div class="scrolling menu items"> + <a class="{{if eq .ContextUser.ID .SignedUser.ID}}active selected{{end}} item truncated-item-container" href="{{AppSubUrl}}/{{if .PageIsIssues}}issues{{else if .PageIsPulls}}pulls{{else if .PageIsMilestonesDashboard}}milestones{{end}}"> + {{ctx.AvatarUtils.Avatar .SignedUser}} + <span class="truncated-item-name">{{.SignedUser.ShortName 40}}</span> + <span class="org-visibility"> + {{if .SignedUser.Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}} + {{if .SignedUser.Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}} + </span> + </a> + {{range .Orgs}} + <a class="{{if eq $.ContextUser.ID .ID}}active selected{{end}} item truncated-item-container" title="{{.Name}}" href="{{.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}"> + {{ctx.AvatarUtils.Avatar .}} + <span class="truncated-item-name">{{.ShortName 40}}</span> + <span class="org-visibility"> + {{if .Visibility.IsLimited}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}} + {{if .Visibility.IsPrivate}}<div class="ui basic tiny horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</div>{{end}} + </span> + </a> + {{end}} + </div> + </div> + </div> + </div> + {{if .ContextUser.IsOrganization}} + <div class="item"> + <div class="ui floating dropdown jump"> + <span class="text"> + {{svg "octicon-people" 18}} + {{if .Team}} + {{.Team.Name}} + {{else}} + {{ctx.Locale.Tr "org.teams"}} + {{end}} + </span> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="context user overflow menu"> + <div class="ui header"> + {{ctx.Locale.Tr "home.filter_by_team_repositories"}} + </div> + <div class="scrolling menu items"> + <a class="{{if not $.Team}}active selected{{end}} item" title="{{ctx.Locale.Tr "all"}}" href="{{$.Org.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}"> + {{ctx.Locale.Tr "all"}} + </a> + {{range .Teams}} + {{if not .IncludesAllRepositories}} + <a class="{{if $.Team}}{{if eq $.Team.ID .ID}}active selected{{end}}{{end}} item" title="{{.Name}}" href="{{$.Org.OrganisationLink}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}/{{.Name}}"> + {{.Name}} + </a> + {{end}} + {{end}} + </div> + </div> + </div> + </div> + {{end}} + + {{if .ContextUser.IsOrganization}} + <div class="right menu"> + <a class="{{if .PageIsNews}}active {{end}}item tw-ml-auto" href="{{.ContextUser.DashboardLink}}{{if .Team}}/{{PathEscape .Team.Name}}{{end}}"> + {{svg "octicon-rss"}} {{ctx.Locale.Tr "activities"}} + </a> + {{if not .UnitIssuesGlobalDisabled}} + <a class="{{if .PageIsIssues}}active {{end}}item" href="{{.ContextUser.OrganisationLink}}/issues{{if .Team}}/{{PathEscape .Team.Name}}{{end}}"> + {{svg "octicon-issue-opened"}} {{ctx.Locale.Tr "issues"}} + </a> + {{end}} + {{if not .UnitPullsGlobalDisabled}} + <a class="{{if .PageIsPulls}}active {{end}}item" href="{{.ContextUser.OrganisationLink}}/pulls{{if .Team}}/{{PathEscape .Team.Name}}{{end}}"> + {{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "pull_requests"}} + </a> + {{end}} + {{if and .ShowMilestonesDashboardPage (not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled))}} + <a class="{{if .PageIsMilestonesDashboard}}active {{end}}item" href="{{.ContextUser.OrganisationLink}}/milestones{{if .Team}}/{{PathEscape .Team.Name}}{{end}}"> + {{svg "octicon-milestone"}} {{ctx.Locale.Tr "milestones"}} + </a> + {{end}} + <div class="item"> + <a class="ui basic button" href="{{.ContextUser.HomeLink}}" title="{{ctx.Locale.Tr "home.view_home" .ContextUser.Name}}"> + {{ctx.Locale.Tr "home.view_home" (.ContextUser.ShortName 40)}} + </a> + </div> + </div> + {{end}} + </div> +</div> diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl new file mode 100644 index 0000000..2781f71 --- /dev/null +++ b/templates/user/dashboard/repolist.tmpl @@ -0,0 +1,55 @@ +<script type="module"> +const data = { + ...window.config.pageData.dashboardRepoList, // it only contains searchLimit and uid + + isMirrorsEnabled: {{.MirrorsEnabled}}, + isStarsEnabled: {{not .IsDisableStars}}, + + textMyRepos: {{ctx.Locale.Tr "home.my_repos"}}, + textSearchRepos: {{ctx.Locale.Tr "search.repo_kind"}}, + textFilter: {{ctx.Locale.Tr "home.filter"}}, + textShowArchived: {{ctx.Locale.Tr "home.show_archived"}}, + textShowPrivate: {{ctx.Locale.Tr "home.show_private"}}, + + textShowBothArchivedUnarchived: {{ctx.Locale.Tr "home.show_both_archived_unarchived"}}, + textShowOnlyUnarchived: {{ctx.Locale.Tr "home.show_only_unarchived"}}, + textShowOnlyArchived: {{ctx.Locale.Tr "home.show_only_archived"}}, + + textShowBothPrivatePublic: {{ctx.Locale.Tr "home.show_both_private_public"}}, + textShowOnlyPublic: {{ctx.Locale.Tr "home.show_only_public"}}, + textShowOnlyPrivate: {{ctx.Locale.Tr "home.show_only_private"}}, + + textAll: {{ctx.Locale.Tr "all"}}, + textSources: {{ctx.Locale.Tr "sources"}}, + textForks: {{ctx.Locale.Tr "forks"}}, + textMirrors: {{ctx.Locale.Tr "mirrors"}}, + textCollaborative: {{ctx.Locale.Tr "collaborative"}}, + + textFirstPage: {{ctx.Locale.Tr "admin.first_page"}}, + textPreviousPage: {{ctx.Locale.Tr "repo.issues.previous"}}, + textNextPage: {{ctx.Locale.Tr "repo.issues.next"}}, + textLastPage: {{ctx.Locale.Tr "admin.last_page"}}, + + textMyOrgs: {{ctx.Locale.Tr "home.my_orgs"}}, + + textOrgVisibilityLimited: {{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}, + textOrgVisibilityPrivate: {{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}, +}; + +{{if .Team}} +data.teamId = {{.Team.ID}}; +{{end}} + +{{if not .ContextUser.IsOrganization}} +data.organizations = [{{range .Orgs}}{'name': {{.Name}}, 'num_repos': {{.NumRepos}}, 'org_visibility': {{.Visibility}}},{{end}}]; +data.isOrganization = false; +data.organizationsTotalCount = {{.UserOrgsCount}}; +data.canCreateOrganization = {{.SignedUser.CanCreateOrganization}}; +{{else}} +data.organizationId = {{.ContextUser.ID}}; +{{end}} + +window.config.pageData.dashboardRepoList = data; +</script> + +<div id="dashboard-repo-list" class="flex-container-sidebar"></div> diff --git a/templates/user/heatmap.tmpl b/templates/user/heatmap.tmpl new file mode 100644 index 0000000..05403e3 --- /dev/null +++ b/templates/user/heatmap.tmpl @@ -0,0 +1,13 @@ +{{if .HeatmapData}} + <div id="user-heatmap" class="is-loading" + data-heatmap-data="{{JsonUtils.EncodeToString .HeatmapData}}" + data-locale-total-contributions="{{ctx.Locale.Tr "heatmap.number_of_contributions_in_the_last_12_months" (ctx.Locale.PrettyNumber .HeatmapTotalContributions)}}" + data-locale-contributions-zero="{{ctx.Locale.Tr "heatmap.contributions_zero"}}" + data-locale-contributions-format="{{ctx.Locale.Tr "heatmap.contributions_format"}}" + data-locale-contributions-one="{{ctx.Locale.Tr "heatmap.contributions_one"}}" + data-locale-contributions-few="{{ctx.Locale.Tr "heatmap.contributions_few"}}" + data-locale-more="{{ctx.Locale.Tr "heatmap.more"}}" + data-locale-less="{{ctx.Locale.Tr "heatmap.less"}}" + ></div> + <div class="divider"></div> +{{end}} diff --git a/templates/user/notification/notification.tmpl b/templates/user/notification/notification.tmpl new file mode 100644 index 0000000..b483c15 --- /dev/null +++ b/templates/user/notification/notification.tmpl @@ -0,0 +1,3 @@ +{{template "base/head" .}} +{{template "user/notification/notification_div" .}} +{{template "base/footer" .}} diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl new file mode 100644 index 0000000..5c27ba8 --- /dev/null +++ b/templates/user/notification/notification_div.tmpl @@ -0,0 +1,130 @@ +<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}"> + <div class="ui container"> + {{$notificationUnreadCount := call .NotificationUnreadCount}} + <div class="tw-flex tw-items-center tw-justify-between tw-mb-4"> + <div class="ui secondary partial menu"> + <a class="{{if eq .Status 1}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=unread"> + {{ctx.Locale.Tr "notification.unread"}} + <div class="notifications-unread-count ui label {{if not $notificationUnreadCount}}tw-hidden{{end}}">{{$notificationUnreadCount}}</div> + </a> + <a class="{{if eq .Status 2}}active {{end}}item" href="{{AppSubUrl}}/notifications?q=read"> + {{ctx.Locale.Tr "notification.read"}} + </a> + </div> + <div class="tw-flex button-row"> + <a class="ui tiny button" href="{{AppSubUrl}}/notifications/subscriptions"> + {{ctx.Locale.Tr "notification.subscriptions"}} + </a> + {{if and (eq .Status 1)}} + <form class="{{if not $notificationUnreadCount}}tw-hidden{{end}}" action="{{AppSubUrl}}/notifications/purge" method="post"> + {{$.CsrfTokenHtml}} + <div> + <button class="ui mini button primary tw-mr-0" title="{{ctx.Locale.Tr "notification.mark_all_as_read"}}"> + {{svg "octicon-checklist"}} + </button> + </div> + </form> + {{end}} + </div> + </div> + <div class="tw-p-0"> + <div id="notification_table"> + {{if not .Notifications}} + <div class="tw-flex tw-items-center tw-flex-col tw-p-4"> + {{svg "octicon-inbox" 56 "tw-mb-4"}} + {{if eq .Status 1}} + {{ctx.Locale.Tr "notification.no_unread"}} + {{else}} + {{ctx.Locale.Tr "notification.no_read"}} + {{end}} + </div> + {{else}} + {{range $notification := .Notifications}} + <div class="notifications-item tw-flex tw-items-center tw-flex-wrap tw-gap-2 tw-p-2" id="notification_{{.ID}}" data-status="{{.Status}}"> + <div class="notifications-icon tw-ml-2 tw-mr-1 tw-self-start tw-mt-1"> + {{if .Issue}} + {{template "shared/issueicon" .Issue}} + {{else}} + {{svg "octicon-repo" 16 "text grey"}} + {{end}} + </div> + <a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}"> + <div class="notifications-top-row tw-text-13 tw-break-anywhere"> + {{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}} + {{if eq .Status 3}} + {{svg "octicon-pin" 13 "text blue tw-mt-0.5 tw-ml-1"}} + {{end}} + </div> + <div class="notifications-bottom-row tw-text-16 tw-py-0.5"> + <span class="issue-title tw-break-anywhere"> + {{if .Issue}} + {{RenderRefIssueTitle $.Context .Issue.Title}} + {{else}} + {{.Repository.FullName}} + {{end}} + </span> + </div> + </a> + <div class="notifications-updated tw-items-center tw-mr-2"> + {{if .Issue}} + {{TimeSinceUnix .Issue.UpdatedUnix ctx.Locale}} + {{else}} + {{TimeSinceUnix .UpdatedUnix ctx.Locale}} + {{end}} + </div> + <div class="notifications-buttons tw-items-center tw-justify-end tw-gap-1 tw-px-1"> + {{if ne .Status 3}} + <form action="{{AppSubUrl}}/notifications/status" method="post"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="notification_id" value="{{.ID}}"> + <input type="hidden" name="status" value="pinned"> + <button class="btn interact-bg tw-p-2" title="{{ctx.Locale.Tr "notification.pin"}}" + data-url="{{AppSubUrl}}/notifications/status" + data-status="pinned" + data-page="{{$.Page.Paginater.Current}}" + data-notification-id="{{.ID}}" + data-q="{{$.Keyword}}"> + {{svg "octicon-pin"}} + </button> + </form> + {{end}} + {{if or (eq .Status 1) (eq .Status 3)}} + <form action="{{AppSubUrl}}/notifications/status" method="post"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="notification_id" value="{{.ID}}"> + <input type="hidden" name="status" value="read"> + <input type="hidden" name="page" value="{{$.Page.Paginater.Current}}"> + <button class="btn interact-bg tw-p-2" title="{{ctx.Locale.Tr "notification.mark_as_read"}}" + data-url="{{AppSubUrl}}/notifications/status" + data-status="read" + data-page="{{$.Page.Paginater.Current}}" + data-notification-id="{{.ID}}" + data-q="{{$.Keyword}}"> + {{svg "octicon-check"}} + </button> + </form> + {{else if eq .Status 2}} + <form action="{{AppSubUrl}}/notifications/status" method="post"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="notification_id" value="{{.ID}}"> + <input type="hidden" name="status" value="unread"> + <input type="hidden" name="page" value="{{$.Page.Paginater.Current}}"> + <button class="btn interact-bg tw-p-2" title="{{ctx.Locale.Tr "notification.mark_as_unread"}}" + data-url="{{AppSubUrl}}/notifications/status" + data-status="unread" + data-page="{{$.Page.Paginater.Current}}" + data-notification-id="{{.ID}}" + data-q="{{$.Keyword}}"> + {{svg "octicon-bell"}} + </button> + </form> + {{end}} + </div> + </div> + {{end}} + {{end}} + </div> + </div> + {{template "base/paginate" .}} + </div> +</div> diff --git a/templates/user/notification/notification_subscriptions.tmpl b/templates/user/notification/notification_subscriptions.tmpl new file mode 100644 index 0000000..0a3ae99 --- /dev/null +++ b/templates/user/notification/notification_subscriptions.tmpl @@ -0,0 +1,84 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user notification"> + <div class="ui container"> + <div class="tw-flex tw-items-center tw-justify-between tw-mb-4"> + <div class="ui secondary partial menu"> + <a href="{{AppSubUrl}}/notifications/subscriptions" class="{{if eq .Status 1}}active {{end}}item"> + {{ctx.Locale.Tr "notification.subscriptions"}} + </a> + <a href="{{AppSubUrl}}/notifications/watching" class="{{if eq .Status 2}}active {{end}}item"> + {{ctx.Locale.Tr "notification.watching"}} + </a> + </div> + <a class="ui tiny button" href="{{AppSubUrl}}/notifications"> + {{ctx.Locale.Tr "notifications"}} + </a> + </div> + <div class="ui bottom active tab segment"> + {{if eq .Status 1}} + <div class="tw-flex tw-justify-between"> + <div class="tw-flex"> + <div class="small-menu-items ui compact tiny menu"> + <a class="{{if eq .State "all"}}active {{end}}item" href="?sort={{$.SortType}}&state=all&issueType={{$.IssueType}}&labels={{$.Labels}}"> + {{ctx.Locale.Tr "all"}} + </a> + <a class="{{if eq .State "open"}}active {{end}}item" href="?sort={{$.SortType}}&state=open&issueType={{$.IssueType}}&labels={{$.Labels}}"> + {{svg "octicon-issue-opened" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.issues.open_title"}} + </a> + <a class="{{if eq .State "closed"}}active {{end}}item" href="?sort={{$.SortType}}&state=closed&issueType={{$.IssueType}}&labels={{$.Labels}}"> + {{svg "octicon-issue-closed" 16 "tw-mr-2"}} + {{ctx.Locale.Tr "repo.issues.closed_title"}} + </a> + </div> + </div> + <div class="tw-flex tw-justify-between"> + <div class="ui right aligned secondary filter menu labels"> + <!-- Type --> + <div class="ui dropdown type jump item"> + <span class="text"> + {{ctx.Locale.Tr "repo.issues.filter_type"}} + </span> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="menu"> + <a class="{{if or (eq .IssueType "all") (not .IssueType)}}active {{end}}item" href="?sort={{$.SortType}}&state={{$.State}}&issueType=all&labels={{$.Labels}}">{{ctx.Locale.Tr "all"}}</a> + <a class="{{if eq .IssueType "issues"}}active {{end}}item" href="?sort={{$.SortType}}&state={{$.State}}&issueType=issues&labels={{$.Labels}}">{{ctx.Locale.Tr "issues"}}</a> + <a class="{{if eq .IssueType "pulls"}}active {{end}}item" href="?sort={{$.SortType}}&state={{$.State}}&issueType=pulls&labels={{$.Labels}}">{{ctx.Locale.Tr "pull_requests"}}</a> + </div> + </div> + + <!-- Sort --> + <div class="ui dropdown type jump item"> + <span class="text"> + {{ctx.Locale.Tr "repo.issues.filter_sort"}} + </span> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="menu"> + <a class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?sort=latest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a> + <a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?sort=oldest&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a> + <a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?sort=recentupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a> + <a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?sort=leastupdate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a> + <a class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?sort=mostcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a> + <a class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?sort=leastcomment&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a> + <a class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?sort=nearduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a> + <a class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?sort=farduedate&state={{$.State}}&issueType={{$.IssueType}}&labels={{$.Labels}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a> + </div> + </div> + </div> + </div> + </div> + <div class="divider"></div> + {{if not .Issues}} + {{ctx.Locale.Tr "notification.no_subscriptions"}} + {{else}} + {{template "shared/issuelist" dict "." . "listType" "dashboard"}} + {{end}} + {{else}} + {{template "shared/repo_search" .}} + {{template "explore/repo_list" .}} + {{template "base/paginate" .}} + {{end}} + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl new file mode 100644 index 0000000..ea5d805 --- /dev/null +++ b/templates/user/overview/header.tmpl @@ -0,0 +1,58 @@ +<overflow-menu class="ui secondary pointing tabular borderless menu"> + <div class="overflow-menu-items"> + {{if and .HasProfileReadme .ContextUser.IsIndividual}} + <a class="{{if eq .TabName "overview"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=overview"> + {{svg "octicon-info"}} {{ctx.Locale.Tr "user.overview"}} + </a> + {{end}} + <a class="{{if eq .TabName "repositories"}}active {{end}} item" href="{{.ContextUser.HomeLink}}?tab=repositories"> + {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}} + {{if .RepoCount}} + <div class="ui small label">{{.RepoCount}}</div> + {{end}} + <span hidden test-name="repository-count">{{.RepoCount}}</span> + </a> + {{if or .ContextUser.IsIndividual .CanReadProjects}} + <a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item"> + {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} + {{if .ProjectCount}} + <div class="ui small label">{{.ProjectCount}}</div> + {{end}} + <span hidden test-name="project-count">{{.ProjectCount}}</span> + </a> + {{end}} + {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}} + <a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item"> + {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} + {{if .PackageCount}} + <div class="ui small label">{{.PackageCount}}</div> + {{end}} + <span hidden test-name="package-count">{{.PackageCount}}</span> + </a> + {{end}} + {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}} + <a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item"> + {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}} + </a> + {{end}} + {{if .ContextUser.IsIndividual}} + {{if or (eq .TabName "activity") .IsAdmin (eq .SignedUserID .ContextUser.ID) (not .ContextUser.KeepActivityPrivate)}} + <a class="{{if eq .TabName "activity"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=activity"> + {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} + </a> + {{end}} + {{if not .DisableStars}} + <a class="{{if eq .TabName "stars"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=stars"> + {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}} + {{if .ContextUser.NumStars}} + <div class="ui small label">{{.ContextUser.NumStars}}</div> + {{end}} + </a> + {{else}} + <a class="{{if eq .TabName "watching"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=watching"> + {{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}} + </a> + {{end}} + {{end}} + </div> +</overflow-menu> diff --git a/templates/user/overview/package_versions.tmpl b/templates/user/overview/package_versions.tmpl new file mode 100644 index 0000000..0ac2db0 --- /dev/null +++ b/templates/user/overview/package_versions.tmpl @@ -0,0 +1,24 @@ +{{template "base/head" .}} +{{if .ContextUser.IsOrganization}} + <div role="main" aria-label="{{.Title}}" class="page-content organization packages"> + {{template "org/header" .}} + <div class="ui container"> + {{template "package/shared/versionlist" .}} + </div> + </div> +{{else}} + <div role="main" aria-label="{{.Title}}" class="page-content user profile packages"> + <div class="ui container"> + <div class="ui stackable grid"> + <div class="ui four wide column"> + {{template "shared/user/profile_big_avatar" .}} + </div> + <div class="ui twelve wide column tw-mb-4"> + {{template "user/overview/header" .}} + {{template "package/shared/versionlist" .}} + </div> + </div> + </div> + </div> +{{end}} +{{template "base/footer" .}} diff --git a/templates/user/overview/packages.tmpl b/templates/user/overview/packages.tmpl new file mode 100644 index 0000000..bb2238b --- /dev/null +++ b/templates/user/overview/packages.tmpl @@ -0,0 +1,24 @@ +{{template "base/head" .}} +{{if .ContextUser.IsOrganization}} + <div role="main" aria-label="{{.Title}}" class="page-content organization packages"> + {{template "org/header" .}} + <div class="ui container"> + {{template "package/shared/list" .}} + </div> + </div> +{{else}} + <div role="main" aria-label="{{.Title}}" class="page-content user profile packages"> + <div class="ui container"> + <div class="ui stackable grid"> + <div class="ui four wide column"> + {{template "shared/user/profile_big_avatar" .}} + </div> + <div class="ui twelve wide column tw-mb-4"> + {{template "user/overview/header" .}} + {{template "package/shared/list" .}} + </div> + </div> + </div> + </div> +{{end}} +{{template "base/footer" .}} diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl new file mode 100644 index 0000000..477b838 --- /dev/null +++ b/templates/user/profile.tmpl @@ -0,0 +1,76 @@ +{{template "base/head" .}} +<div role="main" aria-label="{{.Title}}" class="page-content user profile"> + <div class="ui container"> + {{template "base/alert" .}} + <div class="ui stackable grid"> + <div class="ui four wide column"> + {{template "shared/user/profile_big_avatar" .}} + </div> + <div class="ui twelve wide column tw-mb-4"> + {{template "user/overview/header" .}} + {{if eq .TabName "activity"}} + {{if eq .SignedUserID .ContextUser.ID}} + <p id="visibility-hint"> + {{if .ContextUser.KeepActivityPrivate}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_private" "/user/settings#keep-activity-private"}} + {{else}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_public" "/user/settings#keep-activity-private"}} + {{end}} + </p> + {{else}} + {{if .IsAdmin}} + <div id="visibility-hint" class="ui info message"> + {{if .ContextUser.KeepActivityPrivate}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.admin_private"}} + {{else}} + {{ctx.Locale.Tr "user.public_activity.visibility_hint.admin_public"}} + {{end}} + </div> + {{else}} + {{if .ContextUser.KeepActivityPrivate}} + <p id="visibility-hint">{{ctx.Locale.Tr "user.disabled_public_activity"}}</p> + {{end}} + {{end}} + {{end}} + {{if or .IsAdmin (eq .SignedUserID .ContextUser.ID) (not .ContextUser.KeepActivityPrivate)}} + {{template "user/heatmap" .}} + {{template "user/dashboard/feeds" .}} + {{end}} + {{else if eq .TabName "stars"}} + <div class="stars"> + {{template "shared/repo_search" .}} + {{template "explore/repo_list" .}} + {{template "base/paginate" .}} + </div> + {{else if eq .TabName "following"}} + {{template "repo/user_cards" .}} + {{else if eq .TabName "followers"}} + {{template "repo/user_cards" .}} + {{else if eq .TabName "overview"}} + <div id="readme_profile" class="markup">{{.ProfileReadme}}</div> + {{else}} + {{template "shared/repo_search" .}} + {{template "explore/repo_list" .}} + {{template "base/paginate" .}} + {{end}} + </div> + </div> + </div> +</div> + +<div class="ui g-modal-confirm delete modal" id="block-user"> + <div class="header"> + {{ctx.Locale.Tr "user.block_user"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "user.block_user.detail"}}</p> + <ul> + <li>{{ctx.Locale.Tr "user.block_user.detail_1"}}</li> + <li>{{ctx.Locale.Tr "user.block_user.detail_2"}}</li> + <li>{{ctx.Locale.Tr "user.block_user.detail_3"}}</li> + </ul> + </div> + {{template "base/modal_actions_confirm" .}} +</div> + +{{template "base/footer" .}} diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl new file mode 100644 index 0000000..a97136f --- /dev/null +++ b/templates/user/settings/account.tmpl @@ -0,0 +1,180 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings account")}} + <div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.change_password"}} + </h4> + <div class="ui attached segment"> + {{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}} + <form class="ui form ignore-dirty" action="{{AppSubUrl}}/user/settings/account" method="post"> + {{template "base/disable_form_autofill"}} + {{.CsrfTokenHtml}} + {{if .SignedUser.IsPasswordSet}} + <div class="required field {{if .Err_OldPassword}}error{{end}}"> + <label for="old_password">{{ctx.Locale.Tr "settings.old_password"}}</label> + <input id="old_password" name="old_password" type="password" autocomplete="current-password" autofocus required> + </div> + {{end}} + <div class="required field {{if .Err_Password}}error{{end}}"> + <label for="password">{{ctx.Locale.Tr "settings.new_password"}}</label> + <input id="password" name="password" type="password" autocomplete="new-password" required> + </div> + <div class="required field {{if .Err_Password}}error{{end}}"> + <label for="retype">{{ctx.Locale.Tr "settings.retype_new_password"}}</label> + <input id="retype" name="retype" type="password" autocomplete="new-password" required> + </div> + + <div class="field"> + <button class="ui primary button">{{ctx.Locale.Tr "settings.update_password"}}</button> + <a href="{{AppSubUrl}}/user/forgot_password?email={{.Email}}">{{ctx.Locale.Tr "auth.forgot_password"}}</a> + </div> + </form> + {{else}} + <div class="ui info message"> + <p class="text left">{{ctx.Locale.Tr "settings.password_change_disabled"}}</p> + </div> + {{end}} + </div> + + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_emails"}} + </h4> + <div class="ui attached segment"> + <div class="ui list"> + {{if $.EnableNotifyMail}} + <div class="item"> + <div class="tw-mb-2">{{ctx.Locale.Tr "settings.email_desc"}}</div> + <form action="{{AppSubUrl}}/user/settings/account/email" class="ui form" method="post"> + {{$.CsrfTokenHtml}} + <input name="_method" type="hidden" value="NOTIFICATION"> + <div class="tw-flex tw-flex-wrap tw-gap-2"> + <div class="ui selection dropdown"> + <input name="preference" type="hidden" value="{{.EmailNotificationsPreference}}"> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="text"></div> + <div class="menu"> + <div data-value="enabled" class="{{if eq .EmailNotificationsPreference "enabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.enable"}}</div> + <div data-value="andyourown" class="{{if eq .EmailNotificationsPreference "andyourown"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.andyourown"}}</div> + <div data-value="onmention" class="{{if eq .EmailNotificationsPreference "onmention"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.onmention"}}</div> + <div data-value="disabled" class="{{if eq .EmailNotificationsPreference "disabled"}}active selected {{end}}item">{{ctx.Locale.Tr "settings.email_notifications.disable"}}</div> + </div> + </div> + <button class="ui primary button">{{ctx.Locale.Tr "settings.email_notifications.submit"}}</button> + </div> + </form> + </div> + {{end}} + {{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}}"> + {{ctx.Locale.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 primary tiny button">{{ctx.Locale.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 primary tiny button">{{ctx.Locale.Tr "settings.activations_pending"}}</button> + {{else}} + <button class="ui primary tiny button">{{ctx.Locale.Tr "settings.activate_email"}}</button> + {{end}} + </form> + </div> + {{end}} + <div class="content tw-py-2"> + <strong>{{.Email}}</strong> + {{if .IsPrimary}} + <div class="ui primary label">{{ctx.Locale.Tr "settings.primary"}}</div> + {{end}} + {{if .IsActivated}} + <div class="ui green label">{{ctx.Locale.Tr "settings.activated"}}</div> + {{else}} + <div class="ui label">{{ctx.Locale.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">{{ctx.Locale.Tr "settings.add_new_email"}}</label> + <input id="email" name="email" type="email" required {{if not .CanAddEmails}}disabled{{end}}> + </div> + <button class="ui primary button" {{if not .CanAddEmails}}disabled{{end}}> + {{ctx.Locale.Tr "settings.add_email"}} + </button> + </form> + {{/* if ActivationsPending is false, then CanAddEmails must be true, so if CanAddEmails is false, ActivationsPending must be true */}} + {{if not .CanAddEmails}} + <div class="ui warning message">{{ctx.Locale.Tr "settings.can_not_add_email_activations_pending"}}</div> + {{end}} + </div> + + {{if not ($.UserDisabledFeatures.Contains "deletion")}} + <h4 class="ui top attached error header"> + {{ctx.Locale.Tr "settings.delete_account"}} + </h4> + <div class="ui attached error segment"> + <div class="ui red message"> + <p class="text left">{{svg "octicon-alert"}} {{ctx.Locale.Tr "settings.delete_prompt"}}</p> + {{if .UserDeleteWithComments}} + <p class="text left tw-font-semibold">{{ctx.Locale.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxTime}}</p> + {{end}} + </div> + <form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post"> + {{template "base/disable_form_autofill"}} + {{.CsrfTokenHtml}} + <div class="required field {{if .Err_Password}}error{{end}}"> + <label for="password-confirmation">{{ctx.Locale.Tr "password"}}</label> + <input id="password-confirmation" name="password" type="password" autocomplete="off" required> + </div> + <div class="field"> + <button class="ui red button delete-button" data-modal-id="delete-account" data-type="form" data-form="#delete-form"> + {{ctx.Locale.Tr "settings.confirm_delete_account"}} + </button> + </div> + </form> + <div class="ui g-modal-confirm delete modal" id="delete-account"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.delete_account_title"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.delete_account_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> + </div> + {{end}} + </div> + +<div class="ui g-modal-confirm delete modal" id="delete-email"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.email_deletion"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.email_deletion_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} +</div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/actions.tmpl b/templates/user/settings/actions.tmpl new file mode 100644 index 0000000..abc5443 --- /dev/null +++ b/templates/user/settings/actions.tmpl @@ -0,0 +1,12 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings actions")}} + <div class="user-setting-content"> + {{if eq .PageType "secrets"}} + {{template "shared/secrets/add_list" .}} + {{else if eq .PageType "runners"}} + {{template "shared/actions/runner_list" .}} + {{else if eq .PageType "variables"}} + {{template "shared/variables/variable_list" .}} + {{end}} + </div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/appearance.tmpl b/templates/user/settings/appearance.tmpl new file mode 100644 index 0000000..2aaf24a --- /dev/null +++ b/templates/user/settings/appearance.tmpl @@ -0,0 +1,195 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings sshkeys")}} + <div class="user-setting-content"> + + <!-- Theme --> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_themes"}} + </h4> + <div class="ui attached segment"> + <div class="ui email list"> + <div class="item"> + {{ctx.Locale.Tr "settings.theme_desc"}} + </div> + + <form class="ui form" action="{{.Link}}/theme" method="post"> + {{.CsrfTokenHtml}} + <div class="field"> + <label for="ui">{{ctx.Locale.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 primary button">{{ctx.Locale.Tr "settings.update_theme"}}</button> + </div> + </form> + </div> + </div> + + <!-- Language --> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.language.title"}} + </h4> + <div class="ui attached segment"> + <form class="ui form" action="{{.Link}}/language" method="post"> + <div class="tw-mb-4"> + {{ctx.Locale.Tr "settings.language.description"}} + </div> + {{.CsrfTokenHtml}} + <div class="field"> + <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="tw-mb-4"> + {{ctx.Locale.Tr "settings.language.localization_project" "https://forgejo.org/docs/next/contributor/localization/"}} + </div> + <div class="field"> + <button class="ui primary button">{{ctx.Locale.Tr "settings.update_language"}}</button> + </div> + </form> + </div> + + <!-- Hints --> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.hints"}} + </h4> + <div class="ui attached segment"> + <form class="ui form" action="{{.Link}}/hints" method="post"> + {{.CsrfTokenHtml}} + <div class="inline field"> + <div class="ui checkbox" data-tooltip-content="{{ctx.Locale.Tr "settings.additional_repo_units_hint_description"}}"> + <input name="enable_repo_unit_hints" type="checkbox" {{if $.SignedUser.EnableRepoUnitHints}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.additional_repo_units_hint"}}</label> + </div> + </div> + <div class="field"> + <button class="ui primary button">{{ctx.Locale.Tr "settings.update_hints"}}</button> + </div> + </form> + </div> + + <!-- Shown comment event types --> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.hidden_comment_types"}} + </h4> + <div class="ui attached segment"> + <p class="help"> + {{ctx.Locale.Tr "settings.hidden_comment_types_description"}} + </p> + <form class="ui form" action="{{.Link}}/hidden_comments" method="post"> + {{.CsrfTokenHtml}} + <div class="inline field"> + <div class="ui checkbox" data-tooltip-content="{{ctx.Locale.Tr "settings.hidden_comment_types.ref_tooltip"}}"> + <input name="reference" type="checkbox" {{if(call .IsCommentTypeGroupChecked "reference")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_reference"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="label" type="checkbox" {{if (call .IsCommentTypeGroupChecked "label")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_label"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="milestone" type="checkbox" {{if (call .IsCommentTypeGroupChecked "milestone")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_milestone"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="assignee" type="checkbox" {{if (call .IsCommentTypeGroupChecked "assignee")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_assignee"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="title" type="checkbox" {{if (call .IsCommentTypeGroupChecked "title")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_title"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="branch" type="checkbox" {{if (call .IsCommentTypeGroupChecked "branch")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_branch"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="time_tracking" type="checkbox" {{if (call .IsCommentTypeGroupChecked "time_tracking")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_time_tracking"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="deadline" type="checkbox" {{if (call .IsCommentTypeGroupChecked "deadline")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_deadline"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="dependency" type="checkbox" {{if (call .IsCommentTypeGroupChecked "dependency")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_dependency"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="lock" type="checkbox" {{if (call .IsCommentTypeGroupChecked "lock")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_lock"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="review_request" type="checkbox" {{if (call .IsCommentTypeGroupChecked "review_request")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_review_request"}}</label> + </div> + </div> + + <div class="inline field"> + <div class="ui checkbox"> + <input name="pull_request_push" type="checkbox" {{if (call .IsCommentTypeGroupChecked "pull_request_push")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_pull_request_push"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox"> + <input name="project" type="checkbox" {{if (call .IsCommentTypeGroupChecked "project")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_project"}}</label> + </div> + </div> + <div class="inline field"> + <div class="ui checkbox" data-tooltip-content="{{ctx.Locale.Tr "settings.hidden_comment_types.issue_ref_tooltip"}}"> + <input name="issue_ref" type="checkbox" {{if (call .IsCommentTypeGroupChecked "issue_ref")}}checked{{end}}> + <label>{{ctx.Locale.Tr "settings.comment_type_group_issue_ref"}}</label> + </div> + </div> + <div class="field"> + <button class="ui primary button">{{ctx.Locale.Tr "save"}}</button> + </div> + </form> + </div> + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl new file mode 100644 index 0000000..04d4dcd --- /dev/null +++ b/templates/user/settings/applications.tmpl @@ -0,0 +1,113 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications")}} + <div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_access_token"}} + </h4> + <div class="ui attached segment"> + <div class="flex-list"> + <div class="flex-item"> + {{ctx.Locale.Tr "settings.tokens_desc"}} + </div> + {{range .Tokens}} + <div class="flex-item"> + <div class="flex-item-leading"> + <span class="text {{if .HasRecentActivity}}green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.token_state_desc"}}"{{end}}> + {{svg "fontawesome-send" 32}} + </span> + </div> + <div class="flex-item-main"> + <details> + <summary><span class="flex-item-title">{{.Name}}</span></summary> + <p class="tw-my-1"> + {{ctx.Locale.Tr "settings.repo_and_org_access"}}: + {{if .DisplayPublicOnly}} + {{ctx.Locale.Tr "settings.permissions_public_only"}} + {{else}} + {{ctx.Locale.Tr "settings.permissions_access_all"}} + {{end}} + </p> + <p class="tw-my-1">{{ctx.Locale.Tr "settings.permissions_list"}}</p> + <ul class="tw-my-1"> + {{range .Scope.StringSlice}} + {{if (ne . $.AccessTokenScopePublicOnly)}} + <li>{{.}}</li> + {{end}} + {{end}} + </ul> + </details> + <div class="flex-item-body"> + <p>{{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="text green"{{end}}>{{DateTime "short" .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</p> + </div> + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button" data-modal-id="delete-token" data-url="{{$.Link}}/delete" data-id="{{.ID}}"> + {{svg "octicon-trash" 16 "tw-mr-1"}} + {{ctx.Locale.Tr "settings.delete_token"}} + </button> + </div> + </div> + {{end}} + </div> + </div> + <div class="ui attached bottom segment"> + <h5 class="ui top header"> + {{ctx.Locale.Tr "settings.generate_new_token"}} + </h5> + <form id="scoped-access-form" class="ui form ignore-dirty" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="field {{if .Err_Name}}error{{end}}"> + <label for="name">{{ctx.Locale.Tr "settings.token_name"}}</label> + <input id="name" name="name" value="{{.name}}" autofocus required maxlength="255"> + </div> + <div class="field"> + <label>{{ctx.Locale.Tr "settings.repo_and_org_access"}}</label> + <label class="tw-cursor-pointer"> + <input class="enable-system tw-mt-1 tw-mr-1" type="radio" name="scope" value="{{$.AccessTokenScopePublicOnly}}"> + {{ctx.Locale.Tr "settings.permissions_public_only"}} + </label> + <label class="tw-cursor-pointer"> + <input class="enable-system tw-mt-1 tw-mr-1" type="radio" name="scope" value="" checked> + {{ctx.Locale.Tr "settings.permissions_access_all"}} + </label> + </div> + <details class="ui optional field"> + <summary class="tw-pb-4 tw-pl-1"> + {{ctx.Locale.Tr "settings.select_permissions"}} + </summary> + <p class="activity meta"> + <p>{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (`href="https://forgejo.org/docs/latest/user/token-scope/" target="_blank"`|SafeHTML)}}</p> + </p> + <div class="scoped-access-token" + data-is-admin="{{if .IsAdmin}}true{{else}}false{{end}}" + data-no-access-label="{{ctx.Locale.Tr "settings.permission_no_access"}}" + data-read-label="{{ctx.Locale.Tr "settings.permission_read"}}" + data-write-label="{{ctx.Locale.Tr "settings.permission_write"}}" + ></div> + </details> + <button id="scoped-access-submit" class="ui primary button"> + {{ctx.Locale.Tr "settings.generate_token"}} + </button> + </form>{{/* Fomantic ".ui.form .warning.message" is hidden by default, so put the warning message out of the form*/}} + <div id="scoped-access-warning" class="ui warning message center tw-hidden"> + {{ctx.Locale.Tr "settings.at_least_one_permission"}} + </div> + </div> + + {{if .EnableOAuth2}} + {{template "user/settings/grants_oauth2" .}} + {{template "user/settings/applications_oauth2" .}} + {{end}} + </div> + +<div class="ui g-modal-confirm delete modal" id="delete-token"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.access_token_deletion"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.access_token_deletion_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" (dict "ModalButtonColors" "primary")}} +</div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/applications_oauth2.tmpl b/templates/user/settings/applications_oauth2.tmpl new file mode 100644 index 0000000..866a1f8 --- /dev/null +++ b/templates/user/settings/applications_oauth2.tmpl @@ -0,0 +1,6 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_oauth2_applications"}} +</h4> + +{{template "user/settings/applications_oauth2_list" .}} + diff --git a/templates/user/settings/applications_oauth2_edit.tmpl b/templates/user/settings/applications_oauth2_edit.tmpl new file mode 100644 index 0000000..2858ecd --- /dev/null +++ b/templates/user/settings/applications_oauth2_edit.tmpl @@ -0,0 +1,6 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings applications")}} + <div class="user-setting-content"> + + {{template "user/settings/applications_oauth2_edit_form" .}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/applications_oauth2_edit_form.tmpl b/templates/user/settings/applications_oauth2_edit_form.tmpl new file mode 100644 index 0000000..199d43a --- /dev/null +++ b/templates/user/settings/applications_oauth2_edit_form.tmpl @@ -0,0 +1,54 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.edit_oauth2_application"}} +</h4> +<div class="ui attached segment"> + <p>{{ctx.Locale.Tr "settings.oauth2_application_create_description"}}</p> +</div> +<div class="ui attached segment form ignore-dirty"> + {{.CsrfTokenHtml}} + <div class="field"> + <label for="client-id">{{ctx.Locale.Tr "settings.oauth2_client_id"}}</label> + <input id="client-id" readonly value="{{.App.ClientID}}"> + </div> + {{if .ClientSecret}} + <div class="field"> + <label for="client-secret">{{ctx.Locale.Tr "settings.oauth2_client_secret"}}</label> + <input id="client-secret" type="text" readonly value="{{.ClientSecret}}"> + </div> + {{else}} + <div class="field"> + <label for="client-secret">{{ctx.Locale.Tr "settings.oauth2_client_secret"}}</label> + <input id="client-secret" type="password" readonly value="averysecuresecret"> + </div> + {{end}} + <div class="item"> + <!-- TODO add regenerate secret functionality */ --> + <form class="ui form ignore-dirty" action="{{.FormActionPath}}/regenerate_secret" method="post"> + {{.CsrfTokenHtml}} + {{ctx.Locale.Tr "settings.oauth2_regenerate_secret_hint"}} + <button class="ui mini button tw-ml-2" type="submit">{{ctx.Locale.Tr "settings.oauth2_regenerate_secret"}}</button> + </form> + </div> +</div> +<div class="ui attached bottom segment"> + <form class="ui form ignore-dirty" action="{{.FormActionPath}}" method="post"> + {{.CsrfTokenHtml}} + <div class="field {{if .Err_AppName}}error{{end}}"> + <label for="application-name">{{ctx.Locale.Tr "settings.oauth2_application_name"}}</label> + <input id="application-name" value="{{.App.Name}}" name="application_name" required maxlength="255"> + </div> + <div class="field {{if .Err_RedirectURI}}error{{end}}"> + <label for="redirect-uris">{{ctx.Locale.Tr "settings.oauth2_redirect_uris"}}</label> + <textarea name="redirect_uris" id="redirect-uris" required>{{StringUtils.Join .App.RedirectURIs "\n"}}</textarea> + </div> + <div class="field {{if .Err_ConfidentialClient}}error{{end}}"> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label> + <input type="checkbox" name="confidential_client" {{if .App.ConfidentialClient}}checked{{end}}> + </div> + </div> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.save_application"}} + </button> + </form> +</div> diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl new file mode 100644 index 0000000..74cdac4 --- /dev/null +++ b/templates/user/settings/applications_oauth2_list.tmpl @@ -0,0 +1,74 @@ +<div class="ui attached segment"> + <div class="flex-list"> + <div class="flex-item"> + {{ctx.Locale.Tr "settings.oauth2_application_create_description"}} + </div> + {{range .Applications}} + <div class="flex-item tw-items-center"> + <div class="flex-item-leading"> + {{svg "octicon-apps" 32}} + </div> + <div class="flex-item-main"> + <div class="flex-item-title">{{.Name}}</div> + <div class="flex-item-body"> + {{ctx.Locale.Tr "settings.oauth2_client_id"}} + <span class="ui label">{{.ClientID}}</span> + </div> + </div> + {{$isBuiltin := and $.BuiltinApplications (index $.BuiltinApplications .ClientID)}} + <div class="flex-item-trailing"> + {{if $isBuiltin}} + <span class="ui basic label" data-tooltip-content="{{ctx.Locale.Tr "settings.oauth2_application_locked"}}">{{ctx.Locale.Tr "locked"}}</span> + {{else}} + <a href="{{$.Link}}/oauth2/{{.ID}}" class="ui primary tiny button"> + {{svg "octicon-pencil" 16 "tw-mr-1"}} + {{ctx.Locale.Tr "settings.oauth2_application_edit"}} + </a> + <button class="ui red tiny button delete-button" data-modal-id="remove-gitea-oauth2-application" + data-url="{{$.Link}}/oauth2/{{.ID}}/delete"> + {{svg "octicon-trash" 16 "tw-mr-1"}} + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + {{end}} + </div> + </div> + {{end}} + </div> + + <div class="ui g-modal-confirm delete modal" id="remove-gitea-oauth2-application"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.remove_oauth2_application"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.oauth2_application_remove_description"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> + +<div class="ui attached bottom segment"> + <h5 class="ui top header"> + {{ctx.Locale.Tr "settings.create_oauth2_application"}} + </h5> + <form class="ui form ignore-dirty" action="{{.Link}}/oauth2" method="post"> + {{.CsrfTokenHtml}} + <div class="field {{if .Err_AppName}}error{{end}}"> + <label for="application-name">{{ctx.Locale.Tr "settings.oauth2_application_name"}}</label> + <input id="application-name" name="application_name" value="{{.application_name}}" required maxlength="255"> + </div> + <div class="field {{if .Err_RedirectURI}}error{{end}}"> + <label for="redirect-uris">{{ctx.Locale.Tr "settings.oauth2_redirect_uris"}}</label> + <textarea name="redirect_uris" id="redirect-uris" required></textarea> + </div> + <div class="field {{if .Err_ConfidentialClient}}error{{end}}"> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "settings.oauth2_confidential_client"}}</label> + <input type="checkbox" name="confidential_client" checked> + </div> + </div> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.create_oauth2_application_button"}} + </button> + </form> +</div> diff --git a/templates/user/settings/blocked_users.tmpl b/templates/user/settings/blocked_users.tmpl new file mode 100644 index 0000000..5256503 --- /dev/null +++ b/templates/user/settings/blocked_users.tmpl @@ -0,0 +1,10 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings blocked-users")}} +<div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.blocked_users"}} + </h4> + <div class="ui attached segment"> + {{template "shared/blocked_users_list" .}} + </div> +</div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/grants_oauth2.tmpl b/templates/user/settings/grants_oauth2.tmpl new file mode 100644 index 0000000..e89d275 --- /dev/null +++ b/templates/user/settings/grants_oauth2.tmpl @@ -0,0 +1,40 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.authorized_oauth2_applications"}} +</h4> +<div class="ui attached segment"> + <div class="flex-list"> + <div class="flex-item"> + {{ctx.Locale.Tr "settings.authorized_oauth2_applications_description"}} + </div> + {{range .Grants}} + <div class="flex-item"> + <div class="flex-item-leading"> + {{svg "octicon-key" 32}} + </div> + <div class="flex-item-main"> + <div class="flex-item-title">{{.Application.Name}}</div> + <div class="flex-item-body"> + <p>{{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}}</p> + </div> + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button" data-modal-id="revoke-gitea-oauth2-grant" + data-url="{{AppSubUrl}}/user/settings/applications/oauth2/{{.ApplicationID}}/revoke/{{.ID}}"> + {{ctx.Locale.Tr "settings.revoke_key"}} + </button> + </div> + </div> + {{end}} + </div> + + <div class="ui g-modal-confirm delete modal" id="revoke-gitea-oauth2-grant"> + <div class="header"> + {{svg "octicon-shield" 16 "tw-mr-1"}} + {{ctx.Locale.Tr "settings.revoke_oauth2_grant"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.revoke_oauth2_grant_description"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> diff --git a/templates/user/settings/hook_new.tmpl b/templates/user/settings/hook_new.tmpl new file mode 100644 index 0000000..be21f59 --- /dev/null +++ b/templates/user/settings/hook_new.tmpl @@ -0,0 +1,7 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings new webhook")}} + <div class="user-setting-content"> + {{$CustomHeaderTitle := ctx.Locale.Tr "repo.settings.update_webhook"}} + {{if .PageIsSettingsHooksNew}}{{$CustomHeaderTitle = ctx.Locale.Tr "repo.settings.add_webhook"}}{{end}} + {{template "webhook/new" (dict "ctxData" . "CustomHeaderTitle" $CustomHeaderTitle)}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/hooks.tmpl b/templates/user/settings/hooks.tmpl new file mode 100644 index 0000000..477c333 --- /dev/null +++ b/templates/user/settings/hooks.tmpl @@ -0,0 +1,5 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings webhooks")}} + <div class="user-setting-content"> + {{template "repo/settings/webhook/list" .}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/keys.tmpl b/templates/user/settings/keys.tmpl new file mode 100644 index 0000000..e0f5e42 --- /dev/null +++ b/templates/user/settings/keys.tmpl @@ -0,0 +1,11 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings sshkeys")}} + <div class="user-setting-content"> + {{if not ($.UserDisabledFeatures.Contains "manage_ssh_keys")}} + {{template "user/settings/keys_ssh" .}} + {{end}} + {{template "user/settings/keys_principal" .}} + {{if not ($.UserDisabledFeatures.Contains "manage_gpg_keys")}} + {{template "user/settings/keys_gpg" .}} + {{end}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl new file mode 100644 index 0000000..f5e91ce --- /dev/null +++ b/templates/user/settings/keys_gpg.tmpl @@ -0,0 +1,129 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_gpg_keys"}} + <div class="ui right"> + <button class="ui primary tiny show-panel toggle button" data-panel="#add-gpg-key-panel">{{ctx.Locale.Tr "settings.add_key"}}</button> + </div> +</h4> +<div class="ui attached segment"> + <div class="{{if not .HasGPGError}}tw-hidden{{end}} tw-mb-4" id="add-gpg-key-panel"> + <form class="ui form{{if .HasGPGError}} error{{end}}" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <input type="hidden" name="title" value="none"> + <div class="field {{if .Err_Content}}error{{end}}"> + <label for="gpg-key-content">{{ctx.Locale.Tr "settings.key_content"}}</label> + <textarea id="gpg-key-content" name="content" placeholder="{{ctx.Locale.Tr "settings.key_content_gpg_placeholder"}}" required>{{.content}}</textarea> + </div> + {{if .Err_Signature}} + <div class="ui error message"> + <p>{{ctx.Locale.Tr "settings.gpg_token_required"}}</p> + </div> + <div class="field"> + <label for="token">{{ctx.Locale.Tr "settings.gpg_token"}}</label> + <input readonly="" value="{{.TokenToSign}}"> + <div class="help"> + <p>{{ctx.Locale.Tr "settings.gpg_token_help"}}</p> + <p><code>{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` .TokenToSign .PaddedKeyID}}</code></p> + </div> + </div> + <div class="field"> + <label for="gpg-key-signature">{{ctx.Locale.Tr "settings.gpg_token_signature"}}</label> + <textarea id="gpg-key-signature" name="signature" placeholder="{{ctx.Locale.Tr "settings.key_signature_gpg_placeholder"}}" required>{{.signature}}</textarea> + </div> + {{end}} + <input name="type" type="hidden" value="gpg"> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.add_key"}} + </button> + <button class="ui hide-panel button" data-panel="#add-gpg-key-panel"> + {{ctx.Locale.Tr "cancel"}} + </button> + </form> + </div> + <div class="flex-list"> + <div class="flex-item"> + <p> + {{ctx.Locale.Tr "settings.gpg_desc"}}<br> + {{ctx.Locale.Tr "settings.gpg_helper" "https://docs.codeberg.org/security/gpg-key/"}} + </p> + </div> + {{range .GPGKeys}} + <div class="flex-item"> + <div class="flex-item-leading"> + <span class="text {{if or .ExpiredUnix.IsZero ($.PageStartTime.Before .ExpiredUnix.AsTime)}}green{{end}}">{{svg "octicon-key" 32}}</span> + </div> + <div class="flex-item-main"> + {{if .Verified}} + <span class="flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.gpg_key_verified_long"}}">{{svg "octicon-verified"}} <strong>{{ctx.Locale.Tr "settings.gpg_key_verified"}}</strong></span> + {{end}} + {{if .Emails}} + <span class="flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.gpg_key_matched_identities_long"}}">{{svg "octicon-mail"}} {{ctx.Locale.Tr "settings.gpg_key_matched_identities"}} {{range .Emails}}<strong>{{.Email}} </strong>{{end}}</span> + {{end}} + <div class="flex-item-body"> + <b>{{ctx.Locale.Tr "settings.key_id"}}:</b> {{.PaddedKeyID}} + <b>{{ctx.Locale.Tr "settings.subkeys"}}:</b> {{range .SubsKey}} {{.PaddedKeyID}} {{end}} + </div> + <div class="flex-item-body"> + <p> + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .AddedUnix)}} + - + {{if not .ExpiredUnix.IsZero}} + {{ctx.Locale.Tr "settings.valid_until_date" (DateTime "short" .ExpiredUnix)}} + {{else}} + {{ctx.Locale.Tr "settings.valid_forever"}} + {{end}} + </p> + </div> + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button" data-modal-id="delete-gpg" data-url="{{$.Link}}/delete?type=gpg" data-id="{{.ID}}"> + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + {{if and (not .Verified) (ne $.VerifyingID .KeyID)}} + <a class="ui primary tiny button" href="?verify_gpg={{.KeyID}}">{{ctx.Locale.Tr "settings.gpg_key_verify"}}</a> + {{end}} + </div> + </div> + {{if and (not .Verified) (eq $.VerifyingID .KeyID)}} + <div class="ui segment"> + <h4>{{ctx.Locale.Tr "settings.gpg_token_required"}}</h4> + <form class="ui form{{if $.HasGPGVerifyError}} error{{end}}" action="{{$.Link}}" method="post"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="title" value="none"> + <input type="hidden" name="content" value="{{.KeyID}}"> + <input type="hidden" name="key_id" value="{{.KeyID}}"> + <div class="field"> + <label for="token">{{ctx.Locale.Tr "settings.gpg_token"}}</label> + <input readonly="" value="{{$.TokenToSign}}"> + <div class="help"> + <p>{{ctx.Locale.Tr "settings.gpg_token_help"}}</p> + <p><code>{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` $.TokenToSign .PaddedKeyID}}</code></p> + </div> + <br> + </div> + <div class="field"> + <label for="signature">{{ctx.Locale.Tr "settings.gpg_token_signature"}}</label> + <textarea id="gpg-key-signature" name="signature" placeholder="{{ctx.Locale.Tr "settings.key_signature_gpg_placeholder"}}" required>{{$.signature}}</textarea> + </div> + <input name="type" type="hidden" value="verify_gpg"> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.gpg_key_verify"}} + </button> + <a class="ui red button" href="{{$.Link}}"> + {{ctx.Locale.Tr "settings.cancel"}} + </a> + </form> + </div> + {{end}} + {{end}} + </div> + <div class="ui g-modal-confirm delete modal" id="delete-gpg"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.gpg_key_deletion"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.gpg_key_deletion_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> diff --git a/templates/user/settings/keys_principal.tmpl b/templates/user/settings/keys_principal.tmpl new file mode 100644 index 0000000..94b1b2c --- /dev/null +++ b/templates/user/settings/keys_principal.tmpl @@ -0,0 +1,69 @@ +{{if .AllowPrincipals}} + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_ssh_principals"}} + <div class="ui right"> + {{if not .DisableSSH}} + <button class="ui primary tiny show-panel button" data-panel="#add-ssh-principal-panel">{{ctx.Locale.Tr "settings.add_new_principal"}}</button> + {{else}} + <button class="ui primary tiny button disabled">{{ctx.Locale.Tr "settings.ssh_disabled"}}</button> + {{end}} + </div> + </h4> + <div class="ui attached segment"> + <div class="flex-list"> + <div class="flex-item"> + {{ctx.Locale.Tr "settings.principal_desc"}} + </div> + {{range .Principals}} + <div class="flex-item"> + <div class="flex-item-leading"> + <span class="text {{if .HasRecentActivity}}green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.principal_state_desc"}}"{{end}}>{{svg "octicon-key" 32}}</span> + </div> + <div class="flex-item-main"> + <div class="flex-item-title">{{.Name}}</div> + <div class="flex-item-body"> + <p>{{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info" 16}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="green"{{end}}>{{DateTime "short" .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</p> + </div> + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button" data-modal-id="delete-principal" data-url="{{$.Link}}/delete?type=principal" data-id="{{.ID}}"> + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + </div> + </div> + {{end}} + </div> + </div> + <br> + + <div {{if not .HasPrincipalError}}class="tw-hidden"{{end}} id="add-ssh-principal-panel"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.add_new_principal"}} + </h4> + <div class="ui attached segment"> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="field {{if .Err_Content}}error{{end}}"> + <label for="ssh-principal-content">{{ctx.Locale.Tr "settings.principal_content"}}</label> + <input id="ssh-principal-content" name="content" value="{{.content}}" autofocus required> + </div> + <input name="title" type="hidden" value="principal"> + <input name="type" type="hidden" value="principal"> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.add_new_principal"}} + </button> + </form> + </div> + </div> + + <div class="ui g-modal-confirm delete modal" id="delete-principal"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.ssh_principal_deletion"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.ssh_principal_deletion_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +{{end}} diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl new file mode 100644 index 0000000..058dc9c --- /dev/null +++ b/templates/user/settings/keys_ssh.tmpl @@ -0,0 +1,111 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_ssh_keys"}} + <div class="ui right"> + <button id="add-ssh-button" class="ui primary tiny show-panel toggle button" data-panel="#add-ssh-key-panel"> + {{ctx.Locale.Tr "settings.add_key"}} + </button> + </div> +</h4> +<div class="ui attached segment"> + <div class="{{if not .HasSSHError}}tw-hidden{{end}} tw-mb-4" id="add-ssh-key-panel"> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="field {{if .Err_Title}}error{{end}}"> + <label for="ssh-key-title">{{ctx.Locale.Tr "settings.key_name"}}</label> + <input id="ssh-key-title" name="title" value="{{.title}}" autofocus required maxlength="50"> + </div> + <div class="field {{if .Err_Content}}error{{end}}"> + <label for="ssh-key-content">{{ctx.Locale.Tr "settings.key_content"}}</label> + <textarea id="ssh-key-content" name="content" class="js-quick-submit" placeholder="{{ctx.Locale.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea> + </div> + <input name="type" type="hidden" value="ssh"> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.add_key"}} + </button> + <button id="cancel-ssh-button" class="ui hide-panel button" data-panel="#add-ssh-key-panel"> + {{ctx.Locale.Tr "cancel"}} + </button> + </form> + </div> + <div id="keys-ssh" class="flex-list"> + <div class="flex-item"> + <p> + {{ctx.Locale.Tr "settings.ssh_desc"}}<br> + {{ctx.Locale.Tr "settings.ssh_helper" "https://docs.codeberg.org/security/ssh-key/" "https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/troubleshooting-ssh"}} + </p> + </div> + {{if .DisableSSH}} + <div class="flex-item"> + {{ctx.Locale.Tr "settings.ssh_signonly"}} + </div> + {{end}} + {{range $index, $key := .Keys}} + <div class="flex-item"> + <div class="flex-item-leading"> + <span class="text {{if .HasRecentActivity}}green{{end}}" {{if .HasRecentActivity}}data-tooltip-content="{{ctx.Locale.Tr "settings.key_state_desc"}}"{{end}}>{{svg "octicon-key" 32}}</span> + </div> + <div class="flex-item-main"> + {{if .Verified}} + <div class="flex-item-title flex-text-block" data-tooltip-content="{{ctx.Locale.Tr "settings.ssh_key_verified_long"}}">{{svg "octicon-verified"}}{{ctx.Locale.Tr "settings.ssh_key_verified"}}</div> + {{end}} + <div class="flex-item-title">{{.Name}}</div> + <div class="flex-item-body"> + {{.Fingerprint}} + </div> + <div class="flex-item-body"> + <p>{{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="text green"{{end}}>{{DateTime "short" .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}</p> + </div> + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button{{if index $.ExternalKeys $index}} disabled{{end}}" data-modal-id="delete-ssh" data-url="{{$.Link}}/delete?type=ssh" data-id="{{.ID}}"{{if index $.ExternalKeys $index}} title="{{ctx.Locale.Tr "settings.ssh_externally_managed"}}"{{end}}> + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + {{if and (not .Verified) (ne $.VerifyingFingerprint .Fingerprint)}} + <a class="ui primary tiny button" href="?verify_ssh={{.Fingerprint}}">{{ctx.Locale.Tr "settings.ssh_key_verify"}}</a> + {{end}} + </div> + </div> + {{if and (not .Verified) (eq $.VerifyingFingerprint .Fingerprint)}} + <div class="ui segment"> + <h4>{{ctx.Locale.Tr "settings.ssh_token_required"}}</h4> + <form class="ui form{{if $.HasSSHVerifyError}} error{{end}}" action="{{$.Link}}" method="post"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="title" value="none"> + <input type="hidden" name="content" value="{{.Content}}"> + <input type="hidden" name="fingerprint" value="{{.Fingerprint}}"> + <div class="field"> + <label for="token">{{ctx.Locale.Tr "settings.ssh_token"}}</label> + <input readonly="" value="{{$.TokenToSign}}"> + <div class="help"> + <p>{{ctx.Locale.Tr "settings.ssh_token_help"}}</p> + <p><code>{{printf "echo -n '%s' | ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey" $.TokenToSign}}</code></p> + </div> + <br> + </div> + <div class="field"> + <label for="signature">{{ctx.Locale.Tr "settings.ssh_token_signature"}}</label> + <textarea id="ssh-key-signature" name="signature" class="js-quick-submit" placeholder="{{ctx.Locale.Tr "settings.key_signature_ssh_placeholder"}}" required>{{$.signature}}</textarea> + </div> + <input name="type" type="hidden" value="verify_ssh"> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.ssh_key_verify"}} + </button> + <a class="ui red button" href="{{$.Link}}"> + {{ctx.Locale.Tr "settings.cancel"}} + </a> + </form> + </div> + {{end}} + {{end}} + </div> + <div class="ui g-modal-confirm delete modal" id="delete-ssh"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.ssh_key_deletion"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.ssh_key_deletion_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> diff --git a/templates/user/settings/layout_footer.tmpl b/templates/user/settings/layout_footer.tmpl new file mode 100644 index 0000000..46120d5 --- /dev/null +++ b/templates/user/settings/layout_footer.tmpl @@ -0,0 +1,11 @@ +{{if false}}{{/* to make html structure "likely" complete to prevent IDE warnings */}} +<div class="page-content"> + <div class="user-layout-right"> + <div> + {{/* block: user-setting-content */}} +{{end}} + + </div> + </div> +</div> +{{template "base/footer" .}} diff --git a/templates/user/settings/layout_head.tmpl b/templates/user/settings/layout_head.tmpl new file mode 100644 index 0000000..dce496e --- /dev/null +++ b/templates/user/settings/layout_head.tmpl @@ -0,0 +1,13 @@ +{{template "base/head" .ctxData}} +<div role="main" aria-label="{{.ctxData.Title}}" class="page-content {{.pageClass}}"> + <div class="ui container flex-container"> + {{template "user/settings/navbar" .ctxData}} + <div class="flex-container-main"> + {{template "base/alert" .ctxData}} + {{/* block: user-setting-content */}} + +{{if false}}{{/* to make html structure "likely" complete to prevent IDE warnings */}} + </div> + </div> +</div> +{{end}} diff --git a/templates/user/settings/navbar.tmpl b/templates/user/settings/navbar.tmpl new file mode 100644 index 0000000..d45d89e --- /dev/null +++ b/templates/user/settings/navbar.tmpl @@ -0,0 +1,58 @@ +<div class="flex-container-nav"> + <div class="ui fluid vertical menu"> + <div class="header item">{{ctx.Locale.Tr "user.settings"}}</div> + <a class="{{if .PageIsSettingsProfile}}active {{end}}item" href="{{AppSubUrl}}/user/settings"> + {{ctx.Locale.Tr "settings.profile"}} + </a> + <a class="{{if .PageIsSettingsAccount}}active {{end}}item" href="{{AppSubUrl}}/user/settings/account"> + {{ctx.Locale.Tr "settings.account"}} + </a> + <a class="{{if .PageIsSettingsAppearance}}active {{end}}item" href="{{AppSubUrl}}/user/settings/appearance"> + {{ctx.Locale.Tr "settings.appearance"}} + </a> + <a class="{{if .PageIsSettingsSecurity}}active {{end}}item" href="{{AppSubUrl}}/user/settings/security"> + {{ctx.Locale.Tr "settings.security"}} + </a> + <a class="{{if .PageIsSettingsApplications}}active {{end}}item" href="{{AppSubUrl}}/user/settings/applications"> + {{ctx.Locale.Tr "settings.applications"}} + </a> + <a class="{{if .PageIsSettingsKeys}}active {{end}}item" href="{{AppSubUrl}}/user/settings/keys"> + {{ctx.Locale.Tr "settings.ssh_gpg_keys"}} + </a> + {{if .EnableActions}} + <details class="item toggleable-item" {{if or .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}> + <summary>{{ctx.Locale.Tr "actions.actions"}}</summary> + <div class="menu"> + <a class="{{if .PageIsSharedSettingsRunners}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/runners"> + {{ctx.Locale.Tr "actions.runners"}} + </a> + <a class="{{if .PageIsSharedSettingsSecrets}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/secrets"> + {{ctx.Locale.Tr "secrets.secrets"}} + </a> + <a class="{{if .PageIsSharedSettingsVariables}}active {{end}}item" href="{{AppSubUrl}}/user/settings/actions/variables"> + {{ctx.Locale.Tr "actions.variables"}} + </a> + </div> + </details> + {{end}} + {{if .EnablePackages}} + <a class="{{if .PageIsSettingsPackages}}active {{end}}item" href="{{AppSubUrl}}/user/settings/packages"> + {{ctx.Locale.Tr "packages.title"}} + </a> + {{end}} + {{if not DisableWebhooks}} + <a class="{{if .PageIsSettingsHooks}}active {{end}}item" href="{{AppSubUrl}}/user/settings/hooks"> + {{ctx.Locale.Tr "repo.settings.hooks"}} + </a> + {{end}} + <a class="{{if .PageIsSettingsOrganization}}active {{end}}item" href="{{AppSubUrl}}/user/settings/organization"> + {{ctx.Locale.Tr "settings.organization"}} + </a> + <a class="{{if .PageIsSettingsRepos}}active {{end}}item" href="{{AppSubUrl}}/user/settings/repos"> + {{ctx.Locale.Tr "settings.repos"}} + </a> + <a class="{{if .PageIsBlockedUsers}}active {{end}}item" href="{{AppSubUrl}}/user/settings/blocked_users"> + {{ctx.Locale.Tr "settings.blocked_users"}} + </a> + </div> +</div> diff --git a/templates/user/settings/organization.tmpl b/templates/user/settings/organization.tmpl new file mode 100644 index 0000000..16c27b5 --- /dev/null +++ b/templates/user/settings/organization.tmpl @@ -0,0 +1,55 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings organization")}} + <div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.orgs"}} + {{if .SignedUser.CanCreateOrganization}} + <div class="ui right"> + <a class="ui primary tiny button" href="{{AppSubUrl}}/org/create">{{ctx.Locale.Tr "admin.orgs.new_orga"}}</a> + </div> + {{end}} + </h4> + <div class="ui attached segment orgs"> + {{if .Orgs}} + <div class="flex-list"> + {{range .Orgs}} + <div class="flex-item"> + <div class="flex-item-leading"> + {{ctx.AvatarUtils.Avatar . 28 "mini"}} + </div> + <div class="flex-item-main"> + <div class="flex-item-title">{{template "shared/user/name" .}}</div> + <div class="flex-text-body"> + {{.Description}} + </div> + </div> + <div class="flex-item-trailing"> + <form> + {{$.CsrfTokenHtml}} + <button class="ui red button delete-button" data-modal-id="leave-organization" + data-url="{{.OrganisationLink}}/members/action/leave" data-datauid="{{$.SignedUser.ID}}" + data-name="{{$.SignedUser.DisplayName}}" + data-data-organization-name="{{.DisplayName}}">{{ctx.Locale.Tr "org.members.leave"}} + </button> + </form> + </div> + </div> + {{end}} + </div> + {{template "base/paginate" .}} + {{else}} + {{ctx.Locale.Tr "settings.orgs_none"}} + {{end}} + </div> + </div> + +<div class="ui g-modal-confirm delete modal" id="leave-organization"> + <div class="header"> + {{ctx.Locale.Tr "org.members.leave"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "org.members.leave.detail" (`<span class="dataOrganizationName"></span>`|SafeHTML)}}</p> + </div> + {{template "base/modal_actions_confirm" .}} +</div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/packages.tmpl b/templates/user/settings/packages.tmpl new file mode 100644 index 0000000..bd7d69b --- /dev/null +++ b/templates/user/settings/packages.tmpl @@ -0,0 +1,24 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings packages")}} + <div class="user-setting-content"> + {{template "package/shared/cleanup_rules/list" .}} + {{template "package/shared/cargo" .}} + + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "packages.owner.settings.chef.title"}} + </h4> + <div class="ui attached segment"> + <div class="ui form"> + <div class="field"> + <label>{{ctx.Locale.Tr "packages.owner.settings.chef.keypair.description"}}</label> + </div> + <form class="field" action="{{.Link}}/chef/regenerate_keypair" method="post"> + {{.CsrfTokenHtml}} + <button class="ui primary button">{{ctx.Locale.Tr "packages.owner.settings.chef.keypair"}}</button> + </form> + <div class="field"> + <label>{{ctx.Locale.Tr "packages.registry.documentation" "Chef" "https://forgejo.org/docs/latest/user/packages/chef/"}}</label> + </div> + </div> + </div> + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/packages_cleanup_rules_edit.tmpl b/templates/user/settings/packages_cleanup_rules_edit.tmpl new file mode 100644 index 0000000..522b524 --- /dev/null +++ b/templates/user/settings/packages_cleanup_rules_edit.tmpl @@ -0,0 +1,5 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings packages")}} + <div class="user-setting-content"> + {{template "package/shared/cleanup_rules/edit" .}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/packages_cleanup_rules_preview.tmpl b/templates/user/settings/packages_cleanup_rules_preview.tmpl new file mode 100644 index 0000000..d99aee4 --- /dev/null +++ b/templates/user/settings/packages_cleanup_rules_preview.tmpl @@ -0,0 +1,5 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user packages admin")}} + <div class="user-setting-content"> + {{template "package/shared/cleanup_rules/preview" .}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl new file mode 100644 index 0000000..a092a42 --- /dev/null +++ b/templates/user/settings/profile.tmpl @@ -0,0 +1,166 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings profile")}} + <div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.public_profile"}} + </h4> + <div class="ui attached segment"> + <p>{{ctx.Locale.Tr "settings.profile_desc"}}</p> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="required field {{if .Err_Name}}error{{end}}"> + <label for="username">{{ctx.Locale.Tr "username"}} + <span class="text red tw-hidden" id="name-change-prompt"> {{ctx.Locale.Tr "settings.change_username_prompt"}}</span> + <span class="text red tw-hidden" id="name-change-redirect-prompt"> {{ctx.Locale.Tr "settings.change_username_redirect_prompt"}}</span> + </label> + <input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" autofocus required {{if or (not .SignedUser.IsLocal) .IsReverseProxy}}disabled{{end}} maxlength="40"> + {{if or (not .SignedUser.IsLocal) .IsReverseProxy}} + <p class="help text blue">{{ctx.Locale.Tr "settings.password_username_disabled"}}</p> + {{end}} + </div> + <div class="field {{if .Err_FullName}}error{{end}}"> + <label for="full_name">{{ctx.Locale.Tr "settings.full_name"}}</label> + <input id="full_name" name="full_name" value="{{.SignedUser.FullName}}" maxlength="100"> + </div> + <div class="inline field"> + <span class="inline field"><label for="pronouns">{{ctx.Locale.Tr "settings.pronouns"}}</label></span> + <div id="pronouns-dropdown" style="display: none" class="ui selection dropdown"> + <input type="hidden" value="{{.SignedUser.Pronouns}}"> + <div class="text"> + {{if .PronounsAreCustom}} + {{ctx.Locale.Tr "settings.pronouns_custom"}} + {{else if eq "" .SignedUser.Pronouns}} + {{ctx.Locale.Tr "settings.pronouns_unspecified"}} + {{else}} + {{.SignedUser.Pronouns}} + {{end}} + </div> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="menu"> + <div class="item{{if eq "" .SignedUser.Pronouns}} active selected{{end}}" data-value=""><p>{{ctx.Locale.Tr "settings.pronouns_unspecified"}}</p></div> + <div class="item{{if eq "he/him" .SignedUser.Pronouns}} active selected{{end}}" data-value="he/him">he/him</div> + <div class="item{{if eq "she/her" .SignedUser.Pronouns}} active selected{{end}}" data-value="she/her">she/her</div> + <div class="item{{if eq "they/them" .SignedUser.Pronouns}} active selected{{end}}" data-value="they/them">they/them</div> + <div class="item{{if eq "it/its" .SignedUser.Pronouns}} active selected{{end}}" data-value="it/its">it/its</div> + <div class="item{{if eq "any pronouns" .SignedUser.Pronouns}} active selected{{end}}" data-value="any pronouns">any pronouns</div> + {{if .PronounsAreCustom}} + <div class="item active selected" data-value="{{.SignedUser.Pronouns}}"><p>{{ctx.Locale.Tr "settings.pronouns_custom"}}</p></div> + {{else}} + <div class="item" data-value="!"><i>{{ctx.Locale.Tr "settings.pronouns_custom"}}</i></div> + {{end}} + </div> + </div> + <input id="pronouns-custom" name="pronouns" value="{{.SignedUser.Pronouns}}" maxlength="50"> + </div> + <div class="field {{if .Err_Email}}error{{end}}"> + <label>{{ctx.Locale.Tr "email"}}</label> + <p id="signed-user-email">{{.SignedUser.Email}}</p> + </div> + <div class="field {{if .Err_Biography}}error{{end}}"> + <label for="biography">{{ctx.Locale.Tr "user.user_bio"}}</label> + <textarea id="biography" name="biography" rows="2" placeholder="{{ctx.Locale.Tr "settings.biography_placeholder"}}" maxlength="255">{{.SignedUser.Description}}</textarea> + </div> + <div class="field {{if .Err_Website}}error{{end}}"> + <label for="website">{{ctx.Locale.Tr "settings.website"}}</label> + <input id="website" name="website" type="url" value="{{.SignedUser.Website}}" maxlength="255"> + </div> + <div class="field"> + <label for="location">{{ctx.Locale.Tr "settings.location"}}</label> + <input id="location" name="location" placeholder="{{ctx.Locale.Tr "settings.location_placeholder"}}" value="{{.SignedUser.Location}}" maxlength="50"> + </div> + + <div class="divider"></div> + <!-- private block --> + + <div class="field" id="privacy-user-settings"> + <label><strong>{{ctx.Locale.Tr "settings.privacy"}}</strong></label> + </div> + + <div class="inline field {{if .Err_Visibility}}error{{end}}"> + <span class="inline required field"><label>{{ctx.Locale.Tr "settings.visibility"}}</label></span> + <div class="ui selection type dropdown"> + {{if .SignedUser.Visibility.IsPublic}}<input type="hidden" id="visibility" name="visibility" value="0">{{end}} + {{if .SignedUser.Visibility.IsLimited}}<input type="hidden" id="visibility" name="visibility" value="1">{{end}} + {{if .SignedUser.Visibility.IsPrivate}}<input type="hidden" id="visibility" name="visibility" value="2">{{end}} + <div class="text"> + {{if .SignedUser.Visibility.IsPublic}}{{ctx.Locale.Tr "settings.visibility.public"}}{{end}} + {{if .SignedUser.Visibility.IsLimited}}{{ctx.Locale.Tr "settings.visibility.limited"}}{{end}} + {{if .SignedUser.Visibility.IsPrivate}}{{ctx.Locale.Tr "settings.visibility.private"}}{{end}} + </div> + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + <div class="menu"> + {{range $mode := .AllowedUserVisibilityModes}} + {{if $mode.IsPublic}} + <div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.visibility.public_tooltip"}}" data-value="0">{{ctx.Locale.Tr "settings.visibility.public"}}</div> + {{else if $mode.IsLimited}} + <div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.visibility.limited_tooltip"}}" data-value="1">{{ctx.Locale.Tr "settings.visibility.limited"}}</div> + {{else if $mode.IsPrivate}} + <div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.visibility.private_tooltip"}}" data-value="2">{{ctx.Locale.Tr "settings.visibility.private"}}</div> + {{end}} + {{end}} + </div> + </div> + </div> + + <div class="field"> + <div class="ui checkbox"> + <label>{{ctx.Locale.Tr "settings.keep_email_private"}}</label> + <input name="keep_email_private" type="checkbox" {{if .SignedUser.KeepEmailPrivate}}checked{{end}}> + </div> + <span class="help tw-block">{{ctx.Locale.Tr "settings.keep_email_private_popup" .SignedUser.GetPlaceholderEmail}}</span> + </div> + + <div class="field"> + <div class="ui checkbox" id="keep-activity-private"> + <label>{{ctx.Locale.Tr "settings.keep_activity_private"}}</label> + <input name="keep_activity_private" type="checkbox" {{if .SignedUser.KeepActivityPrivate}}checked{{end}}> + </div> + <span class="help tw-block">{{ctx.Locale.Tr "settings.keep_activity_private.description" (printf "/%s?tab=activity" .SignedUser.Name)}}</span> + </div> + + <div class="divider"></div> + + <div class="field"> + <button class="ui primary button">{{ctx.Locale.Tr "settings.update_profile"}}</button> + </div> + </form> + </div> + + <h4 class="ui top attached header"> + {{ctx.Locale.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>{{ctx.Locale.Tr "settings.lookup_avatar_by_mail"}}</label> + </div> + </div> + <div class="field tw-pl-4 {{if .Err_Gravatar}}error{{end}}"> + <label for="gravatar">Avatar {{ctx.Locale.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>{{ctx.Locale.Tr "settings.enable_custom_avatar"}}</label> + </div> + </div> + + <div class="inline field tw-pl-4"> + <label for="new-avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label> + <input id="new-avatar" name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp"> + </div> + + <div class="field"> + <button class="ui primary button">{{ctx.Locale.Tr "settings.update_avatar"}}</button> + <button class="ui red button link-action" data-url="{{.Link}}/avatar/delete">{{ctx.Locale.Tr "settings.delete_current_avatar"}}</button> + </div> + </form> + </div> + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl new file mode 100644 index 0000000..09b00f0 --- /dev/null +++ b/templates/user/settings/repos.tmpl @@ -0,0 +1,130 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings repos")}} + <div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.repos"}} + </h4> + <div class="ui attached segment"> + {{if or .allowAdopt .allowDelete}} + {{if .Dirs}} + <div class="ui list"> + {{range $dirI, $dir := .Dirs}} + {{$repo := index $.ReposMap $dir}} + <div class="item {{if not $repo}}tw-py-1{{end}}">{{/* if not repo, then there are "adapt" buttons, so the padding shouldn't be that default large*/}} + <div class="content"> + {{if $repo}} + {{if $repo.IsPrivate}} + <span class="text gold icon">{{svg "octicon-lock"}}</span> + {{else if $repo.IsFork}} + <span class="icon">{{svg "octicon-repo-forked"}}</span> + {{else if $repo.IsMirror}} + <span class="icon">{{svg "octicon-mirror"}}</span> + {{else if $repo.IsTemplate}} + <span class="icon">{{svg "octicon-repo-template"}}</span> + {{else}} + <span class="icon">{{svg "octicon-repo"}}</span> + {{end}} + <a class="muted name" href="{{$repo.Link}}">{{$repo.OwnerName}}/{{$repo.Name}}</a> + <span class="text light-3" {{if not (eq $repo.Size 0)}} data-tooltip-content="{{$repo.SizeDetailsString ctx.Locale}}"{{end}}>{{ctx.Locale.TrSize $repo.Size}}</span> + {{if $repo.IsFork}} + {{ctx.Locale.Tr "repo.forked_from"}} + <span><a href="{{$repo.BaseRepo.Link}}">{{$repo.BaseRepo.OwnerName}}/{{$repo.BaseRepo.Name}}</a></span> + {{end}} + {{else}} + <span class="icon tw-inline-block tw-pt-2">{{svg "octicon-file-directory-fill"}}</span> + <span class="name tw-inline-block tw-pt-2">{{$.ContextUser.Name}}/{{$dir}}</span> + <div class="tw-float-right"> + {{if $.allowAdopt}} + <button class="ui button primary show-modal tw-p-2" data-modal="#adopt-unadopted-modal-{{$dirI}}"><span class="icon">{{svg "octicon-plus"}}</span><span class="label">{{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</span></button> + <div class="ui g-modal-confirm modal" id="adopt-unadopted-modal-{{$dirI}}"> + <div class="header"> + <span class="label">{{ctx.Locale.Tr "repo.adopt_preexisting"}}</span> + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "repo.adopt_preexisting_content" $dir}}</p> + </div> + <form class="ui form" method="post" action="{{AppSubUrl}}/user/settings/repos/unadopted"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="id" value="{{$dir}}"> + <input type="hidden" name="action" value="adopt"> + {{template "base/modal_actions_confirm" $}} + </form> + </div> + {{end}} + {{if $.allowDelete}} + <button class="ui button red show-modal tw-p-2" data-modal="#delete-unadopted-modal-{{$dirI}}"><span class="icon">{{svg "octicon-x"}}</span><span class="label">{{ctx.Locale.Tr "repo.delete_preexisting_label"}}</span></button> + <div class="ui g-modal-confirm modal" id="delete-unadopted-modal-{{$dirI}}"> + <div class="header"> + <span class="label">{{ctx.Locale.Tr "repo.delete_preexisting"}}</span> + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "repo.delete_preexisting_content" $dir}}</p> + </div> + <form class="ui form" method="post" action="{{AppSubUrl}}/user/settings/repos/unadopted"> + {{$.CsrfTokenHtml}} + <input type="hidden" name="id" value="{{$dir}}"> + <input type="hidden" name="action" value="delete"> + {{template "base/modal_actions_confirm" $}} + </form> + </div> + {{end}} + </div> + {{end}} + </div> + </div> + {{end}} + </div> + {{template "base/paginate" .}} + {{else}} + <div class="item"> + {{ctx.Locale.Tr "settings.repos_none"}} + </div> + {{end}} + {{else}} + {{if .Repos}} + <div class="ui middle aligned divided list"> + {{range .Repos}} + <div class="item"> + <div class="content"> + {{if .IsPrivate}} + {{svg "octicon-lock" 16 "tw-mr-1 iconFloat text gold"}} + {{else if .IsFork}} + {{svg "octicon-repo-forked" 16 "tw-mr-1 iconFloat"}} + {{else if .IsMirror}} + {{svg "octicon-mirror" 16 "tw-mr-1 iconFloat"}} + {{else if .IsTemplate}} + {{svg "octicon-repo-template" 16 "tw-mr-1 iconFloat"}} + {{else}} + {{svg "octicon-repo" 16 "tw-mr-1 iconFloat"}} + {{end}} + <a class="name" href="{{.Link}}">{{.OwnerName}}/{{.Name}}</a> + <span>{{ctx.Locale.TrSize .Size}}</span> + {{if .IsFork}} + {{ctx.Locale.Tr "repo.forked_from"}} + <span><a href="{{.BaseRepo.Link}}">{{.BaseRepo.OwnerName}}/{{.BaseRepo.Name}}</a></span> + {{end}} + </div> + </div> + {{end}} + </div> + {{template "base/paginate" .}} + {{else}} + <div class="item"> + {{ctx.Locale.Tr "settings.repos_none"}} + </div> + {{end}} + {{end}} + </div> + </div> + +<div class="ui g-modal-confirm delete modal"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.remove_account_link"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.remove_account_link_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} +</div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/runner_edit.tmpl b/templates/user/settings/runner_edit.tmpl new file mode 100644 index 0000000..90c58c1 --- /dev/null +++ b/templates/user/settings/runner_edit.tmpl @@ -0,0 +1,5 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings runners")}} + <div class="user-setting-content"> + {{template "shared/actions/runner_edit" .}} + </div> +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/security/accountlinks.tmpl b/templates/user/settings/security/accountlinks.tmpl new file mode 100644 index 0000000..0820844 --- /dev/null +++ b/templates/user/settings/security/accountlinks.tmpl @@ -0,0 +1,62 @@ +{{/* No account links, no way to add account links: Menu will not be shown. */}} +{{if or .AccountLinks .OrderedOAuth2Names}} +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_account_links"}} + {{if .OrderedOAuth2Names}} + <div class="ui right"> + <div class="ui dropdown"> + <div class="ui primary tiny button">{{ctx.Locale.Tr "settings.link_account"}}</div> + <div class="menu"> + {{range $key := .OrderedOAuth2Names}} + {{$provider := index $.OAuth2Providers $key}} + <a class="item" href="{{AppSubUrl}}/user/oauth2/{{$key}}"> + {{$provider.IconHTML 20}} + {{$provider.DisplayName}} + </a> + {{end}} + </div> + </div> + </div> + {{end}} +</h4> + +<div class="ui attached segment"> + <div class="flex-list"> + <div class="flex-item"> + {{ctx.Locale.Tr "settings.manage_account_links_desc"}} + </div> + {{range $loginSource, $provider := .AccountLinks}} + <div class="flex-item"> + {{$providerData := index $.OAuth2Providers $loginSource.Name}} + <div class="flex-item-leading"> + {{$providerData.IconHTML 20}} + </div> + <div class="flex-item-main"> + <span class="flex-item-title" data-tooltip-content="{{$provider}}"> + {{$loginSource.Name}} + </span> + {{if $loginSource.IsActive}} + <span class="flex-text-body text primary">{{ctx.Locale.Tr "repo.settings.active"}}</span> + {{end}} + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button" data-modal-id="delete-account-link" data-url="{{AppSubUrl}}/user/settings/security/account_link" data-id="{{$loginSource.ID}}"> + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + </div> + </div> + {{end}} + </div> + + <div class="ui g-modal-confirm delete modal" id="delete-account-link"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.remove_account_link"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.remove_account_link_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> +{{end}} diff --git a/templates/user/settings/security/openid.tmpl b/templates/user/settings/security/openid.tmpl new file mode 100644 index 0000000..b0473c9 --- /dev/null +++ b/templates/user/settings/security/openid.tmpl @@ -0,0 +1,63 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.manage_openid"}} +</h4> +<div class="ui attached segment"> + <div class="flex-list"> + <div class="flex-item"> + {{ctx.Locale.Tr "settings.openid_desc"}} + </div> + {{range .OpenIDs}} + <div class="flex-item tw-items-center"> + <div class="flex-item-leading"> + {{svg "fontawesome-openid" 20}} + </div> + <div class="flex-item-main"> + <div class="flex-item-title">{{.URI}}</div> + </div> + <div class="flex-item-trailing"> + <form action="{{AppSubUrl}}/user/settings/security/openid/toggle_visibility" method="post"> + {{$.CsrfTokenHtml}} + <input name="id" type="hidden" value="{{.ID}}"> + {{if .Show}} + <button class="ui tiny button"> + {{svg "octicon-eye" 16 "icon"}} + {{ctx.Locale.Tr "settings.hide_openid"}} + </button> + {{else}} + <button class="ui tiny button"> + {{svg "octicon-eye-closed" 16 "icon"}} + {{ctx.Locale.Tr "settings.show_openid"}} + </button> + {{end}} + </form> + <button class="ui red tiny button delete-button" data-modal-id="delete-openid" data-url="{{AppSubUrl}}/user/settings/security/openid/delete" data-id="{{.ID}}"> + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + </div> + </div> + {{end}} + </div> +</div> +<div class="ui attached bottom segment"> + <form class="ui form" action="{{AppSubUrl}}/user/settings/security/openid" method="post"> + {{.CsrfTokenHtml}} + <div class="required field {{if .Err_OpenID}}error{{end}}"> + <label for="openid">{{ctx.Locale.Tr "settings.add_new_openid"}}</label> + <input id="openid" name="openid" type="text" required> + </div> + <button class="ui primary button"> + {{ctx.Locale.Tr "settings.add_openid"}} + </button> + </form> + + <div class="ui g-modal-confirm delete modal" id="delete-openid"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.openid_deletion"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.openid_deletion_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> diff --git a/templates/user/settings/security/security.tmpl b/templates/user/settings/security/security.tmpl new file mode 100644 index 0000000..aee0456 --- /dev/null +++ b/templates/user/settings/security/security.tmpl @@ -0,0 +1,11 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings security")}} + <div class="user-setting-content"> + {{template "user/settings/security/twofa" .}} + {{template "user/settings/security/webauthn" .}} + {{template "user/settings/security/accountlinks" .}} + {{if .EnableOpenIDSignIn}} + {{template "user/settings/security/openid" .}} + {{end}} + </div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/security/twofa.tmpl b/templates/user/settings/security/twofa.tmpl new file mode 100644 index 0000000..adebce4 --- /dev/null +++ b/templates/user/settings/security/twofa.tmpl @@ -0,0 +1,37 @@ +<h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.twofa"}} +</h4> +<div class="ui attached segment"> + <p>{{ctx.Locale.Tr "settings.twofa_desc"}}</p> + {{if .TOTPEnrolled}} + <p>{{ctx.Locale.Tr "settings.twofa_is_enrolled"}}</p> + <form class="ui form" action="{{AppSubUrl}}/user/settings/security/two_factor/regenerate_scratch" method="post" enctype="multipart/form-data"> + {{.CsrfTokenHtml}} + <p>{{ctx.Locale.Tr "settings.regenerate_scratch_token_desc"}}</p> + <button class="ui primary button">{{ctx.Locale.Tr "settings.twofa_scratch_token_regenerate"}}</button> + </form> + <form class="ui form" action="{{AppSubUrl}}/user/settings/security/two_factor/disable" method="post" enctype="multipart/form-data" id="disable-form"> + {{.CsrfTokenHtml}} + <p>{{ctx.Locale.Tr "settings.twofa_disable_note"}}</p> + <button class="ui red button delete-button" data-modal-id="disable-twofa" data-type="form" data-form="#disable-form">{{ctx.Locale.Tr "settings.twofa_disable"}}</button> + </form> + {{else}} + {{/* The recovery tip is there as a means of encouraging a user to enroll */}} + <p>{{ctx.Locale.Tr "settings.twofa_recovery_tip"}}</p> + <p>{{ctx.Locale.Tr "settings.twofa_not_enrolled"}}</p> + <div class="inline field"> + <a class="ui primary button" href="{{AppSubUrl}}/user/settings/security/two_factor/enroll">{{ctx.Locale.Tr "settings.twofa_enroll"}}</a> + </div> + {{end}} + + <div class="ui g-modal-confirm delete modal" id="disable-twofa"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.twofa_disable"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.twofa_disable_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> diff --git a/templates/user/settings/security/twofa_enroll.tmpl b/templates/user/settings/security/twofa_enroll.tmpl new file mode 100644 index 0000000..d6bfadf --- /dev/null +++ b/templates/user/settings/security/twofa_enroll.tmpl @@ -0,0 +1,25 @@ +{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings twofa")}} + <div class="user-setting-content"> + <h4 class="ui top attached header"> + {{ctx.Locale.Tr "settings.twofa_enroll"}} + </h4> + <div class="ui attached segment"> + <p>{{ctx.Locale.Tr "settings.scan_this_image"}}</p> + <img src="{{.QrUri}}" alt="{{.TwofaSecret}}"> + <p>{{ctx.Locale.Tr "settings.or_enter_secret" .TwofaSecret}}</p> + <p>{{ctx.Locale.Tr "settings.then_enter_passcode"}}</p> + <form class="ui form" action="{{.Link}}" method="post"> + {{.CsrfTokenHtml}} + <div class="inline required field {{if .Err_Passcode}}error{{end}}"> + <label for="passcode">{{ctx.Locale.Tr "passcode"}}</label> + <input id="passcode" name="passcode" autofocus required> + </div> + <div class="inline field"> + <label></label> + <button class="ui primary button">{{ctx.Locale.Tr "auth.verify"}}</button> + </div> + </form> + </div> + </div> + +{{template "user/settings/layout_footer" .}} diff --git a/templates/user/settings/security/webauthn.tmpl b/templates/user/settings/security/webauthn.tmpl new file mode 100644 index 0000000..346f61c --- /dev/null +++ b/templates/user/settings/security/webauthn.tmpl @@ -0,0 +1,43 @@ +<h4 class="ui top attached header">{{ctx.Locale.Tr "settings.webauthn"}}</h4> +<div class="ui attached segment"> + <p>{{ctx.Locale.Tr "settings.webauthn_desc" "https://w3c.github.io/webauthn/#webauthn-authenticator"}}</p> + <p>{{ctx.Locale.Tr "settings.webauthn_key_loss_warning"}} {{ctx.Locale.Tr "settings.webauthn_alternative_tip"}}</p> + {{template "user/auth/webauthn_error" .}} + <div class="flex-list"> + {{range .WebAuthnCredentials}} + <div class="flex-item"> + <div class="flex-item-leading"> + {{svg "octicon-key" 32}} + </div> + <div class="flex-item-main"> + <div class="flex-item-title">{{.Name}}</div> + <div class="flex-item-body"> + <p>{{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}}</p> + </div> + </div> + <div class="flex-item-trailing"> + <button class="ui red tiny button delete-button" data-modal-id="delete-registration" data-url="{{$.Link}}/webauthn/delete" data-id="{{.ID}}"> + {{ctx.Locale.Tr "settings.delete_key"}} + </button> + </div> + </div> + {{end}} + </div> + <div class="ui form"> + <div class="required field"> + <label for="nickname">{{ctx.Locale.Tr "settings.webauthn_nickname"}}</label> + <input id="nickname" name="nickname" type="text" required> + </div> + <button id="register-webauthn" class="ui primary button">{{svg "octicon-key"}} {{ctx.Locale.Tr "settings.webauthn_register_key"}}</button> + </div> + <div class="ui g-modal-confirm delete modal" id="delete-registration"> + <div class="header"> + {{svg "octicon-trash"}} + {{ctx.Locale.Tr "settings.webauthn_delete_key"}} + </div> + <div class="content"> + <p>{{ctx.Locale.Tr "settings.webauthn_delete_key_desc"}}</p> + </div> + {{template "base/modal_actions_confirm" .}} + </div> +</div> |