Compare commits

...

174 Commits

Author SHA1 Message Date
Jonathan Miller fe6ca172f6 chore: merge main back into dev after v1.26.1-moko.06.03 release
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-05 00:07:28 -05:00
jmiller 5ca1c888c0 Merge pull request 'feat(custom-fields): template pre-fill + feed generator migration' (#494) from feat/issue-template-custom-fields into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
PR RC Release / Build RC Release (pull_request) Failing after 23s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m22s
2026-06-05 03:15:16 +00:00
Jonathan Miller 8e0388c9d8 fix(custom-fields): log errors instead of silently discarding them
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
- saveCustomFieldsFromForm: log GetCustomFieldsByOwner errors
- resolveExtensionMetadata: log DB errors on custom field lookup
- NewIssue/ViewIssue: log errors from GetCustomFieldsByOwner and
  GetCustomFieldValuesMap instead of blank-assigning
- Composer: fix misleading comment about override source

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 22:14:10 -05:00
Jonathan Miller cd4c701cb6 fix(custom-fields): address code review findings
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
- API: return 500 on GetCustomFieldsByOwner failure instead of silently
  swallowing the error
- resolveExtensionMetadata: add DownloadGating/KeyPrefix to metadata
  struct instead of mutating the caller's cfg pointer (side effect)
- resolveExtensionMetadata: add Description custom field mapping
- Composer: use meta.PHPMinimum instead of bypassing the cascade
- Web form: flash error on custom field save failure instead of silent log

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 21:59:50 -05:00
Jonathan Miller b72f88e78b docs(changelog): add #492 and #493 entries
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 37s
PR RC Release / Build RC Release (pull_request) Successful in 43s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 21:53:04 -05:00
Jonathan Miller 1935889f6b feat(updateserver): resolve extension metadata from custom fields with config fallback
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Add resolveExtensionMetadata() with cascading priority: org-level
repo-scoped custom fields → update_stream_config table → repo-derived
defaults. All six feed generators (Joomla, WordPress, Composer, Drupal,
PrestaShop, WHMCS) now use this unified resolver. Repos can be migrated
to custom fields gradually since the config table remains as fallback.

Ref #492

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 21:48:14 -05:00
Jonathan Miller 9ebe1b26b1 feat(custom-fields): pre-fill custom fields from issue template YAML frontmatter
Add `custom_fields` map to IssueTemplate struct so templates can specify
default values (e.g. `Priority: Medium`). On new issue form, org-level
issue-scoped fields appear in the sidebar with template defaults pre-selected.
NewIssuePost saves the values after issue creation. The API create issue
endpoint also accepts `custom_fields` by name.

Closes #493

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 21:47:15 -05:00
Jonathan Miller 3aec6c2cae fix(migration): set issue_id default to 0 for new custom_field_value inserts
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 24s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 58s
The old issue_id column has NOT NULL without a default, causing inserts
via the new entity_id-based API to fail. Migration now ALTERs the
column to DEFAULT 0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 19:51:56 -05:00
Jonathan Miller 539619be2f feat(api): custom fields API for org definitions, repo metadata, and issue values
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 24s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 55s
API endpoints:
- GET/POST/DELETE /api/v1/orgs/{org}/custom-fields — org field definitions
- GET/PUT /api/v1/repos/{owner}/{repo}/metadata — repo-scoped field values
- GET/PUT /api/v1/repos/{owner}/{repo}/issues/{index}/custom-fields — issue values

All endpoints use field names as keys (not IDs) for ergonomic access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 19:20:21 -05:00
Jonathan Miller 6bd9548b2a feat(custom-fields): move to org-level definitions with issue and repo scopes
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 23s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m4s
- CustomFieldDef now has owner_id (org) and scope (issue/repo)
- Issue sidebar loads fields by org owner_id, not repo_id
- Org Settings > Custom Fields page for managing field definitions
- Repo Settings > Metadata page for filling in repo-scoped values
- Migration v345 adds owner_id, scope, entity_id, entity_type columns
- Per-repo custom field management replaced by org-level
- Replaces .mokogitea/manifest.xml with database-backed metadata

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 19:11:22 -05:00
Jonathan Miller 5665bc545e fix(updateserver): use client=0 for packages to fix Joomla extension matching (#482)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 25s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m5s
Joomla matches updates to installed extensions via element+type+client_id.
Packages in #__extensions have client_id=0. Omitting <client> caused
Joomla to default to client_id=1, resulting in extension_id=0 in
#__updates and updates not appearing.

Fix: output <client>0</client> for package types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 18:44:46 -05:00
Jonathan Miller df58aacc30 fix(downloads): signed-in users with repo access bypass download gating
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 23s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m29s
Download gating only applies to anonymous/external clients (Joomla
update checker). Users who are signed in and have permission to
access the repo can always download releases regardless of gating.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 18:35:57 -05:00
Jonathan Miller 6575d3fce2 fix(ui): remove package count badge from Licenses tab
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 24s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m19s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 18:24:20 -05:00
Jonathan Miller d553c87a9d fix(updateserver): derive maintainer from org profile, infourl from support_url
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 24s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m2s
- Maintainer name: org FullName (from org profile)
- Maintainer URL: org Website (from org profile)
- Info URL: support_url (product page), falls back to releases page
- Removes dependency on separate maintainer/maintainer_url/info_url
  fields in update_stream_config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 18:17:51 -05:00
Jonathan Miller 178e8fffe2 fix(licenses): fix key generation modal not passing package_id
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 22s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m3s
The generate key modal's hidden package_id input was always empty
because show-modal doesn't wire data attributes to hidden inputs.
Fix: pass package_id via the form action URL query string instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 18:13:10 -05:00
Jonathan Miller 2f767e91cb chore: update changelog with today's fixes
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 17:50:58 -05:00
Jonathan Miller 5c22bb04b5 fix(licenses): remove master key banner, sort master keys first in list
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 24s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m0s
Remove the dedicated master key segment at the top of the Licenses
page. Master keys now appear first in the keys table via ORDER BY
is_internal DESC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 17:42:05 -05:00
Jonathan Miller d6d0d5a11f feat(ui): hide license sections on Update Server page when no gating
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 23s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m4s
When download_gating is off, the Update Server page only shows feed
URLs. License packages, keys, master key, and modals are hidden since
they're not relevant without download gating.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 16:43:41 -05:00
Jonathan Miller c948696488 feat(ui): show Update Server tab when no gating, Licenses tab when gated
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
- No gating: tab shows as "Update Server" with broadcast icon
- Gated (prerelease/all): tab shows as "Licenses" with key icon and
  package count badge
- Licensing disabled: tab hidden entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 16:38:49 -05:00
Jonathan Miller 635a13d277 fix(updateserver): only show downloadkey when downloads are gated
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 10s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 26s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m0s
Show <downloadkey> only when download_gating is prerelease or all.
No gating means no license keys are needed, so don't prompt for one.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 16:33:04 -05:00
Jonathan Miller 6be3e5c879 fix(updateserver): always show downloadkey when licensing enabled
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 22s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m3s
Include <downloadkey prefix="dlid="> in Joomla update XML whenever
Update Server is enabled for the repo, not just when require_key is
set. This ensures Joomla shows the Download Key field in Update Sites
even when downloads are currently public.

Also corrected joomlaTagName comment with Joomla source reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 16:28:14 -05:00
Jonathan Miller a86a9afb1a Revert "chore: remove build/ directory from tracking"
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 25s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m20s
This reverts commit 10e76cf033.
2026-06-04 16:18:13 -05:00
Jonathan Miller 5a80b8da33 docs(updateserver): correct joomlaTagName comment with Joomla source reference
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
PR RC Release / Build RC Release (pull_request) Failing after 21s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 58s
Joomla's Update.php maps tags via STABILITY_ + strtoupper(tag).
Valid values: dev, alpha, beta, rc, stable. Full names like
"development" silently fall back to STABILITY_STABLE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 14:29:26 -05:00
jmiller 0b21fe859e Merge pull request 'feat(issues): custom fields in issue sidebar' (#473) from feat/custom-fields-sidebar into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
2026-06-04 18:49:41 +00:00
Jonathan Miller 4ec0db8658 feat(issues): show custom fields in issue sidebar with inline editing
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
- 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>
2026-06-04 13:48:22 -05:00
Jonathan Miller 10e76cf033 chore: remove build/ directory from tracking
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Build artifacts are created by CI, not tracked in source.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 13:33:35 -05:00
jmiller a5bda0f9a6 Merge pull request 'chore: consolidate changelog to minor versions' (#471) from chore/changelog-consolidation into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
PR RC Release / Build RC Release (pull_request) Failing after 20s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
2026-06-04 17:51:05 +00:00
Jonathan Miller a053126bd9 chore: consolidate changelog to minor version numbers only
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Merge patch-level entries into their minor version, drop .00 suffixes,
and add today's Update Server and license UI fixes to moko.06.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 12:50:01 -05:00
gitea-actions[bot] 9fff67ab57 chore(release): build 05.31.00 [skip ci] 2026-06-04 17:25:06 +00:00
jmiller 3eb649a1a6 Merge pull request 'fix(updateserver): merge version fix to main' (#470) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 28s
2026-06-04 17:24:24 +00:00
jmiller f091f4cab3 Merge pull request 'fix(updateserver): version from asset filename takes priority' (#469) from fix/update-xml-version-priority into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 23s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m5s
2026-06-04 17:24:12 +00:00
Jonathan Miller 0de02fdce5 fix(updateserver): prevent stream name tag from overriding asset-derived version
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
When the tag is a stream name (e.g. "release-candidate"), the version
extracted from the asset filename was being overwritten by the release
title version. Remove the isStreamName check since the priority chain
(filename -> tag -> title) already handles this correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 12:22:40 -05:00
gitea-actions[bot] 06f8ab3d1a chore(release): build 05.30.00 [skip ci] 2026-06-04 17:16:24 +00:00
jmiller 3918e8ef9a Merge pull request 'fix(updateserver): merge XML fixes to main' (#468) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 1m16s
2026-06-04 17:15:48 +00:00
jmiller 480aaa088a Merge pull request 'fix(updateserver): extract version from asset filename, omit client for packages' (#467) from fix/joomla-update-xml into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 2s
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 22s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m3s
2026-06-04 17:15:29 +00:00
Jonathan Miller f0aa2c3034 fix(updateserver): extract version from asset filename, omit client for packages
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 0s
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
- Version now extracted from the zip asset filename first (most
  accurate), falling back to tag name then release title. Fixes
  mismatch where title version was updated but asset was stale.
- Omit <client> element for package extension types (packages manage
  their own sub-extension clients per Joomla spec).
- Make Client field omitempty so empty string doesn't render empty tag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 12:14:29 -05:00
jmiller 01d38e13f9 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:58:28 +00:00
jmiller 04338fe159 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:56:32 +00:00
jmiller 0389410efc chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:40:57 +00:00
jmiller ddababa6fa chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:38:59 +00:00
jmiller 3e156e8307 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:32:19 +00:00
jmiller 546245c9bb chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:29:32 +00:00
jmiller 26bb906a96 chore: remove updates.xml [skip ci] 2026-06-04 15:27:32 +00:00
jmiller 6509bd1eb7 chore: remove updates.xml [skip ci] 2026-06-04 15:27:25 +00:00
jmiller 6ac7c0c774 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:19:07 +00:00
jmiller 53c86c9b17 chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-06-04 15:14:04 +00:00
jmiller 4a687a9438 chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] 2026-06-04 14:23:31 +00:00
jmiller 0187f9814f chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] 2026-06-04 14:20:43 +00:00
gitea-actions[bot] 92ca601aa6 chore: update channels for 05.29.00 [skip ci] 2026-06-04 14:19:43 +00:00
gitea-actions[bot] e866d16ee6 chore(release): build 05.29.00 [skip ci] 2026-06-04 14:19:07 +00:00
jmiller 5a1772b026 Merge pull request 'fix(ui): merge Update Server rename to main' (#466) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Deploy MokoGitea / deploy (push) Failing after 28s
2026-06-04 14:18:24 +00:00
jmiller 969015a87a Merge pull request 'fix(ui): rename Licensing to Update Server across settings' (#465) from fix/rename-licensing-to-update-server into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 27s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m11s
2026-06-04 14:17:56 +00:00
Jonathan Miller ee20006b15 fix(ui): rename "Licensing" to "Update Server" across settings UI
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Access control (push) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
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>
2026-06-04 09:16:49 -05:00
jmiller ce38bab2cf chore: sync updates.xml 05.28.00 from main [skip ci] 2026-06-04 14:08:45 +00:00
gitea-actions[bot] aba8021344 chore: update channels for 05.28.00 [skip ci] 2026-06-04 14:08:44 +00:00
gitea-actions[bot] 7cbbfb7505 chore(release): build 05.28.00 [skip ci] 2026-06-04 14:08:10 +00:00
jmiller 7f45e98630 Merge pull request 'feat(licenses): merge domain restriction to main' (#464) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 23s
2026-06-04 14:07:38 +00:00
jmiller 74194f4283 Merge pull request 'feat(licenses): add domain restriction to packages and key generation' (#463) from feat/license-domain-restriction into dev
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 27s
Generic: Repo Health / Access control (push) Successful in 2s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m35s
2026-06-04 14:06:57 +00:00
Jonathan Miller e4ea1303ea feat(licenses): add domain restriction to packages and key generation
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
- 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>
2026-06-04 09:05:44 -05:00
jmiller 7d6cc3152d chore: sync updates.xml 05.27.00 from main [skip ci] 2026-06-04 13:50:31 +00:00
gitea-actions[bot] 42b0ff182c chore: update channels for 05.27.00 [skip ci] 2026-06-04 13:50:26 +00:00
gitea-actions[bot] 32d5a292c7 chore(release): build 05.27.00 [skip ci] 2026-06-04 13:49:44 +00:00
jmiller 33ba1159c3 Merge pull request 'fix(licenses): merge license UI fixes to main' (#462) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 58s
2026-06-04 13:49:05 +00:00
jmiller cd45824a0d Merge pull request 'fix(licenses): fix master key visibility and package creation at repo level' (#461) from fix/licenses-ui into dev
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 24s
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 2m13s
2026-06-04 13:48:50 +00:00
Jonathan Miller bab1acdfe3 fix(licenses): fix master key visibility, package creation, and template structure
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
- 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>
2026-06-04 08:47:11 -05:00
gitea-actions[bot] e59837b250 chore: update channels for 05.26.00 [skip ci] 2026-06-04 13:11:23 +00:00
gitea-actions[bot] 5c1e1cc8cc chore(release): build 05.26.00 [skip ci] 2026-06-04 13:10:51 +00:00
jmiller 6ea5dd37aa Merge pull request 'fix(settings): merge advanced settings UI to main' (#459) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 24s
2026-06-04 13:10:14 +00:00
jmiller 099f30d05b Merge pull request 'fix(settings): add licensing toggle to Advanced Settings, clean up UI' (#458) from fix/advanced-settings-ui into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 25s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m5s
2026-06-04 13:09:59 +00:00
Jonathan Miller cbaf289657 fix(settings): add licensing toggle to Advanced Settings, clean up UI
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
- Add licensing enable/disable checkbox to Advanced Settings page so
  users can toggle licensing without needing the separate Licensing
  settings page
- Restore LicensingEnabled gate on Licensing nav item (shows only when
  licensing is enabled, as intended)
- Replace inline dividers with Fomantic UI dividing headers for cleaner
  visual section separation
- Fix redirect after saving advanced settings to go back to /advanced
  instead of /settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 08:09:23 -05:00
gitea-actions[bot] 619295f469 chore: update channels for 05.25.00 [skip ci] 2026-06-04 13:03:07 +00:00
gitea-actions[bot] eced91be74 chore(release): build 05.25.00 [skip ci] 2026-06-04 13:02:37 +00:00
jmiller 902e3b5edd Merge pull request 'fix(settings): merge licensing nav fix to main' (#457) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 26s
2026-06-04 13:01:56 +00:00
jmiller 95d4259a26 Merge pull request 'fix(settings): always show Licensing nav item in repo settings' (#456) from fix/licensing-nav-always-visible into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
PR RC Release / Build RC Release (pull_request) Failing after 21s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 1m2s
2026-06-04 13:01:10 +00:00
Jonathan Miller 64dc6f28fa fix(settings): always show Licensing nav item in repo settings
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
The Licensing nav item was gated behind LicensingEnabled, creating a
chicken-and-egg problem: users couldn't reach the settings page to
enable licensing because the nav link was hidden until licensing was
already enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 07:58:17 -05:00
jmiller cea3418894 chore: sync updates.xml 05.24.00 from main [skip ci] 2026-06-04 12:54:29 +00:00
gitea-actions[bot] b6671ee1f9 chore: update channels for 05.24.00 [skip ci] 2026-06-04 12:54:28 +00:00
gitea-actions[bot] a52835b8ee chore(release): build 05.24.00 [skip ci] 2026-06-04 12:53:56 +00:00
jmiller c64bafbe80 Merge pull request 'fix(settings): merge nav highlight fix to main' (#455) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 1m0s
2026-06-04 12:53:22 +00:00
jmiller e088589af7 Merge pull request 'fix(settings): prevent double-highlight on Advanced Settings nav item' (#454) from fix/advanced-settings-nav-highlight into dev
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Failing after 9s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 26s
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 2m11s
2026-06-04 12:52:51 +00:00
Jonathan Miller c90edc3efc fix(settings): prevent double-highlight on Advanced Settings nav item
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
SettingsCtxData middleware sets PageIsSettingsOptions=true for all
settings routes including /advanced. The advanced handler now explicitly
clears it so only the Advanced Settings nav item is highlighted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 07:50:41 -05:00
jmiller dda0f6d4ed chore: sync updates.xml 05.23.00 from main [skip ci] 2026-06-04 12:40:18 +00:00
gitea-actions[bot] 21fb789d3c chore: update channels for 05.23.00 [skip ci] 2026-06-04 12:40:16 +00:00
gitea-actions[bot] 558bf37fce chore(release): build 05.23.00 [skip ci] 2026-06-04 12:39:40 +00:00
jmiller 746f1a5a50 Merge pull request 'fix(build): merge UTF-8 fix to main' (#453) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 3m26s
2026-06-04 12:39:06 +00:00
jmiller 9cceb5da0b Merge pull request 'fix(build): replace invalid UTF-8 character in API comment' (#452) from fix/utf8-comment into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 22s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 2m33s
2026-06-04 12:38:19 +00:00
Jonathan Miller fb5002d317 fix(build): replace invalid UTF-8 character in API comment
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
The em dash in the TODO comment was encoded as invalid UTF-8, causing
a Go compiler error. Replace with ASCII hyphen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 07:37:42 -05:00
jmiller f2482a712c chore: sync updates.xml 05.22.00 from main [skip ci] 2026-06-04 12:31:29 +00:00
gitea-actions[bot] 349a326881 chore: update channels for 05.22.00 [skip ci] 2026-06-04 12:31:28 +00:00
gitea-actions[bot] 7dc598104b chore(release): build 05.22.00 [skip ci] 2026-06-04 12:31:01 +00:00
jmiller 402166589b Merge pull request 'fix(build): merge custom fields build fix to main' (#451) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 22s
2026-06-04 12:30:27 +00:00
jmiller 6c9a26ebd3 Merge pull request 'fix(build): remove stale custom field API routes and dead code' (#450) from fix/custom-fields-build into dev
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 21s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 5m27s
2026-06-04 12:28:18 +00:00
Jonathan Miller c15582aa64 fix(build): remove stale custom field API routes, structs, and migration
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Comment out custom-fields API routes in api.go that referenced handler
functions from the deleted routers/api/v1/repo/custom_field.go. Remove
the unreferenced modules/structs/custom_field.go and the duplicate
v1_25/v323 migration (superseded by v1_27/v343).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 07:26:16 -05:00
jmiller 03dee5af39 chore: sync updates.xml 05.21.00 from main [skip ci] 2026-06-04 12:05:07 +00:00
gitea-actions[bot] 2827fa0a4c chore: update channels for 05.21.00 [skip ci] 2026-06-04 12:05:05 +00:00
gitea-actions[bot] 979d6f5964 chore(release): build 05.21.00 [skip ci] 2026-06-04 12:04:33 +00:00
jmiller 396220368f Merge pull request 'fix(build): remove stale custom field API' (#449) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 37s
2026-06-04 12:04:01 +00:00
Jonathan Miller 877f39d4f4 fix(build): remove stale custom field API file from prior session
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
PR RC Release / Build RC Release (pull_request) Successful in 21s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 6m5s
The old API file references non-existent struct names and fields.
Will be rebuilt properly after the web UI is complete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-04 07:02:40 -05:00
jmiller 0492ea399e chore: sync updates.xml 05.20.00 from main [skip ci] 2026-06-04 11:57:57 +00:00
gitea-actions[bot] 53b2d5b754 chore: update channels for 05.20.00 [skip ci] 2026-06-04 11:55:24 +00:00
gitea-actions[bot] 5db84e3932 chore(release): build 05.20.00 [skip ci] 2026-06-04 11:54:44 +00:00
jmiller 02cb4ae1a1 Merge pull request 'fix(build): custom field API function names' (#448) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 1m13s
2026-06-04 11:54:05 +00:00
gitea-actions[bot] 7f2aaa84bd chore: update channels for 05.19.00 [skip ci] 2026-06-04 11:50:07 +00:00
gitea-actions[bot] 6f16459e13 chore(release): build 05.19.00 [skip ci] 2026-06-04 11:49:23 +00:00
jmiller 5cf91a12bc Merge pull request 'feat(issues): custom fields foundation' (#447) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 25s
2026-06-04 11:48:39 +00:00
gitea-actions[bot] 0ab3b7dbd7 chore: update channels for 05.18.00 [skip ci] 2026-06-03 03:00:25 +00:00
gitea-actions[bot] 02495327ee chore(release): build 05.18.00 [skip ci] 2026-06-03 02:59:47 +00:00
jmiller 6f5c40716d Merge pull request 'fix(updates): default Joomla target to 5/6, correct URL mapping' (#446) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 5m19s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-03 02:59:09 +00:00
gitea-actions[bot] 1eff03ab21 chore: update channels for 05.17.00 [skip ci] 2026-06-03 02:56:34 +00:00
gitea-actions[bot] e3e2cb4543 chore(release): build 05.17.00 [skip ci] 2026-06-03 02:56:05 +00:00
jmiller a15139f70b Merge pull request 'fix(updates): correct infourl/maintainerurl mapping' (#445) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 22s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-03 02:55:31 +00:00
gitea-actions[bot] abf961dd1e chore: update channels for 05.16.00 [skip ci] 2026-06-03 01:33:13 +00:00
gitea-actions[bot] b34381e8da chore(release): build 05.16.00 [skip ci] 2026-06-03 01:32:42 +00:00
jmiller f9653411a7 Merge pull request 'docs: CHANGELOG and wiki update for v1.26.1-moko.06.02.00 final' (#444) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 23s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-03 01:32:08 +00:00
gitea-actions[bot] cd2e8b4d34 chore: update channels for 05.15.00 [skip ci] 2026-06-03 00:15:04 +00:00
gitea-actions[bot] acf9b4a4da chore(release): build 05.15.00 [skip ci] 2026-06-03 00:14:33 +00:00
jmiller 23af404ae4 Merge pull request 'fix(licenses): explicit xorm column names for UpdateStreamConfig' (#443) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 21s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-03 00:14:01 +00:00
jmiller a5b4f24b48 Merge pull request 'feat(licenses): ancestor-aware org license handler' (#442) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 3m3s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 23:47:38 +00:00
jmiller f485f14615 Merge pull request 'fix(ui): icons on user settings navbar' (#441) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 1m1s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 22:32:18 +00:00
jmiller b2d2a3b622 Merge pull request 'fix(licenses): allow anonymous download paths on licensed repos' (#440) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 1m11s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 20:51:12 +00:00
jmiller ba9907ba41 Merge pull request 'fix(updates): feed always public, downloads gated separately' (#439) from dev into main
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Deploy MokoGitea / deploy (push) Failing after 25s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 20:46:14 +00:00
Moko Consulting b1b64a3b4e chore(ci): add CI issue reporter for auto-filing gate failures
Generic: Repo Health / Access control (push) Successful in 3s
Generic: Repo Health / Site Health (push) Has been skipped
Deploy MokoGitea / deploy (push) Failing after 27s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 20:37:36 +00:00
Moko Consulting 3cddb46053 chore(ci): add CI issue reporter for auto-filing gate failures
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Deploy MokoGitea / deploy (push) Failing after 29s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 20:37:28 +00:00
Moko Consulting 0a0cc16528 chore(ci): add CI issue reporter for auto-filing gate failures
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (push) Has been skipped
Deploy MokoGitea / deploy (push) Failing after 29s
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-02 20:37:17 +00:00
jmiller da41d7072f Merge pull request 'fix(licenses): restrict downloadsPublic to release paths only' (#438) from dev into main
Deploy MokoGitea / deploy (push) Failing after 17s
2026-06-02 20:31:27 +00:00
jmiller cb3817f5bc Merge pull request 'fix(licenses): allow anonymous downloads when download_gating=none' (#437) from dev into main
Deploy MokoGitea / deploy (push) Failing after 47s
2026-06-02 20:26:46 +00:00
jmiller f657f58fbb Merge pull request 'fix(ui): octicon-settings to octicon-gear' (#436) from dev into main
Deploy MokoGitea / deploy (push) Failing after 41s
2026-06-02 19:47:07 +00:00
jmiller 1709566fa6 Merge pull request 'fix(ui): section headers with dividers, icons on all settings navbar items' (#435) from dev into main
Deploy MokoGitea / deploy (push) Failing after 1m23s
2026-06-02 19:34:36 +00:00
jmiller 95c136d838 Merge pull request 'feat(settings): dedicated advanced settings page at /settings/advanced' (#434) from dev into main
Deploy MokoGitea / deploy (push) Failing after 17s
2026-06-02 19:25:37 +00:00
jmiller 48f32ae961 Merge pull request 'feat(settings): accordion layout for advanced settings' (#433) from dev into main
Deploy MokoGitea / deploy (push) Failing after 4m1s
2026-06-02 19:14:08 +00:00
jmiller dce87fcb5d Merge pull request 'feat(settings): licensing settings page + navbar restructure' (#432) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m40s
2026-06-02 19:03:52 +00:00
jmiller 7004170d64 Merge pull request 'fix(ui): login form on 403 page + visibility badge right-aligned' (#431) from dev into main
Deploy MokoGitea / deploy (push) Failing after 19s
2026-06-02 18:56:17 +00:00
jmiller c045c6abfc Merge pull request 'fix(ui): visibility badge floated right of title' (#430) from dev into main
Deploy MokoGitea / deploy (push) Failing after 22s
2026-06-02 18:51:55 +00:00
jmiller ce35e3a603 Merge pull request 'fix(build): UpdateRepositoryColsWithAutoTime' (#429) from dev into main
Deploy MokoGitea / deploy (push) Failing after 22s
2026-06-02 18:46:56 +00:00
jmiller 3e4cb4d2e5 Merge pull request 'feat(repos): three-level visibility Public/Private/Hidden' (#428) from dev into main
Deploy MokoGitea / deploy (push) Failing after 23s
2026-06-02 18:44:15 +00:00
jmiller ba361c609f Merge pull request 'fix(licenses): RequireUnitReader allows LicensedReadOnly' (#427) from dev into main
Deploy MokoGitea / deploy (push) Failing after 21s
2026-06-02 15:45:48 +00:00
jmiller 7aaf8dcbb7 Merge pull request 'fix(licenses): bypass attachment perm check for licensed downloads' (#426) from dev into main
Deploy MokoGitea / deploy (push) Failing after 26s
2026-06-02 15:17:40 +00:00
jmiller 128b120ad9 Merge pull request 'fix(licenses): allow downloads on private repos with license key' (#425) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m3s
2026-06-02 15:13:47 +00:00
jmiller 3f817babd3 Merge pull request 'fix(ui): styled 403 Access Denied page matching 404 layout' (#424) from dev into main
Deploy MokoGitea / deploy (push) Failing after 20s
2026-06-02 15:06:02 +00:00
jmiller 6290ff07e4 Merge pull request 'fix(security): 403 for all users on private repos' (#423) from dev into main
Deploy MokoGitea / deploy (push) Failing after 1m1s
2026-06-02 14:57:50 +00:00
jmiller c4e51ff55c Merge pull request 'fix(licenses): licensed private repos allow release viewing for signed-in users' (#422) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m10s
2026-06-02 14:52:29 +00:00
jmiller b707c5aff9 Merge pull request 'fix(updates): allow update feeds on private repos' (#421) from dev into main
Deploy MokoGitea / deploy (push) Failing after 2m48s
2026-06-02 14:37:42 +00:00
jmiller 2db1f4eaf6 Merge pull request 'fix(security): 403 Access Denied for signed-in users on private repos' (#420) from dev into main
Deploy MokoGitea / deploy (push) Failing after 1m10s
2026-06-02 14:27:19 +00:00
jmiller 25499fb183 Merge pull request 'fix(build): unused import in drupal.go' (#419) from dev into main
Deploy MokoGitea / deploy (push) Failing after 21s
2026-06-02 14:10:45 +00:00
jmiller 7c15301228 Merge pull request 'feat(updates): PrestaShop, Drupal, WHMCS update feeds (#352, #353, #355)' (#418) from dev into main
Deploy MokoGitea / deploy (push) Failing after 21s
2026-06-02 14:08:33 +00:00
jmiller e4718f5036 Merge pull request 'feat(updates): Composer feed (#354), hide Actions/Licenses tabs for guests' (#417) from dev into main
Deploy MokoGitea / deploy (push) Failing after 2m59s
2026-06-02 14:02:23 +00:00
jmiller 581bfa5f31 Merge pull request 'feat(licenses): key prefix (#406), header button (#408), open feed (#409)' (#416) from dev into main
Deploy MokoGitea / deploy (push) Failing after 18s
2026-06-02 13:52:27 +00:00
jmiller 8ae663e15e Merge pull request 'SECURITY: fix release download gating and require login for actions' (#415) from dev into main
Deploy MokoGitea / deploy (push) Failing after 1m11s
2026-06-02 13:41:10 +00:00
jmiller 4bc962adbf Merge pull request 'fix(build): permanent fixes for recurring build errors' (#414) from dev into main
Deploy MokoGitea / deploy (push) Failing after 20s
2026-06-02 13:35:03 +00:00
jmiller ca841716db Merge pull request 'SECURITY: require login for licenses page' (#413) from dev into main
Deploy MokoGitea / deploy (push) Failing after 25s
2026-06-02 13:26:17 +00:00
jmiller 117daf51c3 Merge pull request 'fix(build): org list API and unused import' (#412) from dev into main
Deploy MokoGitea / deploy (push) Failing after 23s
2026-06-02 13:22:10 +00:00
jmiller a2e0735a26 Merge pull request 'feat(orgs): enterprise sub-org hierarchy (#410)' (#411) from dev into main
Deploy MokoGitea / deploy (push) Failing after 1m18s
2026-06-02 13:15:30 +00:00
jmiller 1a46a8f14f Merge pull request 'fix(build): EditReleaseForm UpdateStream field' (#405) from dev into main
Deploy MokoGitea / deploy (push) Failing after 19s
2026-06-02 12:56:36 +00:00
jmiller b18519e8b9 Merge pull request 'fix(build): pass ctx to WordPress changelog builder' (#404) from dev into main
Deploy MokoGitea / deploy (push) Failing after 1m32s
2026-06-02 12:48:26 +00:00
jmiller 94649efed0 Merge pull request 'feat(updates): manual stream mapping, version extraction fixes, feed visibility' (#403) from dev into main
Deploy MokoGitea / deploy (push) Failing after 22s
2026-06-02 12:43:29 +00:00
jmiller a52ac1bf61 Merge pull request 'feat(licenses): full commercial license management system v1.26.1-moko.06.02.00' (#402) from dev into main
Deploy MokoGitea / deploy (push) Failing after 19s
feat(licenses): full commercial license management system v1.26.1-moko.06.02.00 (#402)
2026-06-02 12:00:19 +00:00
jmiller 5da4b3b314 Merge pull request 'fix(build): remove unused imports' (#377) from dev into main
Deploy MokoGitea / deploy (push) Failing after 21s
2026-05-31 18:51:42 +00:00
jmiller 75e2a21b89 Merge pull request 'chore: merge dev into main — Issue.Ref deprecation, stale TODO cleanup' (#376) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m45s
2026-05-31 18:39:57 +00:00
jmiller e82fe7d021 Merge pull request 'fix(cron): add missing translation for cleanup_expired_license_keys' (#375) from dev into main
Deploy MokoGitea / deploy (push) Failing after 21s
2026-05-31 18:34:46 +00:00
jmiller 24a9bfb30d Merge pull request 'fix(docker): disable openssh s6 service in Dockerfile' (#374) from dev into main
Deploy MokoGitea / deploy (push) Failing after 4m30s
2026-05-31 17:14:36 +00:00
jmiller 257908e083 Merge pull request 'chore: merge dev into main — tech-debt, namespace migration, combo-multiselect' (#373) from dev into main
Deploy MokoGitea / deploy (push) Failing after 6m39s
2026-05-31 17:12:39 +00:00
jmiller 2c3aad51af Merge pull request 'fix(build): Go 1.23 maps.Values slices.Collect' (#371) from dev into main
Deploy MokoGitea / deploy (push) Failing after 25s
2026-05-31 16:38:42 +00:00
jmiller 66a6a2afc1 Merge pull request 'fix(build): Go 1.23 maps.Values compatibility' (#370) from dev into main
Deploy MokoGitea / deploy (push) Failing after 22s
2026-05-31 16:31:39 +00:00
jmiller 74935e3bed Merge pull request 'fix(licenses): remove duplicate DeleteLicenseKey (build fix)' (#358) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m39s
2026-05-31 16:07:13 +00:00
jmiller bc95ecf4d5 Merge pull request 'feat(updates): extension metadata settings, tab visibility, platform support' (#356) from dev into main
Deploy MokoGitea / deploy (push) Failing after 28s
2026-05-31 16:01:49 +00:00
jmiller a35fb4695c Merge pull request 'chore: sync dev to main (namespace rename + all fixes)' (#348) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m18s
2026-05-31 15:40:34 +00:00
jmiller 70c31a4953 Merge pull request 'fix(updates): correct dlid prefix and Joomla standard alignment' (#345) from dev into main
Deploy MokoGitea / deploy (push) Failing after 2m32s
2026-05-31 15:31:09 +00:00
jmiller 6c913abbda Merge pull request 'feat(licenses): plaintext key storage with copy buttons' (#342) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m35s
2026-05-31 15:08:02 +00:00
jmiller 878671ebc9 Merge pull request 'feat(licenses): platform enforcement, key deletion, expired key cleanup' (#340) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m4s
2026-05-31 15:03:46 +00:00
jmiller c7cfcf894b Merge pull request 'fix(licenses): remove repo unit requirement causing 404s' (#339) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m35s
2026-05-31 14:48:30 +00:00
jmiller bbe3e570fe Merge pull request 'chore: migrate namespace from git. to code.mokoconsulting.tech' (#337) from chore/namespace-migration into main
Deploy MokoGitea / deploy (push) Failing after 4m27s
2026-05-31 14:46:21 +00:00
Jonathan Miller 26bbe690fd chore: migrate namespace from git. to code.mokoconsulting.tech (#336)
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 28s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Update all URLs in manifest.xml and updates.xml to use the new
code.mokoconsulting.tech domain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-31 09:45:58 -05:00
jmiller bfa9043bc8 Merge pull request 'feat(licenses): UI/UX cleanup, permissions system, and key management improvements' (#306) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m35s
2026-05-31 14:22:04 +00:00
jmiller b1a9b09f5b Merge pull request 'chore: merge dev into main — toggle fix' (#295) from dev into main
Deploy MokoGitea / deploy (push) Failing after 3m59s
2026-05-31 04:22:47 +00:00
52 changed files with 1759 additions and 1192 deletions
+1 -1
View File
@@ -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.14.00</version>
<version>05.31.00</version>
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
</identity>
<governance>
+5 -3
View File
@@ -102,13 +102,14 @@ jobs:
run: |
php /tmp/moko-platform-api/cli/release_publish.php \
--path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--skip-update-stream
- name: Summary
if: always()
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC + lesser stream releases built, updates.xml synced" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
release:
@@ -167,7 +168,8 @@ jobs:
run: |
php /tmp/moko-platform-api/cli/release_publish.php \
--path . --stability stable --bump minor --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--skip-update-stream
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# VERSION: 05.14.00
# VERSION: 05.31.00
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
+231
View File
@@ -147,6 +147,98 @@ jobs:
echo "PHP lint: ${ERRORS} error(s)"
[ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; }
- name: Joomla JEXEC guard check
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
# Skip vendor, node_modules, and index.html stub files
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
# Check first 10 lines for JEXEC or JPATH guard
if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then
echo "::error file=${file}::Missing JEXEC guard: ${file}"
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0)
if [ "$ERRORS" -gt 0 ]; then
echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard"
echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY
echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "JEXEC guard: OK"
- name: Joomla directory listing protection
if: steps.platform.outputs.platform == 'joomla'
run: |
MISSING=0
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && exit 0
while IFS= read -r dir; do
if [ ! -f "${dir}/index.html" ]; then
echo "::warning::Missing index.html in ${dir} (directory listing protection)"
MISSING=$((MISSING + 1))
fi
done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*")
if [ "$MISSING" -gt 0 ]; then
echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY
echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY
fi
echo "Directory protection: ${MISSING} missing (advisory)"
- name: Joomla script file and asset checks
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
[ -z "$MANIFEST" ] && exit 0
MANIFEST_DIR=$(dirname "$MANIFEST")
# Check scriptfile exists if declared
SCRIPTFILE=$(sed -n 's/.*<scriptfile>\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null)
if [ -n "$SCRIPTFILE" ]; then
if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then
echo "::error::Manifest declares <scriptfile>${SCRIPTFILE}</scriptfile> but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}"
ERRORS=$((ERRORS + 1))
else
echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)"
fi
fi
# Require joomla.asset.json and validate it
ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1)
if [ -z "$ASSET_JSON" ]; then
echo "::error::joomla.asset.json not found — Joomla asset system is required"
ERRORS=$((ERRORS + 1))
else
if command -v php &> /dev/null; then
php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || {
echo "::error::joomla.asset.json is not valid JSON"
ERRORS=$((ERRORS + 1))
}
fi
echo "joomla.asset.json: valid"
fi
# Validate all XML files in src/ are well-formed
XML_ERRORS=0
if command -v php &> /dev/null; then
while IFS= read -r -d '' xmlfile; do
if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then
XML_ERRORS=$((XML_ERRORS + 1))
fi
done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0)
fi
if [ "$XML_ERRORS" -gt 0 ]; then
echo "::error::${XML_ERRORS} XML file(s) are malformed"
ERRORS=$((ERRORS + 1))
else
echo "XML well-formedness: OK"
fi
[ "$ERRORS" -gt 0 ] && exit 1
echo "Joomla asset checks: OK"
- name: Validate platform manifest
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
@@ -164,6 +256,13 @@ jobs:
for ELEMENT in name version description; do
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
done
# Block legacy raw/branch update server URLs on MokoGitea
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true)
if [ -n "$RAW_URLS" ]; then
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
echo "$RAW_URLS"
exit 1
fi
echo "Joomla manifest valid"
;;
dolibarr)
@@ -196,6 +295,138 @@ jobs:
;;
esac
- name: Validate Joomla language files
if: steps.platform.outputs.platform == 'joomla'
run: |
ERRORS=0
WARNINGS=0
# Require both en-GB and en-US language directories
LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1)
if [ -z "$LANG_ROOT" ]; then
echo "No language/ directory found — skipping"
exit 0
fi
if [ ! -d "$LANG_ROOT/en-GB" ]; then
echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)"
ERRORS=$((ERRORS + 1))
fi
if [ ! -d "$LANG_ROOT/en-US" ]; then
echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)"
ERRORS=$((ERRORS + 1))
fi
# Check that en-GB and en-US have matching .ini files
if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then
for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do
[ ! -f "$GB_INI" ] && continue
US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")"
if [ ! -f "$US_INI" ]; then
echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US"
ERRORS=$((ERRORS + 1))
fi
done
for US_INI in "$LANG_ROOT/en-US"/*.ini; do
[ ! -f "$US_INI" ] && continue
GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")"
if [ ! -f "$GB_INI" ]; then
echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB"
ERRORS=$((ERRORS + 1))
fi
done
fi
# Find all .ini language files
INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null)
if [ -z "$INI_FILES" ]; then
echo "No .ini language files found"
[ "$ERRORS" -gt 0 ] && exit 1
exit 0
fi
echo "Found $(echo "$INI_FILES" | wc -l) language file(s)"
for FILE in $INI_FILES; do
FNAME=$(basename "$FILE")
LINENUM=0
SEEN_KEYS=""
while IFS= read -r line || [ -n "$line" ]; do
LINENUM=$((LINENUM + 1))
# Skip empty lines and comments
[ -z "$line" ] && continue
echo "$line" | grep -qE '^\s*;' && continue
echo "$line" | grep -qE '^\s*$' && continue
# Must match KEY="VALUE" format
if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then
echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}"
ERRORS=$((ERRORS + 1))
continue
fi
# Extract key and check for duplicates
KEY=$(echo "$line" | sed 's/=.*//')
if echo "$SEEN_KEYS" | grep -qx "$KEY"; then
echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}"
ERRORS=$((ERRORS + 1))
fi
SEEN_KEYS="${SEEN_KEYS}
${KEY}"
done < "$FILE"
echo " ${FILE}: checked ${LINENUM} lines"
done
# Cross-check en-GB vs en-US key consistency
GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1)
US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1)
if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then
for GB_FILE in "$GB_DIR"/*.ini; do
[ ! -f "$GB_FILE" ] && continue
FNAME=$(basename "$GB_FILE")
US_FILE="$US_DIR/$FNAME"
[ ! -f "$US_FILE" ] && continue
GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort)
US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort)
# Keys in en-GB but not en-US
MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
if [ -n "$MISSING_US" ]; then
echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:"
echo "$MISSING_US" | while read -r k; do echo " - $k"; done
WARNINGS=$((WARNINGS + 1))
fi
# Keys in en-US but not en-GB
MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
if [ -n "$MISSING_GB" ]; then
echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:"
echo "$MISSING_GB" | while read -r k; do echo " - $k"; done
WARNINGS=$((WARNINGS + 1))
fi
done
fi
{
echo "### Language File Validation"
echo "| Metric | Count |"
echo "|---|---|"
echo "| Files checked | $(echo "$INI_FILES" | wc -l) |"
echo "| Errors | ${ERRORS} |"
echo "| Warnings | ${WARNINGS} |"
} >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -gt 0 ]; then
echo "::error::Language validation failed with ${ERRORS} error(s)"
exit 1
fi
echo "Language files: OK (${WARNINGS} warning(s))"
- name: Check changelog has unreleased entry
run: |
if [ ! -f "CHANGELOG.md" ]; then
+71 -502
View File
@@ -1,9 +1,9 @@
# Changelog
This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com).
## [v1.26.1-moko.06.02.00] - 2026-06-02
All notable changes to MokoGitea are documented here. Versions follow the format
`v{upstream}-moko.{major}.{minor}` (e.g. `v1.26.1-moko.06.03`).
## [v1.26.1-moko.06.03] - 2026-06-04
* FEATURES
* feat(licenses): full commercial license management system
@@ -11,40 +11,53 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Search keys by customer, domain, key number, email, or payment ref
* Download gating (none/prerelease/all modes)
* Domain lock grace period (DomainLockHours)
* Domain restriction on packages and keys (comma-separated allowed domains)
* RepoScope enforcement — packages scoped to specific repos
* Configurable license key prefix per organization
* Master key auto-generates, sorts first in key list
* License package creation at repo level via modal
* Key generation modal with licensee name, email, and domain fields
* Manual release-to-stream mapping with UI selector
* Joomla changelog XML endpoint (/changelog.xml)
* SHA256 checksums from sidecar files in Joomla updates.xml
* Joomla-standard tag values (dev/alpha/beta/rc/stable)
* Double confirmation modals for permanent deletion
* Combolist channel picker (replaces checkboxes)
* Extension metadata in repo settings (per-repo override)
* API: package CRUD, key revoke, key renew, settings GET/PUT
* API: purchase webhook with PaymentRef idempotency
* API: public validation endpoint (no auth)
* Migration v340-v342: all new columns synced
* feat(updates): 7 platform update feeds
* Joomla XML with downloadkey, SHA256, changelog URL
* Migration v340-v344: all new columns synced
* feat(updates): Update Server system (renamed from "Licensing")
* Joomla XML with SHA256, changelog URL, version from asset filename
* Dolibarr JSON with channel filtering
* WordPress PUC-compatible JSON (plugin-update-checker)
* Composer packages.json
* PrestaShop module update XML
* Drupal update status XML
* WHMCS module update JSON
* feat(updates): feed always public — downloads gated separately
* feat(updates): stream-name tags supported alongside version tags
* feat(updates): version extraction via regex from release titles
* feat(updates): infourl defaults to release listing / support URL
* feat(updates): downloadkey prefix matches Akeeba pattern (dlid=)
* Feed always public — downloads gated separately
* Stream-name tags supported alongside version tags
* Omit `<client>` for package extension types
* `<downloadkey>` only when download_gating is prerelease or all
* Version extracted from asset filename (matches actual download)
* Joomla tag values verified: dev, alpha, beta, rc, stable
* feat(orgs): enterprise sub-org hierarchy with parent-child relationships
* feat(repos): three-level visibility — Public (200), Private (403), Hidden (404)
* feat(settings): separate licensing settings page (/settings/licensing)
* feat(settings): advanced settings on dedicated page (/settings/advanced)
* feat(settings): section headers with dividers and icons
* feat(ui): icons on all settings navbars (repo, org, user, admin)
* feat(settings): Update Server settings page with enable toggle in Advanced Settings
* feat(settings): advanced settings on dedicated page with dividing headers
* feat(settings): icons on all settings navbars (repo, org, user, admin)
* feat(ui): styled 403 Access Denied page with inline login form
* feat(ui): open-in-new-tab button on feed URLs
* feat(issues): custom fields with inline editing in issue sidebar
* feat(issues): pre-fill custom fields from issue template YAML frontmatter (#493)
* Templates specify `custom_fields:` map (field name → default value)
* New issue sidebar shows org-level fields with template defaults pre-selected
* API create issue accepts `custom_fields` map by name
* feat(updateserver): resolve extension metadata from org-level custom fields (#492)
* Cascading fallback: custom fields → config table → repo-derived defaults
* All six generators updated (Joomla, WordPress, Composer, Drupal, PrestaShop, WHMCS)
* Repos can be migrated to custom fields gradually
* feat(ui): two-in-one Update Server / Licenses tab
* No gating: shows "Update Server" tab with feed URLs only
* Gated: shows "Licenses" tab with full key management
* `<downloadkey>` only appears when downloads are gated
* SECURITY
* fix(security): ownership guards on all API handlers (cross-org prevention)
* fix(security): RepoScope JSON parsing (substring matching bug)
@@ -55,48 +68,50 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* fix(security): licensed private repos allow release viewing for signed-in users
* fix(security): anonymous download access respects download_gating setting
* FIXES
* fix(licenses): expanded delete permissions to org owners + site admins
* fix(licenses): explicit xorm column names for UpdateStreamConfig fields
* fix(licenses): feed always public when licensing enabled
* fix(settings): prevent double-highlight on Advanced Settings nav item
* fix(settings): redirect back to /settings/advanced after save
* fix(build): remove stale custom field API routes and dead code
* fix(build): replace invalid UTF-8 character in API comment
* fix(build): permanent fixes for AI migration, feed/file.go, unused imports
* fix(updateserver): version extracted from asset filename (not release title)
* fix(updateserver): omit `<client>` for package types per Joomla spec
* fix(updateserver): `<downloadkey>` only shown when downloads are gated
* fix(updateserver): prevent stream name tag from overriding asset-derived version
* fix(build): restore build/ directory after accidental deletion
* fix(licenses): master key banner removed, master keys sort first in table
* fix(issues): issue sidebar loads org-level fields instead of legacy repo-level fields
## [v1.26.1-moko.05.15.00] - 2026-05-31
## [v1.26.1-moko.05] - 2026-05-31
* BREAKING CHANGES
* Deprecated Issue.Ref branch selector UI (#307)
* Removed branch/tag selector from issue sidebar and new issue form
* Removed ref badge from issue lists
* Removed POST /ref web route and UpdateIssueRef handler
* DB column and commit-close logic preserved for backward compatibility
* API create/edit still accept `ref` field (no-op) for backward compat
* FEATURES
* feat(ui): add generic combo-multiselect component (#361)
* feat(ui): generic combo-multiselect component (#361)
* Reusable dropdown with search, checkable items, and selected-items display
* Template: `shared/combolist.tmpl` — accepts Items, Name, Title, SelectedValues
* Decoupled from issue sidebar — works in any form context
* Template: `shared/combolist.tmpl`
* feat(updates): extension metadata settings for update feed generation
* feat(licenses): platform enforcement, key deletion, expired key cleanup
* feat(licenses): store keys in plaintext, show full key with copy button
* feat(actions): rebrand actions bot user to mokogitea-actions (#233, #234)
* Backward-compatible: recognizes github-actions[bot], gitea-actions[bot]
* feat(actions): actions bot user in branch protection whitelist (#233, #234)
* WhitelistActionsUser, MergeWhitelistActionsUser, ForcePushAllowlistActionsUser
* TECH DEBT
* chore: full namespace migration from git.mokoconsulting.tech to code.mokoconsulting.tech (#336, #337, #344)
* Go module path, all imports, template URLs, workflow configs (2,276 files)
* chore: full namespace migration to code.mokoconsulting.tech (#336, #337, #344)
* fix(blame): set HasSourceRenderedToggle for renderable files (#344)
* fix(settings): translate team permission strings via data-locale attributes (#344)
* fix(settings): translate team permission strings via data-locale (#344)
* fix(dropzone): use relative path for non-image attachment markdown links (#344)
* fix(templates): add required validation to issue dropdown fields (#350)
* refactor(ts): remove redundant `handled` field from MarkdownHandleIndentionResult (#350)
* refactor(go): rename HasOrgOrUserVisible to IsOwnerVisibleToDoer (#350)
* refactor(go): replace ValuesRepository with maps.Values (Go 1.21+) (#357)
* refactor(go): remove CanEnableEditor wrapper, use CanContentChange directly (#357)
* fix(ts): parseIssueHref now uses URL pathname and trims appSubUrl (#360)
* fix(actions): enforce MaxJobNumPerRun (256) limit when creating jobs (#360)
* refactor(go): remove CanEnableEditor wrapper (#357)
* fix(ts): parseIssueHref uses URL pathname and trims appSubUrl (#360)
* fix(actions): enforce MaxJobNumPerRun (256) limit (#360)
* fix(css): use calc(infinity * 1px) for --border-radius-full (#361)
* fix(css): remove legacy .center class from 2015, replace with tw-text-center (#361)
* chore: remove stale TODO from OAuth2 regenerate secret (already implemented) (#332)
* chore: remove stale pull request test stub TODOs (#328)
* chore: remove stale GetProjectsMode TODO
* chore: remove stale mustNotBeArchived/mustEnableEditor FIXME from API
* fix(routes): remove dead legacy /cherry-pick/{sha} route (replaced by /_cherrypick/)
* fix(css): remove legacy .center class, replace with tw-text-center (#361)
* fix(routes): remove dead legacy /cherry-pick/{sha} route
* fix(feed): use full ref name instead of ShortName for file feed revision
* BUGFIXES
* fix(build): use slices.Collect for maps.Values (Go 1.23+ compat)
@@ -104,25 +119,10 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* fix(licenses): only show licenses tab when licensing is enabled
* fix(licenses): show feed URLs based on repo update platform setting
* fix(updates): correct dlid prefix and align XML with Joomla standard
## [v1.26.1-moko.05.06.00] - 2026-05-30
* FEATURES
* feat(actions): rebrand actions bot user to mokogitea-actions (#233, #234)
* Name: gitea-actions → mokogitea-actions, FullName: MokoGitea Actions
* Email: mokogitea-actions[bot]@mokoconsulting.tech
* Backward-compatible: recognizes github-actions[bot], gitea-actions[bot], mokogitea-actions[bot]
* feat(actions): add actions bot user to branch protection whitelist (#233, #234)
* New toggles: WhitelistActionsUser, MergeWhitelistActionsUser, ForcePushAllowlistActionsUser
* Allows CI/CD workflows to push/merge/force-push to protected branches when enabled
* DB migration v334 adds the three boolean columns
* Exposed in API (create/edit branch protection) and web UI settings
* INFRASTRUCTURE
* fix(ci): auto-deploy to production on merge to main (#235)
* Deploy workflow now triggers on push to main, not just manual dispatch
* Version derived from git describe for auto-deploys
## [v1.26.1-moko.04.00.00] - 2026-05-24
## [v1.26.1-moko.04] - 2026-05-24
* SECURITY
* Backport 12 upstream v1.26.2 security fixes:
@@ -131,468 +131,37 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* OAuth PKCE hardening and refresh token replay protection (#142)
* Wiki git write and LFS token access enforcement (#143)
* Public-only token filtering in API queries (#144)
* Reading permission fix (#145)
* Artifact signature payload hardening (#146)
* AWS credentials encryption (#161)
* Mermaid v11.15.0 security update (#162)
* Composer package permission check (#164)
* BUGFIXES
* fix(actions): nil pointer dereference in concurrency during PR creation (#136)
* fix(ui): actions runs list broken row layout — CSS class mismatch (#138)
* fix: scheduled action panic with null event payload (upstream #37459)
* fix: treat email addresses case-insensitively (upstream #37600)
* fix(ui): actions runs list broken row layout (#138)
* fix: scheduled action panic with null event payload
* fix: treat email addresses case-insensitively
* fix: .mod lexer panic — removed invalid AMPL mapping
* fix: remove unused setting import in action.go
* fix: restore Permission field access in context middleware
* FEATURES
* Joomla-style updates.xml with channel selection (stable/dev/security/rc)
* Update checker reads from updates.xml with configurable CHANNEL setting
* Admin dashboard shows update banner with channel name and docker pull command
* Upstream bug sync workflow — daily automated issue creation from release/v1.26
* Joomla-style updates.xml with channel selection
* Update checker with configurable CHANNEL setting
* Admin dashboard update banner with docker pull command
* Upstream bug sync workflow — daily automated issue creation
* PR RC release workflow — auto-build RC on PR to main
* INFRASTRUCTURE
* New 3-part versioning: v{upstream}-moko.{major}.{minor}.{patch}
* Branding updates: error pages, home page, settings link to MokoGitea
* Branding updates: error pages, home page, settings link
* Deploy workflow updated for new version format
* PROCESS
* Created `type: bug` and `upstream` labels for automated issue tracking
* Deduplicated 19 duplicate feature request issues
* Closed 24 upstream bug/security issues after backporting
## [MokoGitea Unreleased]
## [v1.26.1-moko.03] - 2026-05-15
* FEATURES
* feat(api): Bulk issue operations — add/remove/replace labels, close/reopen, set milestone, and set assignees across multiple issues in a single request (#21)
* `POST /api/v1/repos/{owner}/{repo}/issues/bulk/labels`
* `POST /api/v1/repos/{owner}/{repo}/issues/bulk/state`
* `POST /api/v1/repos/{owner}/{repo}/issues/bulk/milestone`
* `POST /api/v1/repos/{owner}/{repo}/issues/bulk/assignees`
* Partial-failure support: returns per-issue success/failure map
* feat(api): Bulk issue operations — add/remove/replace labels, close/reopen, set milestone, assignees (#21)
* INFRASTRUCTURE
* Grafana: Standardized kiosk header across all 14 playlist dashboards — each now shows dashboard name, kiosk link, terminal/exit/switch instructions
* Grafana: Standardized kiosk header across all 14 playlist dashboards
* PROCESS
* Reopened 9 closed issues lacking documented testing proof (#3, #5, #38, #41, #70, #74, #75, #76, #78)
* Reopened 9 closed issues lacking documented testing proof
* Created `pending: testing` label for features awaiting verification
* Established policy: issues must not be closed without documented testing proof
## [1.26.1](https://github.com/go-gitea/gitea/releases/tag/v1.26.1) - 2026-04-21
* BUGFIXES
* Add event.schedule context for schedule actions task (#37320) (#37348)
* Fix an issue where changing an organization's visibility caused problems when users had forked its repositories. (#37324) (#37344)
* Use modern "git update-index --cacheinfo" syntax to support more file names (#37338) (#37343)
* Fix URL related escaping for oauth2 (#37334) (#37340)
* When the requested arch rpm is missing fall back to noarch (#37236) (#37339)
* Fix actions concurrency groups cross-branch leak (#37311) (#37331)
* Fix bug when accessing user badges (#37321) (#37329)
* Fix AppFullLink (#37325) (#37328)
* Fix container auth for public instance (#37290) (#37294)
* Enhance GetActionWorkflow to support fallback references (#37189) (#37283)
* Fix vite manifest update masking build errors (#37279) (#37310)
* Fix Mermaid diagrams failing when node labels contain line breaks (#37296) (#37299)
* Use TriggerEvent instead of Event in workflow runs API response for scheduled runs (#37288) #37360
* Add URL to Learn more about blocking a user. (#37355) #37367
* Fix button layout shift when collapsing file tree in editor (#37363) #37375
* Fix org team assignee/reviewer lookups for team member permissions (#37365) #37391
* Fix repo init README EOL (#37388) #37399
* Fix: dump with default zip type produces uncompressed zip (#37401)#37402
## [1.26.0](https://github.com/go-gitea/gitea/releases/tag/v1.26.0) - 2026-04-17
* BREAKING
* Correct swagger annotations for enums, status codes, and notification state (#37030)
* Remove GET API registration-token (#36801)
* Support Actions `concurrency` syntax (#32751)
* Make PUBLIC_URL_DETECTION default to "auto" (#36955)
* SECURITY
* Bound PageSize in `ListUnadoptedRepositories` (#36884)
* FEATURES
* Support Actions `concurrency` syntax (#32751)
* Add terraform state registry (#36710)
* Instance-wide (global) info banner and maintenance mode (#36571)
* Support rendering OpenAPI spec (#36449)
* Add keyboard shortcuts for repository file and code search (#36416)
* Add support for archive-upload rpc (#36391)
* Add ability to download subpath archive (#36371)
* Add workflow dependencies visualization (#26062) (#36248) & Restyle Workflow Graph (#36912)
* Automatic generation of release notes (#35977)
* Add "Go to file", "Delete Directory" to repo file list page (#35911)
* Introduce "config edit-ini" sub command to help maintaining INI config file (#35735)
* Add button to re-run failed jobs in Actions (#36924)
* Support actions and reusable workflows from private repos (#32562)
* Add summary to action runs view (#36883)
* Add user badges (#36752)
* Add configurable permissions for Actions automatic tokens (#36173)
* Add per-runner "Disable/Pause" (#36776)
* Feature non-zipped actions artifacts (action v7 / nodejs / npm v6.2.0) (#36786)
* PERFORMANCE
* WorkflowDispatch API optionally return runid (#36706)
* Add render cache for SVG icons (#36863)
* Load `mentionValues` asynchronously (#36739)
* Lazy-load some Vue components, fix heatmap chunk loading on every page (#36719)
* Load heatmap data asynchronously (#36622)
* Use prev/next pagination for user profile activities page to speed up (#36642)
* Refactor cat-file batch operations and support `--batch-command` approach (#35775)
* Use merge tree to detect conflicts when possible (#36400)
* ENHANCEMENTS
* Implement logout redirection for reverse proxy auth setups (#36085) (#37171)
* Adds option to force update new branch in contents routes (#35592)
* Add viewer controller for mermaid (zoom, drag) (#36557)
* Add code editor setting dropdowns (#36534)
* Add `elk` layout support to mermaid (#36486)
* Add resolve/unresolve review comment API endpoints (#36441)
* Allow configuring default PR base branch (fixes #36412) (#36425)
* Add support for RPM Errata (updateinfo.xml) (#37125)
* Require additional user confirmation for making repo private (#36959)
* Add `actions.WORKFLOW_DIRS` setting (#36619)
* Avoid opening new tab when downloading actions logs (#36740)
* Implements OIDC RP-Initiated Logout (#36724)
* Show workflow link (#37070)
* Desaturate dark theme background colors (#37056)
* Refactor "org teams" page and help new users to "add member" to an org (#37051)
* Add webhook name field to improve webhook identification (#37025) (#37040)
* Make task list checkboxes clickable in the preview tab (#37010)
* Improve severity labels in Actions logs and tweak colors (#36993)
* Linkify URLs in Actions workflow logs (#36986)
* Allow text selection on checkbox labels (#36970)
* Support dark/light theme images in markdown (#36922)
* Enable native dark mode for swagger-ui (#36899)
* Rework checkbox styling, remove `input` border hover effect (#36870)
* Refactor storage content-type handling of ServeDirectURL (#36804)
* Use "Enable Gravatar" but not "Disable" (#36771)
* Use case-insensitive matching for Git error "Not a valid object name" (#36728)
* Add "Copy Source" to markup comment menu (#36726)
* Change image transparency grid to CSS (#36711)
* Add "Run" prefix for unnamed action steps (#36624)
* Persist actions log time display settings in `localStorage` (#36623)
* Use first commit title for multi-commit PRs and fix auto-focus title field (#36606)
* Improve BuildCaseInsensitiveLike with lowercase (#36598)
* Improve diff highlighting (#36583)
* Exclude cancelled runs from failure-only email notifications (#36569)
* Use full-file highlighting for diff sections (#36561)
* Color command/error logs in Actions log (#36538)
* Add paging headers (#36521)
* Improve timeline entries for WIP prefix changes in pull requests (#36518)
* Add FOLDER_ICON_THEME configuration option (#36496)
* Normalize guessed languages for code highlighting (#36450)
* Add chunked transfer encoding support for LFS uploads (#36380)
* Indicate when only optional checks failed (#36367)
* Add 'allow_maintainer_edit' API option for creating a pull request (#36283)
* Support closing keywords with URL references (#36221)
* Improve diff file headers (#36215)
* Fix and enhance comment editor monospace toggle (#36181)
* Add git.DIFF_RENAME_SIMILARITY_THRESHOLD option (#36164)
* Add matching pair insertion to markdown textarea (#36121)
* Add sorting/filtering to admin user search API endpoint (#36112)
* Allow action user have read permission in public repo like other user (#36095)
* Disable matchBrackets in monaco (#36089)
* Use GitHub-style commit message for squash merge (#35987)
* Make composer registry support tar.gz and tar.bz2 and fix bugs (#35958)
* Add GITEA_PR_INDEX env variable to githooks (#35938)
* Add proper error message if session provider can not be created (#35520)
* Add button to copy file name in PR files (#35509)
* Move `X_FRAME_OPTIONS` setting from `cors` to `security` section (#30256)
* Add placeholder content for empty content page (#37114)
* Add `DEFAULT_DELETE_BRANCH_AFTER_MERGE` setting (#36917)
* Redirect to the only OAuth2 provider when no other login methods and fix various problems (#36901)
* Add admin badge to navbar avatar (#36790)
* Add `never` option to `PUBLIC_URL_DETECTION` configuration (#36785)
* Add background and run count to actions list page (#36707)
* Add icon to buttons "Close with Comment", "Close Pull Request", "Close Issue" (#36654)
* Add support for in_progress event in workflow_run webhook (#36979)
* Report commit status for pull_request_review events (#36589)
* Render merged pull request title as such in dashboard feed (#36479)
* Feature to be able to filter project boards by milestones (#36321)
* Use user id in noreply emails (#36550)
* Enable pagination on GiteaDownloader.getIssueReactions() (#36549)
* Remove striped tables in UI (#36509)
* Improve control char rendering and escape button styling (#37094)
* Support legacy run/job index-based URLs and refactor migration 326 (#37008)
* Add date to "No Contributions" tooltip (#36190)
* Show edit page confirmation dialog on tree view file change (#36130)
* Mention proc-receive in text for dashboard.resync_all_hooks func (#35991)
* Reuse selectable style for wiki (#35990)
* Support blue yellow colorblind theme (#35910)
* Support selecting theme on the footer (#35741)
* Improve online runner check (#35722)
* Add quick approve button on PR page (#35678)
* Enable commenting on expanded lines in PR diffs (#35662)
* Print PR-Title into tooltip for actions (#35579)
* Use explicit, stronger defaults for newly generated repo signing keys for Debian (#36236)
* Improve the compare page (#36261)
* Unify repo names in system notices (#36491)
* Move package settings to package instead of being tied to version (#37026)
* Add Actions API rerun endpoints for runs and jobs (#36768)
* Add branch_count to repository API (#35351) (#36743)
* Add created_by filter to SearchIssues (#36670)
* Allow admins to rename non-local users (#35970)
* Support updating branch via API (#35951)
* Add an option to automatically verify SSH keys from LDAP (#35927)
* Make "update file" API can create a new file when SHA is not set (#35738)
* Update issue.go with labels documentation (labels content, not ids) (#35522)
* Expose content_version for optimistic locking on issue and PR edits (#37035)
* Pass ServeHeaderOptions by value instead of pointer, fine tune httplib tests (#36982)
* BUGFIXES
* Frontend iframe renderer framework: 3D models, OpenAPI (#37233) (#37273)
* Fix CODEOWNERS absolute path matching. (#37244) (#37264)
* Swift registry metadata: preserve more JSON fields and accept empty metadata (#37254) (#37261)
* Fix user ssh key exporting and tests (#37256) (#37258)
* Fix team member avatar size and add tooltip (#37253)
* Fix commit title rendering in action run and blame (#37243) (#37251)
* Fix corrupted JSON caused by goccy library (#37214) (#37220)
* Add test for "fetch redirect", add CSS value validation for external render (#37207) (#37216)
* Fix incorrect concurrency check (#37205) (#37215)
* Fix handle missing base branch in PR commits API (#37193) (#37203)
* Fix encoding for Matrix Webhooks (#37190) (#37201)
* Fix handle fork-only commits in compare API (#37185) (#37199)
* Indicate form field readonly via background, fix RunUser config (#37175, #37180) (#37178)
* Report structurally invalid workflows to users (#37116) (#37164)
* Fix API not persisting pull request unit config when has_pull_requests is not set (#36718)
* Rename CSS variables and improve colorblind themes (#36353)
* Hide `add-matcher` and `remove-matcher` from actions job logs (#36520)
* Prevent navigation keys from triggering actions during IME composition (#36540)
* Fix vertical alignment of `.commit-sign-badge` children (#36570)
* Fix duplicate startup warnings in admin panel (#36641)
* Fix CODEOWNERS review request attribution using comment metadata (#36348)
* Fix HTML tags appearing in wiki table of contents (#36284)
* Fix various bugs (#37096)
* Fix various legacy problems (#37092)
* Fix RPM Registry 404 when package name contains 'package' (#37087)
* Merge some standalone Vite entries into index.js (#37085)
* Fix various problems (#37077)
* Fix issue label deletion with Actions tokens (#37013)
* Hide delete branch or tag buttons in mirror or archived repositories. (#37006)
* Fix org contact email not clearable once set (#36975)
* Fix a bug when forking a repository in an organization (#36950)
* Preserve sort order of exclusive labels from template repo (#36931)
* Make container registry support Apple Container (basic auth) (#36920)
* Fix the wrong push commits in the pull request when force push (#36914)
* Add class "list-header-filters" to the div for projects (#36889)
* Fix dbfs error handling (#36844)
* Fix incorrect viewed files counter if reverted change was viewed (#36819)
* Refactor avatar package, support default avatar fallback (#36788)
* Fix README symlink resolution in subdirectories like .github (#36775)
* Fix CSS stacking context issue in actions log (#36749)
* Add gpg signing for merge rebase and update by rebase (#36701)
* Delete non-exist branch should return 404 (#36694)
* Fix `TestActionsCollaborativeOwner` (#36657)
* Fix multi-arch Docker build SIGILL by splitting frontend stage (#36646)
* Fix linguist-detectable attribute being ignored for configuration files (#36640)
* Fix state desync in ComboMarkdownEditor (#36625)
* Unify DEFAULT_SHOW_FULL_NAME output in templates and dropdown (#36597)
* Pull Request Pusher should be the author of the merge (#36581)
* Fix various version parsing problems (#36553)
* Fix highlight diff result (#36539)
* Fix mirror sync parser and fix mirror messages (#36504)
* Fix bug when list pull request commits (#36485)
* Fix various bugs (#36446)
* Fix issue filter menu layout (#36426)
* Restrict branch naming when new change matches with protection rules (#36405)
* Fix link/origin referrer and login redirect (#36279)
* Generate IDs for HTML headings without id attribute (#36233)
* Use a migration test instead of a wrong test which populated the meta test repositories and fix a migration bug (#36160)
* Fix issue close timeline icon (#36138)
* Fix diff blob excerpt expansion (#35922)
* Fix external render (#35727)
* Fix review request webhook bug (#35339) (#35723)
* Fix shutdown waitgroup panic (#35676)
* Cleanup ActionRun creation (#35624)
* Fix possible bug when migrating issues/pull requests (#33487)
* Various fixes (#36697)
* Apply notify/register mail flags during install load (#37120)
* Repair duration display for bad stopped timestamps (#37121)
* Fix(upgrade.sh): use HTTPS for GPG key import and restore SELinux context after upgrade (#36930)
* Fix various trivial problems (#36921)
* Fix various trivial problems (#36953)
* Fix NuGet package upload error handling (#37074)
* Fix CodeQL code scanning alerts (#36858)
* Refactor issue sidebar and fix various problems (#37045)
* Fix various problems (#37029)
* Fix relative-time RangeError (#37021)
* Fix chroma lexer mapping (#36629)
* Fix typos and grammar in English locale (#36751)
* Fix milestone/project text overflow in issue sidebar (#36741)
* Fix `no-content` message not rendering after comment edit (#36733)
* Fix theme loading in development (#36605)
* Fix workflow run jobs API returning null steps (#36603)
* Fix timeline event layout overflow with long content (#36595)
* Fix minor UI issues in runner edit page (#36590)
* Fix incorrect vendored detections (#36508)
* Fix editorconfig not respected in PR Conversation view (#36492)
* Don't create self-references in merged PRs (#36490)
* Fix potential incorrect runID in run status update (#36437)
* Fix file-tree ui error when adding files to repo without commits (#36312)
* Improve image captcha contrast for dark mode (#36265)
* Fix panic in blame view when a file has only a single commit (#36230)
* Fix spelling error in migrate-storage cmd utility (#36226)
* Fix code highlighting on blame page (#36157)
* Fix nilnil in onedev downloader (#36154)
* Fix actions lint (#36029)
* Fix oauth2 session gob register (#36017)
* Fix Arch repo pacman.conf snippet (#35825)
* Fix a number of `strictNullChecks`-related issues (#35795)
* Fix URLJoin, markup render link reoslving, sign-in/up/linkaccount page common data (#36861)
* Hide delete directory button for mirror or archive repository and disable the menu item if user have no permission (#36384)
* Update message severity colors, fix navbar double border (#37019)
* Inline and lazy-load EasyMDE CSS, fix border colors (#36714)
* Closed milestones with no issues now show as 100% completed (#36220)
* Add test for ExtendCommentTreePathLength migration and fix bugs (#35791)
* Only turn links to current instance into hash links (#36237)
* Fix typos in code comments: doesnt, dont, wont (#36890)
* REFACTOR
* Clean up and improve non-gitea js error filter (#37148) (#37155)
* Always show owner/repo name in compare page dropdowns (#37172) (#37200)
* Remove dead CSS rules (#37173) (#37177)
* Replace Monaco with CodeMirror (#36764)
* Replace CSRF cookie with `CrossOriginProtection` (#36183)
* Replace index with id in actions routes (#36842)
* Remove unnecessary function parameter (#35765)
* Move jobparser from act repository to Gitea (#36699)
* Refactor compare router param parse (#36105)
* Optimize 'refreshAccesses' to perform update without removing then adding (#35702)
* Clean up checkbox cursor styles (#37016)
* Remove undocumented support of signing key in the repository git configuration file (#36143)
* Switch `cmd/` to use constructor functions. (#36962)
* Use `relative-time` to render absolute dates (#36238)
* Some refactors about GetMergeBase (#36186)
* Some small refactors (#36163)
* Use gitRepo as parameter instead of repopath when invoking sign functions (#36162)
* Move blame to gitrepo (#36161)
* Move some functions to gitrepo package to reduce RepoPath reference directly (#36126)
* Use gitrepo's clone and push when possible (#36093)
* Remove mermaid margin workaround (#35732)
* Move some functions to gitrepo package (#35543)
* Move GetDiverging functions to gitrepo (#35524)
* Use global lock instead of status pool for cron lock (#35507)
* Use explicit mux instead of DefaultServeMux (#36276)
* Use gitrepo's push function (#36245)
* Pass request context to generateAdditionalHeadersForIssue (#36274)
* Move assign project when creating pull request to the same database transaction (#36244)
* Move catfile batch to a sub package of git module (#36232)
* Use gitrepo.Repository instead of wikipath (#35398)
* Use experimental go json v2 library (#35392)
* Refactor template render (#36438)
* Refactor GetRepoRawDiffForFile to avoid unnecessary pipe or goroutine (#36434)
* Refactor text utility classes to Tailwind CSS (#36703)
* Refactor git command stdio pipe (#36422)
* Refactor git command context & pipeline (#36406)
* Refactor git command stdio pipe (#36393)
* Remove unused functions (#36672)
* Refactor Actions Token Access (#35688)
* Move commit related functions to gitrepo package (#35600)
* Move archive function to repo_model and gitrepo (#35514)
* Move some functions to gitrepo package (#35503)
* Use git model to detect whether branch exist instead of gitrepo method (#35459)
* Some refactor for repo path (#36251)
* Extract helper functions from SearchIssues (#36158)
* Refactor merge conan and container auth preserve actions taskID (#36560)
* Refactor Nuget Auth to reuse Basic Auth Token Validation (#36558)
* Refactor ActionsTaskID (#36503)
* Refactor auth middleware (#36848)
* Refactor code render and render control chars (#37078)
* Clean up AppURL, remove legacy origin-url webcomponent (#37090)
* Remove `util.URLJoin` and replace all callers with direct path concatenation (#36867)
* Replace legacy tw-flex utility classes with flex-text-block/inline (#36778)
* Mark unused&immature activitypub as "not implemented" (#36789)
* TESTING
* Add e2e tests for server push events (#36879)
* Rework e2e tests (#36634)
* Add e2e reaction test, improve accessibility, enable parallel testing (#37081)
* Increase e2e test timeouts on CI to fix flaky tests (#37053)
* BUILD
* Upgrade go-git to v5.18.0 (#37269)
* Replace rollup-plugin-license with rolldown-license-plugin (#37130) (#37158)
* Bump min go version to 1.26.2 (#37139) (#37143)
* Convert locale files from ini to json format (#35489)
* Bump golangci-lint to 2.7.2, enable modernize stringsbuilder (#36180)
* Port away from `flake-utils` (#35675)
* Remove nolint (#36252)
* Update the Unlicense copy to latest version (#36636)
* Update to go 1.26.0 and golangci-lint 2.9.0 (#36588)
* Replace `google/go-licenses` with custom generation (#36575)
* Update go dependencies (#36548)
* Bump appleboy/git-push-action from 1.0.0 to 1.2.0 (#36306)
* Remove fomantic form module (#36222)
* Bump setup-node to v6, re-enable cache (#36207)
* Bump crowdin/github-action from 1 to 2 (#36204)
* Revert "Bump alpine to 3.23 (#36185)" (#36202)
* Update chroma to v2.21.1 (#36201)
* Bump astral-sh/setup-uv from 6 to 7 (#36198)
* Bump docker/build-push-action from 5 to 6 (#36197)
* Bump aws-actions/configure-aws-credentials from 4 to 5 (#36196)
* Bump dev-hanz-ops/install-gh-cli-action from 0.1.0 to 0.2.1 (#36195)
* Add JSON linting (#36192)
* Enable dependabot for actions (#36191)
* Bump alpine to 3.23 (#36185)
* Update chroma to v2.21.0 (#36171)
* Update JS deps and eslint enhancements (#36147)
* Update JS deps (#36091)
* update golangci-lint to v2.7.0 (#36079)
* Update JS deps, fix deprecations (#36040)
* Update JS deps (#35978)
* Add toolchain directive to go.mod (#35901)
* Move `gitea-vet` to use `go tool` (#35878)
* Update to go 1.25.4 (#35877)
* Enable TypeScript `strictNullChecks` (#35843)
* Enable `vue/require-typed-ref` eslint rule (#35764)
* Update JS dependencies (#35759)
* Move `codeformat` folder to tools (#35758)
* Update dependencies (#35733)
* Bump happy-dom from 20.0.0 to 20.0.2 (#35677)
* Bump setup-go to v6 (#35660)
* Update JS deps, misc tweaks (#35643)
* Bump happy-dom from 19.0.2 to 20.0.0 (#35625)
* Use bundled version of spectral (#35573)
* Update JS and PY deps (#35565)
* Bump github.com/wneessen/go-mail from 0.6.2 to 0.7.1 (#35557)
* Migrate from webpack to vite (#37002)
* Update JS dependencies and misc tweaks (#37064)
* Update to eslint 10 (#36925)
* Optimize Docker build with dependency layer caching (#36864)
* Update JS deps (#36850)
* Update tool dependencies and fix new lint issues (#36702)
* Remove redundant linter rules (#36658)
* Move Fomantic dropdown CSS to custom module (#36530)
* Remove and forbid `@ts-expect-error` (#36513)
* Refactor git command stderr handling (#36402)
* Enable gocheckcompilerdirectives linter (#36156)
* Replace `lint-go-gopls` with additional `govet` linters (#36028)
* Update golangci-lint to v2.6.0 (#35801)
* Misc tool tweaks (#35734)
* Add cache to container build (#35697)
* Upgrade vite (#37126)
* Update `setup-uv` to v8.0.0 (#37101)
* Upgrade `go-git` to v5.17.2 and related dependencies (#37060)
* Raise minimum Node.js version to 22.18.0 (#37058)
* Upgrade `golang.org/x/image` to v0.38.0 (#37054)
* Update minimum go version to 1.26.1, golangci-lint to 2.11.2, fix test style (#36876)
* Enable eslint concurrency (#36878)
* Vendor relative-time-element as local web component (#36853)
* Update material-icon-theme v5.32.0 (#36832)
* Update Go dependencies (#36781)
* Upgrade minimatch (#36760)
* Remove i18n backport tool at the moment because of translation format changed (#36643)
* Update emoji data for Unicode 16 (#36596)
* Update JS dependencies, adjust webpack config, misc fixes (#36431)
* Update material-icon-theme to v5.31.0 (#36427)
* Update JS and PY deps (#36383)
* Bump alpine to 3.23, add platforms to `docker-dryrun` (#36379)
* Update JS deps (#36354)
* Update goldmark to v1.7.16 (#36343)
* Update chroma to v2.22.0 (#36342)
* DOCS
* Update AI Contribution Policy (#37022)
* Update AGENTS.md with additional guidelines (#37018)
* Add missing cron tasks to example ini (#37012)
* Add AI Contribution Policy to CONTRIBUTING.md (#36651)
* Minor punctuation improvement in CONTRIBUTING.md (#36291)
* Add documentation for markdown anchor post-processing (#36443)
* MISC
* Correct spelling (#36783)
* Update Nix flake (#37110)
* Update Nix flake (#37024)
* Add valid github scopes (#36977)
* Update Nix flake (#36943)
* Update Nix flake (#36902)
* Update Nix flake (#36857)
* Update Nix flake (#36787)
+80 -20
View File
@@ -27,14 +27,26 @@ const (
CustomFieldTypeURL CustomFieldType = "url"
)
// CustomFieldDef defines a custom field available for issues in a repository.
// CustomFieldScope determines where the field appears.
type CustomFieldScope string
const (
CustomFieldScopeIssue CustomFieldScope = "issue" // appears in issue sidebar
CustomFieldScopeRepo CustomFieldScope = "repo" // appears in repo settings metadata
)
// CustomFieldDef defines a custom field at the org level.
// owner_id = org ID, scope = issue or repo.
// repo_id is kept for backward compat but 0 for org-level definitions.
type CustomFieldDef struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL 'repo_id'"`
OwnerID int64 `xorm:"INDEX NOT NULL DEFAULT 0 'owner_id'"` // org that owns this field
RepoID int64 `xorm:"INDEX NOT NULL DEFAULT 0 'repo_id'"` // 0 = org-level (inherited by all repos)
Scope CustomFieldScope `xorm:"VARCHAR(10) NOT NULL DEFAULT 'issue' 'scope'"`
Name string `xorm:"NOT NULL"`
FieldType CustomFieldType `xorm:"VARCHAR(20) NOT NULL 'field_type'"`
Description string `xorm:"TEXT"`
Options string `xorm:"TEXT"` // JSON array for dropdown options
Options string `xorm:"TEXT"` // JSON array for dropdown options
Required bool `xorm:"NOT NULL DEFAULT false"`
SortOrder int `xorm:"NOT NULL DEFAULT 0 'sort_order'"`
IsActive bool `xorm:"NOT NULL DEFAULT true 'is_active'"`
@@ -46,10 +58,11 @@ func (CustomFieldDef) TableName() string {
return "custom_field_def"
}
// CustomFieldValue stores a custom field value for a specific issue.
// CustomFieldValue stores a custom field value for an entity (issue or repo).
type CustomFieldValue struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX NOT NULL 'issue_id'"`
EntityID int64 `xorm:"INDEX NOT NULL 'entity_id'"` // issue ID or repo ID
EntityType string `xorm:"VARCHAR(10) NOT NULL DEFAULT 'issue' 'entity_type'"` // "issue" or "repo"
FieldID int64 `xorm:"INDEX NOT NULL 'field_id'"`
Value string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED 'created_unix'"`
@@ -60,16 +73,55 @@ func (CustomFieldValue) TableName() string {
return "custom_field_value"
}
// GetCustomFieldsByRepo returns all active custom field definitions for a repo.
func GetCustomFieldsByRepo(ctx context.Context, repoID int64) ([]*CustomFieldDef, error) {
// ──────────────────────────────────────────────────────────────────────
// Queries for org-level field definitions
// ──────────────────────────────────────────────────────────────────────
// GetCustomFieldsByOwner returns all active field definitions for an org with a given scope.
func GetCustomFieldsByOwner(ctx context.Context, ownerID int64, scope CustomFieldScope) ([]*CustomFieldDef, error) {
fields := make([]*CustomFieldDef, 0, 10)
return fields, db.GetEngine(ctx).
Where("repo_id = ? AND is_active = ?", repoID, true).
Where("owner_id = ? AND scope = ? AND is_active = ?", ownerID, scope, true).
OrderBy("sort_order ASC, id ASC").
Find(&fields)
}
// GetAllCustomFieldsByRepo returns all custom field definitions including inactive.
// GetAllCustomFieldsByOwner returns all field definitions for an org (including inactive).
func GetAllCustomFieldsByOwner(ctx context.Context, ownerID int64) ([]*CustomFieldDef, error) {
fields := make([]*CustomFieldDef, 0, 10)
return fields, db.GetEngine(ctx).
Where("owner_id = ?", ownerID).
OrderBy("scope ASC, sort_order ASC, id ASC").
Find(&fields)
}
// GetCustomFieldsByOwnerAndScope returns all fields for an org filtered by scope.
func GetCustomFieldsByOwnerAndScope(ctx context.Context, ownerID int64, scope CustomFieldScope) ([]*CustomFieldDef, error) {
fields := make([]*CustomFieldDef, 0, 10)
return fields, db.GetEngine(ctx).
Where("owner_id = ? AND scope = ?", ownerID, scope).
OrderBy("sort_order ASC, id ASC").
Find(&fields)
}
// ──────────────────────────────────────────────────────────────────────
// Backward-compatible queries (load by repo's owner)
// ──────────────────────────────────────────────────────────────────────
// GetCustomFieldsByRepo returns active issue-scoped fields for a repo's org.
// This is the main query used by the issue sidebar.
func GetCustomFieldsByRepo(ctx context.Context, repoID int64) ([]*CustomFieldDef, error) {
// First try org-level fields (owner_id != 0, repo_id = 0)
// Fall back to legacy repo-level fields (repo_id = repoID)
fields := make([]*CustomFieldDef, 0, 10)
return fields, db.GetEngine(ctx).
Where("((owner_id != 0 AND repo_id = 0) OR repo_id = ?) AND scope = ? AND is_active = ?",
repoID, CustomFieldScopeIssue, true).
OrderBy("sort_order ASC, id ASC").
Find(&fields)
}
// GetAllCustomFieldsByRepo returns all field definitions for a repo (for settings page).
func GetAllCustomFieldsByRepo(ctx context.Context, repoID int64) ([]*CustomFieldDef, error) {
fields := make([]*CustomFieldDef, 0, 10)
return fields, db.GetEngine(ctx).
@@ -78,6 +130,10 @@ func GetAllCustomFieldsByRepo(ctx context.Context, repoID int64) ([]*CustomField
Find(&fields)
}
// ──────────────────────────────────────────────────────────────────────
// Field definition CRUD
// ──────────────────────────────────────────────────────────────────────
// GetCustomFieldDefByID returns a single field definition.
func GetCustomFieldDefByID(ctx context.Context, id int64) (*CustomFieldDef, error) {
field := new(CustomFieldDef)
@@ -112,10 +168,14 @@ func DeleteCustomFieldDef(ctx context.Context, id int64) error {
return err
}
// GetCustomFieldValuesMap returns field_id -> value for an issue.
func GetCustomFieldValuesMap(ctx context.Context, issueID int64) (map[int64]string, error) {
// ──────────────────────────────────────────────────────────────────────
// Field values — generic entity-based (works for issues and repos)
// ──────────────────────────────────────────────────────────────────────
// GetCustomFieldValuesMap returns field_id -> value for an entity.
func GetCustomFieldValuesMap(ctx context.Context, entityID int64) (map[int64]string, error) {
values := make([]*CustomFieldValue, 0, 10)
if err := db.GetEngine(ctx).Where("issue_id = ?", issueID).Find(&values); err != nil {
if err := db.GetEngine(ctx).Where("entity_id = ?", entityID).Find(&values); err != nil {
return nil, err
}
result := make(map[int64]string, len(values))
@@ -126,9 +186,9 @@ func GetCustomFieldValuesMap(ctx context.Context, issueID int64) (map[int64]stri
}
// SetCustomFieldValue creates or updates a single custom field value.
func SetCustomFieldValue(ctx context.Context, issueID, fieldID int64, value string) error {
func SetCustomFieldValue(ctx context.Context, entityID, fieldID int64, value string) error {
existing := new(CustomFieldValue)
has, err := db.GetEngine(ctx).Where("issue_id = ? AND field_id = ?", issueID, fieldID).Get(existing)
has, err := db.GetEngine(ctx).Where("entity_id = ? AND field_id = ?", entityID, fieldID).Get(existing)
if err != nil {
return err
}
@@ -138,17 +198,17 @@ func SetCustomFieldValue(ctx context.Context, issueID, fieldID int64, value stri
return err
}
_, err = db.GetEngine(ctx).Insert(&CustomFieldValue{
IssueID: issueID,
FieldID: fieldID,
Value: value,
EntityID: entityID,
FieldID: fieldID,
Value: value,
})
return err
}
// SetCustomFieldValues sets multiple custom field values for an issue.
func SetCustomFieldValues(ctx context.Context, issueID int64, values map[int64]string) error {
// SetCustomFieldValues sets multiple custom field values for an entity.
func SetCustomFieldValues(ctx context.Context, entityID int64, values map[int64]string) error {
for fieldID, value := range values {
if err := SetCustomFieldValue(ctx, issueID, fieldID, value); err != nil {
if err := SetCustomFieldValue(ctx, entityID, fieldID, value); err != nil {
return err
}
}
+3 -2
View File
@@ -130,10 +130,11 @@ func GetLicenseKeyByID(ctx context.Context, id int64) (*LicenseKey, error) {
return key, nil
}
// ListLicenseKeys returns all keys for the given owner.
// ListLicenseKeys returns all keys for the given owner, master keys first.
func ListLicenseKeys(ctx context.Context, ownerID int64) ([]*LicenseKey, error) {
keys := make([]*LicenseKey, 0, 20)
return keys, db.GetEngine(ctx).Where("owner_id = ?", ownerID).Find(&keys)
return keys, db.GetEngine(ctx).Where("owner_id = ?", ownerID).
OrderBy("is_internal DESC, created_unix DESC").Find(&keys)
}
// SearchLicenseKeys searches keys for an owner by key prefix/raw, licensee, email, or domain.
+4
View File
@@ -32,6 +32,10 @@ type LicensePackage struct {
// AllowedChannels defines which update streams keys from this package
// can access. JSON array, e.g. ["stable","rc"]. Empty = all channels.
AllowedChannels string `xorm:"TEXT"`
// DomainRestriction is a comma-separated list of allowed domains.
// Keys generated from this package inherit this unless overridden.
// Empty = no restriction.
DomainRestriction string `xorm:"TEXT"`
IsActive bool `xorm:"NOT NULL DEFAULT true"`
IsArchived bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
+2
View File
@@ -421,6 +421,8 @@ func prepareMigrationTasks() []*migration {
newMigration(341, "Add parent_org_id to user table for enterprise sub-org hierarchy", v1_27.AddParentOrgIDToUser),
newMigration(342, "Add is_hidden to repository for three-level visibility", v1_27.AddIsHiddenToRepository),
newMigration(343, "Add custom field tables for issue custom fields", v1_27.AddCustomFieldTables),
newMigration(344, "Add domain_restriction to license_package table", v1_27.AddDomainRestrictionToLicensePackage),
newMigration(345, "Migrate custom fields to org-level with scope", v1_27.MigrateCustomFieldsToOrgLevel),
}
return preparedMigrations
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright 2026 Moko Consulting. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_25
import "xorm.io/xorm"
func AddCustomFieldTables(x *xorm.Engine) error {
type CustomFieldDefinition struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL"`
Name string `xorm:"NOT NULL"`
FieldType string `xorm:"NOT NULL"` // text, number, date, dropdown, checkbox
Description string
Required bool `xorm:"NOT NULL DEFAULT false"`
Position int `xorm:"NOT NULL DEFAULT 0"`
Options string `xorm:"TEXT"` // JSON array for dropdown options
DefaultVal string
CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"`
}
type CustomFieldValue struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX NOT NULL"`
FieldID int64 `xorm:"INDEX NOT NULL"`
Value string `xorm:"TEXT"`
CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"`
}
return x.Sync(new(CustomFieldDefinition), new(CustomFieldValue))
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package v1_27
import (
"xorm.io/xorm"
)
func AddDomainRestrictionToLicensePackage(x *xorm.Engine) error {
type LicensePackage struct {
DomainRestriction string `xorm:"TEXT"`
}
return x.Sync(new(LicensePackage))
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package v1_27
import (
"xorm.io/xorm"
)
// MigrateCustomFieldsToOrgLevel adds owner_id, scope to custom_field_def
// and renames issue_id to entity_id + adds entity_type in custom_field_value.
func MigrateCustomFieldsToOrgLevel(x *xorm.Engine) error {
// Add new columns to custom_field_def
type CustomFieldDef struct {
OwnerID int64 `xorm:"INDEX NOT NULL DEFAULT 0 'owner_id'"`
Scope string `xorm:"VARCHAR(10) NOT NULL DEFAULT 'issue' 'scope'"`
}
if err := x.Sync(new(CustomFieldDef)); err != nil {
return err
}
// Add entity_type and entity_id to custom_field_value
type CustomFieldValue struct {
EntityID int64 `xorm:"INDEX NOT NULL DEFAULT 0 'entity_id'"`
EntityType string `xorm:"VARCHAR(10) NOT NULL DEFAULT 'issue' 'entity_type'"`
}
if err := x.Sync(new(CustomFieldValue)); err != nil {
return err
}
// Migrate existing data: copy issue_id to entity_id where entity_id is 0
if _, err := x.Exec("UPDATE custom_field_value SET entity_id = issue_id WHERE entity_id = 0 AND issue_id != 0"); err != nil {
return err
}
// Set issue_id default to 0 so new inserts don't require it
_, err := x.Exec("ALTER TABLE custom_field_value MODIFY COLUMN issue_id bigint NOT NULL DEFAULT 0")
return err
}
-65
View File
@@ -1,65 +0,0 @@
// Copyright 2026 Moko Consulting. All rights reserved.
// SPDX-License-Identifier: MIT
package structs
import "time"
// CustomFieldDefinition represents a custom field definition for a repository
// swagger:model
type CustomFieldDefinition struct {
ID int64 `json:"id"`
RepoID int64 `json:"repo_id"`
Name string `json:"name"`
FieldType string `json:"field_type"`
Description string `json:"description"`
Required bool `json:"required"`
Position int `json:"position"`
Options string `json:"options"`
DefaultValue string `json:"default_value"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
// swagger:strfmt date-time
Updated time.Time `json:"updated_at"`
}
// CreateCustomFieldOption options for creating a custom field
// swagger:model
type CreateCustomFieldOption struct {
// required: true
Name string `json:"name" binding:"Required"`
// required: true
FieldType string `json:"field_type" binding:"Required"` // text, number, date, dropdown, checkbox
Description string `json:"description"`
Required bool `json:"required"`
Position int `json:"position"`
Options string `json:"options"` // JSON array for dropdown
DefaultValue string `json:"default_value"`
}
// EditCustomFieldOption options for editing a custom field
// swagger:model
type EditCustomFieldOption struct {
Name *string `json:"name"`
Description *string `json:"description"`
Required *bool `json:"required"`
Position *int `json:"position"`
Options *string `json:"options"`
DefaultValue *string `json:"default_value"`
}
// CustomFieldValue represents a custom field value for an issue
// swagger:model
type CustomFieldValue struct {
ID int64 `json:"id"`
IssueID int64 `json:"issue_id"`
FieldID int64 `json:"field_id"`
Value string `json:"value"`
}
// SetCustomFieldValueOption options for setting a custom field value
// swagger:model
type SetCustomFieldValueOption struct {
// required: true
Value string `json:"value" binding:"Required"`
}
+12 -9
View File
@@ -104,6 +104,8 @@ type CreateIssueOption struct {
// list of project ids
Projects []int64 `json:"projects"`
Closed bool `json:"closed"`
// custom field values keyed by field name
CustomFields map[string]string `json:"custom_fields,omitempty"`
}
// EditIssueOption options for editing an issue
@@ -190,15 +192,16 @@ const (
// IssueTemplate represents an issue template for a repository
// swagger:model
type IssueTemplate struct {
Name string `json:"name" yaml:"name"`
Title string `json:"title" yaml:"title"`
About string `json:"about" yaml:"about"` // Using "description" in a template file is compatible
Labels IssueTemplateStringSlice `json:"labels" yaml:"labels"`
Assignees IssueTemplateStringSlice `json:"assignees" yaml:"assignees"`
Ref string `json:"ref" yaml:"ref"`
Content string `json:"content" yaml:"-"`
Fields []*IssueFormField `json:"body" yaml:"body"`
FileName string `json:"file_name" yaml:"-"`
Name string `json:"name" yaml:"name"`
Title string `json:"title" yaml:"title"`
About string `json:"about" yaml:"about"` // Using "description" in a template file is compatible
Labels IssueTemplateStringSlice `json:"labels" yaml:"labels"`
Assignees IssueTemplateStringSlice `json:"assignees" yaml:"assignees"`
Ref string `json:"ref" yaml:"ref"`
Content string `json:"content" yaml:"-"`
Fields []*IssueFormField `json:"body" yaml:"body"`
FileName string `json:"file_name" yaml:"-"`
CustomFields map[string]string `json:"custom_fields,omitempty" yaml:"custom_fields"`
}
type IssueTemplateStringSlice []string
+31 -10
View File
@@ -1412,6 +1412,7 @@
"repo.issues.new.open_projects": "Open Projects",
"repo.issues.new.closed_projects": "Closed Projects",
"repo.issues.new.no_items": "No items",
"repo.issues.custom_fields": "Custom Fields",
"repo.issues.new.milestone": "Milestone",
"repo.issues.new.no_milestone": "No Milestone",
"repo.issues.new.clear_milestone": "Clear milestone",
@@ -2149,15 +2150,15 @@
"repo.settings.unit_visibility_private": "Private (follow repo visibility)",
"repo.settings.unit_visibility_public": "Public (anyone can read)",
"repo.settings.unit_visibility_releases_help": "Controls whether the releases page is visible to anonymous visitors.",
"repo.settings.licensing_section": "Licensing & Updates",
"repo.settings.licensing_section_desc": "Manage commercial license keys and gated update feeds for this repository. When enabled, the Licenses tab appears and release tags must follow update stream naming.",
"repo.settings.licensing_section": "Update Server",
"repo.settings.licensing_section_desc": "Manage update feeds and optional license key gating for this repository. When enabled, the Licenses tab appears and release tags are served via update feeds.",
"repo.settings.update_platform": "Update Feed Format",
"repo.settings.update_platform_both": "Both (Joomla + Dolibarr)",
"repo.settings.update_platform_help": "Choose which update feed format to generate. All formats support license key validation.",
"repo.settings.require_update_key": "Require license key for update feeds",
"repo.settings.require_update_key_help": "When enabled, update feeds return empty results unless a valid license key is provided. Joomla clients will see a Download Key field in Update Sites.",
"repo.settings.enable_licensing": "Enable licensing for this repository",
"repo.settings.enable_licensing_help": "Show the Licenses tab and enable license key management for this repository.",
"repo.settings.enable_licensing": "Enable Update Server for this repository",
"repo.settings.enable_licensing_help": "Serve update feeds from releases and show the Licenses tab for optional key management.",
"repo.settings.packages_desc": "Enable Repository Packages Registry",
"repo.settings.projects_desc": "Enable Projects",
"repo.settings.projects_mode_desc": "Projects Mode (which kinds of projects to show)",
@@ -2660,12 +2661,16 @@
"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.regenerate_master_key": "Regenerate",
"repo.licenses.regenerate_master_key_help": "Deactivates the current master key and generates a new one. The new key will be shown once.",
"repo.licenses.master_key_regenerated": "Master key regenerated. Copy the new key below — it will not be shown again.",
"repo.licenses.update_feeds": "Update Feed URLs",
"repo.licenses.edit_key": "Edit License Key",
"repo.licenses.licensee_name": "Licensee Name",
"repo.licenses.licensee_email": "Licensee Email",
"repo.licenses.domain_restriction": "Domain Restriction",
"repo.licenses.domain_restriction_help": "Comma-separated list of allowed domains. Leave empty for no restriction.",
"repo.licenses.domain_restriction_help": "Comma-separated list of allowed domains. Leave empty to inherit from the package default.",
"repo.licenses.domain_restriction_package_help": "Default domain restriction for keys generated from this package. Comma-separated. Keys can override this.",
"repo.licenses.use_package_default": "use package default",
"repo.licenses.expires_at": "Expires At",
"repo.licenses.expires_at_help": "Leave empty for no expiry (lifetime).",
@@ -2717,6 +2722,9 @@
"repo.settings.support_url": "Support / Product Page URL",
"repo.settings.support_url_help": "Shown when downloads are gated. Can point to your wiki, product page, or external support site.",
"repo.settings.custom_fields": "Custom Fields",
"repo.settings.metadata": "Metadata",
"repo.settings.metadata_saved": "Repository metadata saved.",
"repo.settings.metadata_empty": "No metadata fields defined. Org admins can add fields in Organization Settings > Custom Fields.",
"repo.settings.custom_field_new": "New Field",
"repo.settings.custom_field_create": "Create Field",
"repo.settings.custom_field_name": "Field Name",
@@ -2890,11 +2898,24 @@
"org.form.create_org_not_allowed": "You are not allowed to create an organization.",
"org.settings": "Settings",
"org.settings.options": "Organization",
"org.settings.update_streams": "Licensing & Update Streams",
"org.settings.licensing": "Licensing",
"org.settings.licensing_desc": "Control commercial license key management and gated update feeds across all repositories in this organization.",
"org.settings.enable_licensing": "Enable licensing for this organization",
"org.settings.enable_licensing_help": "Show the Licenses page in the org menu and enable license key management. Individual repos can also enable licensing independently.",
"org.settings.custom_fields": "Custom Fields",
"org.settings.custom_fields_desc": "Define custom fields that appear across all repositories in this organization. Issue fields show in issue sidebars. Repo fields show in repo settings metadata.",
"org.settings.custom_fields_empty": "No custom fields defined yet.",
"org.settings.custom_field_add": "Add Custom Field",
"org.settings.custom_field_name": "Field Name",
"org.settings.custom_field_scope": "Scope",
"org.settings.custom_field_type": "Type",
"org.settings.custom_field_options": "Options (JSON)",
"org.settings.custom_field_options_help": "For dropdown fields, enter options as a JSON array.",
"org.settings.custom_field_description": "Description",
"org.settings.custom_field_created": "Custom field created.",
"org.settings.custom_field_updated": "Custom field updated.",
"org.settings.custom_field_deleted": "Custom field deleted.",
"org.settings.update_streams": "Update Server",
"org.settings.licensing": "Update Server",
"org.settings.licensing_desc": "Manage update feeds and optional license key gating across all repositories in this organization.",
"org.settings.enable_licensing": "Enable Update Server for this organization",
"org.settings.enable_licensing_help": "Show the Licenses page in the org menu and serve update feeds. Individual repos can also enable this independently.",
"org.settings.require_key": "Require license key for all update feeds",
"org.settings.require_key_help": "Update feeds return empty results unless a valid key is provided. Joomla clients will see a Download Key field. Individual repos can override this.",
"org.settings.feed_visibility": "Update Feed Visibility",
+11 -12
View File
@@ -1655,21 +1655,15 @@ func Routes() *web.Router {
// })
// })
// })
m.Group("/custom-fields", func() {
m.Combo("").Get(repo.ListCustomFields).
Post(reqToken(), reqRepoWriter(unit.TypeIssues), bind(api.CreateCustomFieldOption{}), repo.CreateCustomField)
m.Group("/{fieldId}", func() {
m.Combo("").Get(repo.GetCustomField).
Patch(reqToken(), reqRepoWriter(unit.TypeIssues), bind(api.EditCustomFieldOption{}), repo.EditCustomField).
Delete(reqToken(), reqRepoWriter(unit.TypeIssues), repo.DeleteCustomField)
})
// Repo metadata (repo-scoped custom fields)
m.Group("/metadata", func() {
m.Get("", repo.GetRepoMetadata)
m.Put("", reqToken(), reqRepoWriter(unit.TypeCode), repo.SetRepoMetadata)
})
// Issue custom fields
m.Group("/issues/{index}/custom-fields", func() {
m.Get("", repo.GetIssueCustomFields)
m.Group("/{fieldId}", func() {
m.Put("", reqToken(), reqRepoWriter(unit.TypeIssues), bind(api.SetCustomFieldValueOption{}), repo.SetIssueCustomField)
m.Delete("", reqToken(), reqRepoWriter(unit.TypeIssues), repo.DeleteIssueCustomField)
})
m.Put("", reqToken(), reqRepoWriter(unit.TypeIssues), repo.SetIssueCustomFields)
})
}, repoAssignment(), checkTokenPublicOnly())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue))
@@ -1771,6 +1765,11 @@ func Routes() *web.Router {
m.Delete("", org.UnblockUser)
})
}, reqToken(), reqOrgOwnership())
m.Group("/custom-fields", func() {
m.Get("", org.ListOrgCustomFields)
m.Post("", reqToken(), reqOrgOwnership(), org.CreateOrgCustomField)
m.Delete("/{id}", reqToken(), reqOrgOwnership(), org.DeleteOrgCustomField)
})
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly())
m.Group("/teams/{teamid}", func() {
m.Combo("").Get(reqToken(), org.GetTeam).
+139
View File
@@ -0,0 +1,139 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package org
import (
"encoding/json"
"net/http"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
type apiCustomFieldDef struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
Scope string `json:"scope"`
Name string `json:"name"`
FieldType string `json:"field_type"`
Description string `json:"description"`
Options any `json:"options"`
Required bool `json:"required"`
SortOrder int `json:"sort_order"`
IsActive bool `json:"is_active"`
}
func toAPIFieldDef(f *issues_model.CustomFieldDef) apiCustomFieldDef {
var opts any
if f.Options != "" {
var parsed []string
if json.Unmarshal([]byte(f.Options), &parsed) == nil {
opts = parsed
} else {
opts = f.Options
}
}
return apiCustomFieldDef{
ID: f.ID,
OwnerID: f.OwnerID,
Scope: string(f.Scope),
Name: f.Name,
FieldType: string(f.FieldType),
Description: f.Description,
Options: opts,
Required: f.Required,
SortOrder: f.SortOrder,
IsActive: f.IsActive,
}
}
// ListOrgCustomFields returns all custom field definitions for an org.
func ListOrgCustomFields(ctx *context.APIContext) {
fields, err := issues_model.GetAllCustomFieldsByOwner(ctx, ctx.Org.Organization.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
result := make([]apiCustomFieldDef, 0, len(fields))
for _, f := range fields {
result = append(result, toAPIFieldDef(f))
}
ctx.JSON(http.StatusOK, result)
}
// CreateOrgCustomField creates a new custom field definition.
func CreateOrgCustomField(ctx *context.APIContext) {
var req struct {
Scope string `json:"scope" binding:"Required"`
Name string `json:"name" binding:"Required"`
FieldType string `json:"field_type" binding:"Required"`
Description string `json:"description"`
Options []string `json:"options"`
Required bool `json:"required"`
SortOrder int `json:"sort_order"`
}
if err := ctx.Req.ParseForm(); err != nil {
ctx.APIError(http.StatusBadRequest, err)
return
}
if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
ctx.APIError(http.StatusBadRequest, err)
return
}
if req.Name == "" || req.Scope == "" {
ctx.APIError(http.StatusBadRequest, "name and scope are required")
return
}
scope := issues_model.CustomFieldScope(req.Scope)
if scope != issues_model.CustomFieldScopeIssue && scope != issues_model.CustomFieldScopeRepo {
ctx.APIError(http.StatusBadRequest, "scope must be 'issue' or 'repo'")
return
}
var optionsJSON string
if len(req.Options) > 0 {
data, _ := json.Marshal(req.Options)
optionsJSON = string(data)
}
field := &issues_model.CustomFieldDef{
OwnerID: ctx.Org.Organization.ID,
RepoID: 0,
Scope: scope,
Name: req.Name,
FieldType: issues_model.CustomFieldType(req.FieldType),
Description: req.Description,
Options: optionsJSON,
Required: req.Required,
SortOrder: req.SortOrder,
IsActive: true,
}
if err := issues_model.CreateCustomFieldDef(ctx, field); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, toAPIFieldDef(field))
}
// DeleteOrgCustomField deletes a custom field definition.
func DeleteOrgCustomField(ctx *context.APIContext) {
id := ctx.PathParamInt64("id")
field, err := issues_model.GetCustomFieldDefByID(ctx, id)
if err != nil {
ctx.APIErrorNotFound()
return
}
if field.OwnerID != ctx.Org.Organization.ID {
ctx.APIErrorNotFound()
return
}
if err := issues_model.DeleteCustomFieldDef(ctx, id); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
-196
View File
@@ -1,196 +0,0 @@
// Copyright 2026 Moko Consulting. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"net/http"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
api "code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/structs"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/web"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
func fieldToAPI(f *issues_model.CustomFieldDefinition) *api.CustomFieldDefinition {
return &api.CustomFieldDefinition{
ID: f.ID,
RepoID: f.RepoID,
Name: f.Name,
FieldType: f.FieldType,
Description: f.Description,
Required: f.Required,
Position: f.Position,
Options: f.Options,
DefaultValue: f.DefaultVal,
Created: f.CreatedUnix.AsTime(),
Updated: f.UpdatedUnix.AsTime(),
}
}
// ListCustomFields lists custom field definitions for a repository
func ListCustomFields(ctx *context.APIContext) {
fields, err := issues_model.GetCustomFieldsByRepoID(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
result := make([]*api.CustomFieldDefinition, len(fields))
for i, f := range fields {
result[i] = fieldToAPI(f)
}
ctx.JSON(http.StatusOK, result)
}
// GetCustomField gets a custom field definition by ID
func GetCustomField(ctx *context.APIContext) {
field, err := issues_model.GetCustomFieldDefByID(ctx, ctx.PathParamInt64("fieldId"))
if err != nil {
ctx.APIErrorInternal(err)
return
}
if field == nil || field.RepoID != ctx.Repo.Repository.ID {
ctx.APIErrorNotFound()
return
}
ctx.JSON(http.StatusOK, fieldToAPI(field))
}
// CreateCustomField creates a new custom field definition
func CreateCustomField(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateCustomFieldOption)
validTypes := map[string]bool{"text": true, "number": true, "date": true, "dropdown": true, "checkbox": true}
if !validTypes[form.FieldType] {
ctx.APIError(http.StatusUnprocessableEntity, "field_type must be: text, number, date, dropdown, or checkbox")
return
}
field := &issues_model.CustomFieldDefinition{
RepoID: ctx.Repo.Repository.ID,
Name: form.Name,
FieldType: form.FieldType,
Description: form.Description,
Required: form.Required,
Position: form.Position,
Options: form.Options,
DefaultVal: form.DefaultValue,
}
if err := issues_model.CreateCustomFieldDef(ctx, field); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, fieldToAPI(field))
}
// EditCustomField updates a custom field definition
func EditCustomField(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.EditCustomFieldOption)
field, err := issues_model.GetCustomFieldDefByID(ctx, ctx.PathParamInt64("fieldId"))
if err != nil {
ctx.APIErrorInternal(err)
return
}
if field == nil || field.RepoID != ctx.Repo.Repository.ID {
ctx.APIErrorNotFound()
return
}
if form.Name != nil {
field.Name = *form.Name
}
if form.Description != nil {
field.Description = *form.Description
}
if form.Required != nil {
field.Required = *form.Required
}
if form.Position != nil {
field.Position = *form.Position
}
if form.Options != nil {
field.Options = *form.Options
}
if form.DefaultValue != nil {
field.DefaultVal = *form.DefaultValue
}
if err := issues_model.UpdateCustomFieldDef(ctx, field); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, fieldToAPI(field))
}
// DeleteCustomField deletes a custom field and all its values
func DeleteCustomField(ctx *context.APIContext) {
field, err := issues_model.GetCustomFieldDefByID(ctx, ctx.PathParamInt64("fieldId"))
if err != nil {
ctx.APIErrorInternal(err)
return
}
if field == nil || field.RepoID != ctx.Repo.Repository.ID {
ctx.APIErrorNotFound()
return
}
if err := issues_model.DeleteCustomFieldDef(ctx, field.ID); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// GetIssueCustomFields gets all custom field values for an issue
func GetIssueCustomFields(ctx *context.APIContext) {
values, err := issues_model.GetCustomFieldValuesMap(ctx, ctx.PathParamInt64("index"))
if err != nil {
ctx.APIErrorInternal(err)
return
}
result := make([]*api.CustomFieldValue, len(values))
for i, v := range values {
result[i] = &api.CustomFieldValue{
ID: v.ID,
IssueID: v.IssueID,
FieldID: v.FieldID,
Value: v.Value,
}
}
ctx.JSON(http.StatusOK, result)
}
// SetIssueCustomField sets a custom field value on an issue
func SetIssueCustomField(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.SetCustomFieldValueOption)
issueID := ctx.PathParamInt64("index")
fieldID := ctx.PathParamInt64("fieldId")
// Verify field belongs to this repo
field, err := issues_model.GetCustomFieldDefByID(ctx, fieldID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
if field == nil || field.RepoID != ctx.Repo.Repository.ID {
ctx.APIErrorNotFound()
return
}
if err := issues_model.SetCustomFieldValue(ctx, issueID, fieldID, form.Value); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// DeleteIssueCustomField removes a custom field value from an issue
func DeleteIssueCustomField(ctx *context.APIContext) {
issueID := ctx.PathParamInt64("index")
fieldID := ctx.PathParamInt64("fieldId")
if err := issues_model.DeleteCustomFieldValue(ctx, issueID, fieldID); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
+137
View File
@@ -0,0 +1,137 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package repo
import (
"encoding/json"
"net/http"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
// GetRepoMetadata returns all repo-scoped custom field values.
func GetRepoMetadata(ctx *context.APIContext) {
ownerID := ctx.Repo.Repository.OwnerID
repoID := ctx.Repo.Repository.ID
fields, err := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeRepo)
if err != nil {
ctx.APIErrorInternal(err)
return
}
values, err := issues_model.GetCustomFieldValuesMap(ctx, repoID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
result := make(map[string]string, len(fields))
for _, f := range fields {
result[f.Name] = values[f.ID]
}
ctx.JSON(http.StatusOK, result)
}
// SetRepoMetadata sets repo-scoped custom field values.
func SetRepoMetadata(ctx *context.APIContext) {
ownerID := ctx.Repo.Repository.OwnerID
repoID := ctx.Repo.Repository.ID
var req map[string]string
if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
ctx.APIError(http.StatusBadRequest, err)
return
}
fields, err := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeRepo)
if err != nil {
ctx.APIErrorInternal(err)
return
}
// Build name->ID map
nameToID := make(map[string]int64, len(fields))
for _, f := range fields {
nameToID[f.Name] = f.ID
}
for name, value := range req {
if fieldID, ok := nameToID[name]; ok {
if err := issues_model.SetCustomFieldValue(ctx, repoID, fieldID, value); err != nil {
ctx.APIErrorInternal(err)
return
}
}
}
ctx.Status(http.StatusNoContent)
}
// GetIssueCustomFields returns custom field values for an issue.
func GetIssueCustomFields(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
ctx.APIErrorNotFound()
return
}
ownerID := ctx.Repo.Repository.OwnerID
fields, err := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeIssue)
if err != nil {
ctx.APIErrorInternal(err)
return
}
values, err := issues_model.GetCustomFieldValuesMap(ctx, issue.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
result := make(map[string]string, len(fields))
for _, f := range fields {
result[f.Name] = values[f.ID]
}
ctx.JSON(http.StatusOK, result)
}
// SetIssueCustomFields sets custom field values for an issue.
func SetIssueCustomFields(ctx *context.APIContext) {
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
if err != nil {
ctx.APIErrorNotFound()
return
}
var req map[string]string
if err := json.NewDecoder(ctx.Req.Body).Decode(&req); err != nil {
ctx.APIError(http.StatusBadRequest, err)
return
}
ownerID := ctx.Repo.Repository.OwnerID
fields, err := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeIssue)
if err != nil {
ctx.APIErrorInternal(err)
return
}
nameToID := make(map[string]int64, len(fields))
for _, f := range fields {
nameToID[f.Name] = f.ID
}
for name, value := range req {
if fieldID, ok := nameToID[name]; ok {
if err := issues_model.SetCustomFieldValue(ctx, issue.ID, fieldID, value); err != nil {
ctx.APIErrorInternal(err)
return
}
}
}
ctx.Status(http.StatusNoContent)
}
+23
View File
@@ -702,6 +702,29 @@ func CreateIssue(ctx *context.APIContext) {
return
}
// Save custom field values if provided (resolve field names to IDs).
if len(form.CustomFields) > 0 {
defs, defErr := issues_model.GetCustomFieldsByOwner(ctx, ctx.Repo.Repository.OwnerID, issues_model.CustomFieldScopeIssue)
if defErr != nil {
ctx.APIErrorInternal(defErr)
return
}
if len(defs) > 0 {
vals := make(map[int64]string)
for _, def := range defs {
if v, ok := form.CustomFields[def.Name]; ok {
vals[def.ID] = v
}
}
if len(vals) > 0 {
if setErr := issues_model.SetCustomFieldValues(ctx, issue.ID, vals); setErr != nil {
ctx.APIErrorInternal(setErr)
return
}
}
}
}
if form.Closed {
if err := issue_service.CloseIssue(ctx, issue, ctx.Doer, ""); err != nil {
if issues_model.IsErrDependenciesLeft(err) {
+120
View File
@@ -0,0 +1,120 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package org
import (
"net/http"
"strconv"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
const tplOrgCustomFields templates.TplName = "org/settings/custom_fields"
// SettingsCustomFields shows the org-level custom fields management page.
func SettingsCustomFields(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("org.settings.custom_fields")
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsCustomFields"] = true
fields, err := issues_model.GetAllCustomFieldsByOwner(ctx, ctx.Org.Organization.ID)
if err != nil {
ctx.ServerError("GetAllCustomFieldsByOwner", err)
return
}
ctx.Data["CustomFields"] = fields
ctx.HTML(http.StatusOK, tplOrgCustomFields)
}
// SettingsCustomFieldsCreatePost creates a new org-level custom field.
func SettingsCustomFieldsCreatePost(ctx *context.Context) {
sortOrder, _ := strconv.Atoi(ctx.FormString("sort_order"))
scope := issues_model.CustomFieldScope(ctx.FormString("scope"))
if scope != issues_model.CustomFieldScopeIssue && scope != issues_model.CustomFieldScopeRepo {
scope = issues_model.CustomFieldScopeIssue
}
field := &issues_model.CustomFieldDef{
OwnerID: ctx.Org.Organization.ID,
RepoID: 0, // org-level
Scope: scope,
Name: ctx.FormString("name"),
FieldType: issues_model.CustomFieldType(ctx.FormString("field_type")),
Description: ctx.FormString("description"),
Options: ctx.FormString("options"),
Required: ctx.FormString("required") == "on",
SortOrder: sortOrder,
IsActive: true,
}
if field.Name == "" {
ctx.Flash.Error("Field name is required")
ctx.Redirect(ctx.Org.OrgLink + "/settings/custom-fields")
return
}
if err := issues_model.CreateCustomFieldDef(ctx, field); err != nil {
ctx.ServerError("CreateCustomFieldDef", err)
return
}
ctx.Flash.Success(ctx.Tr("org.settings.custom_field_created"))
ctx.Redirect(ctx.Org.OrgLink + "/settings/custom-fields")
}
// SettingsCustomFieldsEditPost updates an org-level custom field.
func SettingsCustomFieldsEditPost(ctx *context.Context) {
id := ctx.PathParamInt64("id")
field, err := issues_model.GetCustomFieldDefByID(ctx, id)
if err != nil {
ctx.ServerError("GetCustomFieldDefByID", err)
return
}
if field.OwnerID != ctx.Org.Organization.ID {
ctx.NotFound(nil)
return
}
field.Name = ctx.FormString("name")
field.FieldType = issues_model.CustomFieldType(ctx.FormString("field_type"))
field.Description = ctx.FormString("description")
field.Options = ctx.FormString("options")
field.Required = ctx.FormString("required") == "on"
field.IsActive = ctx.FormString("is_active") == "on"
sortOrder, _ := strconv.Atoi(ctx.FormString("sort_order"))
field.SortOrder = sortOrder
if err := issues_model.UpdateCustomFieldDef(ctx, field); err != nil {
ctx.ServerError("UpdateCustomFieldDef", err)
return
}
ctx.Flash.Success(ctx.Tr("org.settings.custom_field_updated"))
ctx.Redirect(ctx.Org.OrgLink + "/settings/custom-fields")
}
// SettingsCustomFieldsDeletePost deletes an org-level custom field.
func SettingsCustomFieldsDeletePost(ctx *context.Context) {
id := ctx.PathParamInt64("id")
field, err := issues_model.GetCustomFieldDefByID(ctx, id)
if err != nil {
ctx.ServerError("GetCustomFieldDefByID", err)
return
}
if field.OwnerID != ctx.Org.Organization.ID {
ctx.NotFound(nil)
return
}
if err := issues_model.DeleteCustomFieldDef(ctx, id); err != nil {
ctx.ServerError("DeleteCustomFieldDef", err)
return
}
ctx.Flash.Success(ctx.Tr("org.settings.custom_field_deleted"))
ctx.Redirect(ctx.Org.OrgLink + "/settings/custom-fields")
}
+1 -1
View File
@@ -630,7 +630,7 @@ func (cpi *comparePageInfoType) prepareCreatePullRequestPage(ctx *context.Contex
if ctx.Written() {
return
}
_, templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates, pageMetaData)
_, templateErrs, _ := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates, pageMetaData)
if len(templateErrs) > 0 {
ctx.Flash.Warning(renderErrorOfTemplates(ctx, templateErrs), true)
}
+6
View File
@@ -30,6 +30,12 @@ func CheckDownloadGating(ctx *context.Context, tagName string) bool {
return true // no download gating configured
}
// Signed-in users with repo access bypass download gating.
// The gate is for anonymous/external clients (Joomla update checker).
if ctx.IsSigned && ctx.Repo.Permission.HasAnyUnitAccess() {
return true
}
// For prerelease-only gating, check if this is a prerelease tag.
if gating == "prerelease" && tagName != "" {
streams := licenses.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID)
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package repo
import (
"fmt"
"net/http"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
// UpdateIssueCustomField handles POST to set a custom field value on an issue.
func UpdateIssueCustomField(ctx *context.Context) {
issueID := ctx.PathParamInt64("id")
fieldID := ctx.PathParamInt64("field_id")
value := ctx.FormString("value")
// Look up issue to get the index for redirect.
issue, err := issues_model.GetIssueByID(ctx, issueID)
if err != nil {
ctx.ServerError("GetIssueByID", err)
return
}
if err := issues_model.SetCustomFieldValue(ctx, issueID, fieldID, value); err != nil {
ctx.ServerError("SetCustomFieldValue", err)
return
}
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index), http.StatusSeeOther)
}
+65 -5
View File
@@ -4,6 +4,7 @@
package repo
import (
"encoding/json"
"errors"
"fmt"
"html/template"
@@ -36,10 +37,11 @@ import (
)
// Tries to load and set an issue template. The first return value indicates if a template was loaded.
func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles []string, metaData *IssuePageMetaData) (bool, map[string]error) {
// The third return value contains the template's custom_fields map (field name → default value).
func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles []string, metaData *IssuePageMetaData) (bool, map[string]error, map[string]string) {
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
return false, nil
return false, nil, nil
}
templateCandidates := make([]string, 0, 1+len(possibleFiles))
@@ -84,9 +86,9 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
}
metaData.AssigneesData.SelectedAssigneeIDs = strings.Join(selectedAssigneeIDStrings, ",")
return true, templateErrs
return true, templateErrs, template.CustomFields
}
return false, templateErrs
return false, templateErrs, nil
}
// NewIssue render creating issue page
@@ -128,7 +130,7 @@ func NewIssue(ctx *context.Context) {
ctx.Data["Tags"] = tags
ret := issue_service.ParseTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo)
templateLoaded, errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates, pageMetaData)
templateLoaded, errs, templateCustomFields := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates, pageMetaData)
maps.Copy(ret.TemplateErrors, errs)
if ctx.Written() {
return
@@ -140,6 +142,35 @@ func NewIssue(ctx *context.Context) {
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.Permission.CanWrite(unit.TypeIssues)
// Load org-level issue-scoped custom fields for the new issue sidebar.
customFieldDefs, cfErr := issues_model.GetCustomFieldsByOwner(ctx, ctx.Repo.Repository.OwnerID, issues_model.CustomFieldScopeIssue)
if cfErr != nil {
log.Error("NewIssue: GetCustomFieldsByOwner: %v", cfErr)
}
ctx.Data["CustomFieldDefs"] = customFieldDefs
customFieldValues := make(map[int64]string)
fieldOptions := make(map[int64][]string)
if len(customFieldDefs) > 0 {
// Resolve template custom_fields (name → value) to field IDs.
if len(templateCustomFields) > 0 {
for _, def := range customFieldDefs {
if val, ok := templateCustomFields[def.Name]; ok {
customFieldValues[def.ID] = val
}
}
}
for _, f := range customFieldDefs {
if f.Options != "" {
var opts []string
if err := json.Unmarshal([]byte(f.Options), &opts); err == nil {
fieldOptions[f.ID] = opts
}
}
}
}
ctx.Data["CustomFieldValues"] = customFieldValues
ctx.Data["CustomFieldOptions"] = fieldOptions
if !issueConfig.BlankIssuesEnabled && hasTemplates && !templateLoaded {
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if blank issues are disabled, just redirect to the "issues/choose" page with these parameters.
ctx.Redirect(fmt.Sprintf("%s/issues/new/choose?%s", ctx.Repo.Repository.Link(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
@@ -377,6 +408,9 @@ func NewIssuePost(ctx *context.Context) {
return
}
// Save custom field values submitted from the new issue form.
saveCustomFieldsFromForm(ctx, repo.OwnerID, issue.ID)
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" && len(projectIDs) > 0 {
// When issue is in multiple projects, redirect to first project from form order.
@@ -392,3 +426,29 @@ func NewIssuePost(ctx *context.Context) {
}
ctx.JSONRedirect(issue.Link())
}
// saveCustomFieldsFromForm reads custom field values from the form
// (submitted as "custom-field-{fieldID}") and persists them for the issue.
func saveCustomFieldsFromForm(ctx *context.Context, ownerID, issueID int64) {
defs, err := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeIssue)
if err != nil {
log.Error("saveCustomFieldsFromForm: GetCustomFieldsByOwner: %v", err)
return
}
if len(defs) == 0 {
return
}
vals := make(map[int64]string)
for _, def := range defs {
v := ctx.Req.FormValue(fmt.Sprintf("custom-field-%d", def.ID))
if v != "" {
vals[def.ID] = v
}
}
if len(vals) > 0 {
if err := issues_model.SetCustomFieldValues(ctx, issueID, vals); err != nil {
log.Error("saveCustomFieldsFromForm: %v", err)
ctx.Flash.Error("Failed to save custom field values")
}
}
}
+27
View File
@@ -4,6 +4,7 @@
package repo
import (
"encoding/json"
"fmt"
"math/big"
"net/http"
@@ -337,6 +338,32 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["IsProjectsEnabled"] = ctx.Repo.Permission.CanRead(unit.TypeProjects)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
// Load custom fields for the issue sidebar (org-level issue-scoped fields).
customFieldDefs, cfErr := issues_model.GetCustomFieldsByOwner(ctx, ctx.Repo.Repository.OwnerID, issues_model.CustomFieldScopeIssue)
if cfErr != nil {
log.Error("ViewIssue: GetCustomFieldsByOwner: %v", cfErr)
}
ctx.Data["CustomFieldDefs"] = customFieldDefs
customFieldValues := make(map[int64]string)
fieldOptions := make(map[int64][]string)
if len(customFieldDefs) > 0 {
var cvErr error
customFieldValues, cvErr = issues_model.GetCustomFieldValuesMap(ctx, issue.ID)
if cvErr != nil {
log.Error("ViewIssue: GetCustomFieldValuesMap: %v", cvErr)
}
for _, f := range customFieldDefs {
if f.Options != "" {
var opts []string
if err := json.Unmarshal([]byte(f.Options), &opts); err == nil {
fieldOptions[f.ID] = opts
}
}
}
}
ctx.Data["CustomFieldValues"] = customFieldValues
ctx.Data["CustomFieldOptions"] = fieldOptions
upload.AddUploadContext(ctx, "comment")
if err := issue.LoadAttributes(ctx); err != nil {
+81 -13
View File
@@ -90,6 +90,10 @@ func Licenses(ctx *context.Context) {
}
}
// Always load the master key for display (prefix + status).
masterKey, _ := licenses.GetMasterKey(ctx, ownerID)
ctx.Data["MasterKey"] = masterKey
pkgs, err := licenses.ListLicensePackages(ctx, ownerID)
if err != nil {
ctx.ServerError("ListLicensePackages", err)
@@ -183,15 +187,16 @@ func LicensesCreatePackage(ctx *context.Context) {
}
pkg := &licenses.LicensePackage{
OwnerID: ctx.Repo.Repository.OwnerID,
Name: name,
Description: ctx.FormString("description"),
DurationDays: durationDays,
MaxSites: maxSites,
DomainLockHours: domainLockHours,
AllowedChannels: allowedChannels,
RepoScope: repoScope,
IsActive: true,
OwnerID: ctx.Repo.Repository.OwnerID,
Name: name,
Description: ctx.FormString("description"),
DurationDays: durationDays,
MaxSites: maxSites,
DomainLockHours: domainLockHours,
AllowedChannels: allowedChannels,
DomainRestriction: strings.TrimSpace(ctx.FormString("domain_restriction")),
RepoScope: repoScope,
IsActive: true,
}
if err := licenses.CreateLicensePackage(ctx, pkg); err != nil {
@@ -203,9 +208,62 @@ func LicensesCreatePackage(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink + "/licenses")
}
// LicensesRegenerateMasterKey handles POST to regenerate the master key.
func LicensesRegenerateMasterKey(ctx *context.Context) {
ownerID := ctx.Repo.Repository.OwnerID
// Deactivate the old master key.
oldKey, _ := licenses.GetMasterKey(ctx, ownerID)
if oldKey != nil {
oldKey.IsActive = false
_ = licenses.UpdateLicenseKey(ctx, oldKey)
}
// Find the master package.
pkgs, err := licenses.ListLicensePackages(ctx, ownerID)
if err != nil {
ctx.ServerError("ListLicensePackages", err)
return
}
var masterPkg *licenses.LicensePackage
for _, pkg := range pkgs {
if pkg.Name == licenses.MasterPackageName {
masterPkg = pkg
break
}
}
if masterPkg == nil {
ctx.Flash.Error("Master package not found")
ctx.Redirect(ctx.Repo.RepoLink + "/licenses")
return
}
// Create a new master key.
newKey := &licenses.LicenseKey{
PackageID: masterPkg.ID,
OwnerID: ownerID,
IsInternal: true,
IsActive: true,
}
rawKey, err := licenses.CreateLicenseKey(ctx, newKey)
if err != nil {
ctx.ServerError("CreateLicenseKey", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.licenses.master_key_regenerated"))
ctx.Data["NewMasterKey"] = rawKey
Licenses(ctx)
}
// LicensesGenerateKey handles POST to generate a new key from a package.
func LicensesGenerateKey(ctx *context.Context) {
packageID, _ := strconv.ParseInt(ctx.FormString("package_id"), 10, 64)
// Accept package_id from form body or query string (modal sets it via form action URL).
pkgIDStr := ctx.FormString("package_id")
if pkgIDStr == "" {
pkgIDStr = ctx.Req.URL.Query().Get("package_id")
}
packageID, _ := strconv.ParseInt(pkgIDStr, 10, 64)
if packageID == 0 {
ctx.Flash.Error("Invalid package")
ctx.Redirect(ctx.Repo.RepoLink + "/licenses")
@@ -218,10 +276,19 @@ func LicensesGenerateKey(ctx *context.Context) {
return
}
// Domain restriction: use form input, fall back to package default.
domainRestriction := strings.TrimSpace(ctx.FormString("domain_restriction"))
if domainRestriction == "" && pkg.DomainRestriction != "" {
domainRestriction = pkg.DomainRestriction
}
key := &licenses.LicenseKey{
PackageID: packageID,
OwnerID: ctx.Repo.Repository.OwnerID,
IsActive: true,
PackageID: packageID,
OwnerID: ctx.Repo.Repository.OwnerID,
IsActive: true,
DomainRestriction: domainRestriction,
LicenseeName: strings.TrimSpace(ctx.FormString("licensee_name")),
LicenseeEmail: strings.TrimSpace(ctx.FormString("licensee_email")),
}
// Auto-calculate expiry from package duration.
@@ -448,6 +515,7 @@ func LicensesEditPackagePost(ctx *context.Context) {
pkg.AllowedChannels = ""
}
pkg.DomainRestriction = strings.TrimSpace(ctx.FormString("domain_restriction"))
pkg.IsActive = ctx.FormString("is_active") == "on"
if err := licenses.UpdateLicensePackage(ctx, pkg); err != nil {
+1
View File
@@ -15,6 +15,7 @@ const tplSettingsAdvanced templates.TplName = "repo/settings/advanced"
// AdvancedSettings displays the advanced (feature units) settings page.
func AdvancedSettings(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.advanced_settings")
ctx.Data["PageIsSettingsOptions"] = false
ctx.Data["PageIsSettingsAdvanced"] = true
ctx.HTML(http.StatusOK, tplSettingsAdvanced)
}
+64
View File
@@ -0,0 +1,64 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package setting
import (
"encoding/json"
"fmt"
"net/http"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/templates"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/services/context"
)
const tplSettingsMetadata templates.TplName = "repo/settings/metadata"
// Metadata displays the repo metadata page (repo-scoped custom field values).
func Metadata(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.metadata")
ctx.Data["PageIsSettingsMetadata"] = true
ownerID := ctx.Repo.Repository.OwnerID
repoID := ctx.Repo.Repository.ID
fields, _ := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeRepo)
ctx.Data["CustomFieldDefs"] = fields
values := make(map[int64]string)
fieldOptions := make(map[int64][]string)
if len(fields) > 0 {
values, _ = issues_model.GetCustomFieldValuesMap(ctx, repoID)
for _, f := range fields {
if f.Options != "" {
var opts []string
if err := json.Unmarshal([]byte(f.Options), &opts); err == nil {
fieldOptions[f.ID] = opts
}
}
}
}
ctx.Data["CustomFieldValues"] = values
ctx.Data["CustomFieldOptions"] = fieldOptions
ctx.HTML(http.StatusOK, tplSettingsMetadata)
}
// MetadataPost saves repo-scoped custom field values.
func MetadataPost(ctx *context.Context) {
repoID := ctx.Repo.Repository.ID
ownerID := ctx.Repo.Repository.OwnerID
fields, _ := issues_model.GetCustomFieldsByOwner(ctx, ownerID, issues_model.CustomFieldScopeRepo)
for _, f := range fields {
val := ctx.Req.FormValue(fmt.Sprintf("field_%d", f.ID))
if err := issues_model.SetCustomFieldValue(ctx, repoID, f.ID, val); err != nil {
ctx.ServerError("SetCustomFieldValue", err)
return
}
}
ctx.Flash.Success(ctx.Tr("repo.settings.metadata_saved"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/metadata")
}
+1 -1
View File
@@ -703,7 +703,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
ctx.Redirect(ctx.Repo.RepoLink + "/settings/advanced")
}
func handleSettingsPostSigning(ctx *context.Context) {
+3 -1
View File
@@ -94,7 +94,9 @@ func ServeUpdatesXML(ctx *context.Context) {
}
repoCfg, _ := licenses.GetRepoConfig(ctx, ctx.Repo.Repository.ID)
requireKey := repoCfg != nil && repoCfg.RequireKey
// Show <downloadkey> only when downloads are gated (prerelease or all).
// No gating = no license keys needed = no downloadkey element.
requireKey := repoCfg != nil && repoCfg.DownloadGating != "" && repoCfg.DownloadGating != "none"
xmlData, err := updateserver.GenerateJoomlaXML(ctx, ctx.Repo.Repository, requireKey, stripDownloads, allowedChannels...)
if err != nil {
+9 -6
View File
@@ -1061,6 +1061,12 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Get("", org.SettingsUpdateStreams)
m.Post("", org.SettingsUpdateStreamsPost)
})
m.Group("/custom-fields", func() {
m.Get("", org.SettingsCustomFields)
m.Post("", org.SettingsCustomFieldsCreatePost)
m.Post("/{id}/edit", org.SettingsCustomFieldsEditPost)
m.Post("/{id}/delete", org.SettingsCustomFieldsDeletePost)
})
}, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true))
}, context.OrgAssignment(context.OrgAssignmentOptions{RequireOwner: true}))
}, reqSignIn)
@@ -1187,12 +1193,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Combo("/advanced").Get(repo_setting.AdvancedSettings).Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
}, repo_setting.SettingsCtxData)
m.Combo("/licensing").Get(repo_setting.LicensingSettings).Post(repo_setting.LicensingSettingsPost)
m.Group("/custom-fields", func() {
m.Get("", repo_setting.CustomFields)
m.Post("", repo_setting.CustomFieldsCreatePost)
m.Post("/{id}/edit", repo_setting.CustomFieldsEditPost)
m.Post("/{id}/delete", repo_setting.CustomFieldsDeletePost)
})
m.Combo("/metadata").Get(repo_setting.Metadata).Post(repo_setting.MetadataPost)
m.Group("/collaboration", func() {
m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
@@ -1397,6 +1398,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Post("/projects/column", reqRepoIssuesOrPullsWriter, reqRepoProjectsWriter, repo.UpdateIssueProjectColumn)
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
m.Post("/{id}/custom-fields/{field_id}", reqRepoIssuesOrPullsWriter, repo.UpdateIssueCustomField)
m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
@@ -1549,6 +1551,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
m.Post("/packages/{id}/archive", repo.LicensesArchivePackage)
m.Post("/packages/{id}/unarchive", repo.LicensesUnarchivePackage)
m.Post("/keys/generate", repo.LicensesGenerateKey)
m.Post("/master-key/regenerate", repo.LicensesRegenerateMasterKey)
m.Get("/keys/{id}/edit", repo.LicensesEditKey)
m.Post("/keys/{id}/edit", repo.LicensesEditKeyPost)
m.Post("/keys/{id}/revoke", repo.LicensesRevokeKey)
+2
View File
@@ -663,6 +663,8 @@ func repoAssignmentPrepareTemplateData(ctx *Context, data *repoAssignmentPrepare
ctx.Data["NumLicensePackages"] = numLicensePackages
ctx.Data["EnableLicenses"] = licensingEnabled || numLicensePackages > 0
ctx.Data["LicensingEnabled"] = licensingEnabled
downloadGated := repoUpdateCfg != nil && repoUpdateCfg.DownloadGating != "" && repoUpdateCfg.DownloadGating != "none"
ctx.Data["DownloadGated"] = downloadGated
// Determine release page access based on feed visibility mode.
feedVis := "public"
+7 -10
View File
@@ -68,13 +68,15 @@ func GenerateComposerJSON(ctx context.Context, repo *repo_model.Repository, lice
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID)
meta := resolveExtensionMetadata(ctx, repo, cfg)
// Composer package name: vendor/package
// Composer package name: vendor/package (override with resolved extension name if set)
packageName := fmt.Sprintf("%s/%s", strings.ToLower(repo.Owner.Name), strings.ToLower(repo.Name))
if cfg != nil && cfg.ExtensionName != "" {
packageName = cfg.ExtensionName
if meta.Element != strings.ToLower(repo.Name) {
packageName = meta.Element
}
description := meta.Description
maintainer := repo.Owner.Name
maintainerURL := fmt.Sprintf("%s/%s", baseURL, repo.Owner.Name)
if cfg != nil && cfg.Maintainer != "" {
@@ -84,14 +86,9 @@ func GenerateComposerJSON(ctx context.Context, repo *repo_model.Repository, lice
maintainerURL = cfg.MaintainerURL
}
description := ""
if cfg != nil && cfg.Description != "" {
description = cfg.Description
}
phpMin := ""
if cfg != nil && cfg.PHPMinimum != "" {
phpMin = ">=" + cfg.PHPMinimum
if meta.PHPMinimum != "" {
phpMin = ">=" + meta.PHPMinimum
}
streams := licenses.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID)
+3 -10
View File
@@ -66,16 +66,9 @@ func GenerateDrupalXML(ctx context.Context, repo *repo_model.Repository, allowed
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID)
shortName := strings.ToLower(repo.Name)
title := repo.Name
if cfg != nil {
if cfg.ExtensionName != "" {
shortName = cfg.ExtensionName
}
if cfg.DisplayName != "" {
title = cfg.DisplayName
}
}
meta := resolveExtensionMetadata(ctx, repo, cfg)
shortName := meta.Element
title := meta.DisplayName
streams := licenses.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID)
+161 -43
View File
@@ -13,8 +13,10 @@ import (
"time"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
issues_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/issues"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/licenses"
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/storage"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
)
@@ -37,7 +39,7 @@ type xmlUpdate struct {
TargetPlatform xmlTargetPlat `xml:"targetplatform"`
SHA256 string `xml:"sha256,omitempty"`
SHA512 string `xml:"sha512,omitempty"`
Client string `xml:"client"`
Client string `xml:"client,omitempty"`
PHPMinimum string `xml:"php_minimum,omitempty"`
Description string `xml:"description,omitempty"`
CreationDate string `xml:"creationDate,omitempty"`
@@ -120,7 +122,10 @@ func isStreamName(s string, streams []licenses.StreamDef) bool {
}
// joomlaTagName maps internal stream names to Joomla-standard tag values.
// Joomla recognizes: dev, alpha, beta, rc, stable.
// Joomla's Update.php maps tags via STABILITY_ + strtoupper(tag) constants.
// Valid values: dev (0), alpha (1), beta (2), rc (3), stable (4).
// Using full names like "development" or "release-candidate" would silently
// fall back to STABILITY_STABLE, breaking pre-release channel filtering.
func joomlaTagName(channel string) string {
switch channel {
case ChannelDevelopment:
@@ -157,9 +162,121 @@ func NormalizeChannel(ch string) string {
}
}
// extensionMetadata holds resolved metadata for feed generation.
// Fields are resolved with priority: custom field → config table → default.
type extensionMetadata struct {
Element string
DisplayName string
ExtType string
TargetVersion string
PHPMinimum string
Description string
SupportURL string
DownloadGating string
KeyPrefix string
}
// resolveExtensionMetadata loads extension metadata with cascading fallback:
// org-level repo-scoped custom fields → update_stream_config → repo-derived defaults.
func resolveExtensionMetadata(ctx context.Context, repo *repo_model.Repository, cfg *licenses.UpdateStreamConfig) extensionMetadata {
m := extensionMetadata{
Element: strings.ToLower(repo.Name),
DisplayName: fmt.Sprintf("%s - %s", repo.Owner.Name, repo.Name),
ExtType: "component",
TargetVersion: "(5|6)\\..*",
}
// Apply config table values.
if cfg != nil {
if cfg.ExtensionName != "" {
m.Element = cfg.ExtensionName
}
if cfg.DisplayName != "" {
m.DisplayName = cfg.DisplayName
}
if cfg.ExtensionType != "" {
m.ExtType = cfg.ExtensionType
}
if cfg.TargetVersion != "" {
m.TargetVersion = cfg.TargetVersion
}
if cfg.PHPMinimum != "" {
m.PHPMinimum = cfg.PHPMinimum
}
if cfg.Description != "" {
m.Description = cfg.Description
}
if cfg.SupportURL != "" {
m.SupportURL = cfg.SupportURL
}
if cfg.DownloadGating != "" {
m.DownloadGating = cfg.DownloadGating
}
if cfg.KeyPrefix != "" {
m.KeyPrefix = cfg.KeyPrefix
}
}
// Override with custom field values (highest priority).
fields, err := issues_model.GetCustomFieldsByOwner(ctx, repo.OwnerID, issues_model.CustomFieldScopeRepo)
if err != nil {
log.Error("resolveExtensionMetadata: GetCustomFieldsByOwner for repo %d: %v", repo.ID, err)
return m
}
if len(fields) == 0 {
return m
}
values, err := issues_model.GetCustomFieldValuesMap(ctx, repo.ID)
if err != nil {
log.Error("resolveExtensionMetadata: GetCustomFieldValuesMap for repo %d: %v", repo.ID, err)
return m
}
if len(values) == 0 {
return m
}
// Build name → value map from field definitions + values.
named := make(map[string]string, len(fields))
for _, f := range fields {
if v, ok := values[f.ID]; ok && v != "" {
named[f.Name] = v
}
}
if v := named["Extension Name"]; v != "" {
m.Element = v
}
if v := named["Display Name"]; v != "" {
m.DisplayName = v
}
if v := named["Extension Type"]; v != "" {
m.ExtType = v
}
if v := named["Target Version"]; v != "" {
m.TargetVersion = v
}
if v := named["PHP Minimum"]; v != "" {
m.PHPMinimum = v
}
if v := named["Support URL"]; v != "" {
m.SupportURL = v
}
if v := named["Description"]; v != "" {
m.Description = v
}
if v := named["Download Gating"]; v != "" {
m.DownloadGating = v
}
if v := named["Key Prefix"]; v != "" {
m.KeyPrefix = v
}
return m
}
// GenerateJoomlaXML builds a Joomla-compatible updates.xml from repository releases.
// It returns the raw XML bytes. Extension metadata is read from the update stream config;
// falls back to repo name/owner when not configured.
// It returns the raw XML bytes. Extension metadata is resolved from custom fields first,
// then the update stream config, then repo-derived defaults.
// allowedChannels optionally restricts output to specific channels (nil = all).
func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, requireKey, stripDownloads bool, allowedChannels ...string) ([]byte, error) {
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
@@ -182,42 +299,26 @@ func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, require
}
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
// Load extension metadata from config (falls back to repo-derived values).
// Load extension metadata with cascading fallback:
// custom fields → config table → repo-derived defaults.
cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID)
meta := resolveExtensionMetadata(ctx, repo, cfg)
element := strings.ToLower(repo.Name)
if cfg != nil && cfg.ExtensionName != "" {
element = cfg.ExtensionName
element := meta.Element
displayName := meta.DisplayName
extType := meta.ExtType
targetVersion := meta.TargetVersion
phpMinimum := meta.PHPMinimum
feedDescription := meta.Description
// Maintainer and URL always come from the org profile.
maintainer := repo.Owner.FullName
if maintainer == "" {
maintainer = repo.Owner.Name
}
displayName := fmt.Sprintf("%s - %s", repo.Owner.Name, repo.Name)
if cfg != nil && cfg.DisplayName != "" {
displayName = cfg.DisplayName
}
extType := "component"
if cfg != nil && cfg.ExtensionType != "" {
extType = cfg.ExtensionType
}
maintainer := repo.Owner.Name
if cfg != nil && cfg.Maintainer != "" {
maintainer = cfg.Maintainer
}
maintainerURL := fmt.Sprintf("%s/%s", baseURL, repo.Owner.Name)
if cfg != nil && cfg.SupportURL != "" {
maintainerURL = cfg.SupportURL
} else if cfg != nil && cfg.MaintainerURL != "" {
maintainerURL = cfg.MaintainerURL
}
targetVersion := "(5|6)\\..*"
if cfg != nil && cfg.TargetVersion != "" {
targetVersion = cfg.TargetVersion
}
phpMinimum := ""
if cfg != nil && cfg.PHPMinimum != "" {
phpMinimum = cfg.PHPMinimum
}
feedDescription := ""
if cfg != nil && cfg.Description != "" {
feedDescription = cfg.Description
maintainerURL := repo.Owner.Website
if maintainerURL == "" {
maintainerURL = fmt.Sprintf("%s/%s", baseURL, repo.Owner.Name)
}
// Resolve effective streams (repo override → org default → Joomla default).
@@ -286,9 +387,16 @@ func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, require
downloadURL = fmt.Sprintf("%s/archive/%s.zip", repoLink, rel.TagName)
}
version := extractVersion(rel.TagName)
// If the tag is a stream name (not a version), try the release title instead.
if version == "" || isStreamName(rel.TagName, streams) {
// Extract version: prefer asset filename (matches actual download),
// then tag name, then release title. Only fall through when empty.
version := ""
if zipName != "" {
version = extractVersion(zipName)
}
if version == "" {
version = extractVersion(rel.TagName)
}
if version == "" {
version = extractVersion(rel.Title)
}
// Last resort: use the tag name as-is.
@@ -308,9 +416,19 @@ func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, require
desc = fmt.Sprintf("%s %s build.", displayName, ch)
}
// Info URL: use support_url (product page), fall back to releases page.
infoURL := fmt.Sprintf("%s/releases", repoLink)
if cfg != nil && cfg.InfoURL != "" {
infoURL = cfg.InfoURL
if meta.SupportURL != "" {
infoURL = meta.SupportURL
}
// Joomla <client> element: packages use client_id=0 in #__extensions,
// so we must output <client>0</client> for Joomla to match the update
// to the installed extension. Other types default to "site" (client_id=0)
// or "administrator" (client_id=1).
client := "site"
if extType == "package" {
client = "0"
}
u := xmlUpdate{
@@ -318,7 +436,7 @@ func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, require
Description: desc,
Element: element,
Type: extType,
Client: "site",
Client: client,
Version: version,
CreationDate: time.Unix(int64(rel.CreatedUnix), 0).Format("2006-01-02"),
InfoURL: xmlInfoURL{
+6 -16
View File
@@ -55,23 +55,13 @@ func GeneratePrestaShopXML(ctx context.Context, repo *repo_model.Repository, all
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID)
moduleName := strings.ToLower(repo.Name)
displayName := repo.Name
meta := resolveExtensionMetadata(ctx, repo, cfg)
moduleName := meta.Element
displayName := meta.DisplayName
description := meta.Description
maintainer := repo.Owner.Name
description := ""
if cfg != nil {
if cfg.ExtensionName != "" {
moduleName = cfg.ExtensionName
}
if cfg.DisplayName != "" {
displayName = cfg.DisplayName
}
if cfg.Maintainer != "" {
maintainer = cfg.Maintainer
}
if cfg.Description != "" {
description = cfg.Description
}
if cfg != nil && cfg.Maintainer != "" {
maintainer = cfg.Maintainer
}
streams := licenses.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID)
+3 -8
View File
@@ -50,23 +50,18 @@ func GenerateWHMCSJSON(ctx context.Context, repo *repo_model.Repository, license
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID)
displayName := repo.Name
meta := resolveExtensionMetadata(ctx, repo, cfg)
displayName := meta.DisplayName
description := meta.Description
maintainer := repo.Owner.Name
maintainerURL := fmt.Sprintf("%s/%s", baseURL, repo.Owner.Name)
description := ""
if cfg != nil {
if cfg.DisplayName != "" {
displayName = cfg.DisplayName
}
if cfg.Maintainer != "" {
maintainer = cfg.Maintainer
}
if cfg.MaintainerURL != "" {
maintainerURL = cfg.MaintainerURL
}
if cfg.Description != "" {
description = cfg.Description
}
}
streams := licenses.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID)
+11 -17
View File
@@ -57,36 +57,30 @@ func GenerateWordPressJSON(ctx context.Context, repo *repo_model.Repository, lic
baseURL := strings.TrimSuffix(setting.AppURL, "/")
repoLink := fmt.Sprintf("%s/%s/%s", baseURL, repo.Owner.Name, repo.Name)
// Load extension metadata.
// Load extension metadata with cascading fallback:
// custom fields → config table → repo-derived defaults.
cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID)
meta := resolveExtensionMetadata(ctx, repo, cfg)
slug := strings.ToLower(repo.Name)
displayName := fmt.Sprintf("%s - %s", repo.Owner.Name, repo.Name)
slug := meta.Element
displayName := meta.DisplayName
requiresPHP := meta.PHPMinimum
homepage := repoLink
if meta.SupportURL != "" {
homepage = meta.SupportURL
}
maintainer := repo.Owner.Name
maintainerURL := fmt.Sprintf("%s/%s", baseURL, repo.Owner.Name)
homepage := repoLink
requiresPHP := ""
if cfg != nil {
if cfg.ExtensionName != "" {
slug = cfg.ExtensionName
}
if cfg.DisplayName != "" {
displayName = cfg.DisplayName
}
if cfg.Maintainer != "" {
maintainer = cfg.Maintainer
}
if cfg.MaintainerURL != "" {
maintainerURL = cfg.MaintainerURL
}
if cfg.SupportURL != "" {
homepage = cfg.SupportURL
} else if cfg.InfoURL != "" {
if homepage == repoLink && cfg.InfoURL != "" {
homepage = cfg.InfoURL
}
if cfg.PHPMinimum != "" {
requiresPHP = cfg.PHPMinimum
}
}
// Resolve streams and find the latest stable release.
+85
View File
@@ -0,0 +1,85 @@
{{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings custom-fields")}}
<h4 class="ui top attached header">
{{ctx.Locale.Tr "org.settings.custom_fields"}}
</h4>
<div class="ui attached segment">
<p class="text grey">{{ctx.Locale.Tr "org.settings.custom_fields_desc"}}</p>
{{if .CustomFields}}
<table class="ui compact table">
<thead>
<tr>
<th>{{ctx.Locale.Tr "org.settings.custom_field_name"}}</th>
<th>{{ctx.Locale.Tr "org.settings.custom_field_scope"}}</th>
<th>{{ctx.Locale.Tr "org.settings.custom_field_type"}}</th>
<th>{{ctx.Locale.Tr "org.settings.custom_field_options"}}</th>
<th></th>
</tr>
</thead>
<tbody>
{{range .CustomFields}}
<tr>
<td><strong>{{.Name}}</strong>{{if .Description}}<br><small class="text grey">{{.Description}}</small>{{end}}</td>
<td>{{if eq .Scope "issue"}}{{svg "octicon-issue-opened" 14}} Issue{{else}}{{svg "octicon-repo" 14}} Repo{{end}}</td>
<td><code>{{.FieldType}}</code></td>
<td>{{if .Options}}<code class="tw-text-xs">{{.Options}}</code>{{else}}<span class="text grey">-</span>{{end}}</td>
<td class="tw-text-right">
<form method="post" action="{{$.OrgLink}}/settings/custom-fields/{{.ID}}/delete" class="tw-inline">
{{$.CsrfTokenHtml}}
<button class="ui tiny red icon button" type="submit" title="{{ctx.Locale.Tr "remove"}}">{{svg "octicon-trash" 14}}</button>
</form>
</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
<div class="empty-placeholder">
<p>{{ctx.Locale.Tr "org.settings.custom_fields_empty"}}</p>
</div>
{{end}}
<div class="divider"></div>
<h5>{{ctx.Locale.Tr "org.settings.custom_field_add"}}</h5>
<form class="ui form" method="post" action="{{.OrgLink}}/settings/custom-fields">
{{.CsrfTokenHtml}}
<div class="three fields">
<div class="required field">
<label>{{ctx.Locale.Tr "org.settings.custom_field_name"}}</label>
<input name="name" required placeholder="e.g. Status, Platform, Priority">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "org.settings.custom_field_scope"}}</label>
<select name="scope" class="ui dropdown">
<option value="issue">{{svg "octicon-issue-opened" 14}} Issue (sidebar)</option>
<option value="repo">{{svg "octicon-repo" 14}} Repo (metadata)</option>
</select>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "org.settings.custom_field_type"}}</label>
<select name="field_type" class="ui dropdown">
<option value="dropdown">Dropdown</option>
<option value="text">Text</option>
<option value="number">Number</option>
<option value="date">Date</option>
<option value="checkbox">Checkbox</option>
<option value="url">URL</option>
</select>
</div>
</div>
<div class="two fields">
<div class="field">
<label>{{ctx.Locale.Tr "org.settings.custom_field_options"}}</label>
<input name="options" placeholder='["Option 1","Option 2","Option 3"]'>
<p class="help">{{ctx.Locale.Tr "org.settings.custom_field_options_help"}}</p>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "org.settings.custom_field_description"}}</label>
<input name="description" placeholder="Help text shown to users">
</div>
</div>
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "org.settings.custom_field_add"}}</button>
</form>
</div>
{{template "org/settings/layout_footer" .}}
+4 -1
View File
@@ -26,7 +26,10 @@
</a>
{{end}}
<a class="{{if .PageIsSettingsUpdateStreams}}active {{end}}item" href="{{.OrgLink}}/settings/update-streams">
{{svg "octicon-key"}} {{ctx.Locale.Tr "org.settings.update_streams"}}
{{svg "octicon-broadcast"}} {{ctx.Locale.Tr "org.settings.update_streams"}}
</a>
<a class="{{if .PageIsSettingsCustomFields}}active {{end}}item" href="{{.OrgLink}}/settings/custom-fields">
{{svg "octicon-list-unordered"}} {{ctx.Locale.Tr "org.settings.custom_fields"}}
</a>
{{if .EnableActions}}
<details class="item toggleable-item" {{if or .PageIsOrgSettingsActionsGeneral .PageIsSharedSettingsRunners .PageIsSharedSettingsSecrets .PageIsSharedSettingsVariables}}open{{end}}>
+4 -3
View File
@@ -130,9 +130,10 @@
{{if and .LicensingEnabled .IsSigned}}
<a href="{{.RepoLink}}/licenses" class="{{if .IsLicensesPage}}active {{end}}item">
{{svg "octicon-key"}} {{ctx.Locale.Tr "repo.licenses"}}
{{if .NumLicensePackages}}
<span class="ui small label">{{CountFmt .NumLicensePackages}}</span>
{{if .DownloadGated}}
{{svg "octicon-key"}} {{ctx.Locale.Tr "repo.licenses"}}
{{else}}
{{svg "octicon-broadcast"}} {{ctx.Locale.Tr "repo.settings.licensing_section"}}
{{end}}
</a>
{{end}}
+25
View File
@@ -59,6 +59,31 @@
{{end}}
{{template "repo/issue/sidebar/assignee_list" $.IssuePageMetaData}}
{{if .CustomFieldDefs}}
<div class="divider"></div>
<div class="tw-flex tw-flex-col tw-gap-2">
{{$values := .CustomFieldValues}}
{{$fieldOptions := .CustomFieldOptions}}
{{range .CustomFieldDefs}}
{{$currentVal := index $values .ID}}
<div class="tw-flex tw-items-center tw-justify-between tw-gap-2">
<span class="text grey tw-text-sm" {{if .Description}}title="{{.Description}}"{{end}}>{{.Name}}</span>
{{if ne .Options ""}}
{{$opts := index $fieldOptions .ID}}
<select name="custom-field-{{.ID}}" class="ui compact mini dropdown tw-max-w-48">
<option value="">—</option>
{{range $opts}}
<option value="{{.}}" {{if eq . $currentVal}}selected{{end}}>{{.}}</option>
{{end}}
</select>
{{else}}
<input name="custom-field-{{.ID}}" type="text" class="tw-max-w-48 tw-text-sm" value="{{$currentVal}}" placeholder="—">
{{end}}
</div>
{{end}}
</div>
{{end}}
{{if and .PageIsComparePull (not (eq .HeadRepo.FullName .BaseCompareRepo.FullName)) .CanWriteToHeadRepo}}
<div class="divider"></div>
<div class="ui checkbox">
@@ -0,0 +1,33 @@
{{if .CustomFieldDefs}}
<div class="divider"></div>
<div class="tw-flex tw-flex-col tw-gap-2">
{{$issueID := .Issue.ID}}
{{$repoLink := .RepoLink}}
{{$canModify := .HasIssuesOrPullsWritePermission}}
{{$values := .CustomFieldValues}}
{{$fieldOptions := .CustomFieldOptions}}
{{range .CustomFieldDefs}}
{{$currentVal := index $values .ID}}
<div class="tw-flex tw-items-center tw-justify-between tw-gap-2">
<span class="text grey tw-text-sm" {{if .Description}}title="{{.Description}}"{{end}}>{{.Name}}</span>
{{if and $canModify (eq .Options "")}}
{{/* Non-dropdown: just display the value */}}
<span class="tw-text-sm">{{if $currentVal}}{{$currentVal}}{{else}}<span class="text grey">—</span>{{end}}</span>
{{else if $canModify}}
<form method="post" action="{{$repoLink}}/issues/{{$issueID}}/custom-fields/{{.ID}}" class="tw-inline">
{{$.CsrfTokenHtml}}
<select name="value" class="ui compact mini dropdown tw-max-w-48" onchange="this.form.submit()">
<option value="">—</option>
{{$opts := index $fieldOptions .ID}}
{{range $opts}}
<option value="{{.}}" {{if eq . $currentVal}}selected{{end}}>{{.}}</option>
{{end}}
</select>
</form>
{{else}}
<span class="tw-text-sm">{{if $currentVal}}{{$currentVal}}{{else}}<span class="text grey">—</span>{{end}}</span>
{{end}}
</div>
{{end}}
</div>
{{end}}
@@ -7,6 +7,8 @@
{{template "repo/issue/sidebar/label_list" $.IssuePageMetaData}}
{{template "repo/issue/sidebar/custom_fields" $}}
{{template "repo/issue/sidebar/milestone_list" $.IssuePageMetaData}}
{{if .IsProjectsEnabled}}
{{template "repo/issue/sidebar/project_list" $.IssuePageMetaData}}
+105 -67
View File
@@ -3,6 +3,7 @@
{{template "repo/header" .}}
<div class="ui container">
{{if .DownloadGated}}
{{if .NewMasterKey}}
<div class="ui info message">
<div class="header">{{ctx.Locale.Tr "repo.licenses.master_key_created"}}</div>
@@ -26,11 +27,10 @@
{{end}}
{{/* License Packages */}}
<details id="new-package-details">
<h4 class="ui top attached header tw-flex tw-items-center tw-justify-between">
<span>{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.licenses.packages"}}</span>
{{if .IsRepoAdmin}}
<summary class="ui primary small button">{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</summary>
<button class="ui primary small button show-modal" data-modal="#new-package-modal">{{svg "octicon-plus" 14}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</button>
{{end}}
</h4>
<div class="ui attached segment">
@@ -56,16 +56,12 @@
<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 tw-flex tw-gap-1 tw-justify-end">
<form method="post" action="{{$.RepoLink}}/licenses/keys/generate" class="tw-inline tw-flex tw-gap-1 tw-items-center">
{{$.CsrfTokenHtml}}
<input type="hidden" name="package_id" value="{{.ID}}">
{{if $.IsSiteAdmin}}
<input type="text" name="custom_key" placeholder="{{ctx.Locale.Tr "repo.licenses.custom_key_placeholder"}}" class="tw-w-32 tw-text-xs" title="{{ctx.Locale.Tr "repo.licenses.custom_key_help"}}">
{{end}}
<button class="ui tiny primary button" type="submit" title="{{ctx.Locale.Tr "repo.licenses.generate_key"}}">
{{svg "octicon-plus" 14}}
</button>
</form>
<button class="ui tiny primary button show-modal"
data-modal="#generate-key-modal"
data-modal-form.action="{{$.RepoLink}}/licenses/keys/generate?package_id={{.ID}}"
title="{{ctx.Locale.Tr "repo.licenses.generate_key"}}">
{{svg "octicon-plus" 14}}
</button>
{{if ne .Name "Master (Internal)"}}
<a class="ui tiny button" href="{{$.RepoLink}}/licenses/packages/{{.ID}}/edit" title="{{ctx.Locale.Tr "repo.licenses.edit_package"}}">
{{svg "octicon-pencil" 14}}
@@ -94,60 +90,6 @@
{{end}}
</div>
{{/* Create New License Package (form panel, toggled by header button) */}}
{{if .IsRepoAdmin}}
<div class="ui attached segment">
<form class="ui form" method="post" action="{{.RepoLink}}/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="e.g. Pro Annual, Basic Monthly">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.description"}}</label>
<input name="description" placeholder="e.g. Annual pro subscription with all channels">
</div>
</div>
<div class="fields">
<div class="four wide 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">
<p class="help">0 = {{ctx.Locale.Tr "repo.licenses.lifetime"}}</p>
</div>
<div class="four wide field">
<label>{{ctx.Locale.Tr "repo.licenses.max_sites"}}</label>
<input name="max_sites" type="number" value="0" min="0">
<p class="help">0 = {{ctx.Locale.Tr "repo.licenses.unlimited"}}</p>
</div>
<div class="four wide field">
<label>{{ctx.Locale.Tr "repo.licenses.domain_lock_hours"}}</label>
<input name="domain_lock_hours" type="number" value="0" min="0">
<p class="help">{{ctx.Locale.Tr "repo.licenses.domain_lock_hours_help"}}</p>
</div>
<div class="field">
{{template "shared/combolist" dict "Name" "allowed_channels" "Title" (ctx.Locale.Tr "repo.licenses.channels") "Items" $.ChannelItems "SelectedValues" "" "EmptyText" (ctx.Locale.Tr "repo.licenses.all_channels") "Icon" "octicon-tag"}}
<p class="help">{{ctx.Locale.Tr "repo.licenses.channels_help"}}</p>
</div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.repo_scope"}}</label>
<select name="repo_scope" class="ui dropdown">
<option value="all">{{ctx.Locale.Tr "repo.licenses.repo_scope_all"}}</option>
{{if .OrgRepos}}
{{range .OrgRepos}}
<option value="{{.ID}}">{{.Name}}</option>
{{end}}
{{end}}
</select>
<p class="help">{{ctx.Locale.Tr "repo.licenses.repo_scope_help"}}</p>
</div>
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "repo.licenses.create_package"}}</button>
</form>
</div>
</details>
{{end}}
{{/* Issued Keys */}}
{{if or .LicenseKeys .SearchQuery}}
<h4 class="ui top attached header tw-mt-4">
@@ -183,7 +125,7 @@
<td>
<div class="tw-flex tw-items-center tw-gap-1">
<code class="js-license-key-{{.ID}}">{{if .KeyRaw}}{{.KeyRaw}}{{else}}{{.KeyPrefix}}{{end}}</code>
{{if .KeyRaw}}<button class="ui tiny icon button" data-clipboard-target=".js-license-key-{{.ID}}" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}">{{svg "octicon-copy" 12}}</button>{{end}}
<button class="ui tiny icon button" data-clipboard-text="{{if .KeyRaw}}{{.KeyRaw}}{{else}}{{.KeyPrefix}}{{end}}" data-tooltip-content="{{ctx.Locale.Tr "copy_url"}}">{{svg "octicon-copy" 12}}</button>
{{if .IsInternal}} <span class="ui tiny orange label">{{ctx.Locale.Tr "repo.licenses.master_label"}}</span>{{end}}
</div>
</td>
@@ -260,6 +202,8 @@
</details>
{{end}}
{{end}}{{/* end DownloadGated */}}
{{/* Update Feed URLs */}}
{{if .LicensingEnabled}}
<h4 class="ui top attached header tw-mt-4">
@@ -309,7 +253,100 @@
</div>
</div>
{{/* Create New License Package Modal */}}
{{if .IsRepoAdmin}}
<div class="ui small modal" id="new-package-modal">
<div class="header">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.licenses.new_package"}}</div>
<div class="content">
<form class="ui form" method="post" action="{{.RepoLink}}/licenses/packages">
{{.CsrfTokenHtml}}
<div class="required field">
<label>{{ctx.Locale.Tr "repo.licenses.package_name"}}</label>
<input name="name" required placeholder="e.g. Pro Annual, Basic Monthly">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.description"}}</label>
<input name="description" placeholder="e.g. Annual pro subscription with all channels">
</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">
<p class="help">0 = {{ctx.Locale.Tr "repo.licenses.lifetime"}}</p>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.max_sites"}}</label>
<input name="max_sites" type="number" value="0" min="0">
<p class="help">0 = {{ctx.Locale.Tr "repo.licenses.unlimited"}}</p>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.domain_lock_hours"}}</label>
<input name="domain_lock_hours" type="number" value="0" min="0">
<p class="help">{{ctx.Locale.Tr "repo.licenses.domain_lock_hours_help"}}</p>
</div>
</div>
<div class="field">
{{template "shared/combolist" dict "Name" "allowed_channels" "Title" (ctx.Locale.Tr "repo.licenses.channels") "Items" $.ChannelItems "SelectedValues" "" "EmptyText" (ctx.Locale.Tr "repo.licenses.all_channels") "Icon" "octicon-tag"}}
<p class="help">{{ctx.Locale.Tr "repo.licenses.channels_help"}}</p>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.domain_restriction"}}</label>
<input name="domain_restriction" placeholder="example.com, example.org">
<p class="help">{{ctx.Locale.Tr "repo.licenses.domain_restriction_package_help"}}</p>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.repo_scope"}}</label>
<select name="repo_scope" class="ui dropdown">
<option value="all">{{ctx.Locale.Tr "repo.licenses.repo_scope_all"}}</option>
{{if .OrgRepos}}
{{range .OrgRepos}}
<option value="{{.ID}}">{{.Name}}</option>
{{end}}
{{end}}
</select>
<p class="help">{{ctx.Locale.Tr "repo.licenses.repo_scope_help"}}</p>
</div>
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.licenses.create_package"))}}
</form>
</div>
</div>
{{end}}
{{/* Generate Key Modal */}}
{{if and .DownloadGated .IsRepoAdmin}}
<div class="ui small modal" id="generate-key-modal">
<div class="header">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.licenses.generate_key"}}</div>
<div class="content">
<form class="ui form" method="post" action="{{.RepoLink}}/licenses/keys/generate">
{{.CsrfTokenHtml}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.licensee_name"}}</label>
<input name="licensee_name" placeholder="Customer name">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.licensee_email"}}</label>
<input name="licensee_email" type="email" placeholder="customer@example.com">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.domain_restriction"}}</label>
<input name="domain_restriction" placeholder="example.com, example.org">
<p class="help">{{ctx.Locale.Tr "repo.licenses.domain_restriction_help"}}</p>
</div>
{{if .IsSiteAdmin}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.custom_key_placeholder"}}</label>
<input name="custom_key" placeholder="MOKO-XXXX-XXXX-XXXX">
<p class="help">{{ctx.Locale.Tr "repo.licenses.custom_key_help"}}</p>
</div>
{{end}}
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" (ctx.Locale.Tr "repo.licenses.generate_key"))}}
</form>
</div>
</div>
{{end}}
{{/* Delete Package Confirmation Modal */}}
{{if .DownloadGated}}
<div class="ui small modal" id="license-delete-package-modal">
<div class="header">{{svg "octicon-trash"}} {{ctx.Locale.Tr "repo.licenses.delete_package"}}</div>
<div class="content">
@@ -340,5 +377,6 @@
</form>
</div>
</div>
{{end}}{{/* end DownloadGated for modals */}}
{{template "base/footer" .}}
@@ -39,6 +39,11 @@
<p class="help">{{ctx.Locale.Tr "repo.licenses.channels_help"}}</p>
</div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.licenses.domain_restriction"}}</label>
<input name="domain_restriction" value="{{.Package.DomainRestriction}}" placeholder="example.com, example.org">
<p class="help">{{ctx.Locale.Tr "repo.licenses.domain_restriction_package_help"}}</p>
</div>
<div class="field">
<div class="ui checkbox">
<input name="is_active" type="checkbox" {{if .Package.IsActive}}checked{{end}}>
+34 -29
View File
@@ -6,9 +6,9 @@
<div class="ui attached segment">
<form class="ui form" method="post">
<input type="hidden" name="action" value="advanced">
{{/* Code */}}
<div class="tw-mb-4">
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-code" 16}} {{ctx.Locale.Tr "repo.code"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-code" 16}} {{ctx.Locale.Tr "repo.code"}}</h5>
{{$isCodeEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeCode}}
{{$isCodeGlobalDisabled := ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled}}
<div class="inline field">
@@ -16,11 +16,10 @@
<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
</div>
</div>
</div>
{{/* Wiki */}}
<div class="tw-mb-4">
<div class="ui divider"></div>
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-book" 16}} {{ctx.Locale.Tr "repo.wiki"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-book" 16}} {{ctx.Locale.Tr "repo.wiki"}}</h5>
{{$isInternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeWiki}}
{{$isExternalWikiEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalWiki}}
{{$isWikiEnabled := or $isInternalWikiEnabled $isExternalWikiEnabled}}
@@ -32,6 +31,7 @@
<input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if $isWikiEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.wiki_desc"}}</label>
</div>
</div>
<div class="field{{if not $isWikiEnabled}} disabled{{end}}" id="wiki_box">
<div class="field">
<div class="ui radio checkbox{{if $isWikiGlobalDisabled}} disabled{{end}}"{{if $isWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -63,21 +63,20 @@
<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
<p class="help">{{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}</p>
</div>
</div>
</div>
{{/* Issues */}}
<div class="tw-mb-4">
<div class="ui divider"></div>
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-issue-opened" 16}} {{ctx.Locale.Tr "repo.issues"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-issue-opened" 16}} {{ctx.Locale.Tr "repo.issues"}}</h5>
{{$isIssuesEnabled := or (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeIssues) (.Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeExternalTracker)}}
{{$isIssuesGlobalDisabled := ctx.Consts.RepoUnitTypeIssues.UnitGlobalDisabled}}
{{$isExternalTrackerGlobalDisabled := ctx.Consts.RepoUnitTypeExternalTracker.UnitGlobalDisabled}}
{{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.issues"}}</label>
<div class="ui checkbox{{if $isIssuesAndExternalGlobalDisabled}} disabled{{end}}"{{if $isIssuesAndExternalGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
<input class="enable-system" name="enable_issues" type="checkbox" data-target="#issue_box" {{if $isIssuesEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.issues_desc"}}</label>
</div>
</div>
<div class="field {{if not $isIssuesEnabled}}disabled{{end}}" id="issue_box">
<div class="field">
<div class="ui radio checkbox{{if $isIssuesGlobalDisabled}} disabled{{end}}"{{if $isIssuesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
@@ -164,20 +163,19 @@
<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc"}}</p>
</div>
</div>
</div>
</div>
{{/* Projects */}}
<div class="tw-mb-4">
<div class="ui divider"></div>
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-project" 16}} {{ctx.Locale.Tr "repo.projects"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-project" 16}} {{ctx.Locale.Tr "repo.projects"}}</h5>
{{$isProjectsEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeProjects}}
{{$isProjectsGlobalDisabled := ctx.Consts.RepoUnitTypeProjects.UnitGlobalDisabled}}
{{$projectsUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypeProjects}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.projects"}}</label>
<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
<input class="enable-system" name="enable_projects" type="checkbox" data-target="#projects_box" {{if $isProjectsEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.projects_desc"}}</label>
</div>
</div>
<div class="field {{if not $isProjectsEnabled}} disabled{{end}} tw-pl-4" id="projects_box">
<p>
{{ctx.Locale.Tr "repo.settings.projects_mode_desc"}}
@@ -206,11 +204,10 @@
<div class="item" data-value="all">{{ctx.Locale.Tr "repo.settings.projects_mode_all"}}</div>
</div>
</div>
</div>
</div>
{{/* Releases */}}
<div class="tw-mb-4">
<div class="ui divider"></div>
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-tag" 16}} {{ctx.Locale.Tr "repo.releases"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-tag" 16}} {{ctx.Locale.Tr "repo.releases"}}</h5>
{{$isReleasesEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypeReleases}}
{{$isReleasesGlobalDisabled := ctx.Consts.RepoUnitTypeReleases.UnitGlobalDisabled}}
<div class="inline field">
@@ -218,6 +215,7 @@
<input class="enable-system" name="enable_releases" type="checkbox" data-target="#releases_visibility_box" {{if $isReleasesEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.releases_desc"}}</label>
</div>
</div>
<div class="field tw-pl-4{{if not $isReleasesEnabled}} disabled{{end}}" id="releases_visibility_box">
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.settings.unit_visibility"}}</label>
@@ -227,11 +225,20 @@
</select>
<p class="help">{{ctx.Locale.Tr "repo.settings.unit_visibility_releases_help"}}</p>
</div>
</div>
{{/* Licensing */}}
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-broadcast" 16}} {{ctx.Locale.Tr "repo.settings.licensing_section"}}</h5>
<div class="inline field">
<div class="ui checkbox">
<input name="enable_licensing" type="checkbox" {{if .LicensingEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.enable_licensing"}}</label>
</div>
</div>
<p class="help">{{ctx.Locale.Tr "repo.settings.enable_licensing_help"}}</p>
{{/* Packages */}}
<div class="tw-mb-4">
<div class="ui divider"></div>
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-package" 16}} {{ctx.Locale.Tr "repo.packages"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-package" 16}} {{ctx.Locale.Tr "repo.packages"}}</h5>
{{$isPackagesEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePackages}}
{{$isPackagesGlobalDisabled := ctx.Consts.RepoUnitTypePackages.UnitGlobalDisabled}}
<div class="inline field">
@@ -239,17 +246,15 @@
<input class="enable-system" name="enable_packages" type="checkbox" {{if $isPackagesEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.packages_desc"}}</label>
</div>
</div>
</div>
{{if not .IsMirror}}
{{/* Pull Requests */}}
<div class="tw-mb-4">
<div class="ui divider"></div>
<h5 class="tw-flex tw-items-center tw-gap-2 tw-mb-2">{{svg "octicon-git-pull-request" 16}} {{ctx.Locale.Tr "repo.pulls"}}</h5>
<h5 class="ui dividing header tw-flex tw-items-center tw-gap-2">{{svg "octicon-git-pull-request" 16}} {{ctx.Locale.Tr "repo.pulls"}}</h5>
{{$pullRequestEnabled := .Repository.UnitEnabled ctx ctx.Consts.RepoUnitTypePullRequests}}
{{$pullRequestGlobalDisabled := ctx.Consts.RepoUnitTypePullRequests.UnitGlobalDisabled}}
{{$prUnit := .Repository.MustGetUnit ctx ctx.Consts.RepoUnitTypePullRequests}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.pulls"}}</label>
<div class="ui checkbox{{if $pullRequestGlobalDisabled}} disabled{{end}}"{{if $pullRequestGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
<input class="enable-system" name="enable_pulls" type="checkbox" data-target="#pull_box" {{if $pullRequestEnabled}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.pulls_desc"}}</label>
@@ -381,7 +386,7 @@
</div>
</div>
{{end}}
</div>
<div class="field tw-mt-4">
<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
</div>
+1 -1
View File
@@ -1,7 +1,7 @@
{{template "repo/settings/layout_head" (dict "pageClass" "repository settings licensing")}}
<div class="user-main-content twelve wide column">
<h4 class="ui top attached header">
{{svg "octicon-key" 16}} {{ctx.Locale.Tr "repo.settings.licensing_section"}}
{{svg "octicon-broadcast" 16}} {{ctx.Locale.Tr "repo.settings.licensing_section"}}
</h4>
<div class="ui attached segment">
<form class="ui form" method="post" action="{{.Link}}">
+49
View File
@@ -0,0 +1,49 @@
{{template "repo/settings/layout_head" (dict "pageClass" "repository settings metadata")}}
<div class="user-main-content twelve wide column">
<h4 class="ui top attached header">
{{svg "octicon-list-unordered" 16}} {{ctx.Locale.Tr "repo.settings.metadata"}}
</h4>
<div class="ui attached segment">
{{if .CustomFieldDefs}}
<form class="ui form" method="post" action="{{.RepoLink}}/settings/metadata">
{{.CsrfTokenHtml}}
{{$values := .CustomFieldValues}}
{{$options := .CustomFieldOptions}}
{{range .CustomFieldDefs}}
{{$currentVal := index $values .ID}}
<div class="field">
<label>{{.Name}}{{if .Description}} <small class="text grey">({{.Description}})</small>{{end}}</label>
{{if .Options}}
{{$opts := index $options .ID}}
<select name="field_{{.ID}}" class="ui dropdown">
<option value="">—</option>
{{range $opts}}
<option value="{{.}}" {{if eq . $currentVal}}selected{{end}}>{{.}}</option>
{{end}}
</select>
{{else if eq (printf "%s" .FieldType) "checkbox"}}
<div class="ui checkbox">
<input type="checkbox" name="field_{{.ID}}" value="true" {{if eq $currentVal "true"}}checked{{end}}>
<label></label>
</div>
{{else if eq (printf "%s" .FieldType) "number"}}
<input type="number" name="field_{{.ID}}" value="{{$currentVal}}">
{{else if eq (printf "%s" .FieldType) "url"}}
<input type="url" name="field_{{.ID}}" value="{{$currentVal}}" placeholder="https://...">
{{else if eq (printf "%s" .FieldType) "date"}}
<input type="date" name="field_{{.ID}}" value="{{$currentVal}}">
{{else}}
<input type="text" name="field_{{.ID}}" value="{{$currentVal}}">
{{end}}
</div>
{{end}}
<div class="field tw-mt-4">
<button class="ui primary button" type="submit">{{ctx.Locale.Tr "save"}}</button>
</div>
</form>
{{else}}
<p class="text grey">{{ctx.Locale.Tr "repo.settings.metadata_empty"}}</p>
{{end}}
</div>
</div>
{{template "repo/settings/layout_footer" .}}
+3 -3
View File
@@ -9,11 +9,11 @@
</a>
{{if .LicensingEnabled}}
<a class="{{if .PageIsSettingsLicensing}}active {{end}}item" href="{{.RepoLink}}/settings/licensing">
{{svg "octicon-key"}} {{ctx.Locale.Tr "repo.settings.licensing_section"}}
{{svg "octicon-broadcast"}} {{ctx.Locale.Tr "repo.settings.licensing_section"}}
</a>
{{end}}
<a class="{{if .PageIsSettingsCustomFields}}active {{end}}item" href="{{.RepoLink}}/settings/custom-fields">
{{svg "octicon-list-unordered"}} {{ctx.Locale.Tr "repo.settings.custom_fields"}}
<a class="{{if .PageIsSettingsMetadata}}active {{end}}item" href="{{.RepoLink}}/settings/metadata">
{{svg "octicon-list-unordered"}} {{ctx.Locale.Tr "repo.settings.metadata"}}
</a>
{{if or .Repository.IsPrivate .Permission.HasAnyUnitPublicAccess}}
<a class="{{if .PageIsSettingsPublicAccess}}active {{end}}item" href="{{.RepoLink}}/settings/public_access">
-103
View File
@@ -1,103 +0,0 @@
<?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.19.00
-->
<updates>
<update>
<name>MokoGitea</name>
<description>MokoGitea dev build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-dev</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="zip">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/development/mokogitea-05.05.00-dev.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>dev</tag></tags>
<changelogurl>https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea alpha build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-alpha</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/alpha</infourl>
<downloads>
<downloadurl type="full" format="zip">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/alpha/mokogitea-05.05.00-alpha.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>alpha</tag></tags>
<changelogurl>https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea beta build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-beta</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/beta</infourl>
<downloads>
<downloadurl type="full" format="zip">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/beta/mokogitea-05.05.00-beta.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>beta</tag></tags>
<changelogurl>https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea rc build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-rc</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/release-candidate</infourl>
<downloads>
<downloadurl type="full" format="zip">https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/release-candidate/mokogitea-05.05.00-rc.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>rc</tag></tags>
<changelogurl>https://code.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea stable build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.19.00</version>
<creationDate>2026-06-04</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.19.00.zip</downloadurl>
</downloads>
<sha256>a0ad5e0a2c3bc8a557bd37071f6891eeedfa8fa868b186ccd1262f6260fe22e9</sha256>
<tags><tag>stable</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*" />
</update>
</updates>