Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d55da332cf | |||
| a04e237f17 | |||
| e7cc4c120f | |||
| aa54f3834e | |||
| 4d93f23037 | |||
| d7e2ffd02b | |||
| b9d81ca5c5 | |||
| 59c62dc687 | |||
| b14ffa083e | |||
| 2cc57bbbbc | |||
| 3cd7687c06 | |||
| c3b2643b0c | |||
| 0159e567e2 | |||
| f194b204b4 | |||
| f118f084ce | |||
| 2821c35326 | |||
| 5b02cf188e | |||
| 689173ecab | |||
| b2fe44fbc3 | |||
| 0e89ef9944 | |||
| 522dadecf0 | |||
| f1b9bb2f3d | |||
| 7bbaf218d5 | |||
| 33a550f838 | |||
| e29ee5f91b | |||
| 984a99188e | |||
| 92fc77a6d1 | |||
| ea411e09be | |||
| 9b141b39c5 |
@@ -4,7 +4,7 @@
|
||||
<name>MokoGitea</name>
|
||||
<org>MokoConsulting</org>
|
||||
<description>Moko fork of Gitea — adding project board REST API endpoints and custom enhancements</description>
|
||||
<version>05.09.00</version>
|
||||
<version>05.13.00</version>
|
||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||
</identity>
|
||||
<governance>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Automation
|
||||
# VERSION: 05.09.00
|
||||
# VERSION: 05.13.00
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
|
||||
)
|
||||
|
||||
const (
|
||||
MasterPackageName = "Master (Internal)"
|
||||
MasterPackageDesc = "Auto-created master package with unlimited access to all channels."
|
||||
)
|
||||
|
||||
// EnsureMasterKey ensures that a master license package and key exist for the given owner.
|
||||
// Returns the master key's raw key string only if it was just created (empty string otherwise).
|
||||
func EnsureMasterKey(ctx context.Context, ownerID int64) (rawKey string, err error) {
|
||||
// Check if a master package already exists.
|
||||
pkgs, err := ListLicensePackages(ctx, ownerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var masterPkg *LicensePackage
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.Name == MasterPackageName {
|
||||
masterPkg = pkg
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Create master package if it doesn't exist.
|
||||
if masterPkg == nil {
|
||||
masterPkg = &LicensePackage{
|
||||
OwnerID: ownerID,
|
||||
Name: MasterPackageName,
|
||||
Description: MasterPackageDesc,
|
||||
DurationDays: 0, // lifetime
|
||||
MaxSites: 0, // unlimited
|
||||
RepoScope: "all",
|
||||
IsActive: true,
|
||||
}
|
||||
if err := CreateLicensePackage(ctx, masterPkg); err != nil {
|
||||
return "", fmt.Errorf("CreateLicensePackage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a master key already exists for this package.
|
||||
keys, err := ListLicenseKeysByPackage(ctx, masterPkg.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if key.IsInternal {
|
||||
return "", nil // already exists, don't return raw key
|
||||
}
|
||||
}
|
||||
|
||||
// Create the master key.
|
||||
masterKey := &LicenseKey{
|
||||
PackageID: masterPkg.ID,
|
||||
OwnerID: ownerID,
|
||||
IsInternal: true,
|
||||
IsActive: true,
|
||||
}
|
||||
rawKey, err = CreateLicenseKey(ctx, masterKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("CreateLicenseKey: %w", err)
|
||||
}
|
||||
|
||||
return rawKey, nil
|
||||
}
|
||||
|
||||
// GetMasterKey returns the master key for an owner, if it exists.
|
||||
func GetMasterKey(ctx context.Context, ownerID int64) (*LicenseKey, error) {
|
||||
key := new(LicenseKey)
|
||||
has, err := db.GetEngine(ctx).Where("owner_id = ? AND is_internal = ?", ownerID, true).Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, nil
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
@@ -2643,6 +2643,8 @@
|
||||
"repo.licenses.key_created_copy": "Copy this key now. It will not be shown again.",
|
||||
"repo.licenses.revoke": "Revoke",
|
||||
"repo.licenses.key_revoked": "License key revoked.",
|
||||
"repo.licenses.master_key_created": "Master License Key Created",
|
||||
"repo.licenses.master_key_created_copy": "This is your organization master key with unlimited access to all update channels. Copy it now — it will not be shown again.",
|
||||
"repo.licenses.update_feeds": "Update Feed URLs",
|
||||
"repo.release.draft": "Draft",
|
||||
"repo.release.prerelease": "Pre-Release",
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/models/licenses"
|
||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
|
||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil"
|
||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
|
||||
)
|
||||
|
||||
const tplOrgLicenses templates.TplName = "org/licenses"
|
||||
|
||||
// LicensePackageDisplay is used in templates.
|
||||
type LicensePackageDisplay struct {
|
||||
*licenses.LicensePackage
|
||||
KeyCount int64
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
// Licenses shows the org-level license packages and keys.
|
||||
func Licenses(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.licenses")
|
||||
ctx.Data["IsLicensesPage"] = true
|
||||
|
||||
org := ctx.Org.Organization
|
||||
ownerID := org.ID
|
||||
|
||||
// Auto-create master key if org owner.
|
||||
if ctx.Org.IsOwner {
|
||||
newMasterKey, err := licenses.EnsureMasterKey(ctx, ownerID)
|
||||
if err != nil {
|
||||
ctx.ServerError("EnsureMasterKey", err)
|
||||
return
|
||||
}
|
||||
if newMasterKey != "" {
|
||||
ctx.Data["NewMasterKey"] = newMasterKey
|
||||
}
|
||||
}
|
||||
|
||||
pkgs, err := licenses.ListLicensePackages(ctx, ownerID)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListLicensePackages", err)
|
||||
return
|
||||
}
|
||||
|
||||
var display []LicensePackageDisplay
|
||||
for _, pkg := range pkgs {
|
||||
count, _ := licenses.CountKeysByPackage(ctx, pkg.ID)
|
||||
display = append(display, LicensePackageDisplay{
|
||||
LicensePackage: pkg,
|
||||
KeyCount: count,
|
||||
Created: time.Unix(int64(pkg.CreatedUnix), 0),
|
||||
})
|
||||
}
|
||||
ctx.Data["LicensePackages"] = display
|
||||
|
||||
keys, err := licenses.ListLicenseKeys(ctx, ownerID)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListLicenseKeys", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["LicenseKeys"] = keys
|
||||
ctx.Data["IsRepoAdmin"] = ctx.Org.IsOwner
|
||||
|
||||
ctx.HTML(http.StatusOK, tplOrgLicenses)
|
||||
}
|
||||
|
||||
// LicensesCreatePackage handles POST to create a new org-level license package.
|
||||
func LicensesCreatePackage(ctx *context.Context) {
|
||||
name := ctx.FormString("name")
|
||||
if name == "" {
|
||||
ctx.Flash.Error("Package name is required")
|
||||
ctx.Redirect(ctx.Org.OrgLink + "/-/licenses")
|
||||
return
|
||||
}
|
||||
|
||||
durationDays, _ := strconv.Atoi(ctx.FormString("duration_days"))
|
||||
maxSites, _ := strconv.Atoi(ctx.FormString("max_sites"))
|
||||
|
||||
pkg := &licenses.LicensePackage{
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
Name: name,
|
||||
Description: ctx.FormString("description"),
|
||||
DurationDays: durationDays,
|
||||
MaxSites: maxSites,
|
||||
AllowedChannels: ctx.FormString("allowed_channels"),
|
||||
RepoScope: "all",
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := licenses.CreateLicensePackage(ctx, pkg); err != nil {
|
||||
ctx.ServerError("CreateLicensePackage", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.licenses.package_created"))
|
||||
ctx.Redirect(ctx.Org.OrgLink + "/-/licenses")
|
||||
}
|
||||
|
||||
// LicensesGenerateKey handles POST to generate a key from an org package.
|
||||
func LicensesGenerateKey(ctx *context.Context) {
|
||||
packageID, _ := strconv.ParseInt(ctx.FormString("package_id"), 10, 64)
|
||||
if packageID == 0 {
|
||||
ctx.Flash.Error("Invalid package")
|
||||
ctx.Redirect(ctx.Org.OrgLink + "/-/licenses")
|
||||
return
|
||||
}
|
||||
|
||||
pkg, err := licenses.GetLicensePackageByID(ctx, packageID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLicensePackageByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
key := &licenses.LicenseKey{
|
||||
PackageID: packageID,
|
||||
OwnerID: ctx.Org.Organization.ID,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if pkg.DurationDays > 0 {
|
||||
expires := time.Now().AddDate(0, 0, pkg.DurationDays)
|
||||
key.ExpiresUnix = timeutil.TimeStamp(expires.Unix())
|
||||
}
|
||||
|
||||
rawKey, err := licenses.CreateLicenseKey(ctx, key)
|
||||
if err != nil {
|
||||
ctx.ServerError("CreateLicenseKey", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Re-render with the new key shown.
|
||||
ctx.Data["Title"] = ctx.Tr("repo.licenses")
|
||||
ctx.Data["IsLicensesPage"] = true
|
||||
ctx.Data["IsRepoAdmin"] = ctx.Org.IsOwner
|
||||
ctx.Data["NewKeyCreated"] = rawKey
|
||||
|
||||
ownerID := ctx.Org.Organization.ID
|
||||
pkgs, _ := licenses.ListLicensePackages(ctx, ownerID)
|
||||
var display []LicensePackageDisplay
|
||||
for _, p := range pkgs {
|
||||
count, _ := licenses.CountKeysByPackage(ctx, p.ID)
|
||||
display = append(display, LicensePackageDisplay{
|
||||
LicensePackage: p,
|
||||
KeyCount: count,
|
||||
Created: time.Unix(int64(p.CreatedUnix), 0),
|
||||
})
|
||||
}
|
||||
ctx.Data["LicensePackages"] = display
|
||||
keys, _ := licenses.ListLicenseKeys(ctx, ownerID)
|
||||
ctx.Data["LicenseKeys"] = keys
|
||||
|
||||
ctx.HTML(http.StatusOK, tplOrgLicenses)
|
||||
}
|
||||
|
||||
// LicensesRevokeKey handles POST to revoke an org license key.
|
||||
func LicensesRevokeKey(ctx *context.Context) {
|
||||
keyID := ctx.PathParamInt64("id")
|
||||
key, err := licenses.GetLicenseKeyByID(ctx, keyID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLicenseKeyByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
key.IsActive = false
|
||||
if err := licenses.UpdateLicenseKey(ctx, key); err != nil {
|
||||
ctx.ServerError("UpdateLicenseKey", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.licenses.key_revoked"))
|
||||
ctx.Redirect(ctx.Org.OrgLink + "/-/licenses")
|
||||
}
|
||||
@@ -32,6 +32,18 @@ func Licenses(ctx *context.Context) {
|
||||
|
||||
ownerID := ctx.Repo.Repository.OwnerID
|
||||
|
||||
// Auto-create master package + key if admin and none exist.
|
||||
if ctx.Repo.Permission.IsAdmin() {
|
||||
newMasterKey, err := licenses.EnsureMasterKey(ctx, ownerID)
|
||||
if err != nil {
|
||||
ctx.ServerError("EnsureMasterKey", err)
|
||||
return
|
||||
}
|
||||
if newMasterKey != "" {
|
||||
ctx.Data["NewMasterKey"] = newMasterKey
|
||||
}
|
||||
}
|
||||
|
||||
pkgs, err := licenses.ListLicensePackages(ctx, ownerID)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListLicensePackages", err)
|
||||
|
||||
@@ -1099,6 +1099,13 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
||||
// at the moment, only editing "owner-level projects" need to "mention", maybe in the future we can relax the permission check
|
||||
m.Get("/mentions-in-owner", reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), org.GetMentionsInOwner)
|
||||
|
||||
m.Group("/licenses", func() {
|
||||
m.Get("", org.Licenses)
|
||||
m.Post("/packages", org.LicensesCreatePackage)
|
||||
m.Post("/keys/generate", org.LicensesGenerateKey)
|
||||
m.Post("/keys/{id}/revoke", org.LicensesRevokeKey)
|
||||
})
|
||||
|
||||
m.Get("/repositories", org.Repositories)
|
||||
m.Get("/heatmap", user.DashboardHeatmap)
|
||||
|
||||
|
||||
@@ -612,6 +612,7 @@ func repoAssignmentPrepareTemplateData(ctx *Context, data *repoAssignmentPrepare
|
||||
})
|
||||
ctx.Data["NumLicensePackages"] = numLicensePackages
|
||||
ctx.Data["EnableLicenses"] = numLicensePackages > 0
|
||||
ctx.Data["IsRepoAdmin"] = ctx.Repo.Permission.IsAdmin()
|
||||
|
||||
ctx.Data["Title"] = repo.Owner.Name + "/" + repo.Name
|
||||
ctx.Data["PageTitleCommon"] = repo.Name + " - " + setting.AppName
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content organization">
|
||||
{{template "org/header" .}}
|
||||
<div class="ui container">
|
||||
|
||||
{{if .NewMasterKey}}
|
||||
<div class="ui info message">
|
||||
<div class="header">{{ctx.Locale.Tr "repo.licenses.master_key_created"}}</div>
|
||||
<p>{{ctx.Locale.Tr "repo.licenses.master_key_created_copy"}}</p>
|
||||
<code class="tw-text-lg tw-select-all">{{.NewMasterKey}}</code>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .NewKeyCreated}}
|
||||
<div class="ui success message">
|
||||
<div class="header">{{ctx.Locale.Tr "repo.licenses.key_created"}}</div>
|
||||
<p>{{ctx.Locale.Tr "repo.licenses.key_created_copy"}}</p>
|
||||
<code class="tw-text-lg tw-select-all">{{.NewKeyCreated}}</code>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<h4 class="ui top attached header tw-flex tw-justify-between tw-items-center">
|
||||
<span>{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}</span>
|
||||
{{if .IsRepoAdmin}}
|
||||
<a class="ui small primary button" href="#new-package-form" onclick="this.parentElement.parentElement.nextElementSibling.querySelector('#new-package-form').style.display='block'; return false;">
|
||||
{{ctx.Locale.Tr "repo.licenses.new_package"}}
|
||||
</a>
|
||||
{{end}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .IsRepoAdmin}}
|
||||
<div id="new-package-form" style="display: none;" class="tw-mb-4">
|
||||
<form class="ui form" method="post" action="{{$.Org.HomeLink}}/-/licenses/packages">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="two fields">
|
||||
<div class="required field">
|
||||
<label>{{ctx.Locale.Tr "repo.licenses.package_name"}}</label>
|
||||
<input name="name" required placeholder="Pro Annual">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.licenses.description"}}</label>
|
||||
<input name="description" placeholder="Annual pro subscription">
|
||||
</div>
|
||||
</div>
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.licenses.duration"}} ({{ctx.Locale.Tr "repo.licenses.days"}})</label>
|
||||
<input name="duration_days" type="number" value="0" min="0">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.licenses.max_sites"}}</label>
|
||||
<input name="max_sites" type="number" value="0" min="0">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.licenses.channels"}}</label>
|
||||
<input name="allowed_channels" placeholder="stable,release-candidate">
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "repo.licenses.create_package"}}</button>
|
||||
</form>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .LicensePackages}}
|
||||
<table class="ui table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.package_name"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.duration"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.channels"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.keys_issued"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.status"}}</th>
|
||||
{{if .IsRepoAdmin}}<th></th>{{end}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .LicensePackages}}
|
||||
<tr>
|
||||
<td><strong>{{.Name}}</strong>{{if .Description}}<br><small class="text grey">{{.Description}}</small>{{end}}</td>
|
||||
<td>{{if eq .DurationDays 0}}{{ctx.Locale.Tr "repo.licenses.lifetime"}}{{else}}{{.DurationDays}} {{ctx.Locale.Tr "repo.licenses.days"}}{{end}}</td>
|
||||
<td>{{if .AllowedChannels}}<code>{{.AllowedChannels}}</code>{{else}}{{ctx.Locale.Tr "repo.licenses.all_channels"}}{{end}}</td>
|
||||
<td>{{.KeyCount}}</td>
|
||||
<td>{{if .IsActive}}<span class="ui green label">{{ctx.Locale.Tr "repo.licenses.active"}}</span>{{else}}<span class="ui grey label">{{ctx.Locale.Tr "repo.licenses.inactive"}}</span>{{end}}</td>
|
||||
{{if $.IsRepoAdmin}}
|
||||
<td class="tw-text-right">
|
||||
<form method="post" action="{{$.Org.HomeLink}}/-/licenses/keys/generate" class="tw-inline">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="package_id" value="{{.ID}}">
|
||||
<button class="ui tiny primary button" type="submit">
|
||||
{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.generate_key"}}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<div class="empty-placeholder">
|
||||
{{svg "octicon-key" 48}}
|
||||
<h2>{{ctx.Locale.Tr "repo.licenses.none"}}</h2>
|
||||
<p>{{ctx.Locale.Tr "repo.licenses.none_desc"}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .LicenseKeys}}
|
||||
<h4 class="ui top attached header tw-mt-4">
|
||||
{{svg "octicon-lock" 16}} {{ctx.Locale.Tr "repo.licenses.issued_keys"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<table class="ui table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.key_prefix"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.licensee"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.expires"}}</th>
|
||||
<th>{{ctx.Locale.Tr "repo.licenses.status"}}</th>
|
||||
{{if .IsRepoAdmin}}<th></th>{{end}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .LicenseKeys}}
|
||||
<tr>
|
||||
<td><code>{{.KeyPrefix}}</code>{{if .IsInternal}} <span class="ui tiny orange label">Master</span>{{end}}</td>
|
||||
<td>{{.LicenseeName}}{{if .LicenseeEmail}} <small>({{.LicenseeEmail}})</small>{{end}}</td>
|
||||
<td>{{if eq .ExpiresUnix 0}}{{ctx.Locale.Tr "repo.licenses.never"}}{{else}}{{DateUtils.TimeSince .ExpiresUnix}}{{end}}</td>
|
||||
<td>{{if .IsActive}}<span class="ui green label">{{ctx.Locale.Tr "repo.licenses.active"}}</span>{{else}}<span class="ui grey label">{{ctx.Locale.Tr "repo.licenses.inactive"}}</span>{{end}}</td>
|
||||
{{if $.IsRepoAdmin}}
|
||||
<td class="tw-text-right">
|
||||
<form method="post" action="{{$.Org.HomeLink}}/-/licenses/keys/{{.ID}}/revoke" class="tw-inline">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<button class="ui tiny red button" type="submit">
|
||||
{{svg "octicon-x" 14}}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
||||
@@ -25,6 +25,11 @@
|
||||
{{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .IsOrganizationMember}}
|
||||
<a class="{{if .IsLicensesPage}}active {{end}}item" href="{{$.Org.HomeLink}}/-/licenses">
|
||||
{{svg "octicon-key"}} {{ctx.Locale.Tr "repo.licenses"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if and .IsRepoIndexerEnabled .CanReadCode}}
|
||||
<a class="{{if .IsCodePage}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code">
|
||||
{{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}}
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
{{if or .EnableLicenses (.Permission.IsAdmin)}}
|
||||
{{if or .EnableLicenses .IsRepoAdmin}}
|
||||
<a href="{{.RepoLink}}/licenses" class="{{if .IsLicensesPage}}active {{end}}item">
|
||||
{{svg "octicon-key"}} {{ctx.Locale.Tr "repo.licenses"}}
|
||||
{{if .NumLicensePackages}}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
{{template "repo/header" .}}
|
||||
<div class="ui container">
|
||||
|
||||
{{if .NewMasterKey}}
|
||||
<div class="ui info message">
|
||||
<div class="header">{{ctx.Locale.Tr "repo.licenses.master_key_created"}}</div>
|
||||
<p>{{ctx.Locale.Tr "repo.licenses.master_key_created_copy"}}</p>
|
||||
<code class="tw-text-lg tw-select-all">{{.NewMasterKey}}</code>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .NewKeyCreated}}
|
||||
<div class="ui success message">
|
||||
<div class="header">{{ctx.Locale.Tr "repo.licenses.key_created"}}</div>
|
||||
@@ -15,14 +23,14 @@
|
||||
<h4 class="ui top attached header tw-flex tw-justify-between tw-items-center">
|
||||
<span>{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}</span>
|
||||
{{if .IsRepoAdmin}}
|
||||
<button class="ui small primary button" onclick="document.getElementById('new-package-form').classList.toggle('tw-hidden')">
|
||||
<a class="ui small primary button" href="#new-package-form" onclick="document.getElementById('new-package-form').style.display=document.getElementById('new-package-form').style.display==='none'?'block':'none'; return false;">
|
||||
{{ctx.Locale.Tr "repo.licenses.new_package"}}
|
||||
</button>
|
||||
</a>
|
||||
{{end}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
{{if .IsRepoAdmin}}
|
||||
<div id="new-package-form" class="tw-hidden tw-mb-4">
|
||||
<div id="new-package-form" style="display: none;" class="tw-mb-4">
|
||||
<form class="ui form" method="post" action="{{.RepoLink}}/licenses/packages">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="two fields">
|
||||
@@ -148,14 +156,14 @@
|
||||
<div class="field">
|
||||
<label>Joomla updates.xml</label>
|
||||
<div class="ui action input tw-w-full">
|
||||
<input type="text" readonly value="{{AppSubUrl}}{{.RepoLink}}/updates.xml" onclick="this.select()">
|
||||
<input type="text" readonly value="{{.Repository.HTMLURL ctx}}/updates.xml" onclick="this.select()">
|
||||
<button class="ui button" onclick="navigator.clipboard.writeText(this.previousElementSibling.value)">{{svg "octicon-copy" 14}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field tw-mt-2">
|
||||
<label>Dolibarr JSON</label>
|
||||
<div class="ui action input tw-w-full">
|
||||
<input type="text" readonly value="{{AppSubUrl}}{{.RepoLink}}/updates/dolibarr.json" onclick="this.select()">
|
||||
<input type="text" readonly value="{{.Repository.HTMLURL ctx}}/updates/dolibarr.json" onclick="this.select()">
|
||||
<button class="ui button" onclick="navigator.clipboard.writeText(this.previousElementSibling.value)">{{svg "octicon-copy" 14}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
VERSION: 05.09.00
|
||||
VERSION: 05.13.00
|
||||
-->
|
||||
|
||||
<updates>
|
||||
@@ -87,13 +87,13 @@
|
||||
<element>mokogitea</element>
|
||||
<type>application</type>
|
||||
<client>site</client>
|
||||
<version>05.09.00</version>
|
||||
<version>05.13.00</version>
|
||||
<creationDate>2026-05-31</creationDate>
|
||||
<infourl title='MokoGitea'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/stable/mokogitea-05.09.00.zip</downloadurl>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/stable/mokogitea-05.13.00.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>e1ad6280b473b8606003a8002746ee3392f4447af4b31c44d77f5cc6700650ae</sha256>
|
||||
<sha256>b8d32fb99a0fde25ea0860b55ac5f3dfb8a15576863d9ddb80e67c5895722dcd</sha256>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
|
||||
Reference in New Issue
Block a user