aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorArthur Zamarin <arthurzam@gentoo.org>2024-02-21 14:02:11 +0200
committerArthur Zamarin <arthurzam@gentoo.org>2024-02-21 14:02:20 +0200
commit991cf1ce67e6410e9c049022db1fc37e9dc5c412 (patch)
tree1b0de9110e4d48b8a84bd98c1ebfa955e275c35e /pkg
parentmigrate single package page (diff)
downloadsoko-991cf1ce67e6410e9c049022db1fc37e9dc5c412.tar.gz
soko-991cf1ce67e6410e9c049022db1fc37e9dc5c412.tar.bz2
soko-991cf1ce67e6410e9c049022db1fc37e9dc5c412.zip
migrate graphiql & user preferences pages
Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/graphql/graphiql/graphiql.go17
-rw-r--r--pkg/api/graphql/graphiql/graphiql.templ33
-rw-r--r--pkg/app/handler/packages/qareport.templ24
-rw-r--r--pkg/app/handler/packages/show.templ33
-rw-r--r--pkg/app/handler/user/arches.templ153
-rw-r--r--pkg/app/handler/user/general.templ93
-rw-r--r--pkg/app/handler/user/maintainers.templ95
-rw-r--r--pkg/app/handler/user/packages.templ407
-rw-r--r--pkg/app/handler/user/preferences.go245
-rw-r--r--pkg/app/handler/user/preferences.templ94
-rw-r--r--pkg/app/handler/user/useflags.templ93
-rw-r--r--pkg/app/handler/user/utils.go71
-rw-r--r--pkg/app/serve.go10
-rw-r--r--pkg/models/userpreferences.go58
14 files changed, 1004 insertions, 422 deletions
diff --git a/pkg/api/graphql/graphiql/graphiql.go b/pkg/api/graphql/graphiql/graphiql.go
deleted file mode 100644
index afc58df..0000000
--- a/pkg/api/graphql/graphiql/graphiql.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package graphiql
-
-import (
- "html/template"
- "net/http"
- "soko/pkg/config"
-)
-
-func Show(w http.ResponseWriter, r *http.Request) {
- w.Header().Add("Content-Type", "text/html")
-
- templates := template.Must(
- template.New("graphiql").
- ParseGlob("web/templates/api/explore/*.tmpl"))
-
- templates.ExecuteTemplate(w, "graphiql.tmpl", template.URL(config.GraphiqlEndpoint()))
-}
diff --git a/pkg/api/graphql/graphiql/graphiql.templ b/pkg/api/graphql/graphiql/graphiql.templ
new file mode 100644
index 0000000..0b3399d
--- /dev/null
+++ b/pkg/api/graphql/graphiql/graphiql.templ
@@ -0,0 +1,33 @@
+package graphiql
+
+import "net/http"
+import "soko/pkg/config"
+
+templ show() {
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <title>GraphiQL - Gentoo Packages</title>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+ <meta name="theme-color" content="#54487a"/>
+ <meta name="description" content="Gentoo Packages GraphiQL GraphQL API Explorer"/>
+ <link rel="icon" href="https://packages.gentoo.org/favicon.ico" type="image/x-icon"/>
+ <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
+ <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
+ <link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css"/>
+ </head>
+ <body>
+ <div id="graphiql">
+ Loading...
+ </div>
+ <script src="https://unpkg.com/graphiql@0.17.5/graphiql.min.js" type="application/javascript"></script>
+ @templ.Raw(`<script>window.graphqlEndpoint = '` + config.GraphiqlEndpoint() + `';</script>`)
+ <script src="/assets/graphiql.js" type="application/javascript"></script>
+ </body>
+ </html>
+}
+
+func Show(w http.ResponseWriter, r *http.Request) {
+ show().Render(r.Context(), w)
+}
diff --git a/pkg/app/handler/packages/qareport.templ b/pkg/app/handler/packages/qareport.templ
index 4b70458..29603a1 100644
--- a/pkg/app/handler/packages/qareport.templ
+++ b/pkg/app/handler/packages/qareport.templ
@@ -11,13 +11,11 @@ templ qaReport(pkg *models.Package, userPreferences *models.UserPreferences) {
<span class="text-muted">All Versions</span>
<ul class="list-group">
for _, res := range pkg.PkgCheckResults {
- if res.Version == "" && userPreferences.ContainsPkgcheckClass(res.Class) {
- <li class="list-group-item">
- <strong>{ res.Class }</strong>
- <br/>
- <span class="kk-version kk-cell-sep-right text-muted">{ res.Message }</span>
- </li>
- }
+ <li class="list-group-item">
+ <strong>{ res.Class }</strong>
+ <br/>
+ <span class="kk-version kk-cell-sep-right text-muted">{ res.Message }</span>
+ </li>
}
</ul>
</li>
@@ -26,13 +24,11 @@ templ qaReport(pkg *models.Package, userPreferences *models.UserPreferences) {
<span class="text-muted">{ ver.Version }</span>
<ul class="list-group">
for _, res := range ver.PkgCheckResults {
- if userPreferences.ContainsPkgcheckClass(res.Class) {
- <li class="list-group-item">
- <strong>{ res.Class }</strong>
- <br/>
- <span class="kk-version kk-cell-sep-right text-muted">{ res.Message }</span>
- </li>
- }
+ <li class="list-group-item">
+ <strong>{ res.Class }</strong>
+ <br/>
+ <span class="kk-version kk-cell-sep-right text-muted">{ res.Message }</span>
+ </li>
}
</ul>
</li>
diff --git a/pkg/app/handler/packages/show.templ b/pkg/app/handler/packages/show.templ
index 8d71781..3ef4341 100644
--- a/pkg/app/handler/packages/show.templ
+++ b/pkg/app/handler/packages/show.templ
@@ -1,13 +1,12 @@
package packages
-import "slices"
import "strconv"
import "soko/pkg/app/handler/packages/components"
import "soko/pkg/app/layout"
import "soko/pkg/models"
func showViewTabs(pkg *models.Package, userPreference *models.PackagesPreferences) []layout.SubTab {
- securityBugs, nonSecurityBugs := countBugs(pkg)
+ securityBugs, nonSecurityBugs := countBugs(pkg)
return []layout.SubTab{
{
Name: "Overview",
@@ -78,14 +77,12 @@ templ tabbedHeader(pkg *models.Package, currentSubTab string, userPreference *mo
<div class="col-md-12 pt-4 mt-1">
<nav class="nav kk-package-nav">
for _, tab := range showViewTabs(pkg, userPreference) {
- if slices.Contains(userPreference.Tabs.Visible, tab.Name) {
- <a class={ "nav-link", templ.KV("active", tab.Name == currentSubTab) } href={ tab.Link }>
- <i class={ tab.Icon } aria-hidden="true"></i> { tab.Name }
- if tab.BadgeValue != "" {
- <span class="ml-1 badge badge-pill kk-misc-badge">{ tab.BadgeValue }</span>
- }
- </a>
- }
+ <a class={ "nav-link", templ.KV("active", tab.Name == currentSubTab) } href={ tab.Link }>
+ <i class={ tab.Icon } aria-hidden="true"></i> { tab.Name }
+ if tab.BadgeValue != "" {
+ <span class="ml-1 badge badge-pill kk-misc-badge">{ tab.BadgeValue }</span>
+ }
+ </a>
}
</nav>
</div>
@@ -98,19 +95,19 @@ templ tabbedHeader(pkg *models.Package, currentSubTab string, userPreference *mo
func collectAllBugs(pkg *models.Package) (generalCount, stabilizationCount, keywordingCount int, bugs []*models.Bug) {
bugs = make([]*models.Bug, 0, len(pkg.Bugs))
- handled := make(map[string]struct{}, len(pkg.Bugs))
+ handled := make(map[string]struct{}, len(pkg.Bugs))
for _, bug := range pkg.Bugs {
if bug.Component == "Current packages" {
generalCount++
bugs = append(bugs, bug)
- handled[bug.Id] = struct{}{}
+ handled[bug.Id] = struct{}{}
}
}
for _, ver := range pkg.Versions {
for _, bug := range ver.Bugs {
- if _, found := handled[bug.Id]; found {
- continue
- }
+ if _, found := handled[bug.Id]; found {
+ continue
+ }
if bug.Component == "Stabilization" {
stabilizationCount++
bugs = append(bugs, bug)
@@ -118,7 +115,7 @@ func collectAllBugs(pkg *models.Package) (generalCount, stabilizationCount, keyw
keywordingCount++
bugs = append(bugs, bug)
}
- handled[bug.Id] = struct{}{}
+ handled[bug.Id] = struct{}{}
}
}
return
@@ -143,8 +140,8 @@ templ show(pkg *models.Package, currentSubTab string, userPreferences models.Use
<div class="tab-content" id="myTabContent">
<div class="container mb-5 tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
switch currentSubTab {
- case "QA report":
- @qaReport(pkg, &userPreferences)
+ case "QA report":
+ @qaReport(pkg, &userPreferences)
case "Pull requests":
@components.PullRequests(len(pkg.PullRequests) > 0, pkg.PullRequests)
case "Bugs":
diff --git a/pkg/app/handler/user/arches.templ b/pkg/app/handler/user/arches.templ
new file mode 100644
index 0000000..7f9fa50
--- /dev/null
+++ b/pkg/app/handler/user/arches.templ
@@ -0,0 +1,153 @@
+package user
+
+import "encoding/base64"
+import "encoding/json"
+import "net/http"
+import "slices"
+import "time"
+import "soko/pkg/app/utils"
+import "soko/pkg/models"
+
+func splitArches(selected []string) (firstColumn, secondColumn []string) {
+ allArches := models.GetAllKeywords()
+ remainingFirstColumn := len(allArches) - (len(allArches) / 2) - len(selected)
+ for _, arch := range allArches {
+ if !slices.Contains(selected, arch) {
+ if len(firstColumn) < remainingFirstColumn {
+ firstColumn = append(firstColumn, arch)
+ } else {
+ secondColumn = append(secondColumn, arch)
+ }
+ }
+ }
+ return
+}
+
+templ archesTwoColumns(selected []string) {
+ if firstColumn, secondColumn := splitArches(selected); true {
+ <ul id="example1" class="list-group col-6">
+ for _, arch := range selected {
+ <li class="list-group-item">
+ <div class="form-check form-check-inline w-100">
+ <input type="checkbox" id={ "visible-arches-" + arch } name="visible-arches" value={ arch } checked/>
+ <label class="form-check-label ml-1" for={ "visible-arches-" + arch }>{ arch }</label> <i class="fa fa-arrows ml-auto" aria-hidden="true"></i>
+ </div>
+ </li>
+ }
+ for _, arch := range firstColumn {
+ <li class="list-group-item">
+ <div class="form-check form-check-inline w-100">
+ <input type="checkbox" id={ "visible-arches-" + arch } name="visible-arches" value={ arch }/>
+ <label class="form-check-label ml-1" for={ "visible-arches-" + arch }>{ arch }</label> <i class="fa fa-arrows ml-auto" aria-hidden="true"></i>
+ </div>
+ </li>
+ }
+ </ul>
+ <ul id="example2" class="list-group col-6">
+ for _, arch := range secondColumn {
+ <li class="list-group-item">
+ <div class="form-check form-check-inline w-100">
+ <input type="checkbox" id={ "visible-arches-" + arch } name="visible-arches" value={ arch }/>
+ <label class="form-check-label ml-1" for={ "visible-arches-" + arch }>{ arch }</label> <i class="fa fa-arrows ml-auto" aria-hidden="true"></i>
+ </div>
+ </li>
+ }
+ </ul>
+ }
+}
+
+templ arches(preferences models.ArchesPreferences) {
+ <div class="row">
+ <div class="col-2 mt-1">
+ <div class="nav flex-column" role="tablist" aria-orientation="vertical" style="position: fixed;">
+ <a class="nav-link user-pref-nav-link active" id="keywords-tab" href="#keywords" aria-controls="overview-settings">Keywords</a>
+ <a class="nav-link user-pref-nav-link" id="defaults-tab" href="#defaults">Defaults</a>
+ </div>
+ </div>
+ <div class="col-10 mt-1">
+ <form method="post" action="/user/preferences/arches/visible">
+ <h3 class="" id="keywords">Keywords</h3>
+ <hr class="mt-1"/>
+ <div class="row pl-3">
+ @archesTwoColumns(preferences.Visible)
+ </div>
+ <h3 class="mt-5" id="defaults">Defaults</h3>
+ <hr class="mt-1"/>
+ <div class="card">
+ <div class="card-body">
+ <div class="row">
+ <div class="col-6">
+ Default arch
+ <select class="form-control" style="max-width: 200px;display: inline;" name="arches-default-arch" id="arches-default-arch">
+ for _, arch := range models.GetAllKeywords() {
+ <option
+ value={ arch }
+ if preferences.DefaultArch == arch {
+ selected
+ }
+ >{ arch }</option>
+ }
+ </select>
+ </div>
+ <div class="col-6">
+ Default page
+ <select class="form-control" style="max-width: 150px;display: inline;" name="arches-default-page" id="arches-default-page">
+ <option
+ value="keyworded"
+ if preferences.DefaultPage == "keyworded" {
+ selected
+ }
+ >keyworded</option>
+ <option
+ value="stable"
+ if preferences.DefaultPage == "stable" {
+ selected
+ }
+ >newly stable</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-12 mt-4">
+ <button type="submit" class="float-right btn btn-sm btn-primary">Save</button>
+ <a class="float-right btn btn-sm btn-outline-danger mr-2" href="/user/preferences/arches/reset">Reset to Defaults</a>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ @sortableScript()
+}
+
+func Arches(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetUserPreferences(r)
+ r.ParseForm()
+ // visible arches
+ visibleArches := r.Form["visible-arches"]
+ userPreferences.Arches.Visible = visibleArches
+ // default arch
+ defaultArch := r.Form.Get("arches-default-arch")
+ userPreferences.Arches.DefaultArch = defaultArch
+ // default arches page
+ defaultPage := r.Form.Get("arches-default-page")
+ userPreferences.Arches.DefaultPage = defaultPage
+ // store cookie
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Arches)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_arches", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/arches", http.StatusSeeOther)
+}
+
+func ResetArches(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetDefaultUserPreferences()
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Arches)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_arches", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/arches", http.StatusSeeOther)
+}
diff --git a/pkg/app/handler/user/general.templ b/pkg/app/handler/user/general.templ
new file mode 100644
index 0000000..799354b
--- /dev/null
+++ b/pkg/app/handler/user/general.templ
@@ -0,0 +1,93 @@
+package user
+
+import "encoding/base64"
+import "encoding/json"
+import "net/http"
+import "time"
+import "soko/pkg/app/utils"
+import "soko/pkg/models"
+
+templ general(preferences models.GeneralPreferences) {
+ <form method="post" action="/user/preferences/general/layout">
+ <div class="row">
+ <div class="col-5 offset-1 mt-1">
+ <div class="card" style="background: transparent;">
+ <div class="card-body">
+ <img id="img1" alt="Recently Added Packages (default)" src="/assets/pgo3.png" style="width: 100%;cursor: pointer;"/>
+ </div>
+ </div>
+ <div class="text-center mt-2">
+ <div class="form-check text-center form-check-inline" style="text-overflow: ellipsis;overflow: hidden;">
+ <input
+ type="radio"
+ id="classicLandingpageLayout"
+ name="landingpage-layout"
+ value="classic"
+ if preferences.LandingPageLayout == "classic" {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="classicLandingpageLayout" style="overflow:hidden;text-overflow: ellipsis;" title="Recently Added Packages (default)">Recently Added Packages <i>(default)</i></label>
+ </div>
+ </div>
+ </div>
+ <div class="col-5 mt-1">
+ <div class="card" style="background: transparent;">
+ <div class="card-body">
+ <img id="img2" alt="Recently Visited Packages" src="/assets/pgo4.png" style="width: 100%;cursor: pointer;"/>
+ </div>
+ </div>
+ <div class="text-center mt-2">
+ <div class="form-check text-center form-check-inline" style="text-overflow: ellipsis;overflow: hidden;">
+ <input
+ type="radio"
+ id="fullLandingpageLayout"
+ name="landingpage-layout"
+ value="full"
+ if preferences.LandingPageLayout == "full" {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="fullLandingpageLayout" style="overflow:hidden;text-overflow: ellipsis;" title="Recently Visited Packages">Recently Visited Packages</label>
+ </div>
+ </div>
+ </div>
+ <div class="col-10 offset-1 mt-4">
+ <button type="submit" class="float-right btn btn-sm btn-primary">Save</button>
+ <a class="float-right btn btn-sm btn-outline-danger mr-2" href="/user/preferences/general/reset">Reset to Defaults</a>
+ </div>
+ </div>
+ </form>
+ <div id="myModal" class="modal">
+ <span class="close">&times;</span>
+ <img class="modal-content" id="img01"/>
+ <div id="caption"></div>
+ </div>
+}
+
+func General(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetUserPreferences(r)
+ r.ParseForm()
+ // landing page layout
+ layout := r.Form.Get("landingpage-layout")
+ if layout == "classic" || layout == "full" {
+ userPreferences.General.LandingPageLayout = layout
+ }
+ // store cookie
+ encodedUserPreferences, err := json.Marshal(&userPreferences.General)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_general", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/general", http.StatusSeeOther)
+}
+
+func ResetGeneral(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetDefaultUserPreferences()
+ encodedUserPreferences, err := json.Marshal(&userPreferences.General)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_general", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/general", http.StatusSeeOther)
+}
diff --git a/pkg/app/handler/user/maintainers.templ b/pkg/app/handler/user/maintainers.templ
new file mode 100644
index 0000000..263cb67
--- /dev/null
+++ b/pkg/app/handler/user/maintainers.templ
@@ -0,0 +1,95 @@
+package user
+
+import "encoding/base64"
+import "encoding/json"
+import "net/http"
+import "time"
+import "soko/pkg/app/utils"
+import "soko/pkg/models"
+import "slices"
+import "soko/pkg/database"
+
+func splitAllProjects() [][]models.Project {
+ var projects []models.Project
+ database.DBCon.Model(&projects).Column("name", "email").Select()
+ split := (3 + len(projects)) / 4
+ return [][]models.Project{projects[:split], projects[split : 2*split], projects[2*split : 3*split], projects[3*split:]}
+}
+
+templ maintainers(preferences models.MaintainersPreferences) {
+ <form method="post" action="/user/preferences/maintainers/edit">
+ <div class="row">
+ <div class="col-12">
+ <h3 class="mt-0" id="qa-report">Include Project Packages</h3>
+ <div class="form-check form-check-inline" style="text-overflow: ellipsis;overflow: hidden; width: 100%;">
+ <input
+ type="checkbox"
+ id="include-packages"
+ name="include-packages"
+ value="true"
+ if preferences.IncludeProjectPackages {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="include-packages" style="overflow:hidden;text-overflow: ellipsis;" title="">Include packages of projects the maintainer is part of</label>
+ </div>
+ <i>If this option is enabled, all packages, QA reports, pull requests and bugs of projects a maintainer is part of will be displayed as well on the maintainer page. That is, if <i>Larry</i> is part of the <i>Python</i> project, all packages, QA reports, pull requests and bugs of the Python project will be displayed as well on the maintainer page of <i>Larry</i>.<br/>Below you can furthermore specify projects that will be excluded on the maintainer page. E.g. if Larry is furthermore part of the proxy-maintainers project, and the project is marked below, packages of the proxy maintainers project won't be shown on Larry's maintainer page.</i>
+ </div>
+ <div class="col-12">
+ <h3 class="mt-4 pt-3" id="qa-report">Excluded Projects</h3>
+ </div>
+ for _, projects := range splitAllProjects() {
+ <div class="col-3">
+ for _, project := range projects {
+ <li class="list-group-item">
+ <div class="form-check form-check-inline" style="text-overflow: ellipsis;overflow: hidden; width: 100%;height:21px;">
+ <input
+ type="checkbox"
+ id={ "excluded-projects-" + project.Email }
+ name="excluded-projects"
+ value={ project.Email }
+ if slices.Contains(preferences.ExcludedProjects, project.Email) {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for={ "excluded-projects-" + project.Email } style="overflow:hidden;text-overflow: ellipsis;height:21px;" title={ project.Name }>{ project.Name }</label>
+ </div>
+ </li>
+ }
+ </div>
+ }
+ <div class="col-12 mt-4">
+ <button type="submit" class="float-right btn btn-sm btn-primary">Save</button>
+ <a class="float-right btn btn-sm btn-outline-danger mr-2" href="/user/preferences/maintainers/reset">Reset to Defaults</a>
+ </div>
+ </div>
+ </form>
+}
+
+func Maintainers(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetUserPreferences(r)
+ r.ParseForm()
+ // excluded projects
+ excludedProjects := r.Form["excluded-projects"]
+ userPreferences.Maintainers.ExcludedProjects = excludedProjects
+ // include projects?
+ includePackages := r.Form.Get("include-packages")
+ userPreferences.Maintainers.IncludeProjectPackages = includePackages == "true"
+ // store cookie
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Maintainers)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_maintainers", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/maintainers", http.StatusSeeOther)
+}
+
+func ResetMaintainers(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetDefaultUserPreferences()
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Maintainers)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_maintainers", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/maintainers", http.StatusSeeOther)
+}
diff --git a/pkg/app/handler/user/packages.templ b/pkg/app/handler/user/packages.templ
new file mode 100644
index 0000000..dd01068
--- /dev/null
+++ b/pkg/app/handler/user/packages.templ
@@ -0,0 +1,407 @@
+package user
+
+import "encoding/base64"
+import "encoding/json"
+import "net/http"
+import "slices"
+import "strconv"
+import "time"
+import "soko/pkg/app/utils"
+import "soko/pkg/models"
+
+templ packages(preferences models.PackagesPreferences) {
+ <div class="row">
+ <div class="col-2 mt-1">
+ <div class="nav flex-column" role="tablist" aria-orientation="vertical" style="position: fixed;">
+ <a class="nav-link user-pref-nav-link active" id="overview-tab" href="#overview" aria-controls="overview-settings">Overview</a>
+ <a class="nav-link user-pref-nav-link" id="dependencies-tab" href="#dependencies">Dependencies</a>
+ <a class="nav-link user-pref-nav-link" id="pull-requests-tab" href="#pull-requests" aria-controls="pull-requests-settings">Pull requests</a>
+ <a class="nav-link user-pref-nav-link" id="bugs-tab" href="#bugs" aria-controls="bugs-settings">Bugs</a>
+ <a class="nav-link user-pref-nav-link" id="security-tab" href="#security" aria-controls="security-settings">Security</a>
+ <a class="nav-link user-pref-nav-link" id="changelog-tab" href="#changelog" aria-controls="changelog-settings">Changelog</a>
+ <a class="nav-link user-pref-nav-link" id="tabs-tab" href="#tabs" aria-controls="tabs-settings">Tabs</a>
+ </div>
+ </div>
+ <div class="col-9 mt-1">
+ <form method="post" action="/user/preferences/packages/edit">
+ <h3 id="overview">Overview</h3>
+ <hr class="mt-1"/>
+ <h4 class="mb-1">Layout</h4>
+ <div class="row">
+ <div class="col-6 mt-1">
+ <div class="card" style="background: transparent;">
+ <div class="card-body">
+ <img id="img1" alt="Versions + Metadata (default)" src="/assets/pgo2.png" style="width: 100%;cursor: pointer;"/>
+ </div>
+ </div>
+ <div class="text-center mt-2">
+ <div class="form-check text-center form-check-inline" style="text-overflow: ellipsis;overflow: hidden;">
+ <input
+ type="radio"
+ id="minimalOverviewLayout"
+ name="overview-layout"
+ value="minimal"
+ if preferences.Overview.Layout == "minimal" {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="minimalOverviewLayout" style="overflow:hidden;text-overflow: ellipsis;" title="Versions + Metadata (default)">Versions + Metadata <i>(default)</i></label>
+ </div>
+ </div>
+ </div>
+ <div class="col-6 mt-1">
+ <div class="card" style="background: transparent;">
+ <div class="card-body">
+ <img id="img2" alt="Versions + Metadata + Changelog" src="/assets/pgo1.png" onclick="document.getElementById('fullOverviewLayout').checked = true;" style="width: 100%;cursor: pointer;"/>
+ </div>
+ </div>
+ <div class="text-center mt-2">
+ <div class="form-check text-center form-check-inline" style="text-overflow: ellipsis;overflow: hidden;">
+ <input
+ type="radio"
+ id="fullOverviewLayout"
+ name="overview-layout"
+ value="full"
+ if preferences.Overview.Layout == "full" {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="fullOverviewLayout" style="overflow:hidden;text-overflow: ellipsis;" title="Versions + Metadata + Changelog">Versions + Metadata + Changelog</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ <h4 class="mt-4 mb-1">Keywords</h4>
+ <div class="row pl-3">
+ @archesTwoColumns(preferences.Overview.Keywords)
+ </div>
+ <h4 class="mt-4 mb-1">EAPI version</h4>
+ <div class="card">
+ <div class="card-body">
+ Show
+ <select class="form-control form-control-sm ml-2" style="max-width: 100px;display: inline;" name="overview-eapi" id="overview-eapi">
+ <option
+ value="none"
+ if preferences.Overview.EAPI == "none" {
+ selected
+ }
+ >none</option>
+ <option
+ value="column"
+ if preferences.Overview.EAPI == "column" {
+ selected
+ }
+ >in column</option>
+ <option
+ value="inline"
+ if preferences.Overview.EAPI == "inline" {
+ selected
+ }
+ >inline</option>
+ </select>
+ </div>
+ </div>
+ <h4 class="mt-4 mb-1">Outdated Versions</h4>
+ <div class="card">
+ <div class="card-body">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ class=""
+ id="overview-showOutdated"
+ name="overview-showOutdated"
+ value="true"
+ if preferences.Overview.ShowOutdated {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-showOutdated">Show Outdated warnings?</label>
+ </div>
+ </div>
+ </div>
+ <h4 class="mt-4 mb-1">Metadata</h4>
+ <div class="card">
+ <div class="card-body">
+ <div class="row">
+ <div class="col-4">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ name="overview-metadata-fields"
+ id="overview-metadata-fields-homepage"
+ value="homepage"
+ if slices.Contains(preferences.Overview.MetadataFields, "homepage") {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-metadata-fields-homepage">Homepage</label>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ name="overview-metadata-fields"
+ id="overview-metadata-fields-upstream"
+ value="upstream"
+ if slices.Contains(preferences.Overview.MetadataFields, "upstream") {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-metadata-fields-upstream">Upstream</label>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ name="overview-metadata-fields"
+ id="overview-metadata-fields-longdescription"
+ value="longdescription"
+ if slices.Contains(preferences.Overview.MetadataFields, "longdescription") {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-metadata-fields-longdescription">Longdescription</label>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ name="overview-metadata-fields"
+ id="overview-metadata-fields-useflags"
+ value="useflags"
+ if slices.Contains(preferences.Overview.MetadataFields, "useflags") {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-metadata-fields-useflags">USE flags</label>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ name="overview-metadata-fields"
+ id="overview-metadata-fields-license"
+ value="license"
+ if slices.Contains(preferences.Overview.MetadataFields, "license") {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-metadata-fields-license">License</label>
+ </div>
+ </div>
+ <div class="col-4">
+ <div class="form-check form-check-inline">
+ <input
+ type="checkbox"
+ name="overview-metadata-fields"
+ id="overview-metadata-fields-maintainers"
+ value="maintainers"
+ if slices.Contains(preferences.Overview.MetadataFields, "maintainers") {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="overview-metadata-fields-maintainers">Maintainers</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <h4 class="mt-4 mb-1">Changelog</h4>
+ <div class="card">
+ <div class="card-body">
+ <div class="row">
+ <div class="col-6">
+ Layout
+ <select class="form-control form-control-sm ml-1" style="max-width: 100px;display: inline;" name="overview-changelog-type" id="overview-changelog-type" disabled>
+ <option value="compact">default</option>
+ </select>
+ </div>
+ <div class="col-6">
+ Size <input type="number" name="overview-changelog-size" class="form-control form-control-sm ml-1" style="width:150px;display: inline;" value={ strconv.Itoa(preferences.Overview.ChangelogLength) }/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <h3 class="mt-5" id="dependencies">Dependencies</h3>
+ <hr class="mt-1"/>
+ <div class="card">
+ <div class="card-body">
+ Default Page
+ <select class="form-control form-control-sm ml-1" style="max-width: 200px;display: inline;" name="dependencies-default-page" id="dependencies-default-page">
+ <option
+ value="dependencies"
+ if preferences.Dependencies.Default == "dependencies" {
+ selected
+ }
+ >dependencies</option>
+ <option
+ value="reverse-dependencies"
+ if preferences.Dependencies.Default == "reverse-dependencies" {
+ selected
+ }
+ >reverse-dependencies</option>
+ </select>
+ </div>
+ </div>
+ <h3 class="mt-5" id="pull-requests">Pull requests</h3>
+ <hr class="mt-1"/>
+ <div class="card">
+ <div class="card-body">
+ Layout
+ <select class="form-control form-control-sm ml-1" style="max-width: 200px;display: inline;" name="pullrequests-layout" id="pullrequests-layout" disabled>
+ <option
+ value="default"
+ if preferences.PullRequests.Layout == "default" {
+ selected
+ }
+ >default</option>
+ </select>
+ </div>
+ </div>
+ <h3 class="mt-5" id="bugs">Bugs</h3>
+ <hr class="mt-1"/>
+ <div class="card">
+ <div class="card-body">
+ Layout
+ <select class="form-control form-control-sm ml-1" style="max-width: 200px;display: inline;" name="bugs-layout" id="bugs-layout" disabled>
+ <option
+ value="default"
+ if preferences.PullRequests.Layout == "default" {
+ selected
+ }
+ >default</option>
+ </select>
+ </div>
+ </div>
+ <h3 class="mt-5" id="security">Security</h3>
+ <hr class="mt-1"/>
+ <div class="card">
+ <div class="card-body">
+ <div class="row">
+ <div class="col-6">
+ Layout
+ <select class="form-control form-control-sm ml-1" style="max-width: 100px;display: inline;" name="security-layout" id="security-layout" disabled>
+ <option value="default">default</option>
+ </select>
+ </div>
+ <div class="col-6">
+ <div class="form-check form-check-inline pt-2">
+ <input
+ type="checkbox"
+ name="security-show-glsas"
+ id="security-show-glsas"
+ class="disabled"
+ value="true"
+ if preferences.Security.ShowGLSAs {
+ selected
+ }
+ disabled
+ />
+ <label class="form-check-label ml-1" style="color:grey;">Show GLSAs</label>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <h3 class="mt-5" id="changelog">Changelog</h3>
+ <hr class="mt-1"/>
+ <div class="card">
+ <div class="card-body">
+ <div class="row">
+ <div class="col-6">
+ Layout
+ <select class="form-control form-control-sm ml-1" style="max-width: 100px;display: inline;" name="changelog-type" id="changelog-type" disabled>
+ <option value="compact">default</option>
+ </select>
+ </div>
+ <div class="col-6">
+ Size <input type="number" name="changelog-size" class="form-control form-control-sm" style="display: inline;width: 150px;" value={ strconv.Itoa(preferences.Changelog.Size) }/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-12 mt-4">
+ <button type="submit" class="float-right btn btn-sm btn-primary">Save</button>
+ <a class="float-right btn btn-sm btn-outline-danger mr-2" href="/user/preferences/packages/reset">Reset to Defaults</a>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ <div id="myModal" class="modal">
+ <span class="close">&times;</span>
+ <img class="modal-content" id="img01"/>
+ <div id="caption"></div>
+ </div>
+}
+
+func EditPackagesPreferences(w http.ResponseWriter, r *http.Request) {
+
+ userPreferences := utils.GetUserPreferences(r)
+
+ r.ParseForm()
+
+ // Overview: Layout
+ overviewLayout := r.Form.Get("overview-layout")
+ if overviewLayout == "minimal" || overviewLayout == "full" {
+ userPreferences.Packages.Overview.Layout = overviewLayout
+ }
+
+ // Overview: Keywords
+ overviewKeywords := r.Form["overview-keywords"]
+ userPreferences.Packages.Overview.Keywords = overviewKeywords
+
+ // EAPI
+ showEAPI := r.Form.Get("overview-eapi")
+ if showEAPI == "none" || showEAPI == "column" || showEAPI == "inline" {
+ userPreferences.Packages.Overview.EAPI = showEAPI
+ }
+
+ // Overview: Show Outdated
+ userPreferences.Packages.Overview.ShowOutdated = r.Form.Get("overview-showOutdated") == "true"
+
+ // Overview: Metadata fields
+ overviewMetadataFields := r.Form["overview-metadata-fields"]
+ userPreferences.Packages.Overview.MetadataFields = overviewMetadataFields
+
+ // Overview: Changelog
+ changelogSize, err := strconv.Atoi(r.Form.Get("overview-changelog-size"))
+ if err == nil {
+ if changelogSize < 100 {
+ userPreferences.Packages.Overview.ChangelogLength = changelogSize
+ } else {
+ userPreferences.Packages.Overview.ChangelogLength = 100
+ }
+ }
+
+ // Dependencies
+ defaultDependenciesPage := r.Form.Get("dependencies-default-page")
+ if defaultDependenciesPage == "dependencies" || defaultDependenciesPage == "reverse-dependencies" {
+ userPreferences.Packages.Dependencies.Default = defaultDependenciesPage
+ }
+
+ //
+ // Store cookie
+ //
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Packages)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_packages", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/packages", http.StatusSeeOther)
+}
+
+func ResetPackages(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetDefaultUserPreferences()
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Packages)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_packages", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/packages", http.StatusSeeOther)
+}
diff --git a/pkg/app/handler/user/preferences.go b/pkg/app/handler/user/preferences.go
deleted file mode 100644
index 8c5deec..0000000
--- a/pkg/app/handler/user/preferences.go
+++ /dev/null
@@ -1,245 +0,0 @@
-package user
-
-import (
- b64 "encoding/base64"
- "encoding/json"
- "net/http"
- "soko/pkg/app/utils"
- "soko/pkg/config"
- "soko/pkg/database"
- "soko/pkg/models"
- "strconv"
- "strings"
- "time"
-)
-
-// Preferences renders a template to show the user preferences page
-func Preferences(w http.ResponseWriter, r *http.Request) {
-
- pageName := "general"
-
- if strings.HasSuffix(r.URL.Path, "/general") {
- pageName = "general"
- } else if strings.HasSuffix(r.URL.Path, "/packages") {
- pageName = "packages"
- } else if strings.HasSuffix(r.URL.Path, "/maintainers") {
- pageName = "maintainers"
- } else if strings.HasSuffix(r.URL.Path, "/useflags") {
- pageName = "useflags"
- } else if strings.HasSuffix(r.URL.Path, "/arches") {
- pageName = "arches"
- }
-
- var allProjects []*models.Project
- database.DBCon.Model(&allProjects).Select()
-
- renderUserTemplate(w, r, allProjects, pageName, "preferences")
-}
-
-func EditPackagesPreferences(w http.ResponseWriter, r *http.Request) {
-
- userPreferences := utils.GetUserPreferences(r)
-
- r.ParseForm()
-
- // Overview: Layout
- overviewLayout := r.Form.Get("overview-layout")
- if overviewLayout == "minimal" || overviewLayout == "full" {
- userPreferences.Packages.Overview.Layout = overviewLayout
- }
-
- // Overview: Keywords
- overviewKeywords := r.Form["overview-keywords"]
- userPreferences.Packages.Overview.Keywords = overviewKeywords
-
- // EAPI
- showEAPI := r.Form.Get("overview-eapi")
- if showEAPI == "none" || showEAPI == "column" || showEAPI == "inline" {
- userPreferences.Packages.Overview.EAPI = showEAPI
- }
-
- // Overview: Show Outdated
- userPreferences.Packages.Overview.ShowOutdated = r.Form.Get("overview-showOutdated") == "true"
-
- // Overview: Metadata fields
- overviewMetadataFields := r.Form["overview-metadata-fields"]
- userPreferences.Packages.Overview.MetadataFields = overviewMetadataFields
-
- // Overview: Changelog
- changelogSize, err := strconv.Atoi(r.Form.Get("overview-changelog-size"))
- if err == nil {
- if changelogSize < 100 {
- userPreferences.Packages.Overview.ChangelogLength = changelogSize
- } else {
- userPreferences.Packages.Overview.ChangelogLength = 100
- }
- }
-
- // Dependencies
- defaultDependenciesPage := r.Form.Get("dependencies-default-page")
- if defaultDependenciesPage == "dependencies" || defaultDependenciesPage == "reverse-dependencies" {
- userPreferences.Packages.Dependencies.Default = defaultDependenciesPage
- }
-
- // QA Report
- qaReportClasses := r.Form["qareport-classes"]
- excludedQAReportClasses := []int{}
- for i := 0; i <= 190; i++ {
- if !contains(qaReportClasses, strconv.Itoa(i)) {
- excludedQAReportClasses = append(excludedQAReportClasses, i)
- }
- }
- userPreferences.Packages.QAReport.ExcludedWarningClasses = excludedQAReportClasses
-
- // Tabs
- visibleTabs := r.Form["visible-tabs"]
- userPreferences.Packages.Tabs.Visible = visibleTabs
-
- //
- // Store cookie
- //
- encodedUserPreferences, err := json.Marshal(&userPreferences.Packages)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_packages", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/packages", http.StatusSeeOther)
-}
-
-func ResetPackages(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetDefaultUserPreferences()
- encodedUserPreferences, err := json.Marshal(&userPreferences.Packages)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_packages", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/packages", http.StatusSeeOther)
-}
-
-func General(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetUserPreferences(r)
- r.ParseForm()
- // landing page layout
- layout := r.Form.Get("landingpage-layout")
- if layout == "classic" || layout == "full" {
- userPreferences.General.LandingPageLayout = layout
- }
- // store cookie
- encodedUserPreferences, err := json.Marshal(&userPreferences.General)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_general", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/general", http.StatusSeeOther)
-}
-
-func ResetGeneral(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetDefaultUserPreferences()
- encodedUserPreferences, err := json.Marshal(&userPreferences.General)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_general", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/general", http.StatusSeeOther)
-}
-
-func Useflags(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetUserPreferences(r)
- r.ParseForm()
- // default use flag page
- layout := r.Form.Get("useflag-default-page")
- if layout == "bubble" || layout == "search" {
- userPreferences.Useflags.Layout = layout
- }
- // store cookie
- encodedUserPreferences, err := json.Marshal(&userPreferences.Useflags)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_useflags", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/useflags", http.StatusSeeOther)
-}
-
-func ResetUseflags(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetDefaultUserPreferences()
- encodedUserPreferences, err := json.Marshal(&userPreferences.Useflags)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_useflags", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/useflags", http.StatusSeeOther)
-}
-
-func Arches(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetUserPreferences(r)
- r.ParseForm()
- // visible arches
- visibleArches := r.Form["visible-arches"]
- userPreferences.Arches.Visible = visibleArches
- // default arch
- defaultArch := r.Form.Get("arches-default-arch")
- userPreferences.Arches.DefaultArch = defaultArch
- // default arches page
- defaultPage := r.Form.Get("arches-default-page")
- userPreferences.Arches.DefaultPage = defaultPage
- // store cookie
- encodedUserPreferences, err := json.Marshal(&userPreferences.Arches)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_arches", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/arches", http.StatusSeeOther)
-}
-
-func ResetArches(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetDefaultUserPreferences()
- encodedUserPreferences, err := json.Marshal(&userPreferences.Arches)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_arches", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/arches", http.StatusSeeOther)
-}
-
-func Maintainers(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetUserPreferences(r)
- r.ParseForm()
- // excluded projects
- excludedProjects := r.Form["excluded-projects"]
- userPreferences.Maintainers.ExcludedProjects = excludedProjects
- // include projects?
- includePackages := r.Form.Get("include-packages")
- userPreferences.Maintainers.IncludeProjectPackages = includePackages == "true"
- // store cookie
- encodedUserPreferences, err := json.Marshal(&userPreferences.Maintainers)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_maintainers", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/maintainers", http.StatusSeeOther)
-}
-
-func ResetMaintainers(w http.ResponseWriter, r *http.Request) {
- userPreferences := utils.GetDefaultUserPreferences()
- encodedUserPreferences, err := json.Marshal(&userPreferences.Maintainers)
- if err == nil {
- sEnc := b64.StdEncoding.EncodeToString(encodedUserPreferences)
- addCookie(w, "userpref_maintainers", "/", sEnc, 365*24*time.Hour)
- }
- http.Redirect(w, r, "/user/preferences/maintainers", http.StatusSeeOther)
-}
-
-// addCookie will apply a new cookie to the response of a http request
-// with the key/value specified.
-func addCookie(w http.ResponseWriter, name, path, value string, ttl time.Duration) {
- expire := time.Now().Add(ttl)
- cookie := http.Cookie{
- Name: name,
- Path: path,
- Value: value,
- Expires: expire,
- HttpOnly: true,
- Secure: config.DevMode() == "false",
- }
- http.SetCookie(w, &cookie)
-}
diff --git a/pkg/app/handler/user/preferences.templ b/pkg/app/handler/user/preferences.templ
new file mode 100644
index 0000000..0b33d5c
--- /dev/null
+++ b/pkg/app/handler/user/preferences.templ
@@ -0,0 +1,94 @@
+package user
+
+import "net/http"
+import "time"
+import "soko/pkg/app/layout"
+import "soko/pkg/app/utils"
+import "soko/pkg/config"
+import "soko/pkg/models"
+
+var viewTabs = []layout.SubTab{
+ {
+ Name: "General",
+ Link: "/user/preferences/general",
+ Icon: "fa fa-globe mr-1",
+ },
+ {
+ Name: "Packages",
+ Link: "/user/preferences/packages",
+ Icon: "fa fa-cube mr-1",
+ },
+ {
+ Name: "Maintainers",
+ Link: "/user/preferences/maintainers",
+ Icon: "fa fa-users mr-1",
+ },
+ {
+ Name: "USE flags",
+ Link: "/user/preferences/useflags",
+ Icon: "fa fa-sliders mr-1",
+ },
+ {
+ Name: "Architectures",
+ Link: "/user/preferences/arches",
+ Icon: "fa fa-server mr-1",
+ },
+}
+
+templ show(currentSubTab string, preferences models.UserPreferences) {
+ <div class="container mb-5">
+ switch currentSubTab {
+ case "General":
+ @general(preferences.General)
+ case "Packages":
+ @packages(preferences.Packages)
+ case "Maintainers":
+ @maintainers(preferences.Maintainers)
+ case "USE flags":
+ @useflags(preferences.Useflags)
+ case "Architectures":
+ @arches(preferences.Arches)
+ }
+ </div>
+ <script src="/assets/userpref.js"></script>
+}
+
+templ sortableScript() {
+ <script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
+ <script>
+ if(document.getElementById("example1") != null && document.getElementById("example2") != null) {
+ new Sortable(example1, {
+ group: 'shared',
+ animation: 150,
+ ghostClass: 'bg-info'
+ });
+ new Sortable(example2, {
+ group: 'shared',
+ animation: 150,
+ ghostClass: 'bg-info'
+ });
+ }
+ </script>
+}
+
+func Preferences(currentSubTab string) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ layout.TabbedLayout("User", "preferences", "Preferences", "fa fa-fw fa-cog", "You can customize the page contents to your needs here", viewTabs,
+ currentSubTab, show(currentSubTab, utils.GetUserPreferences(r))).Render(r.Context(), w)
+ }
+}
+
+// addCookie will apply a new cookie to the response of a http request
+// with the key/value specified.
+func addCookie(w http.ResponseWriter, name, path, value string, ttl time.Duration) {
+ expire := time.Now().Add(ttl)
+ cookie := http.Cookie{
+ Name: name,
+ Path: path,
+ Value: value,
+ Expires: expire,
+ HttpOnly: true,
+ Secure: config.DevMode() == "false",
+ }
+ http.SetCookie(w, &cookie)
+}
diff --git a/pkg/app/handler/user/useflags.templ b/pkg/app/handler/user/useflags.templ
new file mode 100644
index 0000000..438842c
--- /dev/null
+++ b/pkg/app/handler/user/useflags.templ
@@ -0,0 +1,93 @@
+package user
+
+import "encoding/base64"
+import "encoding/json"
+import "net/http"
+import "time"
+import "soko/pkg/app/utils"
+import "soko/pkg/models"
+
+templ useflags(preferences models.UseflagsPreferences) {
+ <form method="post" action="/user/preferences/useflags/edit">
+ <div class="row">
+ <div class="col-5 offset-1 mt-1">
+ <div class="card" style="background: transparent;">
+ <div class="card-body">
+ <img id="img1" alt="Popular USE flags (default)" src="/assets/pgo6.png" style="width: 100%;cursor: pointer;"/>
+ </div>
+ </div>
+ <div class="text-center mt-2">
+ <div class="form-check text-center form-check-inline" style="text-overflow: ellipsis;overflow: hidden;">
+ <input
+ type="radio"
+ id="BubbleUseflagDefaultPage"
+ name="useflag-default-page"
+ value="bubble"
+ if preferences.Layout == "bubble" {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="BubbleUseflagDefaultPage" style="overflow:hidden;text-overflow: ellipsis;" title="Popular USE flags (default)">Popular USE flags <i>(default)</i></label>
+ </div>
+ </div>
+ </div>
+ <div class="col-5 mt-1">
+ <div class="card" style="background: transparent;">
+ <div class="card-body">
+ <img id="img2" alt="USE flags search" src="/assets/pgo5.png" style="width: 100%;cursor: pointer;"/>
+ </div>
+ </div>
+ <div class="text-center mt-2">
+ <div class="form-check text-center form-check-inline" style="text-overflow: ellipsis;overflow: hidden;">
+ <input
+ type="radio"
+ id="SearchUseflagDefaultPage"
+ name="useflag-default-page"
+ value="search"
+ if preferences.Layout == "search" {
+ checked
+ }
+ />
+ <label class="form-check-label ml-1" for="SearchUseflagDefaultPage" style="overflow:hidden;text-overflow: ellipsis;" title="USE flags search">USE flags search</label>
+ </div>
+ </div>
+ </div>
+ <div class="col-10 offset-1 mt-4">
+ <button type="submit" class="float-right btn btn-sm btn-primary">Save</button>
+ <a class="float-right btn btn-sm btn-outline-danger mr-2" href="/user/preferences/useflags/reset">Reset to Defaults</a>
+ </div>
+ </div>
+ </form>
+ <div id="myModal" class="modal">
+ <span class="close">&times;</span>
+ <img class="modal-content" id="img01"/>
+ <div id="caption"></div>
+ </div>
+}
+
+func Useflags(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetUserPreferences(r)
+ r.ParseForm()
+ // default use flag page
+ layout := r.Form.Get("useflag-default-page")
+ if layout == "bubble" || layout == "search" {
+ userPreferences.Useflags.Layout = layout
+ }
+ // store cookie
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Useflags)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_useflags", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/useflags", http.StatusSeeOther)
+}
+
+func ResetUseflags(w http.ResponseWriter, r *http.Request) {
+ userPreferences := utils.GetDefaultUserPreferences()
+ encodedUserPreferences, err := json.Marshal(&userPreferences.Useflags)
+ if err == nil {
+ sEnc := base64.StdEncoding.EncodeToString(encodedUserPreferences)
+ addCookie(w, "userpref_useflags", "/", sEnc, 365*24*time.Hour)
+ }
+ http.Redirect(w, r, "/user/preferences/useflags", http.StatusSeeOther)
+}
diff --git a/pkg/app/handler/user/utils.go b/pkg/app/handler/user/utils.go
deleted file mode 100644
index 0372ef8..0000000
--- a/pkg/app/handler/user/utils.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package user
-
-import (
- "html/template"
- "net/http"
- "soko/pkg/app/utils"
- "soko/pkg/models"
- "strings"
-)
-
-// renderAboutTemplate renders a specific about template
-func renderUserTemplate(w http.ResponseWriter, r *http.Request, allProjects []*models.Project, pageName, page string) {
- templates := template.Must(
- template.Must(
- template.Must(
- template.Must(
- template.New(page).Funcs(template.FuncMap{
- "Contains": contains,
- "ContainsInt": containsInt,
- "CreateSlice": createSlice,
- "GetPkgcheckClass": models.GetPkgcheckClass,
- "add": func(a, b int) int {
- return a + b
- },
- }).
- ParseGlob("web/templates/layout/*.tmpl")).
- ParseGlob("web/templates/user/preferences/*.tmpl")).
- ParseGlob("web/templates/user/userheader.tmpl")).
- ParseGlob("web/templates/user/" + page + ".tmpl"))
-
- templates.ExecuteTemplate(w, page+".tmpl", getPageData(pageName, allProjects, utils.GetUserPreferences(r)))
-}
-
-func contains(list []string, item string) bool {
- return strings.Contains(" "+strings.Join(list, " ")+" ", " "+item+" ")
-}
-
-func containsInt(list []int, item int) bool {
- for _, v := range list {
- if v == item {
- return true
- }
- }
- return false
-}
-
-func createSlice(n int) []int {
- slice := []int{}
- for i := 0; i <= n; i++ {
- slice = append(slice, i)
- }
- return slice
-}
-
-// getPageData returns the data used
-// in all about templates
-func getPageData(pageName string, allProjects []*models.Project, preferences models.UserPreferences) interface{} {
- return struct {
- Header models.Header
- Application models.Application
- PageName string
- Projects []*models.Project
- UserPreferences models.UserPreferences
- }{
- Header: models.Header{Title: "User – ", Tab: "user"},
- Application: utils.GetApplicationData(),
- PageName: pageName,
- Projects: allProjects,
- UserPreferences: preferences,
- }
-}
diff --git a/pkg/app/serve.go b/pkg/app/serve.go
index f3e36e4..66bd220 100644
--- a/pkg/app/serve.go
+++ b/pkg/app/serve.go
@@ -78,9 +78,13 @@ func Serve() {
redirect("/packages/stabilized.atom", "/packages/stable.atom")
setRoute("/packages/search.atom", packages.SearchFeed)
- setRoute("/user", user.Preferences)
- setRoute("/user/preferences", user.Preferences)
- setRoute("/user/preferences/", user.Preferences)
+ setRoute("/user", user.Preferences("General"))
+ setRoute("/user/preferences", user.Preferences("General"))
+ setRoute("/user/preferences/general", user.Preferences("General"))
+ setRoute("/user/preferences/packages", user.Preferences("Packages"))
+ setRoute("/user/preferences/maintainers", user.Preferences("Maintainers"))
+ setRoute("/user/preferences/useflags", user.Preferences("USE flags"))
+ setRoute("/user/preferences/arches", user.Preferences("Architectures"))
setRoute("/user/preferences/general/layout", user.General)
setRoute("/user/preferences/general/reset", user.ResetGeneral)
diff --git a/pkg/models/userpreferences.go b/pkg/models/userpreferences.go
index 3ed7e21..c31c6dc 100644
--- a/pkg/models/userpreferences.go
+++ b/pkg/models/userpreferences.go
@@ -19,12 +19,10 @@ type GeneralPreferences struct {
type PackagesPreferences struct {
Overview PackagesOverviewPreferences
Dependencies PackagesDependenciesPreferences
- QAReport PackagesQAReportPreferences
PullRequests PackagesPullRequestsPreferences
Bugs PackagesBugsPreferences
Security PackagesSecurityPreferences
Changelog PackagesChangelogPreferences
- Tabs PackagesTabsPreferences
}
type PackagesOverviewPreferences struct {
@@ -41,11 +39,6 @@ type PackagesDependenciesPreferences struct {
Default string
}
-type PackagesQAReportPreferences struct {
- ExcludedWarningClasses []int
- ShowAll bool
-}
-
type PackagesPullRequestsPreferences struct {
Layout string
}
@@ -64,10 +57,6 @@ type PackagesChangelogPreferences struct {
Size int
}
-type PackagesTabsPreferences struct {
- Visible []string
-}
-
type MaintainersPreferences struct {
IncludeProjectPackages bool
ExcludedProjects []string
@@ -89,12 +78,10 @@ func GetDefaultUserPreferences() UserPreferences {
userPreferences.Packages = PackagesPreferences{}
userPreferences.Packages.Overview = PackagesOverviewPreferences{}
userPreferences.Packages.Dependencies = PackagesDependenciesPreferences{}
- userPreferences.Packages.QAReport = PackagesQAReportPreferences{}
userPreferences.Packages.PullRequests = PackagesPullRequestsPreferences{}
userPreferences.Packages.Bugs = PackagesBugsPreferences{}
userPreferences.Packages.Security = PackagesSecurityPreferences{}
userPreferences.Packages.Changelog = PackagesChangelogPreferences{}
- userPreferences.Packages.Tabs = PackagesTabsPreferences{}
userPreferences.Maintainers = MaintainersPreferences{}
userPreferences.Useflags = UseflagsPreferences{}
userPreferences.Arches = ArchesPreferences{}
@@ -111,9 +98,6 @@ func GetDefaultUserPreferences() UserPreferences {
userPreferences.Packages.Dependencies.Default = "dependencies"
- userPreferences.Packages.QAReport.ExcludedWarningClasses = []int{}
- userPreferences.Packages.QAReport.ShowAll = true
-
userPreferences.Packages.PullRequests.Layout = "default"
userPreferences.Packages.Bugs.Layout = "default"
@@ -124,8 +108,6 @@ func GetDefaultUserPreferences() UserPreferences {
userPreferences.Packages.Changelog.Layout = "compact"
userPreferences.Packages.Changelog.Size = 15
- userPreferences.Packages.Tabs.Visible = []string{"Overview", "Dependencies", "QA report", "Pull requests", "Bugs", "Security", "Changelog"}
-
userPreferences.Arches.Visible = []string{"amd64", "x86", "alpha", "arm", "arm64", "hppa", "ia64", "ppc", "ppc64", "riscv", "sparc"}
userPreferences.Arches.DefaultArch = "amd64"
userPreferences.Arches.DefaultPage = "keyworded"
@@ -151,7 +133,7 @@ func (u *UserPreferences) Sanitize() {
sanitizedKeywords := []string{}
for _, keyword := range u.Packages.Overview.Keywords {
- if strings.Contains(strings.Join(u.GetAllKeywords(), ","), keyword) {
+ if strings.Contains(strings.Join(GetAllKeywords(), ","), keyword) {
sanitizedKeywords = append(sanitizedKeywords, keyword)
}
}
@@ -201,23 +183,15 @@ func (u *UserPreferences) Sanitize() {
u.Packages.Changelog.Size = 100
}
- sanitizedTabs := []string{}
- for _, tab := range u.Packages.Tabs.Visible {
- if strings.Contains(strings.Join(defaultUserPreferences.Packages.Tabs.Visible, ","), tab) {
- sanitizedTabs = append(sanitizedTabs, tab)
- }
- }
- u.Packages.Tabs.Visible = sanitizedTabs
-
sanitizedVisibleArches := []string{}
for _, keyword := range u.Arches.Visible {
- if strings.Contains(strings.Join(u.GetAllKeywords(), ","), keyword) {
+ if strings.Contains(strings.Join(GetAllKeywords(), ","), keyword) {
sanitizedVisibleArches = append(sanitizedVisibleArches, keyword)
}
}
u.Arches.Visible = sanitizedVisibleArches
- if !strings.Contains(strings.Join(u.GetAllKeywords(), ","), u.Arches.DefaultArch) {
+ if !strings.Contains(strings.Join(GetAllKeywords(), ","), u.Arches.DefaultArch) {
u.Arches.DefaultArch = defaultUserPreferences.Arches.DefaultArch
}
@@ -230,34 +204,10 @@ func (u *UserPreferences) Sanitize() {
}
}
-func (u UserPreferences) ContainsPkgcheckClass(class string) bool {
- for _, v := range u.Packages.QAReport.ExcludedWarningClasses {
- if GetPkgcheckClass(v) == class {
- return false
- }
- }
- return true
-}
-
-func (u UserPreferences) GetAllKeywords() []string {
+func GetAllKeywords() []string {
return []string{"alpha", "amd64", "arm", "arm64", "hppa", "ia64", "loong", "m68k", "mips", "ppc", "ppc64", "riscv", "s390", "sparc", "x86", "amd64-linux", "arm-linux", "arm64-linux", "ppc64-linux", "x86-linux", "ppc-macos", "x64-macos", "sparc-solaris", "sparc64-solaris", "x64-solaris", "x86-solaris", "x64-winnt", "x86-winnt", "x64-cygwin"}
}
-func GetPkgcheckClass(number int) string {
- pkgcheckClasses := []string{"AbsoluteSymlink", "ArchesWithoutProfiles", "BadCommitSummary", "BadDependency", "BadDescription", "BadFilename", "BadHomepage", "BadKeywords", "BadPackageUpdate", "BadProtocol", "BadWhitespaceCharacter", "BannedCharacter", "BannedEapi", "BannedEapiCommand", "BinaryFile", "CatBadlyFormedXml", "CatInvalidXml", "CatMetadataXmlEmptyElement", "CatMetadataXmlIndentation", "CatMetadataXmlInvalidCatRef", "CatMetadataXmlInvalidPkgRef", "CatMissingMetadataXml", "ConflictingAccountIdentifiers", "ConflictingChksums", "DeadUrl", "DeprecatedChksum", "DeprecatedDep", "DeprecatedEapi", "DeprecatedEapiCommand", "DeprecatedEclass", "DeprecatedInsinto", "DirectNoMaintainer", "DirectStableKeywords", "DoubleEmptyLine", "DoublePrefixInPath", "DroppedKeywords", "DroppedStableKeywords", "DroppedUnstableKeywords", "DuplicateEclassInherits", "DuplicateFiles", "DuplicateKeywords", "EbuildIncorrectCopyright", "EbuildInvalidCopyright", "EbuildInvalidLicenseHeader", "EbuildNonGentooAuthorsCopyright", "EbuildOldGentooCopyright", "EclassBashSyntaxError", "EclassIncorrectCopyright", "EclassInvalidCopyright", "EclassInvalidLicenseHeader", "EclassNonGentooAuthorsCopyright", "EclassOldGentooCopyright", "EmptyCategoryDir", "EmptyFile", "EmptyMaintainer", "EmptyPackageDir", "EmptyProject", "EqualVersions", "ExecutableFile", "HomepageInSrcUri", "HttpsUrlAvailable", "IncorrectCopyright", "InvalidBdepend", "InvalidCommitMessage", "InvalidCommitTag", "InvalidCopyright", "InvalidDepend", "InvalidEapi", "InvalidLicense", "InvalidLicenseHeader", "InvalidPN", "InvalidPdepend", "InvalidProperties", "InvalidRdepend", "InvalidRequiredUse", "InvalidRestrict", "InvalidSlot", "InvalidSrcUri", "InvalidUTF8", "InvalidUseFlags", "LaggingProfileEapi", "LaggingStable", "MaintainerWithoutProxy", "MatchingChksums", "MatchingGlobalUse", "MismatchedPN", "MismatchedPerlVersion", "MissingAccountIdentifier", "MissingChksum", "MissingLicense", "MissingLicenseFile", "MissingLicenseRestricts", "MissingManifest", "MissingPackageRevision", "MissingPythonEclass", "MissingSignOff", "MissingSlash", "MissingSlotDep", "MissingTestRestrict", "MissingUnpackerDep", "MissingUri", "MissingUseDepDefault", "MissingVirtualKeywords", "MovedPackageUpdate", "MultiMovePackageUpdate", "NoFinalNewline", "NonGentooAuthorsCopyright", "NonexistentBlocker", "NonexistentDeps", "NonexistentProfilePath", "NonexistentProjectMaintainer", "NonsolvableDepsInDev", "NonsolvableDepsInExp", "NonsolvableDepsInStable", "ObsoleteUri", "OldGentooCopyright", "OldMultiMovePackageUpdate", "OldPackageUpdate", "OutdatedBlocker", "OutsideRangeAccountIdentifier", "OverlappingKeywords", "PkgBadlyFormedXml", "PkgInvalidXml", "PkgMetadataXmlEmptyElement", "PkgMetadataXmlIndentation", "PkgMetadataXmlInvalidCatRef", "PkgMetadataXmlInvalidPkgRef", "PkgMissingMetadataXml", "PotentialGlobalUse", "PotentialLocalUse", "PotentialStable", "ProbableGlobalUse", "ProbableUseExpand", "ProfileError", "ProfileWarning", "PythonEclassError", "PythonMissingDeps", "PythonMissingRequiredUse", "PythonRuntimeDepInAnyR1", "RdependChange", "RedirectedUrl", "RedundantDodir", "RedundantLongDescription", "RedundantUriRename", "RedundantVersion", "RequiredUseDefaults", "SSLCertificateError", "SizeViolation", "SourcingError", "StableRequest", "StaleProxyMaintProject", "StaticSrcUri", "TarballAvailable", "TrailingEmptyLine", "UncheckableDep", "UnderscoreInUseFlag", "UnknownCategories", "UnknownKeywords", "UnknownLicenses", "UnknownManifest", "UnknownMirror", "UnknownPkgDirEntry", "UnknownProfilePackageKeywords", "UnknownProfilePackageUse", "UnknownProfilePackages", "UnknownProfileUse", "UnknownProperties", "UnknownRestrict", "UnknownUseFlags", "UnnecessaryLicense", "UnnecessaryManifest", "UnnecessarySlashStrip", "UnsortedKeywords", "UnstableOnly", "UnstatedIuse", "UnusedEclasses", "UnusedGlobalUse", "UnusedInMastersEclasses", "UnusedInMastersGlobalUse", "UnusedInMastersLicenses", "UnusedInMastersMirrors", "UnusedLicenses", "UnusedLocalUse", "UnusedMirrors", "UnusedProfileDirs", "VariableInHomepage", "VisibleVcsPkg", "VulnerablePackage", "WhitespaceFound", "WrongIndentFound", "WrongMaintainerType"}
- return pkgcheckClasses[number]
-}
-
-func GetPkgcheckClassIndex(class string) int {
- pkgcheckClasses := []string{"AbsoluteSymlink", "ArchesWithoutProfiles", "BadCommitSummary", "BadDependency", "BadDescription", "BadFilename", "BadHomepage", "BadKeywords", "BadPackageUpdate", "BadProtocol", "BadWhitespaceCharacter", "BannedCharacter", "BannedEapi", "BannedEapiCommand", "BinaryFile", "CatBadlyFormedXml", "CatInvalidXml", "CatMetadataXmlEmptyElement", "CatMetadataXmlIndentation", "CatMetadataXmlInvalidCatRef", "CatMetadataXmlInvalidPkgRef", "CatMissingMetadataXml", "ConflictingAccountIdentifiers", "ConflictingChksums", "DeadUrl", "DeprecatedChksum", "DeprecatedDep", "DeprecatedEapi", "DeprecatedEapiCommand", "DeprecatedEclass", "DeprecatedInsinto", "DirectNoMaintainer", "DirectStableKeywords", "DoubleEmptyLine", "DoublePrefixInPath", "DroppedKeywords", "DroppedStableKeywords", "DroppedUnstableKeywords", "DuplicateEclassInherits", "DuplicateFiles", "DuplicateKeywords", "EbuildIncorrectCopyright", "EbuildInvalidCopyright", "EbuildInvalidLicenseHeader", "EbuildNonGentooAuthorsCopyright", "EbuildOldGentooCopyright", "EclassBashSyntaxError", "EclassIncorrectCopyright", "EclassInvalidCopyright", "EclassInvalidLicenseHeader", "EclassNonGentooAuthorsCopyright", "EclassOldGentooCopyright", "EmptyCategoryDir", "EmptyFile", "EmptyMaintainer", "EmptyPackageDir", "EmptyProject", "EqualVersions", "ExecutableFile", "HomepageInSrcUri", "HttpsUrlAvailable", "IncorrectCopyright", "InvalidBdepend", "InvalidCommitMessage", "InvalidCommitTag", "InvalidCopyright", "InvalidDepend", "InvalidEapi", "InvalidLicense", "InvalidLicenseHeader", "InvalidPN", "InvalidPdepend", "InvalidProperties", "InvalidRdepend", "InvalidRequiredUse", "InvalidRestrict", "InvalidSlot", "InvalidSrcUri", "InvalidUTF8", "InvalidUseFlags", "LaggingProfileEapi", "LaggingStable", "MaintainerWithoutProxy", "MatchingChksums", "MatchingGlobalUse", "MismatchedPN", "MismatchedPerlVersion", "MissingAccountIdentifier", "MissingChksum", "MissingLicense", "MissingLicenseFile", "MissingLicenseRestricts", "MissingManifest", "MissingPackageRevision", "MissingPythonEclass", "MissingSignOff", "MissingSlash", "MissingSlotDep", "MissingTestRestrict", "MissingUnpackerDep", "MissingUri", "MissingUseDepDefault", "MissingVirtualKeywords", "MovedPackageUpdate", "MultiMovePackageUpdate", "NoFinalNewline", "NonGentooAuthorsCopyright", "NonexistentBlocker", "NonexistentDeps", "NonexistentProfilePath", "NonexistentProjectMaintainer", "NonsolvableDepsInDev", "NonsolvableDepsInExp", "NonsolvableDepsInStable", "ObsoleteUri", "OldGentooCopyright", "OldMultiMovePackageUpdate", "OldPackageUpdate", "OutdatedBlocker", "OutsideRangeAccountIdentifier", "OverlappingKeywords", "PkgBadlyFormedXml", "PkgInvalidXml", "PkgMetadataXmlEmptyElement", "PkgMetadataXmlIndentation", "PkgMetadataXmlInvalidCatRef", "PkgMetadataXmlInvalidPkgRef", "PkgMissingMetadataXml", "PotentialGlobalUse", "PotentialLocalUse", "PotentialStable", "ProbableGlobalUse", "ProbableUseExpand", "ProfileError", "ProfileWarning", "PythonEclassError", "PythonMissingDeps", "PythonMissingRequiredUse", "PythonRuntimeDepInAnyR1", "RdependChange", "RedirectedUrl", "RedundantDodir", "RedundantLongDescription", "RedundantUriRename", "RedundantVersion", "RequiredUseDefaults", "SSLCertificateError", "SizeViolation", "SourcingError", "StableRequest", "StaleProxyMaintProject", "StaticSrcUri", "TarballAvailable", "TrailingEmptyLine", "UncheckableDep", "UnderscoreInUseFlag", "UnknownCategories", "UnknownKeywords", "UnknownLicenses", "UnknownManifest", "UnknownMirror", "UnknownPkgDirEntry", "UnknownProfilePackageKeywords", "UnknownProfilePackageUse", "UnknownProfilePackages", "UnknownProfileUse", "UnknownProperties", "UnknownRestrict", "UnknownUseFlags", "UnnecessaryLicense", "UnnecessaryManifest", "UnnecessarySlashStrip", "UnsortedKeywords", "UnstableOnly", "UnstatedIuse", "UnusedEclasses", "UnusedGlobalUse", "UnusedInMastersEclasses", "UnusedInMastersGlobalUse", "UnusedInMastersLicenses", "UnusedInMastersMirrors", "UnusedLicenses", "UnusedLocalUse", "UnusedMirrors", "UnusedProfileDirs", "VariableInHomepage", "VisibleVcsPkg", "VulnerablePackage", "WhitespaceFound", "WrongIndentFound", "WrongMaintainerType"}
- for k, v := range pkgcheckClasses {
- if v == class {
- return k
- }
- }
- return -1
-}
-
func createSlice(n int) []int {
slice := []int{}
for i := 0; i <= n; i++ {