Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Require Shopware `6.5.8` or newer. `6.5.0.0` through `6.5.7.4` were listed as compatible but could never install: those versions ship Symfony 6.3, while both the extension's own routes and the UCP SDK need Symfony 6.4. Installation therefore ended in a Composer error about `symfony/config` that a merchant cannot act on. Such a shop now sees the extension as incompatible; updating to `6.5.8.x` fixes that and stays inside the same minor.
- Let Shopware install the UCP SDK instead of shipping it inside the archive. 1.3.0 put the SDK in the plugin's own `vendor/` and loaded the autoloader Composer had generated for it, because Shopware does not load a plugin's `vendor/autoload.php` by itself. Every shop that installed it then carried two separate package registries: FroshTools reported `2 autoloaders registered`, and a question as ordinary as which version of a package is installed could be answered from the plugin's copy instead of the shop's. The extension now carries no dependencies at all. It has Shopware run `composer require` on install and update instead, which is what that mechanism is there for on a zip-installed extension: the extension itself resolves from the `custom/plugins/*` path repository every Shopware project declares, and the SDK version it names comes from Packagist into the shop's own `vendor/`. Nothing changes for a shop that installs through Composer. What is new is that installing has to reach Packagist -- without it the install stops with a Composer error, rather than leaving an extension that cannot run.
- Switch the extension off instead of taking the shop down when the SDK is missing. An update extracts the new files one request before Shopware runs Composer, so an active extension boots at least once without the dependency it needs. On 1.3.0 every page answered `500` from that moment on, the storefront included, until someone installed the SDK by hand. The extension now registers no services, routes or feeds in that state, and writes to `var/log/swag-agentic-commerce.log` what is missing and the command that fixes it. Once the SDK is there it picks up again on its own. The check asks not only whether an SDK is present but whether it is the version this release names, so a shop still holding the previous one waits instead of running new code against an old SDK. The README has a _Troubleshooting_ section covering what that state looks like, how to leave it, and what to expect when updating from 1.3.0 or older. A cluster setup is the exception: Shopware never runs Composer for a plugin there, so nothing would ever install the requirements, and the extension refuses the install rather than reporting success and then doing nothing.
- Keep the settings that other extensions add to the _General_ tab of a sales channel. The extension replaced the tab's content as a whole, so an extension that passes its own data to the tab lost it, and the tab showed the default settings of a storefront sales channel instead.

# 1.3.0

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG_de-DE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Die Erweiterung setzt jetzt Shopware `6.5.8` oder neuer voraus. `6.5.0.0` bis `6.5.7.4` wurden als kompatibel angezeigt, ließen sich aber nie installieren: Diese Versionen enthalten Symfony 6.3, während sowohl die Routen der Erweiterung als auch das UCP-SDK Symfony 6.4 benötigen. Die Installation brach deshalb mit einer Composer-Fehlermeldung zu `symfony/config` ab, mit der ein Händler nichts anfangen kann. In betroffenen Shops wird die Erweiterung jetzt als nicht kompatibel angezeigt; ein Update auf `6.5.8.x` behebt das und bleibt innerhalb derselben Minor-Version.
- Das UCP SDK wird nicht mehr mitgeliefert, sondern von Shopware installiert. 1.3.0 legte es in das plugin-eigene `vendor/` und lud den Autoloader, den Composer dafür erzeugt hatte -- denn das `vendor/autoload.php` einer Erweiterung lädt Shopware nicht von sich aus. Damit führte jeder Shop zwei getrennte Paketverzeichnisse: FroshTools meldete `2 autoloaders registered`, und die Frage, welche Version eines Pakets installiert ist, konnte aus der Kopie der Erweiterung beantwortet werden statt aus der des Shops. Die Erweiterung bringt jetzt keine Abhängigkeiten mehr mit, sondern lässt Shopware bei Installation und Update `composer require` ausführen -- dafür ist dieser Weg bei Erweiterungen aus einer ZIP-Datei gedacht. Die Erweiterung selbst wird dabei über das Pfad-Repository `custom/plugins/*` aufgelöst, das jedes Shopware-Projekt mitbringt, das festgelegte SDK kommt von Packagist in das `vendor/` des Shops. Für Shops, die über Composer installieren, ändert sich nichts. Neu ist, dass die Installation Packagist erreichen muss: Ohne Internetzugang bricht sie mit einem Composer-Fehler ab, statt eine unvollständige Erweiterung zu hinterlassen.
- Fehlt das SDK, schaltet sich die Erweiterung ab, statt den Shop lahmzulegen. Ein Update entpackt die neuen Dateien, bevor Shopware Composer ausführt -- eine aktive Erweiterung startet also mindestens einmal ohne die Abhängigkeit, die sie braucht. In 1.3.0 antwortete daraufhin jede Seite mit `500`, die Storefront eingeschlossen, bis jemand das SDK von Hand nachinstallierte. Jetzt registriert die Erweiterung in diesem Zustand weder Services noch Routen oder Feeds und schreibt nach `var/log/swag-agentic-commerce.log`, was fehlt und mit welchem Befehl es sich beheben lässt. Sobald das SDK da ist, arbeitet sie ohne weiteres Zutun normal weiter. Geprüft wird dabei nicht nur, ob überhaupt ein SDK vorhanden ist, sondern ob es die Version ist, die diese Erweiterung vorgibt: Ein Shop, der noch die vorherige Version hat, wartet lieber ab, als neuen Code mit einem alten SDK auszuführen. Die README beschreibt unter _Troubleshooting_, woran man diesen Zustand erkennt, wie man ihn verlässt und was beim Update von 1.3.0 oder älter zu erwarten ist. Cluster-Setups sind die Ausnahme: Dort führt Shopware für ein Plugin grundsätzlich kein Composer aus, die Anforderungen würden also nie installiert. Deshalb verweigert die Erweiterung dort die Installation, statt Erfolg zu melden und dann nichts zu tun.
- Einstellungen, die andere Erweiterungen im Tab „Allgemein“ eines Verkaufskanals ergänzen, bleiben erhalten. Bisher ersetzte die Erweiterung den gesamten Inhalt des Tabs: Eine Erweiterung, die dem Tab eigene Daten übergab, verlor diese, und der Tab zeigte stattdessen die Standardeinstellungen eines Storefront-Verkaufskanals.

# 1.3.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ export const swSalesChannelDetailOverride = {
},

computed: {
useRouterViewSlot() {
const result = typeof this.$router?.hasRoute === 'function';
return result;
},

isAgenticCommerce() {
if (!this.salesChannel) {
return this.$route.params.typeId === Defaults.agenticCommerceTypeId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,57 +58,3 @@

{% parent %}
{% endblock %}

{% block sw_sales_channel_detail_content_view %}
<template v-if="isLoading">
<sw-skeleton />
<sw-skeleton />
</template>

<template v-else-if="useRouterViewSlot">
<router-view :key="$route.params.id" v-slot="{ Component }">
<component
:is="Component"
:sales-channel="salesChannel"
:product-export="productExport"
:agentic-commerce-export-config="agenticCommerceExportConfig"
:storefront-sales-channel-criteria="storefrontSalesChannelCriteria"
:custom-field-sets="customFieldSets"
:is-loading="isLoading"
:product-comparison-access-url="productComparison.productComparisonAccessUrl"
:template-options="productComparison.templateOptions"
:show-template-modal="productComparison.showTemplateModal"
:template-name="productComparison.templateName"
@template-selected="onTemplateSelected"
@access-key-changed="generateAccessUrl"
@domain-changed="generateAccessUrl"
@invalid-file-name="setInvalidFileName(true)"
@valid-file-name="setInvalidFileName(false)"
@template-modal-close="onTemplateModalClose"
@template-modal-confirm="onTemplateModalConfirm"
/>
</router-view>
</template>

<router-view
v-else
:key="$route.params.id"
:sales-channel="salesChannel"
:product-export="productExport"
:agentic-commerce-export-config="agenticCommerceExportConfig"
:storefront-sales-channel-criteria="storefrontSalesChannelCriteria"
:custom-field-sets="customFieldSets"
:is-loading="isLoading"
:product-comparison-access-url="productComparison.productComparisonAccessUrl"
:template-options="productComparison.templateOptions"
:show-template-modal="productComparison.showTemplateModal"
:template-name="productComparison.templateName"
@template-selected="onTemplateSelected"
@access-key-changed="generateAccessUrl"
@domain-changed="generateAccessUrl"
@invalid-file-name="setInvalidFileName(true)"
@valid-file-name="setInvalidFileName(false)"
@template-modal-close="onTemplateModalClose"
@template-modal-confirm="onTemplateModalConfirm"
/>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @sw-package discovery
*/

const fs = require('fs');
const path = require('path');

const template = fs.readFileSync(
path.resolve(__dirname, '../../../../../src/extension/sw-sales-channel/page/sw-sales-channel-detail/sw-sales-channel-detail.html.twig'),
'utf8',
);

describe('sw-sales-channel-detail template override', () => {
// Replacing this block drops the props other plugins pass to the routed view, e.g. Social Shopping's network form.
it('leaves the routed content view to core', () => {
expect(template).not.toContain('{% block sw_sales_channel_detail_content_view %}');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @sw-package discovery
*/

const Shopware = {
Component: { override: jest.fn() },
Utils: { object: { deepCopyObject: (obj) => JSON.parse(JSON.stringify(obj ?? {})) } },
Defaults: { agenticCommerceTypeId: 'agentic-type-id', productComparisonTypeId: 'product-comparison-type-id' },
};

global.Shopware = Shopware;

jest.mock(
'../../../../../src/extension/sw-sales-channel/view/sw-sales-channel-detail-base/sw-sales-channel-detail-base.html.twig',
() => 'mock-template',
{ virtual: true },
);

// eslint-disable-next-line import/first
const { swSalesChannelDetailBaseOverride } = require('../../../../../src/extension/sw-sales-channel/view/sw-sales-channel-detail-base');

const resolvedAgenticCommerceExportConfig = swSalesChannelDetailBaseOverride.computed.resolvedAgenticCommerceExportConfig;

const googleEntry = { provider: 'google', templateName: 'google' };
const openAiEntry = { provider: 'openai', templateName: 'openai' };

describe('sw-sales-channel-detail-base — resolvedAgenticCommerceExportConfig', () => {
it('reads the page config through inject when no prop is passed', () => {
const ctx = {
agenticCommerceExportConfig: [],
swSalesChannelDetailGetAgenticCommerceExportConfig: () => [googleEntry, openAiEntry],
productExport: { provider: 'openai' },
};

expect(resolvedAgenticCommerceExportConfig.call(ctx)).toEqual([openAiEntry]);
});

it('prefers the prop over the injected config', () => {
const ctx = {
agenticCommerceExportConfig: [googleEntry],
swSalesChannelDetailGetAgenticCommerceExportConfig: () => [openAiEntry],
productExport: { provider: 'google' },
};

expect(resolvedAgenticCommerceExportConfig.call(ctx)).toEqual([googleEntry]);
});

it('is empty when neither the prop nor the page provides a config', () => {
const ctx = {
agenticCommerceExportConfig: [],
swSalesChannelDetailGetAgenticCommerceExportConfig: () => [],
productExport: null,
};

expect(resolvedAgenticCommerceExportConfig.call(ctx)).toEqual([]);
});
});
Loading