From e68b9d00a6e05b3a941f63ffb696f91e554ac5ec Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.3. Signed-off-by: Daniel Baumann --- routers/web/shared/actions/runners.go | 161 ++++++++++++++++++++ routers/web/shared/actions/variables.go | 65 ++++++++ routers/web/shared/packages/packages.go | 260 ++++++++++++++++++++++++++++++++ routers/web/shared/project/column.go | 48 ++++++ routers/web/shared/secrets/secrets.go | 53 +++++++ routers/web/shared/user/header.go | 163 ++++++++++++++++++++ 6 files changed, 750 insertions(+) create mode 100644 routers/web/shared/actions/runners.go create mode 100644 routers/web/shared/actions/variables.go create mode 100644 routers/web/shared/packages/packages.go create mode 100644 routers/web/shared/project/column.go create mode 100644 routers/web/shared/secrets/secrets.go create mode 100644 routers/web/shared/user/header.go (limited to 'routers/web/shared') diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go new file mode 100644 index 0000000..f389332 --- /dev/null +++ b/routers/web/shared/actions/runners.go @@ -0,0 +1,161 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "errors" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" +) + +// RunnersList prepares data for runners list +func RunnersList(ctx *context.Context, opts actions_model.FindRunnerOptions) { + runners, count, err := db.FindAndCount[actions_model.ActionRunner](ctx, opts) + if err != nil { + ctx.ServerError("CountRunners", err) + return + } + + if err := actions_model.RunnerList(runners).LoadAttributes(ctx); err != nil { + ctx.ServerError("LoadAttributes", err) + return + } + + // ownid=0,repo_id=0,means this token is used for global + var token *actions_model.ActionRunnerToken + token, err = actions_model.GetLatestRunnerToken(ctx, opts.OwnerID, opts.RepoID) + if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) { + token, err = actions_model.NewRunnerToken(ctx, opts.OwnerID, opts.RepoID) + if err != nil { + ctx.ServerError("CreateRunnerToken", err) + return + } + } else if err != nil { + ctx.ServerError("GetLatestRunnerToken", err) + return + } + + ctx.Data["Keyword"] = opts.Filter + ctx.Data["Runners"] = runners + ctx.Data["Total"] = count + ctx.Data["RegistrationToken"] = token.Token + ctx.Data["RunnerOwnerID"] = opts.OwnerID + ctx.Data["RunnerRepoID"] = opts.RepoID + ctx.Data["SortType"] = opts.Sort + + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) + + ctx.Data["Page"] = pager +} + +// RunnerDetails prepares data for runners edit page +func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int64) { + runner, err := actions_model.GetRunnerByID(ctx, runnerID) + if err != nil { + ctx.ServerError("GetRunnerByID", err) + return + } + if err := runner.LoadAttributes(ctx); err != nil { + ctx.ServerError("LoadAttributes", err) + return + } + if !runner.Editable(ownerID, repoID) { + err = errors.New("no permission to edit this runner") + ctx.NotFound("RunnerDetails", err) + return + } + + ctx.Data["Runner"] = runner + + opts := actions_model.FindTaskOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: 30, + }, + Status: actions_model.StatusUnknown, // Unknown means all + RunnerID: runner.ID, + } + + tasks, count, err := db.FindAndCount[actions_model.ActionTask](ctx, opts) + if err != nil { + ctx.ServerError("CountTasks", err) + return + } + + if err = actions_model.TaskList(tasks).LoadAttributes(ctx); err != nil { + ctx.ServerError("TasksLoadAttributes", err) + return + } + + ctx.Data["Tasks"] = tasks + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) + ctx.Data["Page"] = pager +} + +// RunnerDetailsEditPost response for edit runner details +func RunnerDetailsEditPost(ctx *context.Context, runnerID, ownerID, repoID int64, redirectTo string) { + runner, err := actions_model.GetRunnerByID(ctx, runnerID) + if err != nil { + log.Warn("RunnerDetailsEditPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL) + ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err) + return + } + if !runner.Editable(ownerID, repoID) { + ctx.NotFound("RunnerDetailsEditPost.Editable", util.NewPermissionDeniedErrorf("no permission to edit this runner")) + return + } + + form := web.GetForm(ctx).(*forms.EditRunnerForm) + runner.Description = form.Description + + err = actions_model.UpdateRunner(ctx, runner, "description") + if err != nil { + log.Warn("RunnerDetailsEditPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) + ctx.Flash.Warning(ctx.Tr("actions.runners.update_runner_failed")) + ctx.Redirect(redirectTo) + return + } + + log.Debug("RunnerDetailsEditPost success: %s", ctx.Req.URL) + + ctx.Flash.Success(ctx.Tr("actions.runners.update_runner_success")) + ctx.Redirect(redirectTo) +} + +// RunnerResetRegistrationToken reset registration token +func RunnerResetRegistrationToken(ctx *context.Context, ownerID, repoID int64, redirectTo string) { + _, err := actions_model.NewRunnerToken(ctx, ownerID, repoID) + if err != nil { + ctx.ServerError("ResetRunnerRegistrationToken", err) + return + } + + ctx.Flash.Success(ctx.Tr("actions.runners.reset_registration_token_success")) + ctx.Redirect(redirectTo) +} + +// RunnerDeletePost response for deleting a runner +func RunnerDeletePost(ctx *context.Context, runnerID int64, + successRedirectTo, failedRedirectTo string, +) { + if err := actions_model.DeleteRunner(ctx, runnerID); err != nil { + log.Warn("DeleteRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) + ctx.Flash.Warning(ctx.Tr("actions.runners.delete_runner_failed")) + + ctx.JSONRedirect(failedRedirectTo) + return + } + + log.Info("DeleteRunnerPost success: %s", ctx.Req.URL) + + ctx.Flash.Success(ctx.Tr("actions.runners.delete_runner_success")) + + ctx.JSONRedirect(successRedirectTo) +} diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go new file mode 100644 index 0000000..79c03e4 --- /dev/null +++ b/routers/web/shared/actions/variables.go @@ -0,0 +1,65 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" + actions_service "code.gitea.io/gitea/services/actions" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" +) + +func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) { + variables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{ + OwnerID: ownerID, + RepoID: repoID, + }) + if err != nil { + ctx.ServerError("FindVariables", err) + return + } + ctx.Data["Variables"] = variables +} + +func CreateVariable(ctx *context.Context, ownerID, repoID int64, redirectURL string) { + form := web.GetForm(ctx).(*forms.EditVariableForm) + + v, err := actions_service.CreateVariable(ctx, ownerID, repoID, form.Name, form.Data) + if err != nil { + log.Error("CreateVariable: %v", err) + ctx.JSONError(ctx.Tr("actions.variables.creation.failed")) + return + } + + ctx.Flash.Success(ctx.Tr("actions.variables.creation.success", v.Name)) + ctx.JSONRedirect(redirectURL) +} + +func UpdateVariable(ctx *context.Context, redirectURL string) { + id := ctx.ParamsInt64(":variable_id") + form := web.GetForm(ctx).(*forms.EditVariableForm) + + if ok, err := actions_service.UpdateVariable(ctx, id, form.Name, form.Data); err != nil || !ok { + log.Error("UpdateVariable: %v", err) + ctx.JSONError(ctx.Tr("actions.variables.update.failed")) + return + } + ctx.Flash.Success(ctx.Tr("actions.variables.update.success")) + ctx.JSONRedirect(redirectURL) +} + +func DeleteVariable(ctx *context.Context, redirectURL string) { + id := ctx.ParamsInt64(":variable_id") + + if err := actions_service.DeleteVariableByID(ctx, id); err != nil { + log.Error("Delete variable [%d] failed: %v", id, err) + ctx.JSONError(ctx.Tr("actions.variables.deletion.failed")) + return + } + ctx.Flash.Success(ctx.Tr("actions.variables.deletion.success")) + ctx.JSONRedirect(redirectURL) +} diff --git a/routers/web/shared/packages/packages.go b/routers/web/shared/packages/packages.go new file mode 100644 index 0000000..af960f1 --- /dev/null +++ b/routers/web/shared/packages/packages.go @@ -0,0 +1,260 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package packages + +import ( + "errors" + "fmt" + "net/http" + "time" + + "code.gitea.io/gitea/models/db" + packages_model "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" + cargo_service "code.gitea.io/gitea/services/packages/cargo" + container_service "code.gitea.io/gitea/services/packages/container" +) + +func SetPackagesContext(ctx *context.Context, owner *user_model.User) { + pcrs, err := packages_model.GetCleanupRulesByOwner(ctx, owner.ID) + if err != nil { + ctx.ServerError("GetCleanupRulesByOwner", err) + return + } + + ctx.Data["CleanupRules"] = pcrs + + ctx.Data["CargoIndexExists"], err = repo_model.IsRepositoryModelExist(ctx, owner, cargo_service.IndexRepositoryName) + if err != nil { + ctx.ServerError("IsRepositoryModelExist", err) + return + } +} + +func SetRuleAddContext(ctx *context.Context) { + setRuleEditContext(ctx, nil) +} + +func SetRuleEditContext(ctx *context.Context, owner *user_model.User) { + pcr := getCleanupRuleByContext(ctx, owner) + if pcr == nil { + return + } + + setRuleEditContext(ctx, pcr) +} + +func setRuleEditContext(ctx *context.Context, pcr *packages_model.PackageCleanupRule) { + ctx.Data["IsEditRule"] = pcr != nil + + if pcr == nil { + pcr = &packages_model.PackageCleanupRule{} + } + ctx.Data["CleanupRule"] = pcr + ctx.Data["AvailableTypes"] = packages_model.TypeList +} + +func PerformRuleAddPost(ctx *context.Context, owner *user_model.User, redirectURL string, template base.TplName) { + performRuleEditPost(ctx, owner, nil, redirectURL, template) +} + +func PerformRuleEditPost(ctx *context.Context, owner *user_model.User, redirectURL string, template base.TplName) { + pcr := getCleanupRuleByContext(ctx, owner) + if pcr == nil { + return + } + + form := web.GetForm(ctx).(*forms.PackageCleanupRuleForm) + + if form.Action == "remove" { + if err := packages_model.DeleteCleanupRuleByID(ctx, pcr.ID); err != nil { + ctx.ServerError("DeleteCleanupRuleByID", err) + return + } + + ctx.Flash.Success(ctx.Tr("packages.owner.settings.cleanuprules.success.delete")) + ctx.Redirect(redirectURL) + } else { + performRuleEditPost(ctx, owner, pcr, redirectURL, template) + } +} + +func performRuleEditPost(ctx *context.Context, owner *user_model.User, pcr *packages_model.PackageCleanupRule, redirectURL string, template base.TplName) { + isEditRule := pcr != nil + + if pcr == nil { + pcr = &packages_model.PackageCleanupRule{} + } + + form := web.GetForm(ctx).(*forms.PackageCleanupRuleForm) + + pcr.Enabled = form.Enabled + pcr.OwnerID = owner.ID + pcr.KeepCount = form.KeepCount + pcr.KeepPattern = form.KeepPattern + pcr.RemoveDays = form.RemoveDays + pcr.RemovePattern = form.RemovePattern + pcr.MatchFullName = form.MatchFullName + + ctx.Data["IsEditRule"] = isEditRule + ctx.Data["CleanupRule"] = pcr + ctx.Data["AvailableTypes"] = packages_model.TypeList + + if ctx.HasError() { + ctx.HTML(http.StatusOK, template) + return + } + + if isEditRule { + if err := packages_model.UpdateCleanupRule(ctx, pcr); err != nil { + ctx.ServerError("UpdateCleanupRule", err) + return + } + } else { + pcr.Type = packages_model.Type(form.Type) + + if has, err := packages_model.HasOwnerCleanupRuleForPackageType(ctx, owner.ID, pcr.Type); err != nil { + ctx.ServerError("HasOwnerCleanupRuleForPackageType", err) + return + } else if has { + ctx.Data["Err_Type"] = true + ctx.HTML(http.StatusOK, template) + return + } + + var err error + if pcr, err = packages_model.InsertCleanupRule(ctx, pcr); err != nil { + ctx.ServerError("InsertCleanupRule", err) + return + } + } + + ctx.Flash.Success(ctx.Tr("packages.owner.settings.cleanuprules.success.update")) + ctx.Redirect(fmt.Sprintf("%s/rules/%d", redirectURL, pcr.ID)) +} + +func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) { + pcr := getCleanupRuleByContext(ctx, owner) + if pcr == nil { + return + } + + if err := pcr.CompiledPattern(); err != nil { + ctx.ServerError("CompiledPattern", err) + return + } + + olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays) + + packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type) + if err != nil { + ctx.ServerError("GetPackagesByType", err) + return + } + + versionsToRemove := make([]*packages_model.PackageDescriptor, 0, 10) + + for _, p := range packages { + pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ + PackageID: p.ID, + IsInternal: optional.Some(false), + Sort: packages_model.SortCreatedDesc, + Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200), + }) + if err != nil { + ctx.ServerError("SearchVersions", err) + return + } + for _, pv := range pvs { + if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil { + ctx.ServerError("ShouldBeSkipped", err) + return + } else if skip { + continue + } + + toMatch := pv.LowerVersion + if pcr.MatchFullName { + toMatch = p.LowerName + "/" + pv.LowerVersion + } + + if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) { + continue + } + if pv.CreatedUnix.AsLocalTime().After(olderThan) { + continue + } + if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) { + continue + } + + pd, err := packages_model.GetPackageDescriptor(ctx, pv) + if err != nil { + ctx.ServerError("GetPackageDescriptor", err) + return + } + versionsToRemove = append(versionsToRemove, pd) + } + } + + ctx.Data["CleanupRule"] = pcr + ctx.Data["VersionsToRemove"] = versionsToRemove +} + +func getCleanupRuleByContext(ctx *context.Context, owner *user_model.User) *packages_model.PackageCleanupRule { + id := ctx.FormInt64("id") + if id == 0 { + id = ctx.ParamsInt64("id") + } + + pcr, err := packages_model.GetCleanupRuleByID(ctx, id) + if err != nil { + if err == packages_model.ErrPackageCleanupRuleNotExist { + ctx.NotFound("", err) + } else { + ctx.ServerError("GetCleanupRuleByID", err) + } + return nil + } + + if pcr != nil && pcr.OwnerID == owner.ID { + return pcr + } + + ctx.NotFound("", fmt.Errorf("PackageCleanupRule[%v] not associated to owner %v", id, owner)) + + return nil +} + +func InitializeCargoIndex(ctx *context.Context, owner *user_model.User) { + err := cargo_service.InitializeIndexRepository(ctx, owner, owner) + if err != nil { + log.Error("InitializeIndexRepository failed: %v", err) + ctx.Flash.Error(ctx.Tr("packages.owner.settings.cargo.initialize.error", err)) + } else { + ctx.Flash.Success(ctx.Tr("packages.owner.settings.cargo.initialize.success")) + } +} + +func RebuildCargoIndex(ctx *context.Context, owner *user_model.User) { + err := cargo_service.RebuildIndex(ctx, owner, owner) + if err != nil { + log.Error("RebuildIndex failed: %v", err) + if errors.Is(err, util.ErrNotExist) { + ctx.Flash.Error(ctx.Tr("packages.owner.settings.cargo.rebuild.no_index")) + } else { + ctx.Flash.Error(ctx.Tr("packages.owner.settings.cargo.rebuild.error", err)) + } + } else { + ctx.Flash.Success(ctx.Tr("packages.owner.settings.cargo.rebuild.success")) + } +} diff --git a/routers/web/shared/project/column.go b/routers/web/shared/project/column.go new file mode 100644 index 0000000..599842e --- /dev/null +++ b/routers/web/shared/project/column.go @@ -0,0 +1,48 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package project + +import ( + project_model "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/services/context" +) + +// MoveColumns moves or keeps columns in a project and sorts them inside that project +func MoveColumns(ctx *context.Context) { + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err) + return + } + if !project.CanBeAccessedByOwnerRepo(ctx.ContextUser.ID, ctx.Repo.Repository) { + ctx.NotFound("CanBeAccessedByOwnerRepo", nil) + return + } + + type movedColumnsForm struct { + Columns []struct { + ColumnID int64 `json:"columnID"` + Sorting int64 `json:"sorting"` + } `json:"columns"` + } + + form := &movedColumnsForm{} + if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil { + ctx.ServerError("DecodeMovedColumnsForm", err) + return + } + + sortedColumnIDs := make(map[int64]int64) + for _, column := range form.Columns { + sortedColumnIDs[column.Sorting] = column.ColumnID + } + + if err = project_model.MoveColumnsOnProject(ctx, project, sortedColumnIDs); err != nil { + ctx.ServerError("MoveColumnsOnProject", err) + return + } + + ctx.JSONOK() +} diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go new file mode 100644 index 0000000..3bd421f --- /dev/null +++ b/routers/web/shared/secrets/secrets.go @@ -0,0 +1,53 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package secrets + +import ( + "code.gitea.io/gitea/models/db" + secret_model "code.gitea.io/gitea/models/secret" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" + secret_service "code.gitea.io/gitea/services/secrets" +) + +func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { + secrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID}) + if err != nil { + ctx.ServerError("FindSecrets", err) + return + } + + ctx.Data["Secrets"] = secrets +} + +func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) { + form := web.GetForm(ctx).(*forms.AddSecretForm) + + s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data)) + if err != nil { + log.Error("CreateOrUpdateSecret failed: %v", err) + ctx.JSONError(ctx.Tr("secrets.creation.failed")) + return + } + + ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name)) + ctx.JSONRedirect(redirectURL) +} + +func PerformSecretsDelete(ctx *context.Context, ownerID, repoID int64, redirectURL string) { + id := ctx.FormInt64("id") + + err := secret_service.DeleteSecretByID(ctx, ownerID, repoID, id) + if err != nil { + log.Error("DeleteSecretByID(%d) failed: %v", id, err) + ctx.JSONError(ctx.Tr("secrets.deletion.failed")) + return + } + + ctx.Flash.Success(ctx.Tr("secrets.deletion.success")) + ctx.JSONRedirect(redirectURL) +} diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go new file mode 100644 index 0000000..fd7605c --- /dev/null +++ b/routers/web/shared/user/header.go @@ -0,0 +1,163 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user + +import ( + "net/url" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + packages_model "code.gitea.io/gitea/models/packages" + access_model "code.gitea.io/gitea/models/perm/access" + project_model "code.gitea.io/gitea/models/project" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/optional" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/context" +) + +// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu) +// It is designed to be fast and safe to be called multiple times in one request +func prepareContextForCommonProfile(ctx *context.Context) { + ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["EnableFeed"] = setting.Other.EnableFeed + ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink() +} + +// PrepareContextForProfileBigAvatar set the context for big avatar view on the profile page +func PrepareContextForProfileBigAvatar(ctx *context.Context) { + prepareContextForCommonProfile(ctx) + + ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID) + ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) + ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate + if setting.Service.UserLocationMapURL != "" { + ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) + } + // Show OpenID URIs + openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID) + if err != nil { + ctx.ServerError("GetUserOpenIDs", err) + return + } + ctx.Data["OpenIDs"] = openIDs + if len(ctx.ContextUser.Description) != 0 { + content, err := markdown.RenderString(&markup.RenderContext{ + Metas: map[string]string{"mode": "document"}, + Ctx: ctx, + }, ctx.ContextUser.Description) + if err != nil { + ctx.ServerError("RenderString", err) + return + } + ctx.Data["RenderedDescription"] = content + } + + showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) + orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ + UserID: ctx.ContextUser.ID, + IncludePrivate: showPrivate, + }) + if err != nil { + ctx.ServerError("FindOrgs", err) + return + } + ctx.Data["Orgs"] = orgs + ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(ctx, orgs, ctx.Doer) + + badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser) + if err != nil { + ctx.ServerError("GetUserBadges", err) + return + } + ctx.Data["Badges"] = badges + + // in case the numbers are already provided by other functions, no need to query again (which is slow) + if _, ok := ctx.Data["NumFollowers"]; !ok { + _, ctx.Data["NumFollowers"], _ = user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1}) + } + if _, ok := ctx.Data["NumFollowing"]; !ok { + _, ctx.Data["NumFollowing"], _ = user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1}) + } +} + +func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) { + profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ".profile") + if err == nil { + perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer) + if err == nil && !profileDbRepo.IsEmpty && perm.CanRead(unit.TypeCode) { + if profileGitRepo, err = gitrepo.OpenRepository(ctx, profileDbRepo); err != nil { + log.Error("FindUserProfileReadme failed to OpenRepository: %v", err) + } else { + if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil { + log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err) + } else { + profileReadmeBlob, _ = commit.GetBlobByFoldedPath("README.md") + } + } + } + } else if !repo_model.IsErrRepoNotExist(err) { + log.Error("FindUserProfileReadme failed to GetRepositoryByName: %v", err) + } + return profileDbRepo, profileGitRepo, profileReadmeBlob, func() { + if profileGitRepo != nil { + _ = profileGitRepo.Close() + } + } +} + +func RenderUserHeader(ctx *context.Context) { + prepareContextForCommonProfile(ctx) + + _, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer) + defer profileClose() + ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil +} + +func LoadHeaderCount(ctx *context.Context) error { + prepareContextForCommonProfile(ctx) + + var err error + + ctx.Data["RepoCount"], err = repo_model.CountRepository(ctx, &repo_model.SearchRepoOptions{ + Actor: ctx.Doer, + OwnerID: ctx.ContextUser.ID, + Private: ctx.IsSigned, + Collaborate: optional.Some(false), + IncludeDescription: setting.UI.SearchRepoDescription, + }) + if err != nil { + return err + } + + var projectType project_model.Type + if ctx.ContextUser.IsOrganization() { + projectType = project_model.TypeOrganization + } else { + projectType = project_model.TypeIndividual + } + ctx.Data["ProjectCount"], err = db.Count[project_model.Project](ctx, project_model.SearchOptions{ + OwnerID: ctx.ContextUser.ID, + IsClosed: optional.Some(false), + Type: projectType, + }) + if err != nil { + return err + } + ctx.Data["PackageCount"], err = packages_model.CountOwnerPackages(ctx, ctx.ContextUser.ID) + if err != nil { + return err + } + + return nil +} -- cgit v1.2.3