diff --git a/cmd/account/account_producer_extension_info_pull.go b/cmd/account/account_producer_extension_info_pull.go index 1bb70c6a..3cb4d951 100644 --- a/cmd/account/account_producer_extension_info_pull.go +++ b/cmd/account/account_producer_extension_info_pull.go @@ -123,11 +123,17 @@ var accountCompanyProducerExtensionInfoPullCmd = &cobra.Command{ englishMetaTitle := "" germanMetaDescription := "" englishMetaDescription := "" + germanLabel := "" + englishLabel := "" + germanShortDescription := "" + englishShortDescription := "" for _, info := range storeExt.Infos { language := info.Locale.Name[0:2] if language == "de" { + germanLabel = info.Name + germanShortDescription = info.ShortDescription germanDescription = "file:src/Resources/store/description.de.html" germanInstallationManual = "file:src/Resources/store/installation_manual.de.html" germanMetaTitle = info.MetaTitle @@ -160,6 +166,8 @@ var accountCompanyProducerExtensionInfoPullCmd = &cobra.Command{ faqDE = append(faqDE, extension.ConfigStoreFaq{Question: element.Question, Answer: element.Answer, Position: element.Position}) } } else { + englishLabel = info.Name + englishShortDescription = info.ShortDescription englishDescription = "file:src/Resources/store/description.en.html" englishInstallationManual = "file:src/Resources/store/installation_manual.en.html" englishMetaTitle = info.MetaTitle @@ -195,6 +203,22 @@ var accountCompanyProducerExtensionInfoPullCmd = &cobra.Command{ } } + if germanLabel != "" || englishLabel != "" || germanShortDescription != "" || englishShortDescription != "" { + err = zipExt.UpdateMetaData(&extension.ExtensionMetadata{ + Label: extension.ExtensionTranslated{ + German: germanLabel, + English: englishLabel, + }, + Description: extension.ExtensionTranslated{ + German: germanShortDescription, + English: englishShortDescription, + }, + }) + if err != nil { + return fmt.Errorf("cannot update extension metadata: %w", err) + } + } + extType := "extension" if storeExt.ProductType != nil { diff --git a/internal/extension/app.go b/internal/extension/app.go index 12655e3c..b4acc078 100644 --- a/internal/extension/app.go +++ b/internal/extension/app.go @@ -136,22 +136,87 @@ func (a App) GetIconPath() string { return filepath.Join(a.GetRootDir(), iconPath) } -func (a App) GetMetaData() *extensionMetadata { +func (a App) GetMetaData() *ExtensionMetadata { german := []string{"de-DE", "de"} english := []string{"en-GB", "en-US", "en", ""} - return &extensionMetadata{ - Label: extensionTranslated{ + return &ExtensionMetadata{ + Label: ExtensionTranslated{ German: a.manifest.Meta.Label.GetValueByLanguage(german), English: a.manifest.Meta.Label.GetValueByLanguage(english), }, - Description: extensionTranslated{ + Description: ExtensionTranslated{ German: a.manifest.Meta.Description.GetValueByLanguage(german), English: a.manifest.Meta.Description.GetValueByLanguage(english), }, } } +func (a App) UpdateMetaData(metadata *ExtensionMetadata) error { + manifestFile := fmt.Sprintf("%s/manifest.xml", a.path) + + manifestBytes, err := os.ReadFile(manifestFile) + if err != nil { + return fmt.Errorf("could not read manifest.xml: %w", err) + } + + var manifest Manifest + if err := xml.Unmarshal(manifestBytes, &manifest); err != nil { + return fmt.Errorf("could not parse manifest.xml: %w", err) + } + + manifest.Meta.Label = updateTranslatableString(manifest.Meta.Label, metadata.Label) + manifest.Meta.Description = updateTranslatableString(manifest.Meta.Description, metadata.Description) + + newXml, err := xml.MarshalIndent(manifest, "", " ") + if err != nil { + return fmt.Errorf("could not marshal manifest.xml: %w", err) + } + + newXml = append([]byte(xml.Header), newXml...) + + if err := os.WriteFile(manifestFile, newXml, os.ModePerm); err != nil { + return fmt.Errorf("could not write manifest.xml: %w", err) + } + + return nil +} + +func updateTranslatableString(existing TranslatableString, translated ExtensionTranslated) TranslatableString { + translations := []struct { + lang string + value string + }{ + {"en-GB", translated.English}, + {"de-DE", translated.German}, + } + + matched := make(map[string]bool) + + for i, entry := range existing { + for _, t := range translations { + if t.value == "" { + continue + } + if entry.Lang == t.lang || (entry.Lang == "" && t.lang == "en-GB") { + existing[i].Value = t.value + matched[t.lang] = true + } + } + } + + for _, t := range translations { + if t.value != "" && !matched[t.lang] { + existing = append(existing, struct { + Value string `xml:",chardata"` + Lang string `xml:"lang,attr,omitempty"` + }{Value: t.value, Lang: t.lang}) + } + } + + return existing +} + func (a App) Validate(_ context.Context, check validation.Check) { validateTheme(a, check) diff --git a/internal/extension/bundle.go b/internal/extension/bundle.go index da1c248a..3ff3889b 100644 --- a/internal/extension/bundle.go +++ b/internal/extension/bundle.go @@ -146,19 +146,23 @@ func (p ShopwareBundle) GetIconPath() string { return "" } -func (p ShopwareBundle) GetMetaData() *extensionMetadata { - return &extensionMetadata{ - Label: extensionTranslated{ +func (p ShopwareBundle) GetMetaData() *ExtensionMetadata { + return &ExtensionMetadata{ + Label: ExtensionTranslated{ German: "FALLBACK", English: "FALLBACK", }, - Description: extensionTranslated{ + Description: ExtensionTranslated{ German: "FALLBACK", English: "FALLBACK", }, } } +func (p ShopwareBundle) UpdateMetaData(_ *ExtensionMetadata) error { + return nil +} + func (p ShopwareBundle) Validate(c context.Context, check validation.Check) { // ShopwareBundle validation is currently empty but signature updated to match interface } diff --git a/internal/extension/extension_test.go b/internal/extension/extension_test.go index 92a9d7cf..a7fa7ef6 100644 --- a/internal/extension/extension_test.go +++ b/internal/extension/extension_test.go @@ -84,7 +84,11 @@ func (m *mockExtension) GetChangelog() (*ExtensionChangelog, error) { return &ExtensionChangelog{}, nil } -func (m *mockExtension) GetMetaData() *extensionMetadata { +func (m *mockExtension) GetMetaData() *ExtensionMetadata { + return nil +} + +func (m *mockExtension) UpdateMetaData(_ *ExtensionMetadata) error { return nil } diff --git a/internal/extension/platform.go b/internal/extension/platform.go index 44976a1b..dc89e42f 100644 --- a/internal/extension/platform.go +++ b/internal/extension/platform.go @@ -160,20 +160,89 @@ func (p PlatformPlugin) GetPath() string { return p.path } -func (p PlatformPlugin) GetMetaData() *extensionMetadata { - return &extensionMetadata{ +func (p PlatformPlugin) GetMetaData() *ExtensionMetadata { + return &ExtensionMetadata{ Name: p.Composer.Name, - Label: extensionTranslated{ + Label: ExtensionTranslated{ German: p.Composer.Extra.Label["de-DE"], English: p.Composer.Extra.Label["en-GB"], }, - Description: extensionTranslated{ + Description: ExtensionTranslated{ German: p.Composer.Extra.Description["de-DE"], English: p.Composer.Extra.Description["en-GB"], }, } } +func (p PlatformPlugin) UpdateMetaData(metadata *ExtensionMetadata) error { + composerJsonFile := fmt.Sprintf("%s/composer.json", p.path) + + composerJson, err := os.ReadFile(composerJsonFile) + if err != nil { + return fmt.Errorf("could not read composer.json: %w", err) + } + + var composerJsonStruct map[string]interface{} + if err := json.Unmarshal(composerJson, &composerJsonStruct); err != nil { + return fmt.Errorf("could not unmarshal composer.json: %w", err) + } + + extra, ok := composerJsonStruct["extra"].(map[string]interface{}) + if !ok { + extra = make(map[string]interface{}) + composerJsonStruct["extra"] = extra + } + + changed := false + + if metadata.Label.German != "" || metadata.Label.English != "" { + label, ok := extra["label"].(map[string]interface{}) + if !ok { + label = make(map[string]interface{}) + } + if metadata.Label.German != "" { + label["de-DE"] = metadata.Label.German + } + if metadata.Label.English != "" { + label["en-GB"] = metadata.Label.English + } + extra["label"] = label + changed = true + } + + if metadata.Description.German != "" || metadata.Description.English != "" { + description, ok := extra["description"].(map[string]interface{}) + if !ok { + description = make(map[string]interface{}) + } + if metadata.Description.German != "" { + description["de-DE"] = metadata.Description.German + } + if metadata.Description.English != "" { + description["en-GB"] = metadata.Description.English + } + extra["description"] = description + changed = true + } + + if !changed { + return nil + } + + newComposerJson, err := json.MarshalIndent(composerJsonStruct, "", " ") + if err != nil { + return fmt.Errorf("could not marshal composer.json: %w", err) + } + + newComposerJson = append(newComposerJson, '\n') + + if err := os.WriteFile(composerJsonFile, newComposerJson, os.ModePerm); err != nil { + return fmt.Errorf("could not write composer.json: %w", err) + } + + return nil +} + func (p PlatformPlugin) GetIconPath() string { pluginIcon := p.Composer.Extra.PluginIcon diff --git a/internal/extension/root.go b/internal/extension/root.go index 0c18e550..0aa5c99d 100644 --- a/internal/extension/root.go +++ b/internal/extension/root.go @@ -83,7 +83,7 @@ func GetExtensionByZip(ctx context.Context, filePath string) (Extension, error) return GetExtensionByFolder(ctx, fmt.Sprintf("%s/%s", dir, extName)) } -type extensionTranslated struct { +type ExtensionTranslated struct { German string `json:"german"` English string `json:"english"` } @@ -94,10 +94,10 @@ type ExtensionChangelog struct { Changelogs map[string]string } -type extensionMetadata struct { +type ExtensionMetadata struct { Name string - Label extensionTranslated - Description extensionTranslated + Label ExtensionTranslated + Description ExtensionTranslated } type Extension interface { @@ -118,7 +118,8 @@ type Extension interface { GetType() string GetPath() string GetChangelog() (*ExtensionChangelog, error) - GetMetaData() *extensionMetadata + GetMetaData() *ExtensionMetadata + UpdateMetaData(*ExtensionMetadata) error GetExtensionConfig() *Config Validate(context.Context, validation.Check) } diff --git a/internal/extension/root_test.go b/internal/extension/root_test.go index d1665b6e..f8dcac43 100644 --- a/internal/extension/root_test.go +++ b/internal/extension/root_test.go @@ -169,6 +169,109 @@ func TestGetExtensionByFolder_PrefersManifestOverComposer(t *testing.T) { assert.Equal(t, TypePlatformApp, ext.GetType()) } +func TestUpdateMetaData_PlatformPlugin(t *testing.T) { + tmpDir := t.TempDir() + + composerContent := `{ + "name": "test/test-plugin", + "type": "shopware-platform-plugin", + "version": "1.0.0", + "license": "MIT", + "description": "Test plugin", + "authors": [{"name": "Test"}], + "require": { + "shopware/core": "~6.5.0" + }, + "autoload": { + "psr-4": { + "Test\\TestPlugin\\": "src/" + } + }, + "extra": { + "shopware-plugin-class": "Test\\TestPlugin\\TestPlugin", + "label": { + "de-DE": "Altes Label DE", + "en-GB": "Old Label EN" + }, + "description": { + "de-DE": "Alte Beschreibung", + "en-GB": "Old description" + } + } +}` + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(composerContent), 0644)) + + ext, err := GetExtensionByFolder(t.Context(), tmpDir) + require.NoError(t, err) + + err = ext.UpdateMetaData(&ExtensionMetadata{ + Label: ExtensionTranslated{ + German: "Neues Label DE", + English: "New Label EN", + }, + Description: ExtensionTranslated{ + German: "Neue Beschreibung", + English: "New description", + }, + }) + require.NoError(t, err) + + // Re-read the extension to verify the changes were persisted + ext2, err := GetExtensionByFolder(t.Context(), tmpDir) + require.NoError(t, err) + + meta := ext2.GetMetaData() + assert.Equal(t, "Neues Label DE", meta.Label.German) + assert.Equal(t, "New Label EN", meta.Label.English) + assert.Equal(t, "Neue Beschreibung", meta.Description.German) + assert.Equal(t, "New description", meta.Description.English) +} + +func TestUpdateMetaData_App(t *testing.T) { + tmpDir := t.TempDir() + + manifestContent := ` + + + TestApp + + + Old description + Alte Beschreibung + Test Author + (c) Test + 1.0.0 + MIT + +` + require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "manifest.xml"), []byte(manifestContent), 0644)) + + ext, err := GetExtensionByFolder(t.Context(), tmpDir) + require.NoError(t, err) + + err = ext.UpdateMetaData(&ExtensionMetadata{ + Label: ExtensionTranslated{ + German: "Neues Label DE", + English: "New Label EN", + }, + Description: ExtensionTranslated{ + German: "Neue Beschreibung", + English: "New description", + }, + }) + require.NoError(t, err) + + // Re-read the extension to verify the changes were persisted + ext2, err := GetExtensionByFolder(t.Context(), tmpDir) + require.NoError(t, err) + + meta := ext2.GetMetaData() + assert.Equal(t, "Neues Label DE", meta.Label.German) + assert.Equal(t, "New Label EN", meta.Label.English) + assert.Equal(t, "Neue Beschreibung", meta.Description.German) + assert.Equal(t, "New description", meta.Description.English) +} + func TestGetShopwareVersionConstraintFromComposer(t *testing.T) { t.Run("uses config constraint when set", func(t *testing.T) { config := &Config{