diff --git a/models/licenses/update_stream_config.go b/models/licenses/update_stream_config.go index 6235fd6622..d203f2276e 100644 --- a/models/licenses/update_stream_config.go +++ b/models/licenses/update_stream_config.go @@ -24,9 +24,19 @@ type UpdateStreamConfig struct { OwnerID int64 `xorm:"INDEX NOT NULL"` // org or user RepoID int64 `xorm:"INDEX NOT NULL DEFAULT 0"` // 0 = org-level default StreamMode string `xorm:"NOT NULL DEFAULT 'joomla'"` // joomla, custom - Platform string `xorm:"NOT NULL DEFAULT 'joomla'"` // joomla, dolibarr, both + Platform string `xorm:"NOT NULL DEFAULT 'joomla'"` // joomla, dolibarr, both, wordpress, prestashop, drupal LicensingEnabled bool `xorm:"NOT NULL DEFAULT false"` // master toggle for licensing system RequireKey bool `xorm:"NOT NULL DEFAULT false"` // require license key for update feed + // Extension metadata — used in update feed generation. + ExtensionName string `xorm:"TEXT"` // element identifier (e.g. pkg_mokowaas, com_mokowaas) + DisplayName string `xorm:"TEXT"` // human-readable name (e.g. "Package - MokoWaaS") + Description string `xorm:"TEXT"` // short description for update feeds + ExtensionType string `xorm:"VARCHAR(50)"` // component, module, plugin, package, template, library + Maintainer string `xorm:"TEXT"` // maintainer/author name + MaintainerURL string `xorm:"TEXT"` // maintainer website + InfoURL string `xorm:"TEXT"` // extension info/product page URL + TargetVersion string `xorm:"TEXT"` // target platform version regex (e.g. "(5|6)\..*") + PHPMinimum string `xorm:"VARCHAR(20)"` // minimum PHP version (e.g. "8.1") // CustomStreams is a JSON array of stream definitions. // Each entry: {"name":"lts","suffix":"-lts","description":"Long-term support"} CustomStreams string `xorm:"TEXT"` @@ -121,6 +131,19 @@ func GetEffectiveStreams(ctx context.Context, ownerID, repoID int64) []StreamDef return DefaultJoomlaStreams() } +// GetEffectiveConfig returns the full config for a repo: repo override → org default. +func GetEffectiveConfig(ctx context.Context, ownerID, repoID int64) *UpdateStreamConfig { + repoCfg, err := GetRepoConfig(ctx, repoID) + if err == nil && repoCfg != nil { + return repoCfg + } + orgCfg, err := GetOrgConfig(ctx, ownerID) + if err == nil && orgCfg != nil { + return orgCfg + } + return nil +} + // SaveConfig creates or updates an update stream config. func SaveConfig(ctx context.Context, cfg *UpdateStreamConfig) error { existing := new(UpdateStreamConfig) diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index aa615059fe..e8adfd2668 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -2837,6 +2837,21 @@ "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.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.extension_metadata": "Extension Metadata", + "org.settings.extension_metadata_desc": "Configure how this extension appears in update feeds. These fields are used when generating updates.xml, JSON feeds, and package metadata.", + "org.settings.update_platform": "Update Feed Format", + "org.settings.extension_name": "Element Name", + "org.settings.extension_name_help": "The unique extension identifier as registered in the CMS (e.g. pkg_mokowaas, com_akeebabackup).", + "org.settings.display_name": "Display Name", + "org.settings.display_name_help": "Human-readable name shown in the CMS update manager.", + "org.settings.extension_type": "Extension Type", + "org.settings.target_version": "Target Platform Version", + "org.settings.target_version_help": "Regex pattern for compatible CMS versions (e.g. (5|6)\\..*). Leave empty for all versions.", + "org.settings.maintainer": "Maintainer", + "org.settings.maintainer_url": "Maintainer URL", + "org.settings.info_url": "Info/Product URL", + "org.settings.info_url_help": "Link to the extension's product or documentation page.", + "org.settings.php_minimum": "Minimum PHP Version", "org.settings.update_streams_heading": "Update Streams", "org.settings.update_streams_desc": "Configure the default update streams for all repositories. Release tags are matched to streams by their suffix. Repos can override with per-repo settings.", "org.settings.stream_mode": "Stream Mode", diff --git a/routers/web/org/update_streams.go b/routers/web/org/update_streams.go index 9f5b5ace06..6f019ab62f 100644 --- a/routers/web/org/update_streams.go +++ b/routers/web/org/update_streams.go @@ -40,9 +40,19 @@ func SettingsUpdateStreamsPost(ctx *context.Context) { OwnerID: orgID, RepoID: 0, StreamMode: ctx.FormString("stream_mode"), + Platform: ctx.FormString("platform"), CustomStreams: ctx.FormString("custom_streams"), LicensingEnabled: ctx.FormString("licensing_enabled") == "on", RequireKey: ctx.FormString("require_key") == "on", + ExtensionName: ctx.FormString("extension_name"), + DisplayName: ctx.FormString("display_name"), + Description: ctx.FormString("feed_description"), + ExtensionType: ctx.FormString("extension_type"), + Maintainer: ctx.FormString("maintainer"), + MaintainerURL: ctx.FormString("maintainer_url"), + InfoURL: ctx.FormString("info_url"), + TargetVersion: ctx.FormString("target_version"), + PHPMinimum: ctx.FormString("php_minimum"), } if cfg.StreamMode == "" { diff --git a/services/updateserver/joomla.go b/services/updateserver/joomla.go index 2d27bb93ae..2fe229b67c 100644 --- a/services/updateserver/joomla.go +++ b/services/updateserver/joomla.go @@ -125,8 +125,8 @@ func NormalizeChannel(ch string) string { } // GenerateJoomlaXML builds a Joomla-compatible updates.xml from repository releases. -// It returns the raw XML bytes. The element, maintainer, and target platform -// are derived from the repo name and owner. +// It returns the raw XML bytes. Extension metadata is read from the update stream config; +// falls back to repo name/owner when not configured. // allowedChannels optionally restricts output to specific channels (nil = all). func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, requireKey bool, allowedChannels ...string) ([]byte, error) { releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ @@ -149,7 +149,41 @@ 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). + cfg := licenses.GetEffectiveConfig(ctx, repo.OwnerID, repo.ID) + element := strings.ToLower(repo.Name) + if cfg != nil && cfg.ExtensionName != "" { + element = cfg.ExtensionName + } + 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.MaintainerURL != "" { + maintainerURL = cfg.MaintainerURL + } + targetVersion := ".*" + 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 + } // Resolve effective streams (repo override → org default → Joomla default). streams := licenses.GetEffectiveStreams(ctx, repo.OwnerID, repo.ID) @@ -215,30 +249,41 @@ func GenerateJoomlaXML(ctx context.Context, repo *repo_model.Repository, require version = version + suffix } + desc := feedDescription + if desc == "" { + desc = fmt.Sprintf("%s %s build.", displayName, ch) + } + + infoURL := fmt.Sprintf("%s/releases/tag/%s", repoLink, rel.TagName) + if cfg != nil && cfg.InfoURL != "" { + infoURL = cfg.InfoURL + } + u := xmlUpdate{ - Name: fmt.Sprintf("%s - %s", repo.Owner.Name, repo.Name), - Description: fmt.Sprintf("%s - %s %s build.", repo.Owner.Name, repo.Name, ch), + Name: displayName, + Description: desc, Element: element, - Type: "component", + Type: extType, Client: "site", Version: version, CreationDate: time.Unix(int64(rel.CreatedUnix), 0).Format("2006-01-02"), InfoURL: xmlInfoURL{ - Title: fmt.Sprintf("%s - %s", repo.Owner.Name, repo.Name), - URL: fmt.Sprintf("%s/releases/tag/%s", repoLink, rel.TagName), + Title: displayName, + URL: infoURL, }, Downloads: xmlDownloads{ DownloadURL: []xmlDownloadURL{ {Type: "full", Format: "zip", URL: downloadURL}, }, }, - Tags: xmlTags{Tag: ch}, + Tags: xmlTags{Tag: ch}, ChangelogURL: fmt.Sprintf("%s/raw/branch/%s/CHANGELOG.md", repoLink, repo.DefaultBranch), - Maintainer: repo.Owner.Name, - MaintainerURL: fmt.Sprintf("%s/%s", baseURL, repo.Owner.Name), + Maintainer: maintainer, + MaintainerURL: maintainerURL, + PHPMinimum: phpMinimum, TargetPlatform: xmlTargetPlat{ Name: "joomla", - Version: ".*", + Version: targetVersion, }, } diff --git a/templates/org/settings/update_streams.tmpl b/templates/org/settings/update_streams.tmpl index 16bcad18c3..2706855faf 100644 --- a/templates/org/settings/update_streams.tmpl +++ b/templates/org/settings/update_streams.tmpl @@ -29,7 +29,86 @@
- {{/* ── Section 2: Update Streams ── */}} + {{/* ── Section 2: Extension Metadata ── */}} +
{{svg "octicon-package" 14}} {{ctx.Locale.Tr "org.settings.extension_metadata"}}
+

{{ctx.Locale.Tr "org.settings.extension_metadata_desc"}}

+ +
+ + +
+ +
+
+ + +

{{ctx.Locale.Tr "org.settings.extension_name_help"}}

+
+
+ + +

{{ctx.Locale.Tr "org.settings.display_name_help"}}

+
+
+ +
+ + +
+ +
+
+ + +
+
+ + +

{{ctx.Locale.Tr "org.settings.target_version_help"}}

+
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +

{{ctx.Locale.Tr "org.settings.info_url_help"}}

+
+
+ + +
+
+ +
+ + {{/* ── Section 3: Update Streams ── */}}
{{svg "octicon-rss" 14}} {{ctx.Locale.Tr "org.settings.update_streams_heading"}}

{{ctx.Locale.Tr "org.settings.update_streams_desc"}}