- Load custom field definitions and values in ViewIssue handler
- New sidebar template displays dropdown fields with onchange submit
- POST handler at /issues/{id}/custom-fields/{field_id} saves values
- Dropdown options parsed from JSON and passed to template
- Non-dropdown fields display as read-only text
- Section appears between Labels and Milestone in sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The update server system works without license keys — the primary
feature is serving update feeds. Rename all UI labels from "Licensing"
to "Update Server" to reflect this. License key management remains
under the Licenses tab.
- Repo settings nav: "Licensing & Updates" -> "Update Server"
- Advanced settings toggle: "Enable licensing" -> "Enable Update Server"
- Org settings nav: "Licensing & Update Streams" -> "Update Server"
- Icon changed from octicon-key to octicon-broadcast
- Updated help text to emphasize update feeds over licensing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add DomainRestriction field to LicensePackage model with migration
- Packages can define default allowed domains (comma-separated)
- Key generation form now includes licensee name, email, and domain
fields in a proper modal instead of a tiny inline form
- Keys inherit domain restriction from their package if not overridden
- Package create/edit forms include domain restriction input
- Domain enforcement already exists in heartbeat validation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Always show master key prefix and status in a dedicated segment with
a Regenerate button that deactivates the old key and creates a new one
- Fix broken <details> structure where </details> was inside an {{if}}
block, causing malformed HTML
- Move create package form into a proper modal instead of a broken
details/summary toggle
- Add copy button for all key prefixes (not just full keys)
- Add POST /licenses/master-key/regenerate route and handler
- Add locale keys for regenerate master key feature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract licensing/update feed settings to its own page at
/settings/licensing with dedicated template and handler.
Navbar additions:
- Advanced Settings link (points to existing options page)
- Licensing link with key icon (when licensing enabled)
New handler: LicensingSettings/LicensingSettingsPost serves the
standalone licensing form with all fields (platform, gating,
metadata, extensions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add IsHidden field to Repository model. Three visibility modes:
- Public: visible to everyone (green label)
- Private: members only, non-members see 403 Access Denied (orange)
- Hidden: members only, non-members see 404 Not Found (red)
Private mode is for commercial repos — customers know the repo
exists and see a styled 403 page with sign-in button. Licensed
update feeds and key-gated downloads still work.
Hidden mode is for internal/secret repos — complete stealth, as
if the repo doesn't exist.
Settings UI: radio button selector in danger zone replaces the
old binary toggle. Each option shows a colored label with
description.
Migration v342: adds is_hidden column to repository table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add templates/status/403.tmpl mirroring the 404 page design with
a centered error message and sign-in button for anonymous users.
New Forbidden() method on Context renders the styled 403 template
for HTML requests, falls back to plain text for API/non-HTML.
RepoAssignment now calls ctx.Forbidden() instead of raw HTTPError
for both anonymous and signed-in users without access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#406: Add KeyPrefix field to UpdateStreamConfig. GenerateKeyString
now accepts a prefix parameter, looked up from org config. Default
remains MOKO if not set. Auto-uppercased, max 20 chars.
#408: Move "New Package" button into the packages header bar,
right-aligned. Uses details/summary pattern — clicking the button
expands the create form below. Cleaner layout on both repo and org.
#409: Add open-in-new-tab button (external link icon) next to every
copy button on feed URLs. All four feeds: Joomla XML, Dolibarr JSON,
WordPress JSON, Changelog XML.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ParentOrgID field to User model for org hierarchy. Parent orgs
can have child orgs, enabling enterprise structures like
MokoConsulting → client orgs.
Model changes:
- ParentOrgID int64 on User (INDEX, DEFAULT 0)
- GetChildOrgs, GetAncestorOrgIDs, GetParentOrg helpers
- Max 10 hierarchy levels with cycle detection
License integration:
- ListLicensePackagesWithAncestors — shows packages from parent orgs
- ListLicenseKeysWithAncestors — shows keys from parent orgs
- SearchLicenseKeysWithAncestors — searches across hierarchy
- Master keys from parent orgs validate for child org repos
UI:
- Parent org dropdown in org settings (owners/admins only)
- Shows all orgs user owns except self
Migration v341: adds parent_org_id column to user table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add release_stream_map table for explicitly assigning releases to
update streams. When a mapping exists, it overrides automatic tag
detection. When absent, falls back to tag name/suffix matching.
New model: ReleaseStreamMap with SetReleaseStream, GetReleaseStream,
ResolveReleaseStream (manual first, auto fallback).
UI: stream selector dropdown on release create/edit page, shown when
licensing is enabled. Options: auto-detect (default) or any
configured stream (stable, release-candidate, beta, etc.).
All three feed generators (Joomla, Dolibarr, WordPress) now use
ResolveReleaseStream instead of MatchStreamFromTag.
Migration v340 updated with release_stream_map table creation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In "no-download" feed visibility mode, anonymous users can browse
the release listing page and read release notes, but the download
section (attachments, source archives) is replaced with a "Sign in
to download" message.
Only "hidden" mode redirects anonymous users to login entirely.
"no-download" keeps the page public — release notes, changelogs,
and version info are all visible. Just the actual files are gated.
This matches the commercial pattern: let users see what's available
to motivate purchase, while protecting the actual deliverables.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add FeedVisibility field to UpdateStreamConfig with three modes:
- public: full feed with download URLs (default)
- no-download: version info visible but download URLs stripped
- hidden: empty feed returned without a valid license key
The "no-download" mode is the key commercial pattern — customers
see updates exist (motivating purchase/renewal) but cannot download
without a valid key. Joomla shows "update available" in admin.
Applied consistently across all update feed endpoints (Joomla XML,
Dolibarr JSON, WordPress JSON) via the shared validateUpdateKey()
which now returns a stripDownloads flag.
Also: when licensing is enabled, the release listing page requires
login. Anonymous users are redirected to the login page. This
prevents browsing release notes and download links without auth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repo settings now include extension metadata fields (element name,
display name, type, target version, maintainer, PHP minimum) under
the Licensing section. These override org-level defaults per-repo.
Empty fields inherit from the organization's update stream config.
Extension type dropdown includes: package, component, module,
plugin, template, library — plus an "(inherit from org)" option.
Also adds form fields to the RepoSettingsForm struct for all
metadata fields.
Closes#335
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New endpoint: GET /{owner}/{repo}/updates/wordpress.json
Generates JSON compatible with the YahnisElsts plugin-update-checker
library — the standard for commercial WordPress plugin self-hosted
updates. Returns name, slug, version, download_url, homepage,
requires_php, author, sections (changelog HTML), icons, and banners.
License key validation: reads from ?license_key=, ?dlid=, or ?key=
query params (PUC sends these via addQueryArgFilter). When RequireKey
is enabled, returns minimal empty response without download_url.
Changelog section built from release notes (last 10 stable releases),
converting markdown list items to HTML <ul>/<li> elements.
Icon/banner URLs point to conventional paths in the repo:
assets/icon-128x128.png, assets/icon-256x256.png
assets/banner-772x250.png, assets/banner-1544x500.png
Route registered at /updates/wordpress.json alongside existing
/updates.xml (Joomla) and /updates/dolibarr.json.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace link-action delete buttons with show-modal pattern that opens
a Fomantic UI modal with a form. Package deletion requires typing
the package name in a required field before the submit button works.
Key deletion shows a warning modal with confirmation.
Uses Gitea's existing form-fetch-action + modal_actions_confirm
pattern, matching how repo deletion works in settings.
Both repo and org templates updated with matching modals.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Domain lock timer: add DomainLockHours to LicensePackage and
FirstUsedUnix to LicenseKey. During the grace period after first
use, any domain is accepted and auto-added to the restriction list.
After the grace period, only listed domains are allowed. Set 0 for
immediate lock-on-first-use (default).
Fix infourl: default to /releases listing page instead of specific
tag page. Falls back to SupportURL or InfoURL if configured.
Match Akeeba Backup Pro XML format: downloadkey prefix is "dlid="
(not "&dlid="), matching how Joomla stores extra_query. Verified
against production Akeeba/JCE/AdminTools manifests via SSH.
Update migration v340 with FirstUsedUnix and DomainLockHours columns.
Add DomainLockHours field to create/edit package forms for both
repo and org levels with help text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Migration v340: sync all missing columns (key_raw, payment_ref,
last_heartbeat_unix, is_archived, licensing_enabled, download_gating,
support_url, and all extension metadata fields).
Package archiving (#384): add IsArchived field with archive/unarchive
handlers and collapsible "Archived Packages" section in templates.
Existing keys from archived packages continue to work.
Expanded delete permissions (#385): org owners and site admins can
permanently delete packages and keys (previously site admin only).
Search (#392): server-side search across key_prefix, key_raw,
licensee_name, licensee_email, domain_restriction, and payment_ref
via ?q= query parameter on both repo and org licenses pages.
Sortable tables (#390): Fomantic UI sortable class on keys table
with new Domain column showing DomainRestriction per key.
Download gating (#347): three modes — none, prerelease-only, and
all downloads. CheckDownloadGating() intercepts both release
attachment and git archive download handlers.
Support URL (#393): configurable SupportURL field on
UpdateStreamConfig for wiki or external site links.
Changelog XML (#343): ServeChangelogXML endpoint at /changelog.xml
generates Joomla-compatible changelog from release notes. Parses
Keep-a-Changelog markdown sections into <security>, <fix>,
<addition>, <change>, <remove>, <note> XML elements.
API renew (#387): POST /license-keys/{id}/renew endpoint extends
key expiration by package duration.
Closes#384, #385, #386, #387, #389, #390, #392, #393
Refs #343, #346, #347
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add configurable fields: element name, display name, description,
extension type, maintainer, maintainer URL, info URL, target version,
PHP minimum
- Add platform dropdown: joomla, dolibarr, wordpress, prestashop,
drupal, composer, both
- Update Joomla XML generator to use metadata from config (falls back
to repo-derived values when not set)
- Add GetEffectiveConfig() for resolving repo → org → nil config chain
- Add locale keys for all new settings fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- fix(blame): set HasSourceRenderedToggle for renderable files so that
permalinks include ?display=source for correct line-number linking
- fix(settings): translate team permission strings using data-locale
attributes instead of raw API values
- fix(dropzone): use relative path for non-image attachment markdown
links for consistency with image attachments
Refs #318, #334
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
License keys are domain-locked so hashing is unnecessary. Store the
full key in KeyRaw column for permanent visibility. Keys table now
shows the complete key with a clipboard copy button per row.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Block update feed endpoints based on repo platform setting:
Joomla-only repos return 404 on /updates/dolibarr.json and vice versa
- Show feed URLs section only when licensing is enabled
- Add delete button for license keys (site admin only)
- Add weekly cron job to purge expired keys older than 1 year
- Add DeleteLicenseKey and DeleteExpiredKeys model functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace confirm() with Gitea modal system (link-action + data-modal-confirm)
- Add confirmation modal to revoke key action
- Fix clipboard copy to use data-clipboard-target with tooltip feedback
- Localize all hardcoded English strings (feed labels, "unlimited", "Master")
- Improve key creation flash with security-focused message + copy button
- Add count badge to org licenses nav tab
- Add icon to org settings navbar for update streams
- Add help text to "Active" checkboxes explaining deactivation impact
- Fix empty state message to reference UI creation (not just API)
- Compact tables for denser license data display
- Add orange "Master" label to master package rows
- Conditional feed buttons on release page (only when licensing enabled)
- Add TypeLicenses unit type with Read/Write/Admin team permissions
- Route-level permission enforcement via RequireUnitReader/Writer
- Add "Renew" action for license keys (extends by package duration)
- Auto-associate domain on first heartbeat (lock-on-first-use)
- Enforce max_sites limit during domain auto-association
- Allow site admins and org owners to set custom license key values
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add key editing, domain enforcement, purchase webhooks, public
validation API, channels multiselect, Joomla downloadkey element,
licensing feature toggle, unified update system, release tag
enforcement, heartbeat tracking, and improved settings UX.
Phase 1: Full key display with AbsoluteShort dates, master package
protection (hide edit/delete in UI, reject in handlers).
Phase 2: Key edit page with template, handlers, and routes for both
repo and org levels. Master keys redirect away.
Phase 3: Domain restriction checking against CSV allowlist,
MaxSites enforcement via CountUniqueDomainsByKey and
IsDomainKnownForKey, dlid query param support for Joomla.
Phase 4: Purchase webhook (POST /license-keys/purchase) with
PaymentRef idempotency. Public validation endpoint
(POST /license-keys/validate) outside auth middleware.
PATCH /license-keys/{id} for API key editing.
Phase 5: Channels multiselect using org UpdateStreamConfig streams
rendered as checkboxes, stored as JSON arrays.
Additional: downloadkey XML element, LicensingEnabled toggle on
UpdateStreamConfig, Dolibarr endpoint unified with key validation,
release tag suffix enforcement, LastHeartbeatUnix field with
TouchHeartbeat, and cleaned-up settings pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add edit and delete actions for license packages:
- Edit button (pencil icon) opens edit form with all package fields
- Delete button (trash icon) with confirmation dialog
- Edit form includes active/inactive toggle
- Routes: GET/POST /licenses/packages/{id}/edit, POST /licenses/packages/{id}/delete
Ref #239
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an admin first visits the Licenses page, a master license package
and key are automatically created:
- Master package: lifetime, unlimited, all channels, all repos
- Master key: IsInternal=true, never expires
- Raw key shown once with copy instructions
- If master key is revoked, a new one is created on next visit
The master key is always present — revoking it and revisiting the page
generates a fresh one.
Ref #239
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add full license management web forms to the Licenses page:
- "New Package" form: name, description, duration, max sites, channels
- "Generate Key" button per package: creates key with auto-expiry
- "Revoke" button per key: deactivates the key
- New key display: shows raw key once with copy instructions
- Update Feed URLs section: copyable Joomla/Dolibarr endpoint URLs
- Admin-only controls: forms only visible to repo admins
Ref #239
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a Licenses tab in the repo header that shows when license packages
exist for the repo's owner. The tab displays:
- License packages with name, duration, allowed channels, key count
- Issued keys with prefix, licensee, expiry, and status
Also includes:
- Org-level default update streams with per-repo override (#265)
- Full Joomla channel names in update feeds
- Update Feed button on releases page
- DB migration v336 for update_stream_config table
The Licenses tab appears after Packages in the repo header, gated by
whether any license packages exist for the owner.
Ref #239, #265
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add an "Update Feed" button next to the RSS link on the releases page
that links to the repo's updates.xml endpoint. Only shown on the
releases view (not tags).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clarify that update feeds (updates.xml, dolibarr.json) are always
accessible regardless of the releases visibility setting. The
visibility dropdown controls whether the releases page is browsable
by anonymous visitors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add per-unit visibility dropdown (Private / Public) directly on the
repo settings page next to each unit's enable checkbox. This replaces
the need to navigate to a separate Public Access settings page.
Supported units: Code, Wiki, Issues, Releases. Each gets a dropdown
that controls AnonymousAccessMode — when set to Public, the unit is
readable by anonymous visitors even on private repos.
Closes#238
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a dynamic config option to set the default landing page for
unauthenticated visitors from Site Administration > Settings. Options:
- Home (default explore page)
- Explore (repository explore)
- Organizations (org explore)
- Login (redirect to login)
- Custom path (any internal URL like /MokoConsulting)
The setting takes effect immediately without restart, using the same
dynamic config system as maintenance mode and web banner. Falls back
to the static LANDING_PAGE setting from app.ini if not configured.
Closes#240
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebrand the built-in actions bot user from upstream Gitea naming to
MokoGitea branding:
- Name: gitea-actions → mokogitea-actions
- FullName: Gitea Actions → MokoGitea Actions
- Email: teabot@gitea.io → mokogitea-actions[bot]@mokoconsulting.tech
Add backward-compatible name recognition so all three bot name variants
(mokogitea-actions, gitea-actions, github-actions) with optional [bot]
suffix resolve to the same system user.
Add WhitelistActionsUser, MergeWhitelistActionsUser, and
ForcePushAllowlistActionsUser toggles to branch protection rules,
allowing CI/CD workflows to push to protected branches when explicitly
enabled. Previously the actions bot (virtual user ID -2) could never be
added to whitelist because updateUserWhitelist() only validates real
database users.
Closes#233
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add runtime ${APP_NAME} placeholder substitution in locale strings so
all user-facing text reflects the configured APP_NAME from app.ini.
Replace 52 hardcoded locale strings, template literals, HTTP auth
realm headers, and Swagger API titles/descriptions with the
configurable value.
Closes#1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Locale changes:
- Renamed "Gitea" -> "MokoGitea" in all user-facing strings across
28 language files (928 references)
- Preserved upstream feature names: "Gitea Actions", "Gitea API",
"gitea.com", "Gitea instances"
Branding page:
- Changed from three-card layout to single-column stacked segments
- Each image type shown inline with preview, label, and upload form
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When viewing repo-level Actions secrets or variables, also display
inherited org-level entries as read-only with an "overridden by
repository" badge when a repo entry shadows an org entry. Includes
a link to org settings for management.
Closes#78
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repositories with names starting with "." are now treated as system
repositories that are always private and cannot be made public. This is
enforced at every code path: API create, web create, migrate, template
create, push-to-create, API edit, web settings, and public access
settings. On creation paths, privacy is silently forced. On edit paths,
a clear error is returned.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add HELP_URL and SUPPORT_URL settings to app.ini (defaults to docs.gitea.com)
- Replace hardcoded docs.gitea.com in navbar with configurable HelpURL
- Expose HelpURL/SupportURL template functions
- Show Help URL and Support URL in Site Admin > Configuration
- Add locale strings for new admin config entries
- Create VERSION file (gitignored) for local builds with 1261.0.0 convention
The 1261.xx.xx version convention marks the fork starting point from
upstream Gitea. Set via VERSION file locally or GITEA_VERSION env in CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Fix ugly commit form "warning" message
2. Use JSONError for "Update PR Branch" response
3. Remove useless "timeline" class
4. Make timeline review default to "comment" to avoid icon missing
5. Align PR's "command line instructions" UI
6. Simply "Update PR branch" button logic
And then some TODOs are fixed.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Almost done
`pull_merge_box.tmpl` only has about 80 lines now, and (almost) all
variable accesses are strictly typed.
---------
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: Nicolas <bircni@icloud.com>