Some checks failed
Build and Release / Create Release (push) Successful in 0s
Build and Release / Unit Tests (push) Successful in 3m22s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 5m21s
Build and Release / Lint (push) Successful in 5m38s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 2m59s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 8h5m19s
Build and Release / Build Binaries (amd64, darwin, macos) (push) Successful in 7m10s
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been cancelled
Build and Release / Build Binary (linux/arm64) (push) Has been cancelled
Adds keyword search and tag filtering to repository blog list with GetRepoTopTags for popular tags display. Implements user-level package privacy setting (KeepPackagesPrivate) to hide packages from profile page. Updates blog UI with search box, tag cloud, and clear filters button. Adds subscription CTA buttons and active subscription indicators.
635 lines
19 KiB
Go
635 lines
19 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package user
|
|
|
|
import (
|
|
gocontext "context"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"code.gitcaddy.com/server/v3/models/db"
|
|
org_model "code.gitcaddy.com/server/v3/models/organization"
|
|
packages_model "code.gitcaddy.com/server/v3/models/packages"
|
|
container_model "code.gitcaddy.com/server/v3/models/packages/container"
|
|
"code.gitcaddy.com/server/v3/models/perm"
|
|
access_model "code.gitcaddy.com/server/v3/models/perm/access"
|
|
repo_model "code.gitcaddy.com/server/v3/models/repo"
|
|
"code.gitcaddy.com/server/v3/models/unit"
|
|
user_model "code.gitcaddy.com/server/v3/models/user"
|
|
"code.gitcaddy.com/server/v3/modules/container"
|
|
"code.gitcaddy.com/server/v3/modules/httplib"
|
|
"code.gitcaddy.com/server/v3/modules/log"
|
|
"code.gitcaddy.com/server/v3/modules/optional"
|
|
alpine_module "code.gitcaddy.com/server/v3/modules/packages/alpine"
|
|
arch_module "code.gitcaddy.com/server/v3/modules/packages/arch"
|
|
container_module "code.gitcaddy.com/server/v3/modules/packages/container"
|
|
debian_module "code.gitcaddy.com/server/v3/modules/packages/debian"
|
|
rpm_module "code.gitcaddy.com/server/v3/modules/packages/rpm"
|
|
"code.gitcaddy.com/server/v3/modules/setting"
|
|
"code.gitcaddy.com/server/v3/modules/templates"
|
|
"code.gitcaddy.com/server/v3/modules/util"
|
|
"code.gitcaddy.com/server/v3/modules/web"
|
|
packages_helper "code.gitcaddy.com/server/v3/routers/api/packages/helper"
|
|
shared_user "code.gitcaddy.com/server/v3/routers/web/shared/user"
|
|
"code.gitcaddy.com/server/v3/services/context"
|
|
"code.gitcaddy.com/server/v3/services/forms"
|
|
packages_service "code.gitcaddy.com/server/v3/services/packages"
|
|
container_service "code.gitcaddy.com/server/v3/services/packages/container"
|
|
)
|
|
|
|
const (
|
|
tplPackagesList templates.TplName = "user/overview/packages"
|
|
tplPackagesView templates.TplName = "package/view"
|
|
tplPackageVersionList templates.TplName = "user/overview/package_versions"
|
|
tplPackagesSettings templates.TplName = "package/settings"
|
|
)
|
|
|
|
// canViewPrivatePackages checks if the viewer can see private packages of the owner
|
|
func canViewPrivatePackages(ctx gocontext.Context, owner, viewer *user_model.User) bool {
|
|
// Not logged in - can't see private packages
|
|
if viewer == nil || viewer.IsGhost() {
|
|
return false
|
|
}
|
|
|
|
// Site admin can see all
|
|
if viewer.IsAdmin {
|
|
return true
|
|
}
|
|
|
|
// Owner can see their own private packages
|
|
if owner.ID == viewer.ID {
|
|
return true
|
|
}
|
|
|
|
// For organizations, check if viewer has read access to packages (is a member)
|
|
if owner.IsOrganization() {
|
|
org := org_model.OrgFromUser(owner)
|
|
teams, err := org_model.GetUserOrgTeams(ctx, org.ID, viewer.ID)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
for _, t := range teams {
|
|
if t.UnitAccessMode(ctx, unit.TypePackages) >= perm.AccessModeRead {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// ListPackages displays a list of all packages of the context user
|
|
func ListPackages(ctx *context.Context) {
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
|
return
|
|
}
|
|
|
|
// Check if packages are private for this user
|
|
if ctx.ContextUser.KeepPackagesPrivate {
|
|
isOwnerOrAdmin := ctx.Doer != nil && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
|
if !isOwnerOrAdmin {
|
|
ctx.Data["PackagesPrivate"] = true
|
|
ctx.HTML(200, tplPackagesList)
|
|
return
|
|
}
|
|
}
|
|
|
|
page := max(ctx.FormInt("page"), 1)
|
|
query := ctx.FormTrim("q")
|
|
packageType := ctx.FormTrim("type")
|
|
|
|
// Check if viewer can see private packages
|
|
canSeePrivate := canViewPrivatePackages(ctx, ctx.ContextUser, ctx.Doer)
|
|
|
|
searchOpts := &packages_model.PackageSearchOptions{
|
|
Paginator: &db.ListOptions{
|
|
PageSize: setting.UI.PackagesPagingNum,
|
|
Page: page,
|
|
},
|
|
OwnerID: ctx.ContextUser.ID,
|
|
Type: packages_model.Type(packageType),
|
|
Name: packages_model.SearchValue{Value: query},
|
|
IsInternal: optional.Some(false),
|
|
}
|
|
|
|
// If user can't see private packages, filter them out
|
|
if !canSeePrivate {
|
|
searchOpts.IsPrivate = optional.Some(false)
|
|
}
|
|
|
|
pvs, total, err := packages_model.SearchLatestVersions(ctx, searchOpts)
|
|
if err != nil {
|
|
ctx.ServerError("SearchLatestVersions", err)
|
|
return
|
|
}
|
|
|
|
pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
|
|
if err != nil {
|
|
ctx.ServerError("GetPackageDescriptors", err)
|
|
return
|
|
}
|
|
|
|
repositoryAccessMap := make(map[int64]bool)
|
|
for _, pd := range pds {
|
|
if pd.Repository == nil {
|
|
continue
|
|
}
|
|
if _, has := repositoryAccessMap[pd.Repository.ID]; has {
|
|
continue
|
|
}
|
|
|
|
permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer)
|
|
if err != nil {
|
|
ctx.ServerError("GetUserRepoPermission", err)
|
|
return
|
|
}
|
|
repositoryAccessMap[pd.Repository.ID] = permission.HasAnyUnitAccess()
|
|
}
|
|
|
|
hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID)
|
|
if err != nil {
|
|
ctx.ServerError("HasOwnerPackages", err)
|
|
return
|
|
}
|
|
|
|
ctx.Data["Title"] = ctx.Tr("packages.title")
|
|
ctx.Data["IsPackagesPage"] = true
|
|
ctx.Data["Query"] = query
|
|
ctx.Data["PackageType"] = packageType
|
|
ctx.Data["AvailableTypes"] = packages_model.TypeList
|
|
ctx.Data["HasPackages"] = hasPackages
|
|
ctx.Data["PackageDescriptors"] = pds
|
|
ctx.Data["Total"] = total
|
|
ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
|
|
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
|
return
|
|
}
|
|
|
|
// TODO: context/org -> HandleOrgAssignment() can not be used
|
|
if ctx.ContextUser.IsOrganization() {
|
|
org := org_model.OrgFromUser(ctx.ContextUser)
|
|
ctx.Data["Org"] = org
|
|
ctx.Data["OrgLink"] = ctx.ContextUser.OrganisationLink()
|
|
|
|
if ctx.Doer != nil {
|
|
ctx.Data["IsOrganizationMember"], _ = org_model.IsOrganizationMember(ctx, org.ID, ctx.Doer.ID)
|
|
ctx.Data["IsOrganizationOwner"], _ = org_model.IsOrganizationOwner(ctx, org.ID, ctx.Doer.ID)
|
|
} else {
|
|
ctx.Data["IsOrganizationMember"] = false
|
|
ctx.Data["IsOrganizationOwner"] = false
|
|
}
|
|
}
|
|
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
|
|
pager.AddParamFromRequest(ctx.Req)
|
|
ctx.Data["Page"] = pager
|
|
ctx.HTML(http.StatusOK, tplPackagesList)
|
|
}
|
|
|
|
// RedirectToLastVersion redirects to the latest package version
|
|
func RedirectToLastVersion(ctx *context.Context) {
|
|
p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.PathParam("type")), ctx.PathParam("name"))
|
|
if err != nil {
|
|
if err == packages_model.ErrPackageNotExist {
|
|
ctx.NotFound(err)
|
|
} else {
|
|
ctx.ServerError("GetPackageByName", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
|
|
PackageID: p.ID,
|
|
IsInternal: optional.Some(false),
|
|
})
|
|
if err != nil {
|
|
ctx.ServerError("GetPackageByName", err)
|
|
return
|
|
}
|
|
if len(pvs) == 0 {
|
|
ctx.NotFound(err)
|
|
return
|
|
}
|
|
|
|
pd, err := packages_model.GetPackageDescriptor(ctx, pvs[0])
|
|
if err != nil {
|
|
ctx.ServerError("GetPackageDescriptor", err)
|
|
return
|
|
}
|
|
ctx.Redirect(pd.VersionWebLink())
|
|
}
|
|
|
|
func viewPackageContainerImage(ctx gocontext.Context, pd *packages_model.PackageDescriptor, digest string) (*container_module.Metadata, error) {
|
|
manifestBlob, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
|
OwnerID: pd.Owner.ID,
|
|
Image: pd.Package.LowerName,
|
|
Digest: digest,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
manifestReader, err := packages_service.OpenBlobStream(manifestBlob.Blob)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer manifestReader.Close()
|
|
_, _, metadata, err := container_service.ParseManifestMetadata(ctx, manifestReader, pd.Owner.ID, pd.Package.LowerName)
|
|
return metadata, err
|
|
}
|
|
|
|
// ViewPackageVersion displays a single package version
|
|
func ViewPackageVersion(ctx *context.Context) {
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
|
return
|
|
}
|
|
|
|
versionSub := ctx.PathParam("version_sub")
|
|
pd := ctx.Package.Descriptor
|
|
ctx.Data["Title"] = pd.Package.Name
|
|
ctx.Data["IsPackagesPage"] = true
|
|
ctx.Data["PackageDescriptor"] = pd
|
|
|
|
registryHostURL, err := url.Parse(httplib.GuessCurrentHostURL(ctx))
|
|
if err != nil {
|
|
registryHostURL, _ = url.Parse(setting.AppURL)
|
|
}
|
|
ctx.Data["PackageRegistryHost"] = registryHostURL.Host
|
|
|
|
switch pd.Package.Type {
|
|
case packages_model.TypeAlpine:
|
|
branches := make(container.Set[string])
|
|
repositories := make(container.Set[string])
|
|
architectures := make(container.Set[string])
|
|
|
|
for _, f := range pd.Files {
|
|
for _, pp := range f.Properties {
|
|
switch pp.Name {
|
|
case alpine_module.PropertyBranch:
|
|
branches.Add(pp.Value)
|
|
case alpine_module.PropertyRepository:
|
|
repositories.Add(pp.Value)
|
|
case alpine_module.PropertyArchitecture:
|
|
architectures.Add(pp.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.Data["Branches"] = util.Sorted(branches.Values())
|
|
ctx.Data["Repositories"] = util.Sorted(repositories.Values())
|
|
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
|
|
case packages_model.TypeArch:
|
|
repositories := make(container.Set[string])
|
|
architectures := make(container.Set[string])
|
|
|
|
for _, f := range pd.Files {
|
|
for _, pp := range f.Properties {
|
|
switch pp.Name {
|
|
case arch_module.PropertyRepository:
|
|
repositories.Add(pp.Value)
|
|
case arch_module.PropertyArchitecture:
|
|
architectures.Add(pp.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.Data["Repositories"] = util.Sorted(repositories.Values())
|
|
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
|
|
case packages_model.TypeDebian:
|
|
distributions := make(container.Set[string])
|
|
components := make(container.Set[string])
|
|
architectures := make(container.Set[string])
|
|
|
|
for _, f := range pd.Files {
|
|
for _, pp := range f.Properties {
|
|
switch pp.Name {
|
|
case debian_module.PropertyDistribution:
|
|
distributions.Add(pp.Value)
|
|
case debian_module.PropertyComponent:
|
|
components.Add(pp.Value)
|
|
case debian_module.PropertyArchitecture:
|
|
architectures.Add(pp.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.Data["Distributions"] = util.Sorted(distributions.Values())
|
|
ctx.Data["Components"] = util.Sorted(components.Values())
|
|
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
|
|
case packages_model.TypeRpm:
|
|
groups := make(container.Set[string])
|
|
architectures := make(container.Set[string])
|
|
|
|
for _, f := range pd.Files {
|
|
for _, pp := range f.Properties {
|
|
switch pp.Name {
|
|
case rpm_module.PropertyGroup:
|
|
groups.Add(pp.Value)
|
|
case rpm_module.PropertyArchitecture:
|
|
architectures.Add(pp.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.Data["Groups"] = util.Sorted(groups.Values())
|
|
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
|
|
case packages_model.TypeContainer:
|
|
imageMetadata := pd.Metadata
|
|
if versionSub != "" {
|
|
imageMetadata, err = viewPackageContainerImage(ctx, pd, versionSub)
|
|
if errors.Is(err, util.ErrNotExist) {
|
|
ctx.NotFound(nil)
|
|
return
|
|
} else if err != nil {
|
|
ctx.ServerError("viewPackageContainerImage", err)
|
|
return
|
|
}
|
|
}
|
|
ctx.Data["ContainerImageMetadata"] = imageMetadata
|
|
}
|
|
var pvs []*packages_model.PackageVersion
|
|
var pvsTotal int64
|
|
if pd.Package.Type == packages_model.TypeContainer {
|
|
pvs, pvsTotal, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
|
|
Paginator: db.NewAbsoluteListOptions(0, 5),
|
|
PackageID: pd.Package.ID,
|
|
IsTagged: true,
|
|
})
|
|
} else {
|
|
pvs, pvsTotal, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
|
Paginator: db.NewAbsoluteListOptions(0, 5),
|
|
PackageID: pd.Package.ID,
|
|
IsInternal: optional.Some(false),
|
|
})
|
|
}
|
|
if err != nil {
|
|
ctx.ServerError("", err)
|
|
return
|
|
}
|
|
ctx.Data["LatestVersions"] = pvs
|
|
ctx.Data["TotalVersionCount"] = pvsTotal
|
|
|
|
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
|
|
|
|
hasRepositoryAccess := false
|
|
if pd.Repository != nil {
|
|
permission, err := access_model.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer)
|
|
if err != nil {
|
|
ctx.ServerError("GetUserRepoPermission", err)
|
|
return
|
|
}
|
|
hasRepositoryAccess = permission.HasAnyUnitAccess()
|
|
}
|
|
ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess
|
|
ctx.HTML(http.StatusOK, tplPackagesView)
|
|
}
|
|
|
|
// ListPackageVersions lists all versions of a package
|
|
func ListPackageVersions(ctx *context.Context) {
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
|
return
|
|
}
|
|
|
|
p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.PathParam("type")), ctx.PathParam("name"))
|
|
if err != nil {
|
|
if err == packages_model.ErrPackageNotExist {
|
|
ctx.NotFound(err)
|
|
} else {
|
|
ctx.ServerError("GetPackageByName", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
page := max(ctx.FormInt("page"), 1)
|
|
pagination := &db.ListOptions{
|
|
PageSize: setting.UI.PackagesPagingNum,
|
|
Page: page,
|
|
}
|
|
|
|
query := ctx.FormTrim("q")
|
|
sort := ctx.FormTrim("sort")
|
|
|
|
ctx.Data["Title"] = ctx.Tr("packages.title")
|
|
ctx.Data["IsPackagesPage"] = true
|
|
ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
|
|
Package: p,
|
|
Owner: ctx.Package.Owner,
|
|
}
|
|
ctx.Data["Query"] = query
|
|
ctx.Data["Sort"] = sort
|
|
|
|
var (
|
|
total int64
|
|
pvs []*packages_model.PackageVersion
|
|
)
|
|
switch p.Type {
|
|
case packages_model.TypeContainer:
|
|
tagged := ctx.FormTrim("tagged")
|
|
|
|
ctx.Data["Tagged"] = tagged
|
|
|
|
pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
|
|
Paginator: pagination,
|
|
PackageID: p.ID,
|
|
Query: query,
|
|
IsTagged: tagged == "" || tagged == "tagged",
|
|
Sort: sort,
|
|
})
|
|
if err != nil {
|
|
ctx.ServerError("SearchImageTags", err)
|
|
return
|
|
}
|
|
default:
|
|
pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
|
Paginator: pagination,
|
|
PackageID: p.ID,
|
|
Version: packages_model.SearchValue{
|
|
ExactMatch: false,
|
|
Value: query,
|
|
},
|
|
IsInternal: optional.Some(false),
|
|
Sort: sort,
|
|
})
|
|
if err != nil {
|
|
ctx.ServerError("SearchVersions", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx.Data["PackageDescriptors"], err = packages_model.GetPackageDescriptors(ctx, pvs)
|
|
if err != nil {
|
|
ctx.ServerError("GetPackageDescriptors", err)
|
|
return
|
|
}
|
|
|
|
ctx.Data["Total"] = total
|
|
|
|
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
|
|
pager.AddParamFromRequest(ctx.Req)
|
|
ctx.Data["Page"] = pager
|
|
|
|
ctx.HTML(http.StatusOK, tplPackageVersionList)
|
|
}
|
|
|
|
// PackageSettings displays the package settings page
|
|
func PackageSettings(ctx *context.Context) {
|
|
pd := ctx.Package.Descriptor
|
|
|
|
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
|
|
ctx.ServerError("RenderUserOrgHeader", err)
|
|
return
|
|
}
|
|
|
|
ctx.Data["Title"] = pd.Package.Name
|
|
ctx.Data["IsPackagesPage"] = true
|
|
ctx.Data["PackageDescriptor"] = pd
|
|
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
|
|
|
|
if pd.Package.RepoID > 0 {
|
|
repo, err := repo_model.GetRepositoryByID(ctx, pd.Package.RepoID)
|
|
if err != nil {
|
|
ctx.ServerError("GetRepositoryByID", err)
|
|
return
|
|
}
|
|
ctx.Data["LinkedRepoName"] = repo.Name
|
|
}
|
|
|
|
ctx.HTML(http.StatusOK, tplPackagesSettings)
|
|
}
|
|
|
|
// PackageSettingsPost updates the package settings
|
|
func PackageSettingsPost(ctx *context.Context) {
|
|
form := web.GetForm(ctx).(*forms.PackageSettingForm)
|
|
switch form.Action {
|
|
case "link":
|
|
packageSettingsPostActionLink(ctx, form)
|
|
case "delete":
|
|
packageSettingsPostActionDelete(ctx)
|
|
case "global":
|
|
packageSettingsPostActionGlobal(ctx)
|
|
case "visibility":
|
|
packageSettingsPostActionVisibility(ctx)
|
|
default:
|
|
ctx.NotFound(nil)
|
|
}
|
|
}
|
|
|
|
func packageSettingsPostActionLink(ctx *context.Context, form *forms.PackageSettingForm) {
|
|
pd := ctx.Package.Descriptor
|
|
if form.RepoName == "" { // remove the link
|
|
if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, 0); err != nil {
|
|
ctx.JSONError(ctx.Tr("packages.settings.unlink.error"))
|
|
return
|
|
}
|
|
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.unlink.success"))
|
|
ctx.JSONRedirect("")
|
|
return
|
|
}
|
|
|
|
repo, err := repo_model.GetRepositoryByName(ctx, pd.Owner.ID, form.RepoName)
|
|
if err != nil {
|
|
if repo_model.IsErrRepoNotExist(err) {
|
|
ctx.JSONError(ctx.Tr("packages.settings.link.repo_not_found", form.RepoName))
|
|
} else {
|
|
ctx.ServerError("GetRepositoryByOwnerAndName", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repo.ID); err != nil {
|
|
ctx.JSONError(ctx.Tr("packages.settings.link.error"))
|
|
return
|
|
}
|
|
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.link.success"))
|
|
ctx.JSONRedirect("")
|
|
}
|
|
|
|
func packageSettingsPostActionDelete(ctx *context.Context) {
|
|
err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version)
|
|
if err != nil {
|
|
log.Error("Error deleting package: %v", err)
|
|
ctx.Flash.Error(ctx.Tr("packages.settings.delete.error"))
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
|
}
|
|
|
|
redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages"
|
|
// redirect to the package if there are still versions available
|
|
if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has {
|
|
redirectURL = ctx.Package.Descriptor.PackageWebLink()
|
|
}
|
|
|
|
ctx.Redirect(redirectURL)
|
|
}
|
|
|
|
func packageSettingsPostActionGlobal(ctx *context.Context) {
|
|
// Only admins can set global flag
|
|
if !ctx.IsUserSiteAdmin() {
|
|
ctx.NotFound(nil)
|
|
return
|
|
}
|
|
|
|
pd := ctx.Package.Descriptor
|
|
isGlobal := ctx.FormBool("is_global")
|
|
|
|
if err := packages_model.SetPackageIsGlobal(ctx, pd.Package.ID, isGlobal); err != nil {
|
|
ctx.Flash.Error(ctx.Tr("packages.settings.global_access.error"))
|
|
ctx.Redirect(ctx.Package.Descriptor.VersionWebLink() + "/settings")
|
|
return
|
|
}
|
|
|
|
if isGlobal {
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.global_access.enabled"))
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.global_access.disabled"))
|
|
}
|
|
ctx.Redirect(ctx.Package.Descriptor.VersionWebLink() + "/settings")
|
|
}
|
|
|
|
func packageSettingsPostActionVisibility(ctx *context.Context) {
|
|
pd := ctx.Package.Descriptor
|
|
// Toggle the visibility
|
|
newIsPrivate := !pd.Package.IsPrivate
|
|
|
|
if err := packages_model.SetPackageIsPrivate(ctx, pd.Package.ID, newIsPrivate); err != nil {
|
|
ctx.Flash.Error(ctx.Tr("packages.settings.visibility.error"))
|
|
ctx.Redirect(ctx.Package.Descriptor.VersionWebLink() + "/settings")
|
|
return
|
|
}
|
|
|
|
if newIsPrivate {
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.visibility.private.success"))
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("packages.settings.visibility.public.success"))
|
|
}
|
|
ctx.Redirect(ctx.Package.Descriptor.VersionWebLink() + "/settings")
|
|
}
|
|
|
|
// DownloadPackageFile serves the content of a package file
|
|
func DownloadPackageFile(ctx *context.Context) {
|
|
pf, err := packages_model.GetFileForVersionByID(ctx, ctx.Package.Descriptor.Version.ID, ctx.PathParamInt64("fileid"))
|
|
if err != nil {
|
|
if err == packages_model.ErrPackageFileNotExist {
|
|
ctx.NotFound(err)
|
|
} else {
|
|
ctx.ServerError("GetFileForVersionByID", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method)
|
|
if err != nil {
|
|
ctx.ServerError("OpenFileForDownload", err)
|
|
return
|
|
}
|
|
|
|
packages_helper.ServePackageFile(ctx, s, u, pf)
|
|
}
|