fix: update feed generates wrong element and type for Joomla packages #592

Open
opened 2026-06-09 19:18:43 +00:00 by jmiller · 4 comments
Owner

Problem

The dynamic updates.xml feed generates incorrect values for Joomla package extensions:

  • <element>mokosuitehq</element> — should be pkg_mokosuitehq (full element with type prefix)
  • <type>component</type> — should be package
  • <client>site</client> — admin packages should be administrator

Joomla matches updates by element + type against #__extensions. When these don't match, the site can't find available updates.

Expected

For a package extension pkg_mokosuitehq:

<element>pkg_mokosuitehq</element>
<type>package</type>
<client>administrator</client>

Current

<element>mokosuitehq</element>
<type>component</type>
<client>site</client>

Affected

Any Joomla package extension using MokoGitea's dynamic update feed. Currently blocks MokoSuiteHQ updates on all client sites.

Notes

The manifest API has element_name and package_type fields that could be used to populate these correctly. The feed generator should read from the manifest when available.

## Problem The dynamic updates.xml feed generates incorrect values for Joomla package extensions: - `<element>mokosuitehq</element>` — should be `pkg_mokosuitehq` (full element with type prefix) - `<type>component</type>` — should be `package` - `<client>site</client>` — admin packages should be `administrator` Joomla matches updates by `element` + `type` against `#__extensions`. When these don't match, the site can't find available updates. ## Expected For a package extension `pkg_mokosuitehq`: ```xml <element>pkg_mokosuitehq</element> <type>package</type> <client>administrator</client> ``` ## Current ```xml <element>mokosuitehq</element> <type>component</type> <client>site</client> ``` ## Affected Any Joomla package extension using MokoGitea's dynamic update feed. Currently blocks MokoSuiteHQ updates on all client sites. ## Notes The manifest API has `element_name` and `package_type` fields that could be used to populate these correctly. The feed generator should read from the manifest when available.
Author
Owner

Branch created: feature/592-fix-update-feed-generates-wrong-element-

git fetch origin
git checkout feature/592-fix-update-feed-generates-wrong-element-
Branch created: [`feature/592-fix-update-feed-generates-wrong-element-`](https://code.mokoconsulting.tech/MokoConsulting/MokoGitea-APP/src/branch/feature/592-fix-update-feed-generates-wrong-element-) ```bash git fetch origin git checkout feature/592-fix-update-feed-generates-wrong-element- ```
Author
Owner

Implementation Plan

Root Cause

resolveExtensionMetadata() in services/updateserver/joomla.go (line 179-275) does not read from the repo manifest API. It only uses:

  1. Repo name as default element (line 183): strings.ToLower(repo.Name) → e.g. mokosuitehq
  2. Hardcoded default type (line 185): "component"
  3. Config table overrides (update_stream_config)
  4. Custom field overrides (highest priority)

The manifest model (models/repo/repo_manifest.go) has ElementName and PackageType fields that are already populated via the manifest API, but the feed generator never reads them.

Fix Plan

File: services/updateserver/joomla.go

  1. In resolveExtensionMetadata(), add manifest as a fallback layer between repo defaults and config table:
// After line 187 (defaults), before line 189 (config table):

// Apply manifest fields as first fallback.
manifest, err := repo_model.GetRepoManifest(ctx, repo.ID)
if err == nil && manifest != nil {
    if manifest.ElementName != "" {
        m.Element = manifest.ElementName
    }
    if manifest.PackageType != "" {
        m.ExtType = manifest.PackageType
    }
    if manifest.DisplayName != "" {
        m.DisplayName = manifest.DisplayName
    }
    if manifest.TargetVersion != "" {
        m.TargetVersion = manifest.TargetVersion
    }
    if manifest.PHPMinimum != "" {
        m.PHPMinimum = manifest.PHPMinimum
    }
    if manifest.Description != "" {
        m.Description = manifest.Description
    }
}
  1. Fix client field logic (line 429-432) — "package" should map to "administrator" not "0":
client := "site"
if extType == "package" || extType == "component" || extType == "library" || extType == "file" {
    client = "administrator"
}

Priority Cascade (after fix)

  1. Custom fields (highest) — manual per-repo overrides
  2. Config table — org/repo-level update stream config
  3. Manifest API — first-class fields set via API or manifest_detect.php
  4. Repo name (lowest) — strings.ToLower(repo.Name)

Files to Change

File Change
services/updateserver/joomla.go Add manifest read in resolveExtensionMetadata(), fix client logic

Testing

  • Set element_name=pkg_mokosuitehq and package_type=package on MokoSuiteHQ manifest
  • Verify updates.xml output has <element>pkg_mokosuitehq</element>, <type>package</type>, <client>administrator</client>
  • Verify Joomla finds the update on a dev site
## Implementation Plan ### Root Cause `resolveExtensionMetadata()` in `services/updateserver/joomla.go` (line 179-275) does **not** read from the repo manifest API. It only uses: 1. Repo name as default element (line 183): `strings.ToLower(repo.Name)` → e.g. `mokosuitehq` 2. Hardcoded default type (line 185): `"component"` 3. Config table overrides (`update_stream_config`) 4. Custom field overrides (highest priority) The manifest model (`models/repo/repo_manifest.go`) has `ElementName` and `PackageType` fields that are already populated via the manifest API, but the feed generator never reads them. ### Fix Plan **File: `services/updateserver/joomla.go`** 1. In `resolveExtensionMetadata()`, add manifest as a fallback layer between repo defaults and config table: ```go // After line 187 (defaults), before line 189 (config table): // Apply manifest fields as first fallback. manifest, err := repo_model.GetRepoManifest(ctx, repo.ID) if err == nil && manifest != nil { if manifest.ElementName != "" { m.Element = manifest.ElementName } if manifest.PackageType != "" { m.ExtType = manifest.PackageType } if manifest.DisplayName != "" { m.DisplayName = manifest.DisplayName } if manifest.TargetVersion != "" { m.TargetVersion = manifest.TargetVersion } if manifest.PHPMinimum != "" { m.PHPMinimum = manifest.PHPMinimum } if manifest.Description != "" { m.Description = manifest.Description } } ``` 2. Fix client field logic (line 429-432) — `"package"` should map to `"administrator"` not `"0"`: ```go client := "site" if extType == "package" || extType == "component" || extType == "library" || extType == "file" { client = "administrator" } ``` ### Priority Cascade (after fix) 1. **Custom fields** (highest) — manual per-repo overrides 2. **Config table** — org/repo-level update stream config 3. **Manifest API** — first-class fields set via API or `manifest_detect.php` 4. **Repo name** (lowest) — `strings.ToLower(repo.Name)` ### Files to Change | File | Change | |------|--------| | `services/updateserver/joomla.go` | Add manifest read in `resolveExtensionMetadata()`, fix client logic | ### Testing - Set `element_name=pkg_mokosuitehq` and `package_type=package` on MokoSuiteHQ manifest - Verify `updates.xml` output has `<element>pkg_mokosuitehq</element>`, `<type>package</type>`, `<client>administrator</client>` - Verify Joomla finds the update on a dev site
Author
Owner

Updated Plan — Manifest as Single Source of Truth

Change of approach

Instead of adding manifest as a fallback layer, the manifest API should be the primary and only source for extension metadata in the update feed. The config table fields (ExtensionName, ExtensionType, DisplayName, etc.) and custom field overrides for these should be deprecated.

Mapping: Manifest → updates.xml

Manifest Field XML Output Current Default
element_name <element> strings.ToLower(repo.Name)
package_type <type> "component"
display_name <name> "{owner} - {repo}"
target_version <targetplatform version=""> "(5|6)\..*"
php_minimum <php_minimum> (empty)
description <description> (empty)

Client field derivation

Derived from package_type:

  • package, component, library, fileadministrator
  • module, plugin, template → check manifest or default site

Implementation

File: services/updateserver/joomla.goresolveExtensionMetadata()

Replace the config table + custom field cascade with a single manifest read:

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)\\..*",
    }

    // Manifest is the source of truth for extension metadata
    manifest, err := repo_model.GetRepoManifest(ctx, repo.ID)
    if err == nil && manifest != nil {
        if manifest.ElementName != "" { m.Element = manifest.ElementName }
        if manifest.PackageType != "" { m.ExtType = manifest.PackageType }
        if manifest.DisplayName != "" { m.DisplayName = manifest.DisplayName }
        if manifest.TargetVersion != "" { m.TargetVersion = manifest.TargetVersion }
        if manifest.PHPMinimum != "" { m.PHPMinimum = manifest.PHPMinimum }
        if manifest.Description != "" { m.Description = manifest.Description }
    }

    // Config table only for non-manifest fields (gating, key prefix, support URL)
    if cfg != nil {
        if cfg.DownloadGating != "" { m.DownloadGating = cfg.DownloadGating }
        if cfg.KeyPrefix != "" { m.KeyPrefix = cfg.KeyPrefix }
        if cfg.SupportURL != "" { m.SupportURL = cfg.SupportURL }
    }

    return m
}

Config table fields to deprecate

These fields in update_stream_config are superseded by manifest:

  • ExtensionNamemanifest.element_name
  • ExtensionTypemanifest.package_type
  • DisplayNamemanifest.display_name
  • TargetVersionmanifest.target_version
  • PHPMinimummanifest.php_minimum
  • Descriptionmanifest.description

Keep in config table (not in manifest):

  • DownloadGating — licensing/gating behavior
  • KeyPrefix — download key prefix
  • SupportURL — info/support link

Custom fields to deprecate

Remove the "Extension Name", "Extension Type", "Display Name", "Target Version", "PHP Minimum", "Description" custom field lookups from resolveExtensionMetadata().

Migration path

  1. Implement manifest read in feed generator
  2. Run manifest_detect.php --update across all Joomla repos to populate manifest fields
  3. Leave config table fields readable but not consulted (backward compat for a release cycle)
  4. Remove config table fields in next major version
## Updated Plan — Manifest as Single Source of Truth ### Change of approach Instead of adding manifest as a fallback layer, the manifest API should be the **primary and only source** for extension metadata in the update feed. The config table fields (`ExtensionName`, `ExtensionType`, `DisplayName`, etc.) and custom field overrides for these should be deprecated. ### Mapping: Manifest → updates.xml | Manifest Field | XML Output | Current Default | |---|---|---| | `element_name` | `<element>` | `strings.ToLower(repo.Name)` | | `package_type` | `<type>` | `"component"` | | `display_name` | `<name>` | `"{owner} - {repo}"` | | `target_version` | `<targetplatform version="">` | `"(5\|6)\..*"` | | `php_minimum` | `<php_minimum>` | (empty) | | `description` | `<description>` | (empty) | ### Client field derivation Derived from `package_type`: - `package`, `component`, `library`, `file` → `administrator` - `module`, `plugin`, `template` → check manifest or default `site` ### Implementation **File: `services/updateserver/joomla.go` — `resolveExtensionMetadata()`** Replace the config table + custom field cascade with a single manifest read: ```go 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)\\..*", } // Manifest is the source of truth for extension metadata manifest, err := repo_model.GetRepoManifest(ctx, repo.ID) if err == nil && manifest != nil { if manifest.ElementName != "" { m.Element = manifest.ElementName } if manifest.PackageType != "" { m.ExtType = manifest.PackageType } if manifest.DisplayName != "" { m.DisplayName = manifest.DisplayName } if manifest.TargetVersion != "" { m.TargetVersion = manifest.TargetVersion } if manifest.PHPMinimum != "" { m.PHPMinimum = manifest.PHPMinimum } if manifest.Description != "" { m.Description = manifest.Description } } // Config table only for non-manifest fields (gating, key prefix, support URL) if cfg != nil { if cfg.DownloadGating != "" { m.DownloadGating = cfg.DownloadGating } if cfg.KeyPrefix != "" { m.KeyPrefix = cfg.KeyPrefix } if cfg.SupportURL != "" { m.SupportURL = cfg.SupportURL } } return m } ``` ### Config table fields to deprecate These fields in `update_stream_config` are superseded by manifest: - `ExtensionName` → `manifest.element_name` - `ExtensionType` → `manifest.package_type` - `DisplayName` → `manifest.display_name` - `TargetVersion` → `manifest.target_version` - `PHPMinimum` → `manifest.php_minimum` - `Description` → `manifest.description` Keep in config table (not in manifest): - `DownloadGating` — licensing/gating behavior - `KeyPrefix` — download key prefix - `SupportURL` — info/support link ### Custom fields to deprecate Remove the "Extension Name", "Extension Type", "Display Name", "Target Version", "PHP Minimum", "Description" custom field lookups from `resolveExtensionMetadata()`. ### Migration path 1. Implement manifest read in feed generator 2. Run `manifest_detect.php --update` across all Joomla repos to populate manifest fields 3. Leave config table fields readable but not consulted (backward compat for a release cycle) 4. Remove config table fields in next major version
Author
Owner

Status Update

The Go code fix is on branch fix/manifest-update-feed (commit aeb36e4) but the feed is still broken because the fix hasn't been compiled and deployed to the production Gitea instance.

Current feed output (still wrong):

<element>mokosuitehq</element>
<type>component</type>
<client>site</client>

Workaround needed now

Until the Go fix is deployed, set the manifest fields via API so the current code path (config table) picks them up:

element_name = pkg_mokosuitehq
package_type = package
display_name = Package - MokoSuiteHQ

The current code reads from config table → custom fields. Once the Go fix deploys, it will read from manifest instead.

Deployment needed

The fix/manifest-update-feed branch needs to be:

  1. Merged to main
  2. Built (make build or CI)
  3. Deployed to git.mokoconsulting.tech (Docker container restart)

Blocked

MokoSuiteHQ updates on all client sites are blocked until either:

  • The Go fix is deployed, OR
  • The config table / custom fields are manually populated with correct values
## Status Update The Go code fix is on branch `fix/manifest-update-feed` (commit `aeb36e4`) but the feed is **still broken** because the fix hasn't been compiled and deployed to the production Gitea instance. Current feed output (still wrong): ```xml <element>mokosuitehq</element> <type>component</type> <client>site</client> ``` ### Workaround needed now Until the Go fix is deployed, set the manifest fields via API so the **current** code path (config table) picks them up: ``` element_name = pkg_mokosuitehq package_type = package display_name = Package - MokoSuiteHQ ``` The current code reads from config table → custom fields. Once the Go fix deploys, it will read from manifest instead. ### Deployment needed The `fix/manifest-update-feed` branch needs to be: 1. Merged to main 2. Built (`make build` or CI) 3. Deployed to git.mokoconsulting.tech (Docker container restart) ### Blocked MokoSuiteHQ updates on all client sites are blocked until either: - The Go fix is deployed, OR - The config table / custom fields are manually populated with correct values
Sign in to join this conversation.