├── src
├── Resources
│ ├── public
│ │ ├── .gitkeep
│ │ └── administration
│ │ │ └── css
│ │ │ └── saas-connect.css
│ ├── app
│ │ ├── administration
│ │ │ ├── test
│ │ │ │ └── unit
│ │ │ │ │ ├── __mocks__
│ │ │ │ │ ├── style.mock.js
│ │ │ │ │ ├── template.mock.js
│ │ │ │ │ ├── module.mock.js
│ │ │ │ │ ├── components.mock.js
│ │ │ │ │ └── http.factory.js
│ │ │ │ │ ├── .eslintrc.js
│ │ │ │ │ ├── index.test.js
│ │ │ │ │ ├── jest-setup
│ │ │ │ │ └── setup-shopware.js
│ │ │ │ │ └── __fixtures__
│ │ │ │ │ └── app-system
│ │ │ │ │ ├── app-url-change.fixtures.js
│ │ │ │ │ └── action-buttons.fixtures.js
│ │ │ ├── src
│ │ │ │ ├── main.js
│ │ │ │ ├── module
│ │ │ │ │ ├── sw-my-apps
│ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ └── sw-my-apps-timeout-animation
│ │ │ │ │ │ │ │ ├── sw-my-apps-timeout-animation.scss
│ │ │ │ │ │ │ │ ├── sw-my-apps-timeout-animation.html
│ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ ├── page
│ │ │ │ │ │ │ └── sw-my-apps-page
│ │ │ │ │ │ │ │ ├── sw-my-apps-page.scss
│ │ │ │ │ │ │ │ └── sw-my-apps-page.html.twig
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── snippets
│ │ │ │ │ └── index.js
│ │ │ │ ├── extension
│ │ │ │ │ ├── app
│ │ │ │ │ │ ├── component
│ │ │ │ │ │ │ └── structure
│ │ │ │ │ │ │ │ ├── sw-desktop
│ │ │ │ │ │ │ │ ├── sw-desktop.html.twig
│ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ │ │ ├── sw-page
│ │ │ │ │ │ │ │ ├── sw-page.scss
│ │ │ │ │ │ │ │ └── sw-page.html.twig
│ │ │ │ │ │ │ │ └── sw-admin-menu
│ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── module
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── sw-settings-custom-field
│ │ │ │ │ │ └── page
│ │ │ │ │ │ └── sw-settings-custom-field-set-list
│ │ │ │ │ │ └── index.js
│ │ │ │ ├── app
│ │ │ │ │ ├── component
│ │ │ │ │ │ └── sw-connect-action-button
│ │ │ │ │ │ │ ├── sw-connect-action-button.scss
│ │ │ │ │ │ │ ├── sw-connect-action-button.html.twig
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── state
│ │ │ │ │ │ └── connect-apps.state.js
│ │ │ │ └── core
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── service
│ │ │ │ │ ├── api
│ │ │ │ │ └── app-modules.service.js
│ │ │ │ │ └── index.js
│ │ │ ├── .eslintrc.js
│ │ │ ├── babel.config.js
│ │ │ └── package.json
│ │ ├── e2e
│ │ │ ├── fixtures
│ │ │ │ ├── themeApp
│ │ │ │ │ ├── Resources
│ │ │ │ │ │ ├── app
│ │ │ │ │ │ │ └── storefront
│ │ │ │ │ │ │ │ ├── src
│ │ │ │ │ │ │ │ ├── scss
│ │ │ │ │ │ │ │ │ ├── overrides.scss
│ │ │ │ │ │ │ │ │ └── base.scss
│ │ │ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ │ │ └── example-plugin
│ │ │ │ │ │ │ │ │ └── example-plugin.js
│ │ │ │ │ │ │ │ └── dist
│ │ │ │ │ │ │ │ └── storefront
│ │ │ │ │ │ │ │ └── js
│ │ │ │ │ │ │ │ └── swag-theme.js
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ └── storefront
│ │ │ │ │ │ │ │ └── layout
│ │ │ │ │ │ │ │ └── header
│ │ │ │ │ │ │ │ └── logo.html.twig
│ │ │ │ │ │ └── theme.json
│ │ │ │ │ ├── icon.png
│ │ │ │ │ └── manifest.xml
│ │ │ │ ├── productApp
│ │ │ │ │ └── icon.png
│ │ │ │ └── product.json
│ │ │ ├── babel.config.js
│ │ │ ├── .eslintrc.js
│ │ │ ├── cli-tools
│ │ │ │ ├── example.rest
│ │ │ │ ├── view
│ │ │ │ │ └── show-product-app.html
│ │ │ │ └── actions
│ │ │ │ │ ├── app-registration-service.js
│ │ │ │ │ ├── reset-db.js
│ │ │ │ │ └── clear-cache.js
│ │ │ ├── plugins
│ │ │ │ └── index.js
│ │ │ ├── support
│ │ │ │ ├── commands
│ │ │ │ │ ├── action-button-commands.js
│ │ │ │ │ ├── system-commands.js
│ │ │ │ │ ├── navigation.js
│ │ │ │ │ ├── theme-commands.js
│ │ │ │ │ ├── app-management-commands.js
│ │ │ │ │ └── create-product-command.js
│ │ │ │ └── index.js
│ │ │ ├── integration
│ │ │ │ └── administration
│ │ │ │ │ └── general
│ │ │ │ │ └── sales-channel-set.spec.js
│ │ │ ├── cypress.json
│ │ │ └── package.json
│ │ └── common
│ │ │ └── .eslintrc.js
│ ├── config
│ │ ├── routes.xml
│ │ └── services_test.xml
│ └── views
│ │ └── administration
│ │ └── index.html.twig
├── Core
│ ├── Command
│ │ ├── Exception
│ │ │ └── UserAbortedCommandException.php
│ │ ├── ShopwareSaasStyle.php
│ │ └── VerifyManifestCommand.php
│ ├── Content
│ │ └── App
│ │ │ ├── Exception
│ │ │ ├── AppRegistrationException.php
│ │ │ ├── InvalidArgumentException.php
│ │ │ ├── SaasConnectException.php
│ │ │ ├── CustomFieldTypeNotFoundException.php
│ │ │ └── ActionNotFoundException.php
│ │ │ ├── Lifecycle
│ │ │ ├── Event
│ │ │ │ ├── AppUpdatedEvent.php
│ │ │ │ ├── AppActivatedEvent.php
│ │ │ │ ├── AppDeactivatedEvent.php
│ │ │ │ ├── AppInstalledEvent.php
│ │ │ │ ├── ManifestChangedEvent.php
│ │ │ │ ├── AppChangedEvent.php
│ │ │ │ └── AppDeletedEvent.php
│ │ │ ├── Registration
│ │ │ │ ├── AppHandshakeInterface.php
│ │ │ │ ├── StoreHandshake.php
│ │ │ │ └── HandshakeFactory.php
│ │ │ ├── AppLoaderInterface.php
│ │ │ ├── Persister
│ │ │ │ ├── PermissionGatewayStrategy.php
│ │ │ │ └── PermissionGatewayFactory.php
│ │ │ ├── AppLifecycleInterface.php
│ │ │ └── AppLoader.php
│ │ │ ├── Manifest
│ │ │ └── Xml
│ │ │ │ ├── CustomFieldTypes
│ │ │ │ ├── MultiSelectField.php
│ │ │ │ ├── MediaSelectionField.php
│ │ │ │ ├── BoolField.php
│ │ │ │ ├── ColorPickerField.php
│ │ │ │ ├── CustomFieldTypeFactory.php
│ │ │ │ ├── DateTimeField.php
│ │ │ │ ├── TextAreaField.php
│ │ │ │ └── TextField.php
│ │ │ │ ├── Webhooks.php
│ │ │ │ ├── CustomFields.php
│ │ │ │ ├── Setup.php
│ │ │ │ ├── Permissions.php
│ │ │ │ └── Webhook.php
│ │ │ ├── AppCollection.php
│ │ │ ├── Aggregate
│ │ │ ├── ActionButton
│ │ │ │ └── ActionButtonCollection.php
│ │ │ ├── AppTranslation
│ │ │ │ ├── AppTranslationCollection.php
│ │ │ │ └── AppTranslationDefinition.php
│ │ │ └── ActionButtonTranslation
│ │ │ │ ├── ActionButtonTranslationCollection.php
│ │ │ │ └── ActionButtonTranslationDefinition.php
│ │ │ ├── Subscriber
│ │ │ └── AppLoadedSubscriber.php
│ │ │ ├── AppService.php
│ │ │ └── Action
│ │ │ └── Executor.php
│ ├── Framework
│ │ ├── AppUrlChangeResolver
│ │ │ ├── NoAppUrlChangeDetectedException.php
│ │ │ ├── AppUrlChangeResolverInterface.php
│ │ │ └── AppUrlChangeResolverNotFoundException.php
│ │ ├── ShopId
│ │ │ ├── AppUrlChangeDetectedException.php
│ │ │ └── ShopIdProvider.php
│ │ ├── Webhook
│ │ │ ├── Hookable.php
│ │ │ ├── WebhookCollection.php
│ │ │ ├── WebhookCacheClearer.php
│ │ │ ├── WebhookDefinition.php
│ │ │ └── WebhookEntity.php
│ │ ├── Template
│ │ │ ├── TemplateLoaderInterface.php
│ │ │ ├── TemplateCollection.php
│ │ │ ├── TemplateLoader.php
│ │ │ └── TemplateEntity.php
│ │ └── Api
│ │ │ ├── AppUrlChangeResolverNotFoundHttpException.php
│ │ │ └── Acl
│ │ │ └── AclPrivilegeCollection.php
│ └── System
│ │ └── CustomField
│ │ └── Aggregate
│ │ └── CustomFieldSet
│ │ └── CustomFieldSetExtension.php
├── Storefront
│ └── Theme
│ │ └── StorefrontPluginConfiguration
│ │ ├── StorefrontPluginConfigurationAppFactoryInterface.php
│ │ └── StorefrontPluginConfigurationAppFactory.php
├── Migration
│ ├── Migration1592313163DropAppAccessToken.php
│ ├── Migration1594883800AddPrivacyToApp.php
│ ├── Migration1594115469AddActiveColumnForApps.php
│ ├── Migration1580907311ExtendCustomFieldSet.php
│ ├── Migration1581948516Template.php
│ ├── Migration1581087930Webhook.php
│ └── Migration1597392859RenameAppIdColumn.php
└── SaasConnect.php
├── tests
├── Core
│ ├── Command
│ │ └── _fixtures
│ │ │ ├── empty
│ │ │ └── .gitkeep
│ │ │ ├── withoutPermissions
│ │ │ └── manifest.xml
│ │ │ ├── registrationFailure
│ │ │ └── manifest.xml
│ │ │ └── withPermissions
│ │ │ └── manifest.xml
│ ├── System
│ │ └── Snippet
│ │ │ └── File
│ │ │ └── _fixtures
│ │ │ ├── SnippetsWithWrongName
│ │ │ ├── Resources
│ │ │ │ └── snippet
│ │ │ │ │ ├── en-GB.json
│ │ │ │ │ └── test.json
│ │ │ └── manifest.xml
│ │ │ ├── AppWithSnippets
│ │ │ ├── Resources
│ │ │ │ └── snippet
│ │ │ │ │ ├── storefront.de-DE.json
│ │ │ │ │ └── storefront.en-GB.json
│ │ │ └── manifest.xml
│ │ │ └── AppWithBaseSnippets
│ │ │ ├── Resources
│ │ │ └── snippet
│ │ │ │ ├── storefront.de-DE.base.json
│ │ │ │ └── storefront.en-GB.base.json
│ │ │ └── manifest.xml
│ ├── Framework
│ │ ├── Plugin
│ │ │ └── _fixtures
│ │ │ │ ├── with-webpack
│ │ │ │ ├── Resources
│ │ │ │ │ └── app
│ │ │ │ │ │ └── storefront
│ │ │ │ │ │ └── build
│ │ │ │ │ │ └── webpack.config.js
│ │ │ │ ├── icon.png
│ │ │ │ └── manifest.xml
│ │ │ │ └── theme
│ │ │ │ ├── Resources
│ │ │ │ ├── app
│ │ │ │ │ └── storefront
│ │ │ │ │ │ └── src
│ │ │ │ │ │ ├── scss
│ │ │ │ │ │ ├── overrides.scss
│ │ │ │ │ │ └── base.scss
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ └── example-plugin
│ │ │ │ │ │ └── example-plugin.js
│ │ │ │ └── views
│ │ │ │ │ ├── administration
│ │ │ │ │ └── index.html.twig
│ │ │ │ │ └── storefront
│ │ │ │ │ └── layout
│ │ │ │ │ └── header
│ │ │ │ │ └── logo.html.twig
│ │ │ │ └── icon.png
│ │ └── Webhook
│ │ │ ├── _fixtures
│ │ │ └── BusinessEvents
│ │ │ │ ├── BusinessEventEncoderTestInterface.php
│ │ │ │ ├── InvalidEventType.php
│ │ │ │ ├── InvalidTypeBusinessEvent.php
│ │ │ │ ├── InvalidAvailableDataBusinessEvent.php
│ │ │ │ └── UnstructuredObjectBusinessEvent.php
│ │ │ └── WebhookCacheClearerTest.php
│ └── Content
│ │ └── App
│ │ ├── Manifest
│ │ ├── _fixtures
│ │ │ ├── test
│ │ │ │ ├── icon.png
│ │ │ │ └── Resources
│ │ │ │ │ └── views
│ │ │ │ │ ├── storefront
│ │ │ │ │ └── layout
│ │ │ │ │ │ └── header
│ │ │ │ │ │ └── logo.html.twig
│ │ │ │ │ └── administration
│ │ │ │ │ └── index.html.twig
│ │ │ ├── invalid
│ │ │ │ └── manifest.xml
│ │ │ ├── minimal
│ │ │ │ └── manifest.xml
│ │ │ └── public
│ │ │ │ └── manifest.xml
│ │ ├── Xml
│ │ │ ├── CustomFieldTypes
│ │ │ │ ├── CustomFieldTypeFactoryTest.php
│ │ │ │ └── _fixtures
│ │ │ │ │ ├── bool-field.xml
│ │ │ │ │ ├── date-time-field.xml
│ │ │ │ │ ├── color-picker-field.xml
│ │ │ │ │ ├── media-selection-field.xml
│ │ │ │ │ ├── text-field.xml
│ │ │ │ │ └── text-area-field.xml
│ │ │ ├── PermissionTest.php
│ │ │ ├── CustomFieldsTest.php
│ │ │ ├── MetadataTest.php
│ │ │ └── WebhooksTest.php
│ │ └── ManifestTest.php
│ │ └── Lifecycle
│ │ ├── Registration
│ │ ├── StoreHandshakeTest.php
│ │ └── _fixtures
│ │ │ ├── minimal
│ │ │ └── manifest.xml
│ │ │ └── no-setup
│ │ │ └── manifest.xml
│ │ ├── Event
│ │ ├── AppDeletedEventTest.php
│ │ ├── AppActivatedEventTest.php
│ │ ├── AppDeactivatedEventTest.php
│ │ ├── AppUpdatedEventTest.php
│ │ └── AppInstalledEventTest.php
│ │ └── Persister
│ │ └── PermissionGatewayFactoryTest.php
├── Storefront
│ └── _fixtures
│ │ ├── theme
│ │ ├── Resources
│ │ │ ├── app
│ │ │ │ └── storefront
│ │ │ │ │ └── src
│ │ │ │ │ └── scss
│ │ │ │ │ ├── overrides.scss
│ │ │ │ │ └── base.scss
│ │ │ ├── views
│ │ │ │ ├── administration
│ │ │ │ │ └── index.html.twig
│ │ │ │ └── storefront
│ │ │ │ │ └── layout
│ │ │ │ │ └── header
│ │ │ │ │ └── logo.html.twig
│ │ │ └── theme.json
│ │ ├── icon.png
│ │ └── manifest.xml
│ │ ├── noThemeNoCss
│ │ ├── icon.png
│ │ ├── Resources
│ │ │ └── views
│ │ │ │ ├── storefront
│ │ │ │ └── layout
│ │ │ │ │ └── header
│ │ │ │ │ └── logo.html.twig
│ │ │ │ └── administration
│ │ │ │ └── index.html.twig
│ │ └── manifest.xml
│ │ └── noThemeCustomCss
│ │ ├── icon.png
│ │ ├── Resources
│ │ ├── app
│ │ │ └── storefront
│ │ │ │ └── src
│ │ │ │ └── scss
│ │ │ │ └── base.scss
│ │ └── views
│ │ │ ├── administration
│ │ │ └── index.html.twig
│ │ │ └── storefront
│ │ │ └── layout
│ │ │ └── header
│ │ │ └── logo.html.twig
│ │ └── manifest.xml
├── TestBundle.php
├── TestKernel.php
├── GuzzleHistoryCollector.php
├── SystemConfigTestBehaviour.php
├── TemplateSystemIntegrationTest.php
├── TwigResetCacheTestBehaviour.php
├── EnvTestBehaviour.php
├── AppSystemTestBehaviour.php
├── TestBootstrap.php
└── GuzzleTestClientBehaviour.php
├── dev-ops
├── tools
│ ├── vendor-bin
│ │ ├── psalm
│ │ │ └── composer.json
│ │ ├── phpstan
│ │ │ └── composer.json
│ │ ├── ecs
│ │ │ └── composer.json
│ │ └── phpinsights
│ │ │ └── composer.json
│ ├── Dockerfile
│ └── composer.json
└── gitlab
│ └── cypress.env.json
├── SECURITY.md
├── .gitignore
├── phpstan.neon
├── composer.json
└── LICENSE
/src/Resources/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/Core/Command/_fixtures/empty/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__mocks__/style.mock.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/SnippetsWithWrongName/Resources/snippet/en-GB.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/SnippetsWithWrongName/Resources/snippet/test.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__mocks__/template.mock.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/AppWithSnippets/Resources/snippet/storefront.de-DE.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/AppWithSnippets/Resources/snippet/storefront.en-GB.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/Resources/app/storefront/src/scss/overrides.scss:
--------------------------------------------------------------------------------
1 | $body-bg: red;
2 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/with-webpack/Resources/app/storefront/build/webpack.config.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/AppWithBaseSnippets/Resources/snippet/storefront.de-DE.base.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/AppWithBaseSnippets/Resources/snippet/storefront.en-GB.base.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['jest'],
3 | };
4 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/theme/Resources/app/storefront/src/scss/overrides.scss:
--------------------------------------------------------------------------------
1 | $body-bg: red;
2 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/theme/Resources/app/storefront/src/scss/overrides.scss:
--------------------------------------------------------------------------------
1 | $sw-color-brand-primary: #ffff;
2 |
--------------------------------------------------------------------------------
/dev-ops/tools/vendor-bin/psalm/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "vimeo/psalm": "^3.8"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/main.js:
--------------------------------------------------------------------------------
1 | import SaasConnect from './core';
2 |
3 | SaasConnect.install(Shopware);
4 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__mocks__/module.mock.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | return [];
3 | };
4 |
--------------------------------------------------------------------------------
/dev-ops/tools/vendor-bin/phpstan/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "phpstan/phpstan": "^0.12.7"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__mocks__/components.mock.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | return [];
3 | };
4 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@babel/preset-env',
4 | ],
5 | };
6 |
--------------------------------------------------------------------------------
/dev-ops/tools/vendor-bin/ecs/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "symplify/easy-coding-standard": "^7.2"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/dev-ops/tools/vendor-bin/phpinsights/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "nunomaduro/phpinsights": "^1.11"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/Resources/app/storefront/src/scss/base.scss:
--------------------------------------------------------------------------------
1 | .product-box {
2 | background-color: #000000;
3 | }
4 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/theme/Resources/app/storefront/src/scss/base.scss:
--------------------------------------------------------------------------------
1 | .product-box {
2 | background-color: #000000;
3 | }
4 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/theme/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/tests/Storefront/_fixtures/theme/icon.png
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/src/Resources/app/e2e/fixtures/themeApp/icon.png
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeNoCss/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/tests/Storefront/_fixtures/noThemeNoCss/icon.png
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/productApp/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/src/Resources/app/e2e/fixtures/productApp/icon.png
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/theme/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/tests/Core/Framework/Plugin/_fixtures/theme/icon.png
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeCustomCss/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/tests/Storefront/_fixtures/noThemeCustomCss/icon.png
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/_fixtures/test/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/tests/Core/Content/App/Manifest/_fixtures/test/icon.png
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/module/sw-my-apps/components/sw-my-apps-timeout-animation/sw-my-apps-timeout-animation.scss:
--------------------------------------------------------------------------------
1 | .sw-my-apps-timeout-animation {
2 |
3 | }
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/with-webpack/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shopwareArchive/app-system/HEAD/tests/Core/Framework/Plugin/_fixtures/with-webpack/icon.png
--------------------------------------------------------------------------------
/dev-ops/gitlab/cypress.env.json:
--------------------------------------------------------------------------------
1 | {
2 | "schema": "http",
3 | "host": "docker.vm",
4 | "port": 8000,
5 | "locale": "en-GB",
6 | "cliProxy": {
7 | "port": 8005
8 | }
9 | }
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | ## Reporting a vulnerability
2 |
3 | Visit our [issue tracker](https://issues.shopware.com/createissue) to report security vulnerabilities.
4 | Please make sure to use the issue type "Security Issue".
5 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/module/sw-my-apps/components/sw-my-apps-timeout-animation/sw-my-apps-timeout-animation.html:
--------------------------------------------------------------------------------
1 |
2 | Loading your app resulted in a timeout
3 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/theme/Resources/app/storefront/src/scss/base.scss:
--------------------------------------------------------------------------------
1 | .light{
2 | font-weight:200;
3 | }
4 |
5 | .regular{
6 | font-weight:400;
7 | }
8 |
9 | .bold{
10 | font-weight:700;
11 | }
12 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/snippets/index.js:
--------------------------------------------------------------------------------
1 | export function installSnippets(Shopware) {
2 | Shopware.Locale.extend('de-DE', require('./de-DE'));
3 | Shopware.Locale.extend('en-GB', require('./en-GB'));
4 | }
5 |
--------------------------------------------------------------------------------
/src/Core/Command/Exception/UserAbortedCommandException.php:
--------------------------------------------------------------------------------
1 |
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeNoCss/Resources/views/storefront/layout/header/logo.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends '@Storefront/storefront/layout/header/logo.html.twig' %}
2 |
3 | {% block layout_header_logo_inner %}
4 | {{ parent() }}
5 | Built with <3 on Shopware as a Service
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Exception/SaasConnectException.php:
--------------------------------------------------------------------------------
1 |
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/theme/Resources/views/storefront/layout/header/logo.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends '@Storefront/storefront/layout/header/logo.html.twig' %}
2 |
3 | {% block layout_header_logo_inner %}
4 | {{ parent() }}
5 | Built with <3 on Shopware as a Service
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeCustomCss/Resources/views/administration/index.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends 'administration/index.html.twig' %}
2 |
3 | {% block administration_scripts %}
4 | {{ parent() }}
5 |
6 |
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeCustomCss/Resources/views/storefront/layout/header/logo.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends '@Storefront/storefront/layout/header/logo.html.twig' %}
2 |
3 | {% block layout_header_logo_inner %}
4 | {{ parent() }}
5 | Built with <3 on Shopware as a Service
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeNoCss/Resources/views/administration/index.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends 'administration/index.html.twig' %}
2 |
3 | {% block administration_scripts %}
4 | {{ parent() }}
5 |
6 |
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/theme/Resources/views/storefront/layout/header/logo.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends '@Storefront/storefront/layout/header/logo.html.twig' %}
2 |
3 | {% block layout_header_logo_inner %}
4 | {{ parent() }}
5 | Built with <3 as a theme on Shopware as a Service
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/module/sw-my-apps/components/sw-my-apps-timeout-animation/index.js:
--------------------------------------------------------------------------------
1 | import template from './sw-my-apps-timeout-animation.html';
2 | import './sw-my-apps-timeout-animation.scss';
3 |
4 | export default {
5 | name: 'sw-my-apps-timeout-animation',
6 | template,
7 | };
8 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/_fixtures/test/Resources/views/administration/index.html.twig:
--------------------------------------------------------------------------------
1 | {% sw_extends 'administration/index.html.twig' %}
2 |
3 | {% block administration_scripts %}
4 | {{ parent() }}
5 |
6 |
7 | {% endblock %}
8 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/app/component/structure/sw-desktop/sw-desktop.html.twig:
--------------------------------------------------------------------------------
1 | {% block sw_desktop_content %}
2 | {% parent %}
3 |
4 | {% endblock %}
5 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/index.test.js:
--------------------------------------------------------------------------------
1 | describe('index.js', () => {
2 | test('It registers services after install method', () => {
3 | expect(Shopware.Service('AppActionButtonService')).toBeDefined();
4 | expect(Shopware.Service('AppUrlChangeService')).toBeDefined();
5 | });
6 | });
7 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/_fixtures/invalid/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/index.js:
--------------------------------------------------------------------------------
1 | import { overrideComponents } from './app';
2 | import { installModuleExtensions } from './module';
3 |
4 | function extendAdministration(Shopware) {
5 | overrideComponents(Shopware);
6 | installModuleExtensions(Shopware);
7 | }
8 |
9 | export default extendAdministration;
10 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Webhook/_fixtures/BusinessEvents/BusinessEventEncoderTestInterface.php:
--------------------------------------------------------------------------------
1 | .sw-context-button >.sw-button {
7 | padding: 11px;
8 |
9 | .sw-icon {
10 | color: $color-menu-start;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/Event/AppUpdatedEvent.php:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/app/index.js:
--------------------------------------------------------------------------------
1 | export function overrideComponents(Shopware) {
2 | const { Component } = Shopware;
3 | const context = require.context('./component/', true, /\.js$/);
4 |
5 | return context.keys().forEach((item) => {
6 | const component = context(item).default;
7 | Component.override(component.name, component, 0);
8 | });
9 | }
10 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/theme/Resources/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SwagTheme",
3 | "author": "Shopware AG",
4 | "views": [
5 | "@Storefront",
6 | "@Plugins",
7 | "@SwagTheme"
8 | ],
9 | "style": [
10 | "app/storefront/src/scss/overrides.scss",
11 | "@Storefront",
12 | "app/storefront/src/scss/base.scss"
13 | ],
14 | "script": [
15 | "@Storefront"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/src/Core/Framework/AppUrlChangeResolver/NoAppUrlChangeDetectedException.php:
--------------------------------------------------------------------------------
1 | 'invalid',
13 | ];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/Resources/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SwagTheme",
3 | "author": "Shopware AG",
4 | "views": [
5 | "@Storefront",
6 | "@Plugins",
7 | "@SwagTheme"
8 | ],
9 | "style": [
10 | "app/storefront/src/scss/overrides.scss",
11 | "@Storefront",
12 | "app/storefront/src/scss/base.scss"
13 | ],
14 | "script": [
15 | "@Storefront",
16 | "app/storefront/dist/storefront/js/swag-theme.js"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/MultiSelectField.php:
--------------------------------------------------------------------------------
1 | confirm($question, $default)) {
12 | throw $exception;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/Resources/app/storefront/src/example-plugin/example-plugin.js:
--------------------------------------------------------------------------------
1 | import Plugin from 'src/plugin-system/plugin.class';
2 |
3 | export default class ExamplePlugin extends Plugin {
4 | init() {
5 | window.onscroll = function() {
6 | if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
7 | alert('seems like there\'s nothing more to see here.');
8 | }
9 | };
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/theme/Resources/app/storefront/src/example-plugin/example-plugin.js:
--------------------------------------------------------------------------------
1 | import Plugin from 'src/plugin-system/plugin.class';
2 |
3 | export default class ExamplePlugin extends Plugin {
4 | init() {
5 | window.onscroll = function() {
6 | if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
7 | alert('seems like there\'s nothing more to see here.');
8 | }
9 | };
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/module/sw-settings-custom-field/page/sw-settings-custom-field-set-list/index.js:
--------------------------------------------------------------------------------
1 | const { Criteria } = Shopware.Data;
2 |
3 | export default {
4 | 'name': 'sw-settings-custom-field-set-list',
5 |
6 | computed: {
7 | listingCriteria() {
8 | const criteria = this.$super('listingCriteria');
9 | criteria.addFilter(Criteria.equals('appId', null));
10 |
11 | return criteria;
12 | },
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__mocks__/http.factory.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import AxiosMockAdapter from 'axios-mock-adapter';
3 |
4 | const shopwareHttpFactory = require(path.join(administrationCorePath, 'core/factory/http.factory')).default;
5 |
6 | export default function createHTTPClient(context) {
7 | const client = shopwareHttpFactory(context);
8 | Shopware.Application.getContainer('service').mockAdapter = new AxiosMockAdapter(client);
9 |
10 | return client;
11 | }
12 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Exception/CustomFieldTypeNotFoundException.php:
--------------------------------------------------------------------------------
1 | {
4 | const url = `${config.env.schema}://${config.env.host}`;
5 |
6 | config.baseUrl = `${url}:${config.env.port}`;
7 | config.cliProxyUrl = `${url}:${config.env.cliProxy.port}`;
8 | };
9 |
10 | module.exports = (on, config) => {
11 | shopwareE2ePlugin(on, config);
12 | setUpUrls(on, config);
13 |
14 | return config;
15 | };
16 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/AppLoaderInterface.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | public function load(): array;
17 |
18 | public function getIcon(Manifest $app): ?string;
19 | }
20 |
--------------------------------------------------------------------------------
/src/Resources/public/administration/css/saas-connect.css:
--------------------------------------------------------------------------------
1 | .smart-bar__app_actions{margin-right:8px}.smart-bar__app_actions>.sw-context-button>.sw-button{padding:11px}.smart-bar__app_actions>.sw-context-button>.sw-button .sw-icon{color:#303a4f}.sw-connect-action-button .sw-connect-action-button__icon{width:16px;height:16px;border:none}.sw-connect-action-button .sw-icon{margin-left:4px;vertical-align:baseline}.sw-connect-action-button .sw-icon svg{vertical-align:baseline}.sw-my-apps-page .sw-my-apps-page__app-content{width:100%;height:100%;border:none}
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore ide settings
2 | .idea/
3 | .vscode
4 |
5 | # ignore test output
6 | .phpunit.result.cache
7 | **/app/artifacts
8 |
9 | # ignore dependency folders
10 | vendor
11 | /composer.lock
12 | node_modules
13 | package-lock.json
14 |
15 | #ignore bundle output
16 | /build
17 | src/Resources/app/administration/build/**
18 | !tests/Core/Framework/Plugin/_fixtures/with-webpack/Resources/app/storefront/build/webpack.config.js
19 |
20 | #ignore caches
21 | .php_cs.cache
22 |
23 | cypress.env.json
24 | !dev-ops/gitlab/cypress.env.json
25 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/product.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "product as a service",
3 | "stock": 1,
4 | "productNumber": "SWC-1234",
5 | "descriptionLong": "product description",
6 | "price": [
7 | {
8 | "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca",
9 | "net": 42,
10 | "linked": false,
11 | "gross": 64
12 | }
13 | ],
14 | "product_manufacturer": {
15 | "name": "Cloud Team"
16 | },
17 | "tax": {
18 | "name": "luxury",
19 | "taxRate": 45
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Resources/views/administration/index.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'administration/index.html.twig' %}
2 |
3 | {% block administration_stylesheets %}
4 | {{ parent() }}
5 |
6 |
7 | {% endblock %}
8 |
9 | {% block administration_scripts %}
10 | {{ parent() }}
11 |
12 |
13 | {% endblock %}
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/commands/action-button-commands.js:
--------------------------------------------------------------------------------
1 | function actionButtons() {
2 | cy.get('.sw-page div.smart-bar__content .sw-context-button.sw-page__connect-action-buttons').then((contextButton) => {
3 | if (!contextButton.hasClass('is--active')) {
4 | cy.wrap(contextButton).click();
5 | }
6 |
7 | return cy.get('.sw-context-button__menu-popover');
8 | });
9 | }
10 |
11 | export default function addActionButtonCommands(Cypress) {
12 | Cypress.Commands.add('actionButtons', { prevSubject: false }, actionButtons);
13 | };
14 |
--------------------------------------------------------------------------------
/tests/TestBundle.php:
--------------------------------------------------------------------------------
1 | load('services_test.xml');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/TestKernel.php:
--------------------------------------------------------------------------------
1 | $app
14 | */
15 | public function update(Manifest $manifest, array $app, Context $context): void;
16 |
17 | /**
18 | * @param array $app
19 | */
20 | public function delete(string $appName, array $app, Context $context): void;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Core/Framework/Template/TemplateLoaderInterface.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | public function getTemplatePathsForApp(Manifest $app): array;
19 |
20 | /**
21 | * Returns the content of the template
22 | */
23 | public function getTemplateContent(string $path, Manifest $app): string;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/CustomFieldTypeFactoryTest.php:
--------------------------------------------------------------------------------
1 | executeUpdate('
18 | ALTER TABLE `saas_app`
19 | DROP COLUMN `access_token`
20 | ');
21 | }
22 |
23 | public function updateDestructive(Connection $connection): void
24 | {
25 | // nth
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/integration/administration/general/sales-channel-set.spec.js:
--------------------------------------------------------------------------------
1 | describe('general', () => {
2 | it('uses ', () => {
3 | cy.setLocaleToEnGb();
4 | cy.server();
5 | cy.login('admin');
6 |
7 | cy.get('.sw-admin-menu__sales-channel-item--1 > a').contains('Storefront').click();
8 |
9 | cy.get('.sw-card')
10 | .contains('div.sw-card__title', 'Domains')
11 | .parent('.sw-card')
12 | .within(() => {
13 | cy.inspectDataGrid()
14 | .findRowsWith('URL', Cypress.config('baseUrl'))
15 | .should('exist');
16 | });
17 |
18 | cy.visit('/');
19 | cy.title().should('equal', 'Catalogue #1');
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/AppWithSnippets/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | AppWithSnippet
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/AppWithBaseSnippets/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | AppWithBaseSnippet
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/GuzzleHistoryCollector.php:
--------------------------------------------------------------------------------
1 | executeUpdate('
18 | ALTER TABLE `saas_app`
19 | ADD COLUMN `privacy` VARCHAR(255) NULL AFTER `license`;
20 | ');
21 | }
22 |
23 | public function updateDestructive(Connection $connection): void
24 | {
25 | // nth
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Core/System/Snippet/File/_fixtures/SnippetsWithWrongName/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SnippetsWithWrongName
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "integrationFolder": "./integration",
3 | "fixturesFolder": "./fixtures",
4 | "pluginsFile": "./plugins/index.js",
5 | "supportFile": "./support/index.js",
6 |
7 | "viewportHeight": 1080,
8 | "viewportWidth": 1920,
9 | "watchForFileChanges": false,
10 | "requestTimeout": 10000,
11 | "responseTimeout": 60000,
12 | "defaultCommandTimeout": 10000,
13 | "useDarkTheme": false,
14 | "video": false,
15 | "useShopwareTheme": true,
16 | "theme": "dark",
17 |
18 | "reporter": "junit",
19 | "screenshotsFolder": "../build/artifacts/e2e/screenshots",
20 | "reporterOptions": {
21 | "mochaFile": "../build/artifacts/e2e/saas-administration-e2e.xml",
22 | "toConsole": false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/cli-tools/view/show-product-app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 | Hi this is your product service
10 |
11 |
17 |
18 |
23 |
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "swag/saas-connect",
3 | "authors": [
4 | {
5 | "name": "shopware AG"
6 | }
7 | ],
8 | "description": "A webhook based plugin system",
9 | "type": "shopware-platform-plugin",
10 | "license": "MIT",
11 | "autoload": {
12 | "psr-4": {
13 | "Swag\\SaasConnect\\": "src/"
14 | }
15 | },
16 | "autoload-dev": {
17 | "psr-4": {
18 | "Swag\\SaasConnect\\Test\\": "tests/"
19 | }
20 | },
21 | "extra": {
22 | "shopware-plugin-class": "Swag\\SaasConnect\\SaasConnect",
23 | "label": {
24 | "de-DE": "Cloud-Plugins",
25 | "en-GB": "Cloud plugins"
26 | }
27 | },
28 | "require-dev": {
29 | "opis/json-schema": "^1.0.18"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Core/Content/App/AppCollection.php:
--------------------------------------------------------------------------------
1 | getIterator()
13 | * @method array getElements()
14 | * @method AppEntity|null get(string $key)
15 | * @method AppEntity|null first()
16 | * @method AppEntity|null last()
17 | */
18 | class AppCollection extends EntityCollection
19 | {
20 | protected function getExpectedClass(): string
21 | {
22 | return AppEntity::class;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/app/component/sw-connect-action-button/sw-connect-action-button.html.twig:
--------------------------------------------------------------------------------
1 | {% block sw_connect_action_button %}
2 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/ManifestTest.php:
--------------------------------------------------------------------------------
1 | getPath());
15 | }
16 |
17 | public function testSetPath(): void
18 | {
19 | $manifest = Manifest::createFromXmlFile(__DIR__ . '/_fixtures/test/manifest.xml');
20 |
21 | $manifest->setPath('test');
22 | static::assertEquals('test', $manifest->getPath());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Migration/Migration1594115469AddActiveColumnForApps.php:
--------------------------------------------------------------------------------
1 | executeUpdate('
18 | ALTER TABLE `saas_app`
19 | ADD COLUMN `active` tinyint(1) default 0 not null after `acl_role_id`
20 | ');
21 | }
22 |
23 | public function updateDestructive(Connection $connection): void
24 | {
25 | // implement update destructive
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/app/index.js:
--------------------------------------------------------------------------------
1 | export function installComponents(Shopware) {
2 | const { Component } = Shopware;
3 | const context = require.context('./component/', true, /\.js$/);
4 |
5 | context.keys().forEach((item) => {
6 | const component = context(item).default;
7 |
8 | Component.register(component.name, component);
9 | });
10 | }
11 |
12 | export function addStateModules(Shopware) {
13 | const { State } = Shopware;
14 | const states = ['connect-apps'];
15 |
16 | states.forEach((moduleName) => {
17 | const state = require(`./state/${moduleName}.state`).default;
18 | State.registerModule(moduleName, state);
19 | });
20 | }
21 |
22 | export function initializeAppModules() {
23 | return Shopware.State.dispatch('connect-apps/fetchAppModules');
24 | }
25 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/commands/system-commands.js:
--------------------------------------------------------------------------------
1 | function cleanUpPreviousState() {
2 | const proxyUrl = Cypress.config('cliProxyUrl');
3 | return cy.request({
4 | method: 'DELETE',
5 | url: `${proxyUrl}/cleanup`,
6 | Headers: {
7 | 'content-type': 'application/json',
8 | accept: 'application/json',
9 | },
10 | }).should((res) => {
11 | const body = JSON.parse(res.body);
12 | const dbName = body.message.match(/.* --port='\d+' '(.*)'.*/)[1];
13 | expect(res.status).to.equal(200);
14 | expect(body.code).to.equal(0);
15 | expect(dbName).to.equal('shopware_e2e');
16 | });
17 | }
18 |
19 | export default function addSystemCommands(Cypress) {
20 | Cypress.Commands.overwrite('cleanUpPreviousState', cleanUpPreviousState);
21 | };
22 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/module/index.js:
--------------------------------------------------------------------------------
1 | export function installModules(Shopware) {
2 | const { Module, Component } = Shopware;
3 | const modules = ['sw-my-apps'];
4 |
5 | modules.forEach((moduleName) => {
6 | const module = require(`./${moduleName}`).default;
7 |
8 | if (module.components) {
9 | Object.keys(module.components).forEach((componentName) => {
10 | const component = module.components[componentName];
11 |
12 | if (component.extendsFrom) {
13 | Component.extend(componentName, component.extendsFrom, component);
14 | return;
15 | }
16 |
17 | Component.register(componentName, component);
18 | });
19 | }
20 |
21 | Module.register(module.name, module);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/src/Core/Framework/Webhook/WebhookCollection.php:
--------------------------------------------------------------------------------
1 | getIterator()
13 | * @method array getElements()
14 | * @method WebhookEntity|null get(string $key)
15 | * @method WebhookEntity|null first()
16 | * @method WebhookEntity|null last()
17 | */
18 | class WebhookCollection extends EntityCollection
19 | {
20 | protected function getExpectedClass(): string
21 | {
22 | return WebhookEntity::class;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/app/component/structure/sw-desktop/index.js:
--------------------------------------------------------------------------------
1 | import template from './sw-desktop.html.twig';
2 |
3 | const { Service } = Shopware;
4 |
5 | /**
6 | * @private
7 | */
8 | export default {
9 | name: 'sw-desktop',
10 | template,
11 |
12 | data() {
13 | return {
14 | urlDiff: null,
15 | };
16 | },
17 |
18 | computed: {
19 | appUrlChangeService() {
20 | return Service('AppUrlChangeService');
21 | },
22 | },
23 |
24 | async created() {
25 | await this.updateShowUrlChangedModal();
26 | },
27 |
28 | methods: {
29 | async updateShowUrlChangedModal() {
30 | this.urlDiff = await this.appUrlChangeService.getUrlDiff();
31 | },
32 |
33 | closeModal() {
34 | this.urlDiff = null;
35 | },
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Registration/StoreHandshakeTest.php:
--------------------------------------------------------------------------------
1 | assembleRequest();
15 | }
16 |
17 | public function testGetHandshakeFetchAppProofIsUnimplemented(): void
18 | {
19 | $storeHandshake = new StoreHandshake();
20 | static::expectException(\RuntimeException::class);
21 | $storeHandshake->fetchAppProof();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagTheme
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 |
15 |
16 | __PROXY_URL__/SwagTheme/registration
17 | s3cr3t
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Core/Framework/Api/AppUrlChangeResolverNotFoundHttpException.php:
--------------------------------------------------------------------------------
1 | getMessage(), [], $previous);
14 | }
15 |
16 | public function getStatusCode(): int
17 | {
18 | return Response::HTTP_BAD_REQUEST;
19 | }
20 |
21 | public function getErrorCode(): string
22 | {
23 | return 'SAAS_CONNECT__APP_URL_CHANGE_RESOLVER_NOT_FOUND';
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Core/Framework/Template/TemplateCollection.php:
--------------------------------------------------------------------------------
1 | getIterator()
13 | * @method array getElements()
14 | * @method TemplateEntity|null get(string $key)
15 | * @method TemplateEntity|null first()
16 | * @method TemplateEntity|null last()
17 | */
18 | class TemplateCollection extends EntityCollection
19 | {
20 | protected function getExpectedClass(): string
21 | {
22 | return TemplateEntity::class;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Core/Framework/Webhook/WebhookCacheClearer.php:
--------------------------------------------------------------------------------
1 | dispatcher = $dispatcher;
17 | }
18 |
19 | /**
20 | * @return array
21 | */
22 | public static function getSubscribedEvents(): array
23 | {
24 | return [
25 | 'saas_webhook.written' => 'clearWebhookCache',
26 | ];
27 | }
28 |
29 | public function clearWebhookCache(): void
30 | {
31 | $this->dispatcher->clearInternalCache();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/index.js:
--------------------------------------------------------------------------------
1 | import addAppManagementCommands from './commands/app-management-commands';
2 | import addNavigationCommands from './commands/navigation';
3 | import addSystemCommands from './commands/system-commands';
4 | import addDataGridCommands from './commands/data-grid-commands';
5 | import addActionButtonCommands from './commands/action-button-commands';
6 | import addProductCommand from './commands/create-product-command';
7 | import addThemeCommands from './commands/theme-commands';
8 |
9 | Cypress.Cookies.defaults({
10 | whitelist: ['sw-admin-locale'],
11 | });
12 |
13 | require('@shopware-ag/e2e-testsuite-platform/cypress/support');
14 |
15 | addSystemCommands(Cypress);
16 | addAppManagementCommands(Cypress);
17 | addNavigationCommands(Cypress);
18 | addDataGridCommands(Cypress);
19 | addActionButtonCommands(Cypress);
20 | addProductCommand(Cypress);
21 | addThemeCommands(Cypress);
22 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Registration/_fixtures/minimal/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagTestApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 | https://my.app.com/registration
17 | s3cr3t
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/_fixtures/minimal/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagAppMinimal
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 | https://my.app.com/SwagAppMinimal/registration
17 | s3cr3t
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/_fixtures/public/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagAppPrivate
6 |
7 |
8 | Test for private App authentication
9 | Test für die private App Authentifizierung
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 | https://my.app.com/SwagAppPrivate/registration
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/theme/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagTheme
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagTheme/registration
18 | s3cr3t
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Plugin/_fixtures/with-webpack/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagTest
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagTest/registration
18 | s3cr3t
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeNoCss/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagNoThemeNoCss
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagNoThemeNoCss/registration
18 | s3cr3t
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Migration/Migration1580907311ExtendCustomFieldSet.php:
--------------------------------------------------------------------------------
1 | executeUpdate('
18 | ALTER TABLE `custom_field_set`
19 | ADD COLUMN `saas_app_id` BINARY(16) NULL AFTER `active`,
20 | ADD CONSTRAINT `fk.custom_field_set.saas_app_id` FOREIGN KEY (`saas_app_id`) REFERENCES `saas_app` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
21 | ');
22 | }
23 |
24 | public function updateDestructive(Connection $connection): void
25 | {
26 | // nth
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/Storefront/_fixtures/noThemeCustomCss/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagNoThemeCustomCss
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagNoThemeCustomCss/registration
18 | s3cr3t
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Aggregate/ActionButton/ActionButtonCollection.php:
--------------------------------------------------------------------------------
1 | getIterator()
13 | * @method array getElements()
14 | * @method ActionButtonEntity|null get(string $key)
15 | * @method ActionButtonEntity|null first()
16 | * @method ActionButtonEntity|null last()
17 | */
18 | class ActionButtonCollection extends EntityCollection
19 | {
20 | protected function getExpectedClass(): string
21 | {
22 | return ActionButtonEntity::class;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/core/service/api/app-modules.service.js:
--------------------------------------------------------------------------------
1 | const serviceName = 'AppModulesService';
2 |
3 | export default class AppModulesService{
4 | static get name() {
5 | return serviceName;
6 | }
7 |
8 | constructor(httpClient, loginService) {
9 | this.httpClient = httpClient;
10 | this.loginService = loginService;
11 | this.name = serviceName;
12 | }
13 |
14 | get basicHeaders() {
15 | return {
16 | 'Content-Type': 'application/json',
17 | Accept: 'application/json',
18 | Authorization: `Bearer ${this.loginService.getToken()}`,
19 | };
20 | }
21 |
22 | fetchAppModules() {
23 | return this.httpClient.get(
24 | 'app-system/modules',
25 | {
26 | headers: this.basicHeaders,
27 | },
28 | ).then(({ data }) => {
29 | return data.modules || [];
30 | });
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/app/component/structure/sw-page/sw-page.html.twig:
--------------------------------------------------------------------------------
1 | {% block sw_page_smart_bar_content_actions %}
2 | {% block sw_page_smart_bar_content_app_actions %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 | {% endblock %}
20 |
21 | {% parent() %}
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Webhook/_fixtures/BusinessEvents/InvalidTypeBusinessEvent.php:
--------------------------------------------------------------------------------
1 | add('invalid', new InvalidEventType());
15 | }
16 |
17 | public function getName(): string
18 | {
19 | return 'test';
20 | }
21 |
22 | public function getContext(): Context
23 | {
24 | return Context::createDefaultContext();
25 | }
26 |
27 | public function getInvalid(): string
28 | {
29 | return 'invalid';
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Webhook/_fixtures/BusinessEvents/InvalidAvailableDataBusinessEvent.php:
--------------------------------------------------------------------------------
1 | add('invalid', new ScalarValueType(ScalarValueType::TYPE_STRING));
16 | }
17 |
18 | public function getName(): string
19 | {
20 | return 'test';
21 | }
22 |
23 | public function getContext(): Context
24 | {
25 | return Context::createDefaultContext();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/SystemConfigTestBehaviour.php:
--------------------------------------------------------------------------------
1 | getContainer()->get(SystemConfigService::class);
19 |
20 | // reset internal system config cache
21 | $reflection = new \ReflectionClass($systemConfigService);
22 |
23 | $property = $reflection->getProperty('configs');
24 | $property->setAccessible(true);
25 | $property->setValue($systemConfigService, []);
26 | }
27 |
28 | abstract protected function getContainer(): ContainerInterface;
29 | }
30 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Aggregate/AppTranslation/AppTranslationCollection.php:
--------------------------------------------------------------------------------
1 | getIterator()
13 | * @method array getElements()
14 | * @method AppTranslationEntity|null get(string $key)
15 | * @method AppTranslationEntity|null first()
16 | * @method AppTranslationEntity|null last()
17 | */
18 | class AppTranslationCollection extends EntityCollection
19 | {
20 | protected function getExpectedClass(): string
21 | {
22 | return AppTranslationEntity::class;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/PermissionTest.php:
--------------------------------------------------------------------------------
1 | getPermissions());
15 | static::assertCount(6, $manifest->getPermissions()->getPermissions());
16 | static::assertEquals([
17 | 'product' => ['create', 'update', 'delete'],
18 | 'category' => ['delete'],
19 | 'product_manufacturer' => ['create', 'delete'],
20 | 'tax' => ['create'],
21 | 'language' => ['read'],
22 | 'custom_field_set' => ['update'],
23 | ], $manifest->getPermissions()->getPermissions());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Subscriber/AppLoadedSubscriber.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | public static function getSubscribedEvents(): array
15 | {
16 | return [
17 | 'saas_app.loaded' => 'unserialize',
18 | ];
19 | }
20 |
21 | public function unserialize(EntityLoadedEvent $event): void
22 | {
23 | /** @var AppEntity $app */
24 | foreach ($event->getEntities() as $app) {
25 | $iconRaw = $app->getIconRaw();
26 |
27 | if ($iconRaw !== null) {
28 | $app->setIcon(base64_encode($iconRaw));
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/cli-tools/actions/app-registration-service.js:
--------------------------------------------------------------------------------
1 | import {createHmac} from 'crypto';
2 |
3 | /**
4 | * @module actions/AppRegistrationService
5 | */
6 | /**
7 | * Creates an AppRegistrationService object
8 | * @param {string} secret
9 | * @param {string} confirmationUrl
10 | */
11 | export default function AppRegistrationService(secret, confirmationUrl) {
12 | this.secret = secret;
13 | this.cliProxyUrl = confirmationUrl;
14 | }
15 |
16 | AppRegistrationService.prototype = {
17 |
18 | /**
19 | * Generates a
20 | * @param {string} name
21 | * @param {string} shop
22 | * @return {{confirmation_url: string, proof: PromiseLike | *, secret: string}}
23 | */
24 | registerApp(name, shop) {
25 | const hmac = createHmac('sha256', this.secret);
26 | hmac.update(shop + name);
27 |
28 | return {
29 | proof: hmac.digest('hex'),
30 | secret: 'dont_tell',
31 | confirmation_url: this.cliProxyUrl
32 | };
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/tests/TemplateSystemIntegrationTest.php:
--------------------------------------------------------------------------------
1 | loadAppsFromDir(__DIR__ . '/Core/Content/App/Manifest/_fixtures/test');
23 |
24 | $homepage = $this->request('GET', '/', []);
25 |
26 | static::assertEquals(200, $homepage->getStatusCode());
27 | static::assertStringContainsString('Built with <3 on Shopware as a Service', $homepage->getContent());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/TwigResetCacheTestBehaviour.php:
--------------------------------------------------------------------------------
1 | getContainer()
17 | ->get(Environment::class);
18 |
19 | $reflection = new \ReflectionClass($twigEnv);
20 | $prop = $reflection->getProperty('loadedTemplates');
21 |
22 | $prop->setAccessible(true);
23 | $prop->setValue($twigEnv, null);
24 |
25 | $reflection = new \ReflectionClass($twigEnv);
26 | $prop = $reflection->getProperty('templateClassPrefix');
27 |
28 | $prop->setAccessible(true);
29 | $prop->setValue($twigEnv, '__TwigTemplate_' . Uuid::randomHex() . '_');
30 | }
31 |
32 | abstract protected function getContainer(): ContainerInterface;
33 | }
34 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/module/sw-my-apps/page/sw-my-apps-page/sw-my-apps-page.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ heading }}
4 |
5 |
6 |
7 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tests/EnvTestBehaviour.php:
--------------------------------------------------------------------------------
1 | $value) {
12 | if (!array_key_exists($envVar, $this->originalEnvVars)) {
13 | $this->originalEnvVars[$envVar] = $_SERVER[$envVar];
14 | }
15 | $_SERVER[$envVar] = $value;
16 | $_ENV[$envVar] = $value;
17 | putenv($envVar . '=' . $value);
18 | }
19 | }
20 |
21 | /**
22 | * @after
23 | */
24 | public function resetEnvVars(): void
25 | {
26 | if ($this->originalEnvVars) {
27 | foreach ($this->originalEnvVars as $envVar => $value) {
28 | $_SERVER[$envVar] = $value;
29 | $_ENV[$envVar] = $value;
30 | putenv($envVar . '=' . $value);
31 | }
32 |
33 | $this->originalEnvVars = [];
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 shopware AG
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6 | persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all
9 | copies or substantial portions of the Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
12 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
14 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
--------------------------------------------------------------------------------
/tests/AppSystemTestBehaviour.php:
--------------------------------------------------------------------------------
1 | getContainer()->get('saas_app.repository'),
21 | new AppLoader($appDir)
22 | ),
23 | $this->getContainer()->get(AppLifecycle::class)
24 | );
25 |
26 | $appService->refreshApps($activateApps, Context::createDefaultContext());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Aggregate/ActionButtonTranslation/ActionButtonTranslationCollection.php:
--------------------------------------------------------------------------------
1 | getIterator()
13 | * @method array getElements()
14 | * @method ActionButtonTranslationEntity|null get(string $key)
15 | * @method ActionButtonTranslationEntity|null first()
16 | * @method ActionButtonTranslationEntity|null last()
17 | */
18 | class ActionButtonTranslationCollection extends EntityCollection
19 | {
20 | protected function getExpectedClass(): string
21 | {
22 | return ActionButtonTranslationEntity::class;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Webhook/WebhookCacheClearerTest.php:
--------------------------------------------------------------------------------
1 | 'clearWebhookCache',
16 | ], WebhookCacheClearer::getSubscribedEvents());
17 | }
18 |
19 | public function testClearWebhookCache(): void
20 | {
21 | /** @var MockObject $dispatcherMock */
22 | $dispatcherMock = $this->createMock(WebhookDispatcher::class);
23 | $dispatcherMock->expects(static::once())
24 | ->method('clearInternalCache');
25 |
26 | $cacheClearer = new WebhookCacheClearer($dispatcherMock);
27 | $cacheClearer->clearWebhookCache();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/core/service/index.js:
--------------------------------------------------------------------------------
1 | import AppActionButtonService from './api/app-action-button.service';
2 | import AppModulesService from './api/app-modules.service';
3 | import AppAppUrlChangeService from './api/app-url-change.service';
4 |
5 | function installServices(Shopware) {
6 | Shopware.Application.addServiceProvider(AppActionButtonService.name, () => {
7 | const init = Shopware.Application.getContainer('init');
8 | return new AppActionButtonService(init.httpClient, Shopware.Service('loginService'));
9 | });
10 |
11 | Shopware.Application.addServiceProvider(AppModulesService.name, () => {
12 | const init = Shopware.Application.getContainer('init');
13 | return new AppModulesService(init.httpClient, Shopware.Service('loginService'));
14 | });
15 |
16 | Shopware.Application.addServiceProvider(AppAppUrlChangeService.name, () => {
17 | const init = Shopware.Application.getContainer('init');
18 | return new AppAppUrlChangeService(init.httpClient, Shopware.Service('loginService'));
19 | });
20 | }
21 |
22 | export default installServices;
23 |
--------------------------------------------------------------------------------
/src/Resources/app/common/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ['import'],
3 |
4 | rules: {
5 | 'use-isnan': ['error'],
6 | 'curly': ['error', 'all'],
7 | 'array-callback-return': ['error'],
8 | 'arrow-parens': ['error', 'always'],
9 | 'quotes': ['error', 'single'],
10 | 'no-unused-vars': ['error'],
11 | 'no-console': ['error', { allow: ["error"]}],
12 | 'comma-dangle': ['error', 'always-multiline'],
13 | 'no-multiple-empty-lines': ['error', { max: 1 }],
14 | 'padded-blocks': ['error', 'never'],
15 | 'semi': ['error', 'always'],
16 | 'no-useless-return': ['error'],
17 | 'keyword-spacing': ['error'],
18 |
19 | 'import/no-useless-path-segments': ['warn', { noUselessIndex: true }],
20 | 'max-len': [ 'warn', 125, { 'ignoreRegExpLiterals': true } ],
21 | 'consistent-return': ['warn'],
22 | 'eol-last': ['warn'],
23 | 'array-bracket-spacing': ['warn', 'never'],
24 | 'object-curly-spacing': ['warn', 'always'],
25 | 'comma-spacing': ['warn', {"before": false, "after": true}],
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/commands/navigation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Navigates to a module in the administration
3 | * @param {string} mainNavigation
4 | * @param {string} subNavigation
5 | */
6 | function navigateAdministration(mainNavigation, subNavigation) {
7 | cy.get('aside.sw-admin-menu').within(($adminMenu) => {
8 | // ensure admin menu is expanded
9 | if ($adminMenu.hasClass('is--collapsed')) {
10 | cy.get('button.sw-admin-menu__toggle').click();
11 | }
12 | });
13 |
14 | cy.get('li.sw-admin-menu__navigation-list-item').contains(mainNavigation).click();
15 |
16 | if (subNavigation) {
17 | cy.get('li.sw-admin-menu__navigation-list-item').contains(subNavigation).click();
18 | }
19 | }
20 |
21 | /**
22 | * Opens settings module in administration
23 | */
24 | function openSettings() {
25 | cy.navigateAdministration('Settings');
26 | }
27 |
28 | export default function addNavigationCommands(Cypress) {
29 | Cypress.Commands.add('navigateAdministration', { prevSubject: false }, navigateAdministration);
30 | Cypress.Commands.add('openSettings', { prevSubject: false }, openSettings);
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/MediaSelectionField.php:
--------------------------------------------------------------------------------
1 | > $data
11 | */
12 | private function __construct(array $data)
13 | {
14 | foreach ($data as $property => $value) {
15 | $this->$property = $value;
16 | }
17 | }
18 |
19 | public static function fromXml(\DOMElement $element): CustomFieldType
20 | {
21 | return new self(self::parse($element));
22 | }
23 |
24 | /**
25 | * @return array>
26 | */
27 | protected function toEntityArray(): array
28 | {
29 | return [
30 | 'type' => CustomFieldTypes::TEXT,
31 | 'config' => [
32 | 'componentName' => 'sw-media-field',
33 | 'customFieldType' => 'media',
34 | ],
35 | ];
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saasconnecte2etestsuite",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "description": "E2E testsuite for Shopware SaasConnect",
6 | "public": false,
7 | "license": "MIT",
8 | "author": "shopware AG",
9 | "contributors": [
10 | {
11 | "name": "Sebastian Franze",
12 | "email": "s.franze@shopware.com"
13 | }
14 | ],
15 | "scripts": {
16 | "open": "Cypress open",
17 | "run": "Cypress run",
18 | "lint": "eslint --ext .js",
19 | "lint-fix": "eslint --ext .js",
20 | "start-e2e-proxy": "node --experimental-modules --experimental-json-modules cli-tools/index.js"
21 | },
22 | "dependencies": {
23 | "@babel/core": "^7.8.7",
24 | "@babel/preset-env": "^7.8.7",
25 | "@shopware-ag/e2e-testsuite-platform": "^1.2.4",
26 | "body": "^5.1.0",
27 | "express": "^4.17.1",
28 | "fs-extra": "^8.1.0"
29 | },
30 | "devDependencies": {
31 | "cypress": "^4.5.0",
32 | "eslint": "^6.8.0",
33 | "eslint-plugin-cypress": "^2.10.3",
34 | "eslint-plugin-import": "^2.20.0"
35 | },
36 | "engines": {
37 | "node": ">= 12.14.1 <13.0.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/cli-tools/actions/reset-db.js:
--------------------------------------------------------------------------------
1 | import { spawn } from 'child_process';
2 |
3 | /**
4 | * Resets the tests database. Internally runs psh.phar e2e:restore-db on the server
5 | * @module actions/resetDb
6 | * @param {string} rootDir
7 | */
8 | export default function resetDb(rootDir) {
9 | return new Promise((resolve, reject) => {
10 | const resetDbProcess = spawn(
11 | `${rootDir}/psh.phar`,
12 | [
13 | 'e2e:restore-db',
14 | '--APP_ENV="prod"',
15 | ]
16 | );
17 | const outputBuffer = [];
18 | let output = null;
19 |
20 | resetDbProcess.stdout.on('data', (chunk) => {
21 | outputBuffer.push(chunk);
22 | });
23 | resetDbProcess.stdout.on('end', () => {
24 | output = Buffer.concat(outputBuffer).toString();
25 | });
26 |
27 | resetDbProcess.on('exit', (code) => {
28 | if (code === 0) {
29 | resolve({ code, message: output });
30 | return;
31 | }
32 |
33 | reject({ message: output, code });
34 | });
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/BoolField.php:
--------------------------------------------------------------------------------
1 | > $data
11 | */
12 | private function __construct(array $data)
13 | {
14 | foreach ($data as $property => $value) {
15 | $this->$property = $value;
16 | }
17 | }
18 |
19 | public static function fromXml(\DOMElement $element): CustomFieldType
20 | {
21 | return new self(self::parse($element));
22 | }
23 |
24 | /**
25 | * @return array>
26 | */
27 | protected function toEntityArray(): array
28 | {
29 | return [
30 | 'type' => CustomFieldTypes::BOOL,
31 | 'config' => [
32 | 'type' => 'checkbox',
33 | 'componentName' => 'sw-field',
34 | 'customFieldType' => 'checkbox',
35 | ],
36 | ];
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/jest-setup/setup-shopware.js:
--------------------------------------------------------------------------------
1 | const adminContext = {
2 | apiContext: {
3 | host: 'www.shopware-test.de',
4 | port: '80',
5 | scheme: 'http',
6 | schemeAndHttpHost: 'http://www.shopware-test.de:80',
7 | uri: 'http://www.shopware-test.de:80/admin',
8 | basePath: '',
9 | pathInfo: '/admin',
10 | liveVersionId: '0fa91ce3e96a4bc2be4bd9ce752c3425',
11 | systemLanguageId: '2fbb5fe2e29a4d70aa5854ce7ce3e20b',
12 | apiVersion: 1,
13 | },
14 | appContext: {
15 | features: [],
16 | firstRunWizard: 'true',
17 | systemCurrencyId: 'b7d2554b0ce847cd82f3ac9bd1c0dfca',
18 | },
19 | };
20 |
21 | module.exports = (() => {
22 | require('babel-plugin-require-context-hook/register')();
23 | const Shopware = require('src/core/shopware');
24 | const connect = require('connect/core').default;
25 |
26 | global.Shopware = Shopware;
27 |
28 | require('src/app/main');
29 | Shopware.Application
30 | .initState()
31 | .registerConfig(adminContext)
32 | .initializeFeatureFlags();
33 | connect.install(Shopware);
34 | })();
35 |
--------------------------------------------------------------------------------
/src/Core/System/CustomField/Aggregate/CustomFieldSet/CustomFieldSetExtension.php:
--------------------------------------------------------------------------------
1 | add(
17 | new FkField('saas_app_id', 'saasAppId', AppDefinition::class)
18 | );
19 |
20 | $collection->add(
21 | new ManyToOneAssociationField('saasApp', 'saas_app_id', AppDefinition::class)
22 | );
23 | }
24 |
25 | public function getDefinitionClass(): string
26 | {
27 | return CustomFieldSetDefinition::class;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/ColorPickerField.php:
--------------------------------------------------------------------------------
1 | > $data
11 | */
12 | private function __construct(array $data)
13 | {
14 | foreach ($data as $property => $value) {
15 | $this->$property = $value;
16 | }
17 | }
18 |
19 | public static function fromXml(\DOMElement $element): CustomFieldType
20 | {
21 | return new self(self::parse($element));
22 | }
23 |
24 | /**
25 | * @return array>
26 | */
27 | protected function toEntityArray(): array
28 | {
29 | return [
30 | 'type' => CustomFieldTypes::TEXT,
31 | 'config' => [
32 | 'type' => 'colorpicker',
33 | 'componentName' => 'sw-field',
34 | 'customFieldType' => 'colorpicker',
35 | ],
36 | ];
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/Webhooks.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | protected $webhooks = [];
11 |
12 | /**
13 | * @param array $webhooks
14 | */
15 | private function __construct(array $webhooks)
16 | {
17 | $this->webhooks = $webhooks;
18 | }
19 |
20 | public static function fromXml(\DOMElement $element): self
21 | {
22 | return new self(self::parseWebhooks($element));
23 | }
24 |
25 | /**
26 | * @return array
27 | */
28 | public function getWebhooks(): array
29 | {
30 | return $this->webhooks;
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | private static function parseWebhooks(\DOMElement $element): array
37 | {
38 | $webhooks = [];
39 | /** @var \DOMElement $webhook */
40 | foreach ($element->getElementsByTagName('webhook') as $webhook) {
41 | $webhooks[] = Webhook::fromXml($webhook);
42 | }
43 |
44 | return $webhooks;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/cli-tools/actions/clear-cache.js:
--------------------------------------------------------------------------------
1 |
2 | import { spawn } from 'child_process';
3 |
4 | /**
5 | * Clears the shopware servers cache. Internally runs psh.phar cache on the server
6 | * @module actions/resetDb
7 | * @param {string} rootDir
8 | */
9 | export default function clearCache(rootDir) {
10 | return new Promise((resolve, reject) => {
11 | const clearCacheProcess = spawn(
12 | `${rootDir}/psh.phar`,
13 | [
14 | 'cache',
15 | '--DB_NAME="shopware_e2e"',
16 | '--APP_ENV="prod"',
17 | ],
18 | );
19 | const outputBuffer = [];
20 | let output = null;
21 |
22 | clearCacheProcess.stdout.on('data', (chunk) => {
23 | outputBuffer.push(chunk);
24 | });
25 | clearCacheProcess.stdout.on('end', () => {
26 | output = Buffer.concat(outputBuffer).toString();
27 | });
28 |
29 | clearCacheProcess.on('exit', (code) => {
30 | if (code === 0) {
31 | resolve();
32 | return;
33 | }
34 |
35 | reject({ message: output, code });
36 | });
37 | });
38 | };
39 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/commands/theme-commands.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Navigates to a module in the administration
3 | * @param {string} themeName
4 | */
5 | function activateThemeForStorefront(themeName) {
6 | cy.setLocaleToEnGb();
7 | cy.server();
8 | cy.login('admin');
9 |
10 | cy.get('.sw-admin-menu__sales-channel-item--1 > a').contains('Storefront');
11 | cy.get('.sw-admin-menu__sales-channel-item--1').click();
12 | cy.get('.sw-tabs-item').contains('Theme').click();
13 | cy.get('.sw-button').contains('Change theme').click();
14 |
15 | cy.get('.sw-theme-modal .sw-theme-list-item__title').contains(themeName).click();
16 | cy.get('.sw-theme-modal .sw-button--primary').contains('Save').click();
17 |
18 | cy.route({
19 | method: 'post',
20 | url: 'api/v1/_action/theme/**',
21 | }).as('themeAction');
22 |
23 | cy.get('.sw-modal .sw-button--primary').contains('Change theme').click();
24 |
25 | cy.wait('@themeAction');
26 |
27 | cy.get('.sw-sales-channel-detail__save-action').click();
28 | }
29 |
30 | export default function addThemeCommands(Cypress) {
31 | Cypress.Commands.add('activateThemeForStorefront', { prevSubject: false }, activateThemeForStorefront);
32 | }
33 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/CustomFieldTypeFactory.php:
--------------------------------------------------------------------------------
1 | IntField::class,
11 | 'float' => FloatField::class,
12 | 'text' => TextField::class,
13 | 'text-area' => TextAreaField::class,
14 | 'bool' => BoolField::class,
15 | 'datetime' => DateTimeField::class,
16 | 'single-select' => SingleSelectField::class,
17 | 'multi-select' => MultiSelectField::class,
18 | 'color-picker' => ColorPickerField::class,
19 | 'media-selection' => MediaSelectionField::class,
20 | ];
21 |
22 | public static function createFromXml(\DOMElement $element): CustomFieldType
23 | {
24 | $fieldClass = self::TAG_TO_CLASS_MAPPING[$element->tagName] ?? null;
25 |
26 | if (!$fieldClass) {
27 | throw new CustomFieldTypeNotFoundException($element->tagName);
28 | }
29 |
30 | return $fieldClass::fromXml($element);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saasconnect",
3 | "version": "1.0.0",
4 | "description": "Saas Connect Administration plugin ",
5 | "author": "shopware AG",
6 | "license": "MIT",
7 | "private": true,
8 | "scripts": {
9 | "unit": "jest --config jest.config.js",
10 | "unit-watch": "jest --config jest.config.js --watch",
11 | "eslint": "eslint --ext .js,.vue src test",
12 | "eslint-junit": "eslint --ext .js,.vue --format junit src test"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.8.3",
16 | "@babel/plugin-proposal-class-properties": "^7.7.4",
17 | "@babel/plugin-syntax-dynamic-import": "^7.7.4",
18 | "@babel/plugin-transform-runtime": "^7.8.3",
19 | "@babel/preset-env": "^7.7.7",
20 | "axios-mock-adapter": "^1.17.0",
21 | "babel-core": "^6.26.3",
22 | "babel-jest": "^24.9.0",
23 | "babel-plugin-require-context-hook": "^1.0.0",
24 | "eslint": "^6.8.0",
25 | "eslint-plugin-import": "^2.20.0",
26 | "eslint-plugin-jest": "^23.3.0",
27 | "html-loader-jest": "^0.2.1",
28 | "jest": "^24.9.0",
29 | "jest-junit": "^10.0.0"
30 | },
31 | "dependencies": {
32 | "@babel/runtime": "^7.8.3",
33 | "axios": "^0.19.1"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/module/sw-my-apps/index.js:
--------------------------------------------------------------------------------
1 | import swMyAppsPage from './page/sw-my-apps-page';
2 |
3 | export default {
4 | type: 'plugin',
5 | name: 'sw-my-apps',
6 | title: 'sw-connect.module.sw-my-apps.general.mainMenuItemGeneral',
7 | description: 'sw-connect.module.sw-my-apps.general.moduleDescription',
8 | icon: 'default-view-grid',
9 | color: '#9AA8B5',
10 | routePrefixPath: 'my-apps',
11 |
12 | components: {
13 | 'sw-my-apps-page': swMyAppsPage,
14 | },
15 |
16 | routes: {
17 | index: {
18 | component: 'sw-my-apps-page',
19 | path: ':appName/:moduleName',
20 | props: {
21 | default(route) {
22 | const { appName, moduleName } = route.params;
23 | return {
24 | appName,
25 | moduleName,
26 | };
27 | },
28 | },
29 | },
30 | },
31 |
32 | navigation: [{
33 | id: 'sw-my-apps',
34 | label:'sw-connect.module.sw-my-apps.general.mainMenuItemGeneral',
35 | icon: 'default-view-grid',
36 | color: '#9AA8B5',
37 | position: 100,
38 | }],
39 | };
40 |
41 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/commands/app-management-commands.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Installs fixture apps on the server
4 | * @param {Array.} apps
5 | */
6 | function installE2EApps(apps) {
7 | const proxyUrl = Cypress.config('cliProxyUrl');
8 | return cy.request({
9 | method: 'POST',
10 | url: `${proxyUrl}/install-e2e-apps`,
11 | Headers: {
12 | 'content-type': 'application/json',
13 | accept: 'application/json',
14 | },
15 | body: { apps },
16 | }).its('statusCode').should('be', 204);
17 | }
18 |
19 | /**
20 | * Removes all fixture apps from the server
21 | */
22 | function removeAllE2EApps() {
23 | const proxyUrl = Cypress.config('cliProxyUrl');
24 |
25 | return cy.request({
26 | method: 'DELETE',
27 | url: `${proxyUrl}/remove-e2e-apps`,
28 | Headers: {
29 | 'content-type': 'application/json',
30 | accept: 'application/json',
31 | },
32 | }).its('statusCode').should('be', 204);
33 | }
34 |
35 | export default function addAppManagementCommands(Cypress) {
36 | Cypress.Commands.add('installE2EApps', { prevSubject: false }, installE2EApps);
37 | Cypress.Commands.add('removeE2EApps', { prevSubject: false }, removeAllE2EApps);
38 | };
39 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldsTest.php:
--------------------------------------------------------------------------------
1 | getCustomFields());
16 | static::assertCount(1, $manifest->getCustomFields()->getCustomFieldSets());
17 |
18 | /** @var CustomFieldSet $customFieldSet */
19 | $customFieldSet = $manifest->getCustomFields()->getCustomFieldSets()[0];
20 | static::assertEquals('custom_field_test', $customFieldSet->getName());
21 | static::assertEquals([
22 | 'en-GB' => 'Custom field test',
23 | 'de-DE' => 'Zusatzfeld Test',
24 | ], $customFieldSet->getLabel());
25 | static::assertEquals(['product', 'customer'], $customFieldSet->getRelatedEntities());
26 |
27 | static::assertCount(0, $customFieldSet->getFields());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/support/commands/create-product-command.js:
--------------------------------------------------------------------------------
1 | function createProduct(productData) {
2 | return cy.request({
3 | method: 'POST',
4 | url: 'api/oauth/token',
5 | headers: {
6 | 'content-type': 'application/json',
7 | accept: 'application/json',
8 | },
9 | body: {
10 | 'grant_type': 'password',
11 | 'client_id': 'administration',
12 | 'scopes': 'write',
13 | 'username': 'admin',
14 | 'password': 'shopware',
15 | },
16 | }).then(({ body }) => {
17 | return cy.request({
18 | method: 'POST',
19 | url: '/api/v1/product',
20 | headers: {
21 | 'content-type': 'application/json',
22 | accept: 'application/json',
23 | Authorization: `Bearer ${body['access_token']}`,
24 |
25 | },
26 | body: productData,
27 | });
28 | }).should((response) => {
29 | if (response.status !== 204) {
30 | cy.log(response.body);
31 | }
32 | expect(response.status).to.equals(204);
33 | });
34 | }
35 |
36 | export default function addProductCommand(Cypress) {
37 | Cypress.Commands.add('createProduct', { prevSubject: false }, createProduct);
38 | };
39 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/DateTimeField.php:
--------------------------------------------------------------------------------
1 | > $data
11 | */
12 | private function __construct(array $data)
13 | {
14 | foreach ($data as $property => $value) {
15 | $this->$property = $value;
16 | }
17 | }
18 |
19 | public static function fromXml(\DOMElement $element): CustomFieldType
20 | {
21 | return new self(self::parse($element));
22 | }
23 |
24 | /**
25 | * @return array>>
26 | */
27 | protected function toEntityArray(): array
28 | {
29 | return [
30 | 'type' => CustomFieldTypes::DATETIME,
31 | 'config' => [
32 | 'type' => 'date',
33 | 'componentName' => 'sw-field',
34 | 'customFieldType' => 'date',
35 | 'config' => [
36 | 'time_24hr' => true,
37 | ],
38 | 'dateType' => 'datetime',
39 | ],
40 | ];
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/Event/ManifestChangedEvent.php:
--------------------------------------------------------------------------------
1 | app = $app;
19 | parent::__construct($appId, $context);
20 | }
21 |
22 | abstract public function getName(): string;
23 |
24 | public function getApp(): Manifest
25 | {
26 | return $this->app;
27 | }
28 |
29 | /**
30 | * @return array
31 | */
32 | public function getWebhookPayload(): array
33 | {
34 | return [
35 | 'appVersion' => $this->getApp()->getMetadata()->getVersion(),
36 | ];
37 | }
38 |
39 | /**
40 | * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter
41 | */
42 | public function isAllowed(string $appId, AclPrivilegeCollection $permissions): bool
43 | {
44 | return $appId === $this->getAppId();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFields.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | protected $customFieldSets = [];
11 |
12 | /**
13 | * @param array $customFieldSets
14 | */
15 | private function __construct(array $customFieldSets)
16 | {
17 | $this->customFieldSets = $customFieldSets;
18 | }
19 |
20 | public static function fromXml(\DOMElement $element): self
21 | {
22 | return new self(self::parseCustomFieldSets($element));
23 | }
24 |
25 | /**
26 | * @return array
27 | */
28 | public function getCustomFieldSets(): array
29 | {
30 | return $this->customFieldSets;
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | private static function parseCustomFieldSets(\DOMElement $element): array
37 | {
38 | $customFieldSets = [];
39 | /** @var \DOMElement $customFieldSet */
40 | foreach ($element->getElementsByTagName('custom-field-set') as $customFieldSet) {
41 | $customFieldSets[] = CustomFieldSet::fromXml($customFieldSet);
42 | }
43 |
44 | return $customFieldSets;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/app/component/sw-connect-action-button/index.js:
--------------------------------------------------------------------------------
1 | import template from './sw-connect-action-button.html.twig';
2 | import './sw-connect-action-button.scss';
3 |
4 | export default {
5 | template,
6 | name: 'sw-connect-action-button',
7 |
8 | props: {
9 | action: {
10 | type: Object,
11 | required: true,
12 | },
13 | },
14 |
15 | computed: {
16 | buttonLabel() {
17 | const currentLocale = Shopware.State.get('session').currentLocale;
18 | const fallbackLocale = Shopware.Context.app.fallbackLocale;
19 |
20 | return this.action.label[currentLocale] || this.action.label[fallbackLocale] || '';
21 | },
22 |
23 | openInNewTab() {
24 | return !!this.action.openNewTab;
25 | },
26 |
27 | linkData() {
28 | if (this.openInNewTab) {
29 | return {
30 | target: '_blank',
31 | href: this.action.url,
32 | };
33 | }
34 |
35 | return {};
36 | },
37 | },
38 |
39 | methods: {
40 | runAction() {
41 | if (this.openInNewTab) {
42 | return;
43 | }
44 |
45 | this.$emit('run-app-action', this.action.id);
46 | },
47 | },
48 | };
49 |
50 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__fixtures__/app-system/app-url-change.fixtures.js:
--------------------------------------------------------------------------------
1 | const defaultStrategies = {
2 | status: 200,
3 | statusText: 'OK',
4 | data: {
5 | MoveShopPermanently:
6 | 'Use this URL for communicating with installed apps, this will disable communication to apps on the old' +
7 | ' URLs installation, but the app-data from the old installation will be available in this installation.',
8 | ReinstallApps:
9 | 'Reinstall all apps anew for the new URL, so app communication on the old URLs installation keeps ' +
10 | 'working like before. App-data from the old installation will not be available in this installation.',
11 | UninstallApps:
12 | 'Uninstall all apps on this URL, so app communication on the old URLs installation keeps ' +
13 | 'working like before.',
14 | },
15 | };
16 |
17 | const urlDifference = {
18 | status: 200,
19 | statusText: 'OK',
20 | data: {
21 | oldUrl: 'https://old.com',
22 | newUrl: 'https://new.com',
23 | },
24 | };
25 |
26 | const emptyUrlDifference = {
27 | status: 204,
28 | statusText: 'No Content',
29 | data: '',
30 | };
31 |
32 | export default {
33 | defaultStrategies,
34 | urlDifference,
35 | emptyUrlDifference,
36 | };
37 |
--------------------------------------------------------------------------------
/src/Migration/Migration1581948516Template.php:
--------------------------------------------------------------------------------
1 | executeUpdate('
18 | CREATE TABLE IF NOT EXISTS `saas_template` (
19 | `id` BINARY(16) NOT NULL,
20 | `template` LONGTEXT NOT NULL,
21 | `path` VARCHAR(1024) NOT NULL,
22 | `active` TINYINT(1) NOT NULL,
23 | `app_id` BINARY(16) NOT NULL,
24 | `created_at` DATETIME(3) NOT NULL,
25 | `updated_at` DATETIME(3) NULL,
26 | PRIMARY KEY (`id`),
27 | INDEX `idx.saas_template.path` (`path`(256)),
28 | CONSTRAINT `fk.saas_template.app_id` FOREIGN KEY (`app_id`) REFERENCES `saas_app` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
29 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
30 | ');
31 | }
32 |
33 | public function updateDestructive(Connection $connection): void
34 | {
35 | // nth
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/SaasConnect.php:
--------------------------------------------------------------------------------
1 | keepUserData() === true) {
14 | if (method_exists($uninstallContext, 'enableKeepMigrations')) {
15 | $uninstallContext->enableKeepMigrations();
16 | }
17 |
18 | return;
19 | }
20 |
21 | $connection = $this->container->get(Connection::class);
22 | $connection->executeUpdate('
23 | ALTER TABLE `custom_field_set`
24 | DROP FOREIGN KEY `fk.custom_field_set.saas_app_id`,
25 | DROP COLUMN `saas_app_id`;
26 | ');
27 | $connection->executeUpdate('
28 | DROP TABLE IF EXISTS
29 | `saas_webhook`,
30 | `saas_template`,
31 | `saas_app_action_button_translation`,
32 | `saas_app_action_button`,
33 | `saas_app_translation`,
34 | `saas_app`;
35 | ');
36 | }
37 |
38 | public function rebuildContainer(): bool
39 | {
40 | return false;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Migration/Migration1581087930Webhook.php:
--------------------------------------------------------------------------------
1 | executeUpdate('
18 | CREATE TABLE `saas_webhook` (
19 | `id` BINARY(16) NOT NULL,
20 | `name` VARCHAR(255) NOT NULL,
21 | `event_name` VARCHAR(500) NOT NULL,
22 | `url` VARCHAR(500) NOT NULL,
23 | `app_id` BINARY(16) NULL,
24 | `created_at` DATETIME(3) NOT NULL,
25 | `updated_at` DATETIME(3) NULL,
26 | PRIMARY KEY (`id`),
27 | CONSTRAINT `fk.saas_webhook.app_id` FOREIGN KEY (`app_id`) REFERENCES `saas_app` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
28 | CONSTRAINT `uniq.saas_webhook.name` UNIQUE (`name`, `app_id`)
29 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
30 | ');
31 | }
32 |
33 | public function updateDestructive(Connection $connection): void
34 | {
35 | // nth
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/MetadataTest.php:
--------------------------------------------------------------------------------
1 | getMetadata();
15 | static::assertEquals('SwagApp', $metaData->getName());
16 | static::assertEquals('shopware AG', $metaData->getAuthor());
17 | static::assertEquals('(c) by shopware AG', $metaData->getCopyright());
18 | static::assertEquals('MIT', $metaData->getLicense());
19 | static::assertEquals('https://test.com/privacy', $metaData->getPrivacy());
20 | static::assertEquals('1.0.0', $metaData->getVersion());
21 | static::assertEquals('icon.png', $metaData->getIcon());
22 |
23 | static::assertEquals([
24 | 'en-GB' => 'Swag App Test',
25 | 'de-DE' => 'Swag App Test',
26 | ], $metaData->getLabel());
27 | static::assertEquals([
28 | 'en-GB' => 'Test for App System',
29 | 'de-DE' => 'Test für das App System',
30 | ], $metaData->getDescription());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/WebhooksTest.php:
--------------------------------------------------------------------------------
1 | getWebhooks());
16 | static::assertCount(2, $manifest->getWebhooks()->getWebhooks());
17 |
18 | /** @var Webhook $firstWebhook */
19 | $firstWebhook = $manifest->getWebhooks()->getWebhooks()[0];
20 | static::assertEquals('hook1', $firstWebhook->getName());
21 | static::assertEquals('https://test.com/hook', $firstWebhook->getUrl());
22 | static::assertEquals('checkout.customer.before.login', $firstWebhook->getEvent());
23 |
24 | /** @var Webhook $secondWebhook */
25 | $secondWebhook = $manifest->getWebhooks()->getWebhooks()[1];
26 | static::assertEquals('hook2', $secondWebhook->getName());
27 | static::assertEquals('https://test.com/hook2', $secondWebhook->getUrl());
28 | static::assertEquals('checkout.order.placed', $secondWebhook->getEvent());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/test/unit/__fixtures__/app-system/action-buttons.fixtures.js:
--------------------------------------------------------------------------------
1 | const emptyActionButtonList = {
2 | status: 200,
3 | statusText: 'OK',
4 | data: {
5 | actions: [],
6 | },
7 | };
8 |
9 | const actionButtons = {
10 | status: 200,
11 | statusText: 'OK',
12 | data: {
13 | actions: [
14 | {
15 | action: 'doStuffWithProducts',
16 | app: 'SwagApp',
17 | id: '28e76437999b41d0b8e87e9aab44e41b',
18 | label: {
19 | 'en-GB': 'Do Stuff',
20 | },
21 | openNewTab: false,
22 | url: 'https://swag-test.com/do-stuff',
23 | },
24 | ],
25 | },
26 | };
27 |
28 | const malformedList = {
29 | status: 200,
30 | statusText: 'OK',
31 | data: [
32 | {
33 | action: 'doStuffWithProducts',
34 | app: 'SwagApp',
35 | id: '28e76437999b41d0b8e87e9aab44e41b',
36 | label: {
37 | 'en-GB': 'Do Stuff',
38 | },
39 | openNewTab: false,
40 | url: 'https://swag-test.com/do-stuff',
41 | },
42 | ],
43 | };
44 |
45 | const emptyResponse = {
46 | status: 200,
47 | statusText: 'OK',
48 | data: [],
49 | };
50 |
51 | export default {
52 | emptyActionButtonList,
53 | malformedList,
54 | actionButtons,
55 | emptyResponse,
56 | };
57 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/extension/app/component/structure/sw-admin-menu/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'sw-admin-menu',
3 |
4 | computed: {
5 | appEntries() {
6 | return Shopware.State.getters['connect-apps/navigation'];
7 | },
8 | },
9 |
10 | watch: {
11 | appEntries() {
12 | this.updateAppEntries();
13 | },
14 |
15 | mainMenuEntries() {
16 | this.updateAppEntries();
17 | },
18 | },
19 |
20 | methods: {
21 | updateAppEntries() {
22 | const entryIndex = this.mainMenuEntries.findIndex((entry) => entry.id === 'sw-my-apps');
23 |
24 | if (entryIndex < 0) {
25 | return;
26 | }
27 |
28 | const myAppsEntry = this.mainMenuEntries[entryIndex];
29 | const newEntries = this.getNewEntries(myAppsEntry.children, this.appEntries);
30 |
31 | myAppsEntry.children = [...myAppsEntry.children, ...newEntries];
32 |
33 | this.mainMenuEntries[entryIndex] = { ...myAppsEntry };
34 | },
35 |
36 | getNewEntries(actualNavigationEntries, appNavigationEntries) {
37 | return appNavigationEntries.filter((appNavigationEntry) => {
38 | return !actualNavigationEntries.some((actualEntry) => {
39 | return actualEntry.path === appNavigationEntry.path;
40 | });
41 | });
42 | },
43 | },
44 | };
45 |
--------------------------------------------------------------------------------
/tests/Core/Framework/Webhook/_fixtures/BusinessEvents/UnstructuredObjectBusinessEvent.php:
--------------------------------------------------------------------------------
1 | 'test',
17 | 'bool' => true,
18 | ];
19 |
20 | public static function getAvailableData(): EventDataCollection
21 | {
22 | return (new EventDataCollection())
23 | ->add('nested', new ObjectType());
24 | }
25 |
26 | public function getEncodeValues(string $shopwareVersion): array
27 | {
28 | return [
29 | 'nested' => [
30 | 'string' => 'test',
31 | 'bool' => true,
32 | ],
33 | ];
34 | }
35 |
36 | public function getName(): string
37 | {
38 | return 'test';
39 | }
40 |
41 | public function getContext(): Context
42 | {
43 | return Context::createDefaultContext();
44 | }
45 |
46 | public function getNested(): array
47 | {
48 | return $this->nested;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Core/Content/App/AppService.php:
--------------------------------------------------------------------------------
1 | appLifecycleIterator = $appLifecycleIterator;
28 | $this->appLifecycle = $appLifecycle;
29 | }
30 |
31 | /**
32 | * @return array
33 | */
34 | public function refreshApps(bool $activateInstalled, Context $context): array
35 | {
36 | return $this->appLifecycleIterator->iterate($this->appLifecycle, $activateInstalled, $context);
37 | }
38 |
39 | public function getRefreshableAppInfo(Context $context): RefreshableAppDryRun
40 | {
41 | $appInfo = new RefreshableAppDryRun();
42 |
43 | $this->appLifecycleIterator->iterate($appInfo, false, $context);
44 |
45 | return $appInfo;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/Setup.php:
--------------------------------------------------------------------------------
1 | > $data
19 | */
20 | private function __construct(array $data)
21 | {
22 | foreach ($data as $property => $value) {
23 | $this->$property = $value;
24 | }
25 | }
26 |
27 | public static function fromXml(\DOMElement $element): self
28 | {
29 | return new self(self::parse($element));
30 | }
31 |
32 | public function getRegistrationUrl(): string
33 | {
34 | return $this->registrationUrl;
35 | }
36 |
37 | public function getSecret(): ?string
38 | {
39 | return $this->secret;
40 | }
41 |
42 | /**
43 | * @return array>
44 | */
45 | private static function parse(\DOMElement $element): array
46 | {
47 | $values = [];
48 |
49 | foreach ($element->childNodes as $child) {
50 | if (!$child instanceof \DOMElement) {
51 | continue;
52 | }
53 |
54 | $values[$child->tagName] = $child->nodeValue;
55 | }
56 |
57 | return $values;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Aggregate/ActionButtonTranslation/ActionButtonTranslationDefinition.php:
--------------------------------------------------------------------------------
1 | addFlags(new Required()),
39 | ]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Event/AppDeletedEventTest.php:
--------------------------------------------------------------------------------
1 | getAppId());
23 | static::assertEquals($context, $event->getContext());
24 | static::assertEquals(AppDeletedEvent::NAME, $event->getName());
25 | static::assertEquals([], $event->getWebhookPayload());
26 | }
27 |
28 | public function testIsAllowed(): void
29 | {
30 | $appId = Uuid::randomHex();
31 | $context = Context::createDefaultContext();
32 | $event = new AppDeletedEvent(
33 | $appId,
34 | $context
35 | );
36 |
37 | static::assertTrue($event->isAllowed($appId, new AclPrivilegeCollection()));
38 | static::assertFalse($event->isAllowed(Uuid::randomHex(), new AclPrivilegeCollection()));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/TestBootstrap.php:
--------------------------------------------------------------------------------
1 | load(TEST_PROJECT_DIR . '/.env');
37 |
38 | $testDb = ($_SERVER['DATABASE_URL'] ?? '') . '_test';
39 | putenv('DATABASE_URL=' . $testDb);
40 | $_ENV['DATABASE_URL'] = $testDb;
41 | $_SERVER['DATABASE_URL'] = $testDb;
42 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Event/AppActivatedEventTest.php:
--------------------------------------------------------------------------------
1 | getAppId());
23 | static::assertEquals($context, $event->getContext());
24 | static::assertEquals(AppActivatedEvent::NAME, $event->getName());
25 | static::assertEquals([], $event->getWebhookPayload());
26 | }
27 |
28 | public function testIsAllowed(): void
29 | {
30 | $appId = Uuid::randomHex();
31 | $context = Context::createDefaultContext();
32 | $event = new AppActivatedEvent(
33 | $appId,
34 | $context
35 | );
36 |
37 | static::assertTrue($event->isAllowed($appId, new AclPrivilegeCollection()));
38 | static::assertFalse($event->isAllowed(Uuid::randomHex(), new AclPrivilegeCollection()));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Event/AppDeactivatedEventTest.php:
--------------------------------------------------------------------------------
1 | getAppId());
23 | static::assertEquals($context, $event->getContext());
24 | static::assertEquals(AppDeactivatedEvent::NAME, $event->getName());
25 | static::assertEquals([], $event->getWebhookPayload());
26 | }
27 |
28 | public function testIsAllowed(): void
29 | {
30 | $appId = Uuid::randomHex();
31 | $context = Context::createDefaultContext();
32 | $event = new AppDeactivatedEvent(
33 | $appId,
34 | $context
35 | );
36 |
37 | static::assertTrue($event->isAllowed($appId, new AclPrivilegeCollection()));
38 | static::assertFalse($event->isAllowed(Uuid::randomHex(), new AclPrivilegeCollection()));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Aggregate/AppTranslation/AppTranslationDefinition.php:
--------------------------------------------------------------------------------
1 | addFlags(new Required()),
40 | new LongTextField('description', 'description'),
41 | ]);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/Persister/PermissionGatewayFactory.php:
--------------------------------------------------------------------------------
1 | shopwareVersion = $shopwareVersion;
23 | $this->container = $container;
24 | }
25 |
26 | public function createPermissionGateway(): PermissionGatewayStrategy
27 | {
28 | /** @var Connection $connection */
29 | $connection = $this->container->get(Connection::class);
30 |
31 | if (version_compare($this->shopwareVersion, '6.3.0.0', '<')) {
32 | $privileges = [];
33 |
34 | if ($this->container->hasParameter('acl_resource_privileges')) {
35 | // should be always the case except in tests running on 6.3
36 | $privileges = $this->container->getParameter('acl_resource_privileges');
37 | }
38 |
39 | return new PermissionGateway62(
40 | $connection,
41 | $privileges
42 | );
43 | }
44 |
45 | return new PermissionGateway63($connection);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Action/Executor.php:
--------------------------------------------------------------------------------
1 | guzzleClient = $guzzle;
20 | }
21 |
22 | public function execute(AppAction $action, Context $context): void
23 | {
24 | $payload = $action->asPayload();
25 | $payload['meta'] = [
26 | 'timestamp' => (new \DateTime())->getTimestamp(),
27 | 'reference' => Uuid::randomHex(),
28 | 'language' => $context->getLanguageId(),
29 | ];
30 |
31 | try {
32 | $this->guzzleClient->post(
33 | $action->getTargetUrl(),
34 | [
35 | 'headers' => [
36 | 'shopware-shop-signature' => hash_hmac(
37 | 'sha256',
38 | (string) \json_encode($payload),
39 | $action->getAppSecret()
40 | ),
41 | ],
42 | 'json' => $payload,
43 | ]
44 | );
45 | } catch (ServerException $e) {
46 | // ignore failing requests
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/_fixtures/bool-field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
22 | custom_field_test
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/GuzzleTestClientBehaviour.php:
--------------------------------------------------------------------------------
1 | getContainer()->get(GuzzleHistoryCollector::class)->resetHistory();
23 | $this->getContainer()->get(MockHandler::class)->reset();
24 | }
25 |
26 | public function getLastRequest(): RequestInterface
27 | {
28 | return $this->getContainer()->get(MockHandler::class)->getLastRequest();
29 | }
30 |
31 | public function getPastRequest(int $index): RequestInterface
32 | {
33 | return $this->getContainer()->get(GuzzleHistoryCollector::class)->getHistory()[$index]['request'];
34 | }
35 |
36 | public function getRequestCount(): int
37 | {
38 | return count($this->getContainer()->get(GuzzleHistoryCollector::class)->getHistory());
39 | }
40 |
41 | /**
42 | * @param $response ResponseInterface | Exception | PromiseInterface
43 | */
44 | public function appendNewResponse($response): void
45 | {
46 | $this->getContainer()->get(MockHandler::class)->append($response);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/Core/Command/_fixtures/withoutPermissions/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/_fixtures/date-time-field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
22 | custom_field_test
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/Core/Command/_fixtures/registrationFailure/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/wrongurl/registration
18 | s3cr3t
19 |
20 |
21 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/Event/AppChangedEvent.php:
--------------------------------------------------------------------------------
1 | appId = $appId;
26 | $this->context = $context;
27 | }
28 |
29 | abstract public function getName(): string;
30 |
31 | public function getAppId(): string
32 | {
33 | return $this->appId;
34 | }
35 |
36 | public function getContext(): Context
37 | {
38 | return $this->context;
39 | }
40 |
41 | /**
42 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint
43 | */
44 | public function getWebhookPayload(): array
45 | {
46 | return [];
47 | }
48 |
49 | /**
50 | * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter
51 | */
52 | public function isAllowed(string $appId, AclPrivilegeCollection $permissions): bool
53 | {
54 | return $appId === $this->getAppId();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/_fixtures/color-picker-field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
22 | custom_field_test
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/_fixtures/media-selection-field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
22 | custom_field_test
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/Core/Command/VerifyManifestCommand.php:
--------------------------------------------------------------------------------
1 | $manifestPaths */
19 | $manifestPaths = $input->getArgument('manifests');
20 |
21 | $invalidCount = 0;
22 |
23 | foreach ($manifestPaths as $manifestPath) {
24 | try {
25 | Manifest::createFromXmlFile($manifestPath);
26 | } catch (XmlParsingException $e) {
27 | $io->error($e->getMessage());
28 | ++$invalidCount;
29 | }
30 | }
31 | if ($invalidCount > 0) {
32 | return 1;
33 | }
34 | $io->success('all files valid');
35 |
36 | return 0;
37 | }
38 |
39 | protected function configure(): void
40 | {
41 | $this->setName('app:verify')
42 | ->setDescription('checks manifests for errors')
43 | ->addArgument('manifests', InputArgument::IS_ARRAY, 'The paths of the manifest file');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Migration/Migration1597392859RenameAppIdColumn.php:
--------------------------------------------------------------------------------
1 | executeQuery('SHOW COLUMNS FROM `custom_field_set` LIKE "saas_app_id";')->fetchAll();
22 |
23 | if (count($columnExists) > 0) {
24 | // if column with the new name already exists, we don't need to rename anything
25 | return;
26 | }
27 |
28 | $connection->executeUpdate('
29 | ALTER TABLE `custom_field_set`
30 | DROP FOREIGN KEY `fk.custom_field_set.app_id`,
31 | DROP INDEX `fk.custom_field_set.app_id`,
32 | CHANGE COLUMN `app_id` `saas_app_id` BINARY(16) NULL,
33 | ADD CONSTRAINT `fk.custom_field_set.saas_app_id` FOREIGN KEY (`saas_app_id`) REFERENCES `saas_app` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
34 | ');
35 | }
36 |
37 | public function updateDestructive(Connection $connection): void
38 | {
39 | // nth
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Storefront/Theme/StorefrontPluginConfiguration/StorefrontPluginConfigurationAppFactory.php:
--------------------------------------------------------------------------------
1 | configurationFactory = $configurationFactory;
28 | $this->projectDir = $projectDir;
29 | }
30 |
31 | public function createFromApp(AppEntity $app): StorefrontPluginConfiguration
32 | {
33 | $absolutePath = $this->projectDir . '/' . $app->getPath();
34 | if (file_exists($absolutePath . '/Resources/theme.json')) {
35 | return $this->configurationFactory->createThemeConfig($app->getName(), $absolutePath);
36 | }
37 |
38 | return $this->configurationFactory->createPluginConfig($app->getName(), $absolutePath);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/_fixtures/text-field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
22 | custom_field_test
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Enter a text...
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/Permissions.php:
--------------------------------------------------------------------------------
1 | >
9 | */
10 | protected $permissions;
11 |
12 | /**
13 | * @param array> $permissions
14 | */
15 | private function __construct(array $permissions)
16 | {
17 | $this->permissions = $permissions;
18 | }
19 |
20 | public static function fromXml(\DOMElement $element): self
21 | {
22 | return new self(self::parsePermissions($element));
23 | }
24 |
25 | /**
26 | * @param array> $permissions permissions as array indexed by resource
27 | */
28 | public static function fromArray(array $permissions): self
29 | {
30 | return new self($permissions);
31 | }
32 |
33 | /**
34 | * @return array>
35 | */
36 | public function getPermissions(): array
37 | {
38 | return $this->permissions;
39 | }
40 |
41 | /**
42 | * @return array>
43 | */
44 | private static function parsePermissions(\DOMElement $element): array
45 | {
46 | $permissions = [];
47 |
48 | foreach ($element->childNodes as $child) {
49 | if (!$child instanceof \DOMElement) {
50 | continue;
51 | }
52 |
53 | $permissions[$child->nodeValue][] = $child->tagName;
54 | }
55 |
56 | return $permissions;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Persister/PermissionGatewayFactoryTest.php:
--------------------------------------------------------------------------------
1 | getContainer()
23 | );
24 |
25 | $gateway = $factory->createPermissionGateway();
26 | static::assertInstanceOf($expectedGateway, $gateway);
27 | }
28 |
29 | public function getVersionGateways(): array
30 | {
31 | return [
32 | ['6.1.3', PermissionGateway62::class],
33 | ['6.2.0', PermissionGateway62::class],
34 | ['6.2.3', PermissionGateway62::class],
35 | ['6.3.0.0', PermissionGateway63::class],
36 | ['6.3.0.1', PermissionGateway63::class],
37 | ['6.3.1.1', PermissionGateway63::class],
38 | ['6.4.0.0', PermissionGateway63::class],
39 | ];
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/Webhook.php:
--------------------------------------------------------------------------------
1 | $data
26 | */
27 | private function __construct(array $data)
28 | {
29 | foreach ($data as $property => $value) {
30 | $this->$property = $value;
31 | }
32 | }
33 |
34 | public static function fromXml(\DOMElement $element): self
35 | {
36 | return new self(self::parse($element));
37 | }
38 |
39 | public function getName(): string
40 | {
41 | return $this->name;
42 | }
43 |
44 | public function getUrl(): string
45 | {
46 | return $this->url;
47 | }
48 |
49 | public function getEvent(): string
50 | {
51 | return $this->event;
52 | }
53 |
54 | /**
55 | * @return array
56 | */
57 | private static function parse(\DOMElement $element): array
58 | {
59 | $values = [];
60 |
61 | /** @var \DOMAttr $attribute */
62 | foreach ($element->attributes as $attribute) {
63 | $values[$attribute->name] = XmlUtils::phpize($attribute->value);
64 | }
65 |
66 | return $values;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/TextAreaField.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $placeholder = [];
15 |
16 | /**
17 | * @param array> $data
18 | */
19 | private function __construct(array $data)
20 | {
21 | foreach ($data as $property => $value) {
22 | $this->$property = $value;
23 | }
24 | }
25 |
26 | public static function fromXml(\DOMElement $element): CustomFieldType
27 | {
28 | return new self(self::parse($element, self::TRANSLATABLE_FIELDS));
29 | }
30 |
31 | /**
32 | * @return array
33 | */
34 | public function getPlaceholder(): array
35 | {
36 | return $this->placeholder;
37 | }
38 |
39 | /**
40 | * @return array>>
41 | */
42 | protected function toEntityArray(): array
43 | {
44 | return [
45 | 'type' => CustomFieldTypes::HTML,
46 | 'config' => [
47 | 'placeholder' => $this->placeholder,
48 | 'componentName' => 'sw-text-editor',
49 | 'customFieldType' => 'textEditor',
50 | ],
51 | ];
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Manifest/Xml/CustomFieldTypes/_fixtures/text-area-field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
22 | custom_field_test
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Enter a text...
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Manifest/Xml/CustomFieldTypes/TextField.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $placeholder = [];
15 |
16 | /**
17 | * @param array> $data
18 | */
19 | private function __construct(array $data)
20 | {
21 | foreach ($data as $property => $value) {
22 | $this->$property = $value;
23 | }
24 | }
25 |
26 | public static function fromXml(\DOMElement $element): CustomFieldType
27 | {
28 | return new self(self::parse($element, self::TRANSLATABLE_FIELDS));
29 | }
30 |
31 | /**
32 | * @return array
33 | */
34 | public function getPlaceholder(): array
35 | {
36 | return $this->placeholder;
37 | }
38 |
39 | /**
40 | * @return array>>
41 | */
42 | protected function toEntityArray(): array
43 | {
44 | return [
45 | 'type' => CustomFieldTypes::TEXT,
46 | 'config' => [
47 | 'type' => 'text',
48 | 'placeholder' => $this->placeholder,
49 | 'componentName' => 'sw-field',
50 | 'customFieldType' => 'text',
51 | ],
52 | ];
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/Event/AppDeletedEvent.php:
--------------------------------------------------------------------------------
1 | appId = $appId;
28 | $this->context = $context;
29 | }
30 |
31 | public function getAppId(): string
32 | {
33 | return $this->appId;
34 | }
35 |
36 | public function getContext(): Context
37 | {
38 | return $this->context;
39 | }
40 |
41 | /**
42 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint
43 | */
44 | public function getWebhookPayload(): array
45 | {
46 | return [];
47 | }
48 |
49 | /**
50 | * @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter
51 | */
52 | public function isAllowed(string $appId, AclPrivilegeCollection $permissions): bool
53 | {
54 | return $appId === $this->getAppId();
55 | }
56 |
57 | public function getName(): string
58 | {
59 | return self::NAME;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Resources/config/services_test.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/Resources/app/e2e/fixtures/themeApp/Resources/app/storefront/dist/storefront/js/swag-theme.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([["swag-theme"],{laxr:function(e,t,n){"use strict";function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function r(e,t){for(var n=0;n=document.body.offsetHeight&&alert("seems like there's nothing more to see here.")}}}])&&r(n.prototype,o),f&&r(n,f),t}(n("FGIj").a);window.PluginManager.register("ExamplePlugin",f)}},[["laxr","runtime","vendor-node","vendor-shared"]]]);
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/AppLoader.php:
--------------------------------------------------------------------------------
1 | appDir = $appDir;
23 | }
24 |
25 | /**
26 | * @return array
27 | */
28 | public function load(): array
29 | {
30 | $finder = new Finder();
31 | $finder->in($this->appDir)
32 | ->name('manifest.xml');
33 |
34 | $manifests = [];
35 | foreach ($finder->files() as $xml) {
36 | try {
37 | $manifests[] = Manifest::createFromXmlFile($xml->getPathname());
38 | } catch (XmlParsingException $e) {
39 | //nth, if app is already registered it will be deleted
40 | }
41 | }
42 |
43 | return $manifests;
44 | }
45 |
46 | public function getIcon(Manifest $app): ?string
47 | {
48 | if (!$app->getMetadata()->getIcon()) {
49 | return null;
50 | }
51 |
52 | $iconPath = sprintf('%s/%s', $app->getPath(), $app->getMetadata()->getIcon() ?: '');
53 | $icon = @file_get_contents($iconPath);
54 |
55 | if (!$icon) {
56 | return null;
57 | }
58 |
59 | return $icon;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/Core/Command/_fixtures/withPermissions/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | icon.png
14 | MIT
15 |
16 |
17 | https://my.app.com/SwagApp/registration
18 | s3cr3t
19 |
20 |
21 |
27 |
28 |
29 |
30 |
35 |
36 |
37 |
38 |
39 | product
40 | product
41 | category
42 | order
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/Core/Framework/ShopId/ShopIdProvider.php:
--------------------------------------------------------------------------------
1 | systemConfigService = $systemConfigService;
21 | }
22 |
23 | public function getShopId(): string
24 | {
25 | /** @var array $shopId */
26 | $shopId = $this->systemConfigService->get(self::SHOP_ID_SYSTEM_CONFIG_KEY);
27 |
28 | if (!$shopId) {
29 | $newShopId = $this->generateShopId();
30 | $this->systemConfigService->set(self::SHOP_ID_SYSTEM_CONFIG_KEY, [
31 | 'app_url' => $_SERVER['APP_URL'],
32 | 'value' => $newShopId,
33 | ]);
34 |
35 | return $newShopId;
36 | }
37 |
38 | if ($_SERVER['APP_URL'] !== ($shopId['app_url'] ?? '')) {
39 | $this->systemConfigService->set(self::SHOP_DOMAIN_CHANGE_CONFIG_KEY, true);
40 | /** @var string $appUrl */
41 | $appUrl = $_SERVER['APP_URL'];
42 |
43 | throw new AppUrlChangeDetectedException($shopId['app_url'], $appUrl);
44 | }
45 |
46 | return $shopId['value'];
47 | }
48 |
49 | private function generateShopId(): string
50 | {
51 | return Random::getAlphanumericString(16);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Core/Content/App/Lifecycle/Registration/HandshakeFactory.php:
--------------------------------------------------------------------------------
1 | shopUrl = $shopUrl;
25 | $this->shopIdProvider = $shopIdProvider;
26 | }
27 |
28 | public function create(Manifest $manifest): AppHandshakeInterface
29 | {
30 | $setup = $manifest->getSetup();
31 | $privateSecret = $setup->getSecret();
32 | if ($privateSecret) {
33 | $metadata = $manifest->getMetadata();
34 |
35 | try {
36 | $shopId = $this->shopIdProvider->getShopId();
37 | } catch (AppUrlChangeDetectedException $e) {
38 | throw new AppRegistrationException(
39 | 'The app url changed. Please resolve how the apps should handle this change.'
40 | );
41 | }
42 |
43 | return new PrivateHandshake(
44 | $this->shopUrl,
45 | $privateSecret,
46 | $setup->getRegistrationUrl(),
47 | $metadata->getName(),
48 | $shopId
49 | );
50 | }
51 |
52 | return new StoreHandshake();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Resources/app/administration/src/app/state/connect-apps.state.js:
--------------------------------------------------------------------------------
1 | export default {
2 | namespaced: true,
3 |
4 | state() {
5 | return {
6 | apps: [],
7 | };
8 | },
9 |
10 | getters: {
11 | navigation(state) {
12 | return state.apps.reduce((previousValue, app) => {
13 | previousValue.push(...getNavigationForApps(app));
14 | return previousValue;
15 | }, []);
16 | },
17 | },
18 |
19 | mutations: {
20 | setApps(state, apps) {
21 | state.apps = apps;
22 | },
23 | },
24 |
25 | actions: {
26 | fetchAppModules({ commit }) {
27 | const appModulesService = Shopware.Service('AppModulesService');
28 | return appModulesService.fetchAppModules().then((modules) => {
29 | commit('setApps', modules);
30 | });
31 | },
32 | },
33 | };
34 |
35 | function getNavigationForApps(app) {
36 | const locale = Shopware.State.get('session').currentLocale;
37 | const fallbackLocale = Shopware.Context.app.fallbackLocale;
38 |
39 | const appLabel = app.label[locale] || app.label[fallbackLocale];
40 |
41 | return app.modules.map((adminModule) => {
42 | const moduleLabel = adminModule.label[locale] || adminModule.label[fallbackLocale];
43 |
44 | return {
45 | id: `app-${app.name}-${adminModule.name}`,
46 | path: 'sw.my.apps.index',
47 | params: { appName: app.name, moduleName: adminModule.name },
48 | label: {
49 | translated: true,
50 | label: `${appLabel} - ${moduleLabel}`,
51 | },
52 | color: '#9AA8B5',
53 | parent: 'sw-my-apps',
54 | children: [],
55 | };
56 | });
57 | }
58 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Registration/_fixtures/no-setup/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SwagTestApp
6 |
7 |
8 | Test for App System
9 | Test für das App System
10 | shopware AG
11 | (c) by shopware AG
12 | 1.0.0
13 | MIT
14 |
15 |
16 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/Core/Framework/Api/Acl/AclPrivilegeCollection.php:
--------------------------------------------------------------------------------
1 | =6.3
8 | // only available in <=6.2
9 | public const PRIVILEGE_LIST = 'list';
10 | // only available in <=6.2
11 | public const PRIVILEGE_DETAIL = 'detail';
12 | // only available in >=6.3
13 | public const PRIVILEGE_READ = 'read';
14 | public const PRIVILEGE_CREATE = 'create';
15 | public const PRIVILEGE_UPDATE = 'update';
16 | public const PRIVILEGE_DELETE = 'delete';
17 |
18 | /**
19 | * @var array
20 | */
21 | private $privileges;
22 |
23 | /**
24 | * @param array $privileges
25 | */
26 | public function __construct(array $privileges = [])
27 | {
28 | $this->privileges = $privileges;
29 | }
30 |
31 | public function isAllowed(string $resource, string $privilege): bool
32 | {
33 | return in_array($resource . ':' . $privilege, $this->privileges, true);
34 | }
35 |
36 | public function count(): int
37 | {
38 | return count($this->privileges);
39 | }
40 |
41 | /**
42 | * @return array>
43 | */
44 | public function as62Compatible(): array
45 | {
46 | return array_map(static function (string $privilege): array {
47 | $parts = explode(':', $privilege);
48 |
49 | return [
50 | 'resource' => $parts[0],
51 | 'privilege' => $parts[1],
52 | ];
53 | }, $this->privileges);
54 | }
55 |
56 | /**
57 | * @return array
58 | */
59 | public function as63Compatible(): array
60 | {
61 | return $this->privileges;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Core/Framework/Webhook/WebhookDefinition.php:
--------------------------------------------------------------------------------
1 | addFlags(new PrimaryKey(), new Required()),
38 | (new StringField('name', 'name'))->addFlags(new Required()),
39 | (new StringField('event_name', 'eventName', 500))->addFlags(new Required()),
40 | (new StringField('url', 'url', 500))->addFlags(new Required()),
41 |
42 | new FkField('app_id', 'appId', AppDefinition::class),
43 | new ManyToOneAssociationField('app', 'app_id', AppDefinition::class),
44 | ]);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Core/Framework/Template/TemplateLoader.php:
--------------------------------------------------------------------------------
1 | getPath() . '/Resources/views';
25 |
26 | if (!is_dir($viewDirectory)) {
27 | return [];
28 | }
29 |
30 | $finder = new Finder();
31 | $finder->files()
32 | ->in($viewDirectory)
33 | ->name('*.html.twig')
34 | ->path(self::ALLOWED_TEMPLATE_DIRS)
35 | ->ignoreUnreadableDirs();
36 |
37 | return array_values(array_map(static function (\SplFileInfo $file) use ($viewDirectory): string {
38 | // remove viewDirectory + any leading slashes from pathname
39 | return ltrim(mb_substr($file->getPathname(), mb_strlen($viewDirectory)), '/');
40 | }, iterator_to_array($finder)));
41 | }
42 |
43 | /**
44 | * Returns the content of the template
45 | */
46 | public function getTemplateContent(string $path, Manifest $app): string
47 | {
48 | $content = @file_get_contents($app->getPath() . '/Resources/views/' . $path);
49 |
50 | if ($content === false) {
51 | throw new \RuntimeException(sprintf('Unable to read file from: %s.', $app->getPath() . '/views/' . $path));
52 | }
53 |
54 | return $content;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Event/AppUpdatedEventTest.php:
--------------------------------------------------------------------------------
1 | getAppId());
25 | static::assertInstanceOf(Manifest::class, $event->getApp());
26 | static::assertEquals($context, $event->getContext());
27 | static::assertEquals(AppUpdatedEvent::NAME, $event->getName());
28 | static::assertEquals([
29 | 'appVersion' => '1.0.0',
30 | ], $event->getWebhookPayload());
31 | }
32 |
33 | public function testIsAllowed(): void
34 | {
35 | $appId = Uuid::randomHex();
36 | $context = Context::createDefaultContext();
37 | $event = new AppUpdatedEvent(
38 | $appId,
39 | Manifest::createFromXmlFile(__DIR__ . '/../../Manifest/_fixtures/test/manifest.xml'),
40 | $context
41 | );
42 |
43 | static::assertTrue($event->isAllowed($appId, new AclPrivilegeCollection()));
44 | static::assertFalse($event->isAllowed(Uuid::randomHex(), new AclPrivilegeCollection()));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/Core/Content/App/Lifecycle/Event/AppInstalledEventTest.php:
--------------------------------------------------------------------------------
1 | getAppId());
25 | static::assertInstanceOf(Manifest::class, $event->getApp());
26 | static::assertEquals($context, $event->getContext());
27 | static::assertEquals(AppInstalledEvent::NAME, $event->getName());
28 | static::assertEquals([
29 | 'appVersion' => '1.0.0',
30 | ], $event->getWebhookPayload());
31 | }
32 |
33 | public function testIsAllowed(): void
34 | {
35 | $appId = Uuid::randomHex();
36 | $context = Context::createDefaultContext();
37 | $event = new AppInstalledEvent(
38 | $appId,
39 | Manifest::createFromXmlFile(__DIR__ . '/../../Manifest/_fixtures/test/manifest.xml'),
40 | $context
41 | );
42 |
43 | static::assertTrue($event->isAllowed($appId, new AclPrivilegeCollection()));
44 | static::assertFalse($event->isAllowed(Uuid::randomHex(), new AclPrivilegeCollection()));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Core/Framework/Webhook/WebhookEntity.php:
--------------------------------------------------------------------------------
1 | name;
41 | }
42 |
43 | public function setName(string $name): void
44 | {
45 | $this->name = $name;
46 | }
47 |
48 | public function getEventName(): string
49 | {
50 | return $this->eventName;
51 | }
52 |
53 | public function setEventName(string $eventName): void
54 | {
55 | $this->eventName = $eventName;
56 | }
57 |
58 | public function getUrl(): string
59 | {
60 | return $this->url;
61 | }
62 |
63 | public function setUrl(string $url): void
64 | {
65 | $this->url = $url;
66 | }
67 |
68 | public function getAppId(): ?string
69 | {
70 | return $this->appId;
71 | }
72 |
73 | public function setAppId(?string $appId): void
74 | {
75 | $this->appId = $appId;
76 | }
77 |
78 | public function getApp(): ?AppEntity
79 | {
80 | return $this->app;
81 | }
82 |
83 | public function setApp(?AppEntity $app): void
84 | {
85 | $this->app = $app;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Core/Framework/Template/TemplateEntity.php:
--------------------------------------------------------------------------------
1 | template;
41 | }
42 |
43 | public function setTemplate(string $template): void
44 | {
45 | $this->template = $template;
46 | }
47 |
48 | public function getPath(): string
49 | {
50 | return $this->path;
51 | }
52 |
53 | public function setPath(string $path): void
54 | {
55 | $this->path = $path;
56 | }
57 |
58 | public function isActive(): bool
59 | {
60 | return $this->active;
61 | }
62 |
63 | public function setActive(bool $active): void
64 | {
65 | $this->active = $active;
66 | }
67 |
68 | public function getAppId(): string
69 | {
70 | return $this->appId;
71 | }
72 |
73 | public function setAppId(string $appId): void
74 | {
75 | $this->appId = $appId;
76 | }
77 |
78 | public function getApp(): ?AppEntity
79 | {
80 | return $this->app;
81 | }
82 |
83 | public function setApp(?AppEntity $app): void
84 | {
85 | $this->app = $app;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------