# Joomla Update Feed Audit and Package Relationship Model

## Why this patch exists

The old update-server project stored Joomla 3/4/5 data in separate tables and generated XML from version-specific structures. ExtensionFlow already normalized most of that model into `extensions`, `releases`, `joomla_versions`, `php_versions`, and release compatibility pivot tables. The missing part was not the basic update feed; it was the relationship layer.

Joomla has two update-server concepts:

- `extension` update server: a details XML file for one installed Joomla extension.
- `collection` update server: an `extensionset` XML file that points to multiple extension details XML files.

The collection feed is essential when a Joomla package contains several installed extensions and you want Joomla to update the package and/or the child extensions individually.

## XML field audit

The old project had these fields:

| Old field | ExtensionFlow before patch | Status after patch |
|---|---|---|
| `name` | `extensions.name` | kept |
| `description` | `extensions.description` | kept |
| `element` | `extensions.element` | kept |
| `type` | `extension_types.slug` | kept |
| `folder` | `extensions.folder` | kept; required for plugins |
| `client` | `extensions.client` | kept; important for site modules/templates |
| `version` | `releases.version` | kept |
| `downloadurl` | `releases.download_url` | kept |
| `downloadsource` | missing | added via `release_download_sources`; fallback URLs must use an external domain |
| `download type` | hardcoded `full` | added via `releases.download_type` |
| `download format` | hardcoded `zip` | added via `releases.download_format`; admin defaults to Auto detection from filename/URL |
| `infourl` | generated fallback only | added via `releases.info_url` with fallback |
| `changelogurl` | extension-level only | added release-level `releases.changelog_url` |
| `tags/tag` | non-standard `<stability>` was emitted | fixed to Joomla `<tags><tag>stable</tag></tags>` |
| `php_minimum` | computed from PHP compatibility | kept |
| `sha256` | `releases.sha256_hash` | kept |
| `sha384` | missing | added `releases.sha384_hash` |
| `sha512` | missing | added `releases.sha512_hash` |
| `maintainer` | missing | added `releases.maintainer`, fallback vendor |
| `maintainerurl` | missing | added `releases.maintainer_url` |
| `section` | missing | added `releases.section` |
| `targetplatform version` | generated from compatibility | kept |
| `targetplatform min_dev_level` | missing | added |
| `targetplatform max_dev_level` | missing | added |
| `supported_databases` | missing | added via `release_supported_databases` |

## New model

### Extension

A row in `extensions` is one Joomla-installed unit: component, module, plugin, package, template, library, language, file, or custom type. This must match what Joomla sees in its `#__extensions` table.

### Release

A row in `releases` is one version of one installed unit. Compatibility belongs here, not on the product or package, because a child language pack may support Joomla 3-6 while the package supports only Joomla 5-6.

### Collection

A row in `extension_collections` is a logical update collection / product family. It is not necessarily a Joomla extension. It exists to generate Joomla collection update feeds:

```xml
<extensionset name="Template Helpers" description="Template helper package, library and plugin">
  <extension name="Template Helpers Package" element="pkg_templatehelpers" type="package" version="1.0.3" detailsurl="https://example.com/updates/package/pkg-templatehelpers.xml" />
  <extension name="Template Helpers Library" element="templatehelpers" type="library" version="1.0.3" detailsurl="https://example.com/updates/library/lib-templatehelpers.xml" />
  <extension name="Template Helpers System Plugin" element="templatehelpers" type="plugin" version="1.0.3" detailsurl="https://example.com/updates/plugin/plg-system-templatehelpers.xml" />
</extensionset>
```

### Extension relationships

`extension_relationships` stores semantic relationships such as:

- package contains library
- package contains plugin
- plugin has language pack
- module has language pack
- extension supersedes another extension
- extension requires another extension

### Package release items

`package_release_items` maps a package release to exact child extension releases. This solves a major versioning problem: package `1.0.3` can contain library `1.0.3`, plugin `1.0.3`, and language pack `1.2.0`, each with independent Joomla/PHP compatibility.

## New feed URLs

Individual extension details feeds still work:

```text
/updates/package/pkg-templatehelpers.xml
/updates/library/lib-templatehelpers.xml
/updates/plugin/plg-system-templatehelpers.xml
```

Collection feeds are now available:

```text
/updates/collection/templatehelpers.xml
/updates/collection/eb666easyblogbridge.xml
```

Aliases `/updates/collections/{slug}.xml` also work.

## Seeded known relations

The migration automatically links the known package groups when the extensions exist:

| Collection | Package | Children |
|---|---|---|
| `templatehelpers` | `pkg-templatehelpers` | `lib-templatehelpers`, `plg-system-templatehelpers` |
| `eb666easyblogbridge` | `pkg-eb666easyblogbridge` | `lib-eb666-easyblogbridge`, `plg-system-eb666easyblogbridge` |

## Operational rule

Do not collapse package, library, plugin, module and language pack into one extension row. Joomla does not see them as one row. Keep each installed Joomla unit as its own ExtensionFlow extension, then connect them through collections and package release items.

That gives three correct workflows:

1. **Package update**: Joomla updates `pkg_*` from the package feed.
2. **Child update**: Joomla updates the child module/plugin/library from its own feed.
3. **Collection update discovery**: Joomla reads an `extensionset` feed and discovers all related details feeds.
