Backport #27856 The only conflict is `ThemeName` in `500.tmpl`, it has been resolved manually by keeping using old `{{.SignedUser.Theme}}{{else}}{{DefaultTheme}}`
This commit is contained in:
parent
18a782f73d
commit
6ac2ade97d
|
@ -157,7 +157,6 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||||
ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
|
ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
|
||||||
ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
|
ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
|
||||||
ctx.Data["Link"] = ctx.Link
|
ctx.Data["Link"] = ctx.Link
|
||||||
ctx.Data["locale"] = ctx.Locale
|
|
||||||
|
|
||||||
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
||||||
ctx.PageData = map[string]any{}
|
ctx.PageData = map[string]any{}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/httpcache"
|
"code.gitea.io/gitea/modules/httpcache"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -35,20 +36,18 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) {
|
||||||
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
|
httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform")
|
||||||
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
|
w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
|
||||||
|
|
||||||
data := middleware.GetContextData(req.Context())
|
tmplCtx := context.TemplateContext{}
|
||||||
if data["locale"] == nil {
|
tmplCtx["Locale"] = middleware.Locale(w, req)
|
||||||
data = middleware.CommonTemplateContextData()
|
ctxData := middleware.GetContextData(req.Context())
|
||||||
data["locale"] = middleware.Locale(w, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much.
|
// This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much.
|
||||||
// Otherwise, the 500-page may cause new panics, eg: cache.GetContextWithData, it makes the developer&users couldn't find the original panic.
|
// Otherwise, the 500-page may cause new panics, eg: cache.GetContextWithData, it makes the developer&users couldn't find the original panic.
|
||||||
user, _ := data[middleware.ContextDataKeySignedUser].(*user_model.User)
|
user, _ := ctxData[middleware.ContextDataKeySignedUser].(*user_model.User)
|
||||||
if !setting.IsProd || (user != nil && user.IsAdmin) {
|
if !setting.IsProd || (user != nil && user.IsAdmin) {
|
||||||
data["ErrorMsg"] = "PANIC: " + combinedErr
|
ctxData["ErrorMsg"] = "PANIC: " + combinedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
err = templates.HTMLRenderer().HTML(w, http.StatusInternalServerError, string(tplStatus500), data, nil)
|
err = templates.HTMLRenderer().HTML(w, http.StatusInternalServerError, string(tplStatus500), ctxData, tmplCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error occurs again when rendering error page: %v", err)
|
log.Error("Error occurs again when rendering error page: %v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
|
|
@ -27,6 +27,7 @@ func TestRenderPanicErrorPage(t *testing.T) {
|
||||||
respContent := w.Body.String()
|
respContent := w.Body.String()
|
||||||
assert.Contains(t, respContent, `class="page-content status-page-500"`)
|
assert.Contains(t, respContent, `class="page-content status-page-500"`)
|
||||||
assert.Contains(t, respContent, `</html>`)
|
assert.Contains(t, respContent, `</html>`)
|
||||||
|
assert.Contains(t, respContent, `lang="en-US"`) // make sure the locale work
|
||||||
|
|
||||||
// the 500 page doesn't have normal pages footer, it makes it easier to distinguish a normal page and a failed page.
|
// the 500 page doesn't have normal pages footer, it makes it easier to distinguish a normal page and a failed page.
|
||||||
// especially when a sub-template causes page error, the HTTP response code is still 200,
|
// especially when a sub-template causes page error, the HTTP response code is still 200,
|
||||||
|
|
|
@ -26,7 +26,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
|
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
|
||||||
|
@ -68,15 +67,12 @@ func SendTestMail(email string) error {
|
||||||
func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) {
|
func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) {
|
||||||
locale := translation.NewLocale(language)
|
locale := translation.NewLocale(language)
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"DisplayName": u.DisplayName(),
|
"DisplayName": u.DisplayName(),
|
||||||
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
|
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
|
||||||
"ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, locale),
|
"ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, locale),
|
||||||
"Code": code,
|
"Code": code,
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var content bytes.Buffer
|
var content bytes.Buffer
|
||||||
|
@ -119,15 +115,12 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) {
|
||||||
}
|
}
|
||||||
locale := translation.NewLocale(u.Language)
|
locale := translation.NewLocale(u.Language)
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"DisplayName": u.DisplayName(),
|
"DisplayName": u.DisplayName(),
|
||||||
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
|
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale),
|
||||||
"Code": u.GenerateEmailActivateCode(email.Email),
|
"Code": u.GenerateEmailActivateCode(email.Email),
|
||||||
"Email": email.Email,
|
"Email": email.Email,
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var content bytes.Buffer
|
var content bytes.Buffer
|
||||||
|
@ -152,13 +145,10 @@ func SendRegisterNotifyMail(u *user_model.User) {
|
||||||
locale := translation.NewLocale(u.Language)
|
locale := translation.NewLocale(u.Language)
|
||||||
|
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"DisplayName": u.DisplayName(),
|
"DisplayName": u.DisplayName(),
|
||||||
"Username": u.Name,
|
"Username": u.Name,
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var content bytes.Buffer
|
var content bytes.Buffer
|
||||||
|
@ -185,14 +175,11 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository)
|
||||||
|
|
||||||
subject := locale.Tr("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
|
subject := locale.Tr("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"Subject": subject,
|
"Subject": subject,
|
||||||
"RepoName": repoName,
|
"RepoName": repoName,
|
||||||
"Link": repo.HTMLURL(),
|
"Link": repo.HTMLURL(),
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var content bytes.Buffer
|
var content bytes.Buffer
|
||||||
|
@ -259,6 +246,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
locale := translation.NewLocale(lang)
|
locale := translation.NewLocale(lang)
|
||||||
|
|
||||||
mailMeta := map[string]any{
|
mailMeta := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"FallbackSubject": fallback,
|
"FallbackSubject": fallback,
|
||||||
"Body": body,
|
"Body": body,
|
||||||
"Link": link,
|
"Link": link,
|
||||||
|
@ -275,10 +263,6 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
"ReviewComments": reviewComments,
|
"ReviewComments": reviewComments,
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
"CanReply": setting.IncomingEmail.Enabled && commentType != issues_model.CommentTypePullRequestPush,
|
"CanReply": setting.IncomingEmail.Enabled && commentType != issues_model.CommentTypePullRequestPush,
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mailSubject bytes.Buffer
|
var mailSubject bytes.Buffer
|
||||||
|
@ -469,7 +453,7 @@ func SendIssueAssignedMail(ctx context.Context, issue *issues_model.Issue, doer
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
SendAsyncs(msgs)
|
SendAsync(msgs...)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
SendAsyncs(msgs)
|
SendAsync(msgs...)
|
||||||
receivers = receivers[:i]
|
receivers = receivers[:i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/markdown"
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,13 +68,10 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
|
||||||
|
|
||||||
subject := locale.Tr("mail.release.new.subject", rel.TagName, rel.Repo.FullName())
|
subject := locale.Tr("mail.release.new.subject", rel.TagName, rel.Repo.FullName())
|
||||||
mailMeta := map[string]any{
|
mailMeta := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"Release": rel,
|
"Release": rel,
|
||||||
"Subject": subject,
|
"Subject": subject,
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mailBody bytes.Buffer
|
var mailBody bytes.Buffer
|
||||||
|
@ -95,5 +91,5 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
|
||||||
msgs = append(msgs, msg)
|
msgs = append(msgs, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
SendAsyncs(msgs)
|
SendAsync(msgs...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,6 +64,7 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
||||||
}
|
}
|
||||||
|
|
||||||
data := map[string]any{
|
data := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"Doer": doer,
|
"Doer": doer,
|
||||||
"User": repo.Owner,
|
"User": repo.Owner,
|
||||||
"Repo": repo.FullName(),
|
"Repo": repo.FullName(),
|
||||||
|
@ -72,10 +72,6 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
||||||
"Subject": subject,
|
"Subject": subject,
|
||||||
"Language": locale.Language(),
|
"Language": locale.Language(),
|
||||||
"Destination": destination,
|
"Destination": destination,
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil {
|
if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil {
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,16 +52,13 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod
|
||||||
|
|
||||||
subject := locale.Tr("mail.team_invite.subject", inviter.DisplayName(), org.DisplayName())
|
subject := locale.Tr("mail.team_invite.subject", inviter.DisplayName(), org.DisplayName())
|
||||||
mailMeta := map[string]any{
|
mailMeta := map[string]any{
|
||||||
|
"locale": locale,
|
||||||
"Inviter": inviter,
|
"Inviter": inviter,
|
||||||
"Organization": org,
|
"Organization": org,
|
||||||
"Team": team,
|
"Team": team,
|
||||||
"Invite": invite,
|
"Invite": invite,
|
||||||
"Subject": subject,
|
"Subject": subject,
|
||||||
"InviteURL": inviteURL,
|
"InviteURL": inviteURL,
|
||||||
// helper
|
|
||||||
"locale": locale,
|
|
||||||
"Str2html": templates.Str2html,
|
|
||||||
"DotEscape": templates.DotEscape,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mailBody bytes.Buffer
|
var mailBody bytes.Buffer
|
||||||
|
|
|
@ -426,15 +426,12 @@ func NewContext(ctx context.Context) {
|
||||||
go graceful.GetManager().RunWithCancel(mailQueue)
|
go graceful.GetManager().RunWithCancel(mailQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendAsync send mail asynchronously
|
// SendAsync send emails asynchronously (make it mockable)
|
||||||
func SendAsync(msg *Message) {
|
var SendAsync = sendAsync
|
||||||
SendAsyncs([]*Message{msg})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendAsyncs send mails asynchronously
|
func sendAsync(msgs ...*Message) {
|
||||||
func SendAsyncs(msgs []*Message) {
|
|
||||||
if setting.MailService == nil {
|
if setting.MailService == nil {
|
||||||
log.Error("Mailer: SendAsyncs is being invoked but mail service hasn't been initialized")
|
log.Error("Mailer: SendAsync is being invoked but mail service hasn't been initialized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
|
{{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
|
||||||
* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, DefaultTheme, Str2html
|
* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, DefaultTheme, Str2html
|
||||||
* locale
|
* ctx.Locale
|
||||||
* Flash
|
* .Flash
|
||||||
* ErrorMsg
|
* .ErrorMsg
|
||||||
* SignedUser (optional)
|
* .SignedUser (optional)
|
||||||
*/}}
|
*/}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{.locale.Lang}}" class="theme-{{if .SignedUser.Theme}}{{.SignedUser.Theme}}{{else}}{{DefaultTheme}}{{end}}">
|
<html lang="{{ctx.Locale.Lang}}" class="theme-{{if .SignedUser.Theme}}{{.SignedUser.Theme}}{{else}}{{DefaultTheme}}{{end}}">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Internal Server Error - {{AppName}}</title>
|
<title>Internal Server Error - {{AppName}}</title>
|
||||||
|
@ -19,8 +19,8 @@
|
||||||
<nav class="ui secondary menu gt-border-secondary-bottom">
|
<nav class="ui secondary menu gt-border-secondary-bottom">
|
||||||
<div class="ui container gt-df">
|
<div class="ui container gt-df">
|
||||||
<div class="item gt-f1">
|
<div class="item gt-f1">
|
||||||
<a href="{{AppSubUrl}}/" aria-label="{{.locale.Tr "home"}}">
|
<a href="{{AppSubUrl}}/" aria-label="{{ctx.Locale.Tr "home"}}">
|
||||||
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{.locale.Tr "logo"}}" aria-hidden="true">
|
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
|
@ -37,12 +37,12 @@
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="ui container gt-my-5">
|
<div class="ui container gt-my-5">
|
||||||
{{if .ErrorMsg}}
|
{{if .ErrorMsg}}
|
||||||
<p>{{.locale.Tr "error.occurred"}}:</p>
|
<p>{{ctx.Locale.Tr "error.occurred"}}:</p>
|
||||||
<pre class="gt-whitespace-pre-wrap gt-break-all">{{.ErrorMsg}}</pre>
|
<pre class="gt-whitespace-pre-wrap gt-break-all">{{.ErrorMsg}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="center gt-mt-5">
|
<div class="center gt-mt-5">
|
||||||
{{if or .SignedUser.IsAdmin .ShowFooterVersion}}<p>{{.locale.Tr "admin.config.app_ver"}}: {{AppVer}}</p>{{end}}
|
{{if or .SignedUser.IsAdmin .ShowFooterVersion}}<p>{{ctx.Locale.Tr "admin.config.app_ver"}}: {{AppVer}}</p>{{end}}
|
||||||
{{if .SignedUser.IsAdmin}}<p>{{.locale.Tr "error.report_message" | Str2html}}</p>{{end}}
|
{{if .SignedUser.IsAdmin}}<p>{{ctx.Locale.Tr "error.report_message" | Str2html}}</p>{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue