[
  {
    "path": ".circleci/config.yml",
    "content": "# PHP CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-php/ for more details\n#\nversion: 2\n\njobs:\n  \"php82-mysql80\":\n    docker:\n      - image: aimeos/ci-php:8.2\n      - image: cimg/mysql:8.0\n        environment:\n          MYSQL_ROOT_PASSWORD: rootpw\n          MYSQL_DATABASE: laravel\n          MYSQL_USER: aimeos\n          MYSQL_PASSWORD: aimeos\n    steps:\n      - checkout\n      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer\n      - run: php composer req \"laravel/framework:~10.0\" --no-update\n      - run: php composer req \"phpunit/phpunit:~10.0\" --no-update --dev\n      - restore_cache:\n          keys:\n            - php82-{{ checksum \"composer.json\" }}\n      - run: php composer update -n --prefer-dist\n      - save_cache:\n          key: php82-{{ checksum \"composer.json\" }}\n          paths: [./vendor]\n      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done\n      - run: ./vendor/bin/phpunit\n\n  \"php83-mysql80\":\n    docker:\n      - image: aimeos/ci-php:8.3\n      - image: cimg/mysql:8.0\n        environment:\n          MYSQL_ROOT_PASSWORD: rootpw\n          MYSQL_DATABASE: laravel\n          MYSQL_USER: aimeos\n          MYSQL_PASSWORD: aimeos\n    steps:\n      - checkout\n      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer\n      - run: php composer req \"laravel/framework:~11.0\" --no-update\n      - run: php composer req \"phpunit/phpunit:~11.0\" --no-update --dev\n      - restore_cache:\n          keys:\n            - php83-{{ checksum \"composer.json\" }}\n      - run: php composer update -n --prefer-dist\n      - save_cache:\n          key: php83-{{ checksum \"composer.json\" }}\n          paths: [./vendor]\n      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done\n      - run: ./vendor/bin/phpunit\n\n  \"php84-mysql80\":\n    docker:\n      - image: aimeos/ci-php:8.4\n      - image: cimg/mysql:8.0\n        environment:\n          MYSQL_ROOT_PASSWORD: rootpw\n          MYSQL_DATABASE: laravel\n          MYSQL_USER: aimeos\n          MYSQL_PASSWORD: aimeos\n    steps:\n      - checkout\n      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer\n      - run: php composer req \"laravel/framework:~12.0\" --no-update\n      - run: php composer req \"phpunit/phpunit:~11.0\" --no-update --dev\n      - restore_cache:\n          keys:\n            - php84-{{ checksum \"composer.json\" }}\n      - run: php composer update -n --prefer-dist\n      - save_cache:\n          key: php84-{{ checksum \"composer.json\" }}\n          paths: [./vendor]\n      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done\n      - run: ./vendor/bin/phpunit --coverage-clover coverage.xml\n\n  \"php85-mysql80\":\n    docker:\n      - image: aimeos/ci-php:8.5\n      - image: cimg/mysql:8.0\n        environment:\n          MYSQL_ROOT_PASSWORD: rootpw\n          MYSQL_DATABASE: laravel\n          MYSQL_USER: aimeos\n          MYSQL_PASSWORD: aimeos\n    steps:\n      - checkout\n      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer\n      - run: php composer req \"laravel/framework:~13.0\" --no-update\n      - run: php composer req \"phpunit/phpunit:~12.0\" --no-update --dev\n      - restore_cache:\n          keys:\n            - php84-{{ checksum \"composer.json\" }}\n      - run: php composer update -n --prefer-dist\n      - save_cache:\n          key: php84-{{ checksum \"composer.json\" }}\n          paths: [./vendor]\n      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done\n      - run: ./vendor/bin/phpunit --coverage-clover coverage.xml\n\nworkflows:\n  version: 2\n  unittest:\n    jobs:\n      - \"php82-mysql80\"\n      - \"php83-mysql80\"\n      - \"php84-mysql80\"\n      - \"php85-mysql80\"\n"
  },
  {
    "path": ".coveralls.yml",
    "content": "src_dir: ./\njson_path: coveralls.json\ncoverage_clover: coverage.xml\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n  \"image\": \"mcr.microsoft.com/devcontainers/universal:2\",\n  \"features\": {\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "/.gitattributes export-ignore\n/.github export-ignore\n/.gitignore export-ignore\n/build.xml export-ignore\n/phpunit.xml.dist export-ignore\n/tests export-ignore\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Environment**\n1. Version (e.g. 2020.10)\n2. Operating system (Linux, Mac, Windows)\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem?**\nA clear and concise description of what the problem is.\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nAny alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor\ncomposer.lock\n.phpunit.result.cache\n.phpunit.cache\n.idea/\nnode_modules\nphpunit.xml\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Aimeos\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "<a href=\"https://aimeos.org/\">\n    <img src=\"https://aimeos.org/fileadmin/template/icons/logo.png\" alt=\"Aimeos logo\" title=\"Aimeos\" align=\"right\" height=\"60\" />\n</a>\n\n# Aimeos Laravel ecommerce package\n[![Total Downloads](https://poser.pugx.org/aimeos/aimeos-laravel/d/total.svg)](https://packagist.org/packages/aimeos/aimeos-laravel)\n[![Build Status](https://circleci.com/gh/aimeos/aimeos-laravel.svg?style=shield)](https://circleci.com/gh/aimeos/aimeos-laravel)\n[![Coverage Status](https://coveralls.io/repos/aimeos/aimeos-laravel/badge.svg?branch=master&service=github)](https://coveralls.io/github/aimeos/aimeos-laravel?branch=master)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/aimeos/aimeos-laravel/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/aimeos/aimeos-laravel/?branch=master)\n[![License](https://poser.pugx.org/aimeos/aimeos/license.svg)](https://packagist.org/packages/aimeos/aimeos)\n\n:star: Star us on GitHub — it motivates us a lot! 😀\n\n[Aimeos](https://aimeos.org/Laravel) is THE professional, full-featured and\nultra fast Laravel ecommerce package!  You can install it in your\nexisting Laravel application within 5 minutes and can adapt, extend, overwrite\nand customize anything to your needs.\n\n[![Aimeos Laravel demo](https://aimeos.org/fileadmin/aimeos.org/images/aimeos-github.png)](https://laravel.demo.aimeos.org/)\n\n## Features\n\nAimeos is a full-featured e-commerce package:\n\n* Multi vendor, multi channel and multi warehouse\n* From one to 1,000,000,000+ items\n* Extremly fast down to 20ms\n* For multi-tentant e-commerce SaaS solutions with unlimited vendors\n* Bundles, vouchers, virtual, configurable, custom and event products\n* Subscriptions with recurring payments\n* 100+ payment gateways\n* Full RTL support (frontend and backend)\n* Block/tier pricing out of the box\n* Extension for customer/group based prices\n* Discount and voucher support\n* Flexible basket rule system\n* Full-featured admin backend\n* Beautiful admin dashboard\n* Configurable product data sets\n* JSON REST API based on jsonapi.org\n* GraphQL API for administration\n* Completly modular structure\n* Extremely configurable and extensible\n* Extension for market places with millions of vendors\n* Fully SEO optimized including rich snippets\n* Translated to 30+ languages\n* AI-based text translation\n* Optimized for smart phones and tablets\n* Secure and reviewed implementation\n* High quality source code\n\n... and [more Aimeos features](https://aimeos.org/features)\n\nSupported languages:\n\n\n<p align=\"center\" style=\"display: inline;\">\n    <a href=\"https://www.transifex.com/aimeos/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/us.svg\" title=\"English\" width=\"24\"></a>\n    <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/de/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/de.svg\" title=\"German\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/fr/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/fr.svg\" title=\"French\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/es/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/es.svg\" title=\"Spanish\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/nl/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/nl.svg\" title=\"Dutch\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/it/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/it.svg\" title=\"Italian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/pt/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/pt.svg\" title=\"Portuguese\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/da/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/dk.svg\" title=\"Danish\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/fi/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/fi.svg\" title=\"Finnish\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/sv/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/sv.svg\" title=\"Swedish\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/no/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/no.svg\" title=\"Norwegian\" width=\"24\"></a>\n    &nbsp;&nbsp;&nbsp;\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/pl/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/pl.svg\" title=\"Polish\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/hu/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/hu.svg\" title=\"Hungarian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/ru/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/ru.svg\" title=\"Russian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/uk/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/ua.svg\" title=\"Ukrainian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/hr/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/hr.svg\" title=\"Croatian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/sl/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/si.svg\" title=\"Slovenian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/ro/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/ro.svg\" title=\"Romanian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/cs/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/cz.svg\" title=\"Czech\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/sr/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/sr.svg\" title=\"Serbian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/sk/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/sk.svg\" title=\"Slovak\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/et/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/et.svg\" title=\"Estonian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/lv/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/lv.svg\" title=\"Latvian\" width=\"24\"></a>\n    &nbsp;&nbsp;&nbsp;\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/tr/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/tr.svg\" title=\"Turkish\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/ar/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/sa.svg\" title=\"Arabic\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/fa/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/ir.svg\" title=\"Persian\" width=\"24\"></a>\n    &nbsp;&nbsp;&nbsp;\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/zh/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/cn.svg\" title=\"Chinese\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/ja/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/jp.svg\" title=\"Japanese\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/id/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/id.svg\" title=\"Indonesian\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/vi/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/vi.svg\" title=\"Vietnamese\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/my/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/my.svg\" title=\"Burmese\" width=\"24\"></a>\n     <a href=\"https://www.transifex.com/aimeos/dashboard/all_projects/ko/\"><img src=\"https://flagicons.lipis.dev/flags/4x3/kr.svg\" title=\"Korean\" width=\"24\"></a>\n</p>\n\n\nCheck out the demos:\n\n* [Aimeos frontend demo](https://laravel.demo.aimeos.org)\n* [Aimeos admin demo](https://admin.demo.aimeos.org)\n\n## Alternatives\n\n### Full shop application\n\nIf you want to set up a new application or test Aimeos, we recommend the Aimeos\nshop distribution. It contains everything for a quick start and you will get a\nfully working online shop in less than 5 minutes:\n\n:star: [Aimeos shop distribution](https://github.com/aimeos/aimeos)\n\n### Headless distribution\n\nIf you want to build a single page application (SPA) respectively a progressive web\napplication (PWA) yourself and don't need the Aimeos HTML frontend, then the Aimeos\nheadless distribution is the right choice:\n\n:star:  [Aimeos headless distribution](https://github.com/aimeos/aimeos-headless)\n\n## Table of content\n\n- [Supported versions](#supported-versions)\n- [Requirements](#requirements)\n- [Database](#database)\n- [Installation](#installation)\n- [Authentication](#authentication)\n- [Setup](#setup)\n- [Test](#test)\n- [Hints](#hints)\n- [License](#license)\n- [Links](#links)\n\n## Supported versions\n\nCurrently, the Aimeos Laravel packages **2024.10 and later** are fully supported:\n\n- LTS release: 2025.10+ (Laravel 10.x, 11.x and 12.x)\n- old LTS release: 2024.10+ (Laravel 10.x and 11.x)\n\nIf you want to upgrade between major versions, please have a look into the\n[upgrade guide](https://aimeos.org/docs/latest/laravel/setup/#upgrade)!\n\n## Requirements\n\nThe Aimeos shop distribution requires:\n- Linux/Unix, WAMP/XAMP or MacOS environment\n- PHP >= 8.1\n- MySQL >= 5.7.8, MariaDB >= 10.2.2, PostgreSQL 9.6+, SQL Server 2019+\n- Web server (Apache, Nginx or integrated PHP web server for testing)\n\nIf required PHP extensions are missing, `composer` will tell you about the missing\ndependencies.\n\nIf you want to upgrade between major versions, please have a look into the\n[upgrade guide](https://aimeos.org/docs/latest/laravel/setup/#upgrade)!\n\n## Database\n\nMake sure that you've **created the database** in advance and added the configuration\nto the `.env` file in your application directory. Sometimes, using the .env file makes\nproblems and you will get exceptions that the connection to the database failed. In that\ncase, add the database credentials to the **resource/db section of your ./config/shop.php**\nfile too!\n\nIf you don't have at least MySQL 5.7.8 or MariaDB 10.2.2 installed, you will probably get an error like\n\n```\nSpecified key was too long; max key length is 767 bytes\n```\n\nTo circumvent this problem, drop the new tables if there have been any created and\nchange the charset/collation setting in `./config/database.php` to these values before\ninstalling Aimeos again:\n\n```php\n'connections' => [\n    'mysql' => [\n        // ...\n        'charset' => 'utf8',\n        'collation' => 'utf8_unicode_ci',\n        // ...\n    ]\n]\n```\n\n**Caution:** Also make sure that your MySQL server creates *InnoDB* tables by default as *MyISAM*\ntables won't work and will result in an foreign key constraint error!\n\nIf you want to use a database server other than MySQL, please have a look into the article about\n[supported database servers](https://aimeos.org/docs/latest/infrastructure/databases/)\nand their specific configuration. Supported are:\n\n* MySQL, MariaDB (fully)\n* PostgreSQL (fully)\n* SQL Server (fully)\n\nMake sure, you use one of the supported database servers in your `.env` file, e.g.:\n\n```\nDB_CONNECTION=mysql\nDB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=aimeos\nDB_USERNAME=root\nDB_PASSWORD=\n```\n\n**Caution:** The SQLite database configured by default is **NOT supported!**\n\n## Installation\n\nThe Aimeos Laravel online shop package is a composer based library. It can be\ninstalled easiest by using [Composer 2.1+](https://getcomposer.org) in the root\ndirectory of your existing Laravel application:\n\n```\nwget https://getcomposer.org/download/latest-stable/composer.phar -O composer\n```\n\nThen, add these lines to the composer.json of the **Laravel skeleton application**:\n\n```json\n    \"prefer-stable\": true,\n    \"minimum-stability\": \"dev\",\n    \"require\": {\n        \"aimeos/aimeos-laravel\": \"~2025.10\",\n        ...\n    },\n    \"scripts\": {\n        \"post-update-cmd\": [\n            \"@php artisan vendor:publish --tag=laravel-assets --ansi --force\",\n            \"@php artisan vendor:publish --tag=public --ansi\",\n            \"\\\\Aimeos\\\\Shop\\\\Composer::join\"\n        ],\n        ...\n    }\n```\n\nAfterward, install the Aimeos shop package using\n\n`php composer update -W`\n\nIn the last step, you must now execute these artisan commands to get a working\nor updated Aimeos installation:\n\n```bash\nphp artisan vendor:publish --tag=config --tag=public\nphp artisan migrate\nphp artisan aimeos:setup --option=setup/default/demo:1\n```\n\nIn a production environment or if you don't want that the demo data gets\ninstalled, leave out the `--option=setup/default/demo:1` option.\n\n## Authentication\n\nYou have to set up one of Laravel's authentication starter kits. Laravel Breeze\nis the easiest one but you can also use Jetstream.\n\n```bash\ncomposer require laravel/breeze\nphp artisan breeze:install\nnpm install && npm run build # if not executed automatically by the previous command\n```\n\nLaravel Breeze will ask you a few questions, the most important one is the type of stack you\nwant to use. Select \"Blade\" (it's the easiest way) and use the default values for the others.\n\nIt also adds a route for `/profile` to `./routes/web.php` which may overwrite the\n`aimeos_shop_account` route. To avoid an exception about a missing `aimeos_shop_account`\nroute, change the URL for these lines from `./routes/web.php` file from `/profile` to\n`/profile/me`:\n\n```php\nRoute::middleware('auth')->group(function () {\n    Route::get('/profile/me', [ProfileController::class, 'edit'])->name('profile.edit');\n    Route::patch('/profile/me', [ProfileController::class, 'update'])->name('profile.update');\n    Route::delete('/profile/me', [ProfileController::class, 'destroy'])->name('profile.destroy');\n});\n```\n\nFor more information, please follow the Laravel documentation:\n* [Laravel 12.x](https://laravel.com/docs/12.x/authentication)\n* [Laravel 11.x](https://laravel.com/docs/11.x/authentication)\n* [Laravel 10.x](https://laravel.com/docs/10.x/authentication)\n\n### Configure authentication\n\nAs a last step, you need to extend the `boot()` method of your\n`App\\Providers\\AppServiceProvider` class and add the lines to define how\nauthorization for \"admin\" is checked in `app/Providers/AppServiceProvider.php`:\n\n```php\n    public function boot()\n    {\n        // Keep the lines before\n\n        \\Illuminate\\Support\\Facades\\Gate::define('admin', function($user, $class, $roles) {\n            if( isset( $user->superuser ) && $user->superuser ) {\n                return true;\n            }\n            return app( '\\Aimeos\\Shop\\Base\\Support' )->checkUserGroup( $user, $roles );\n        });\n    }\n```\n\n### Create account\n\nTest if your authentication setup works before you continue. Create an admin account\nfor your Laravel application so you will be able to log into the Aimeos admin interface:\n\n```bash\nphp artisan aimeos:account --super <email>\n```\n\nThe e-mail address is the user name for login and the account will work for the\nfrontend too. To protect the new account, the command will ask you for a password.\nThe same command can create limited accounts by using `--admin`, `--editor` or `--api`\ninstead of `--super` (access to everything).\n\n## Setup\n\nTo reference images correctly, you have to adapt your `.env` file and set the `APP_URL`\nto your real URL, e.g.\n\n```\nAPP_URL=http://127.0.0.1:8000\n```\n\n**Caution:** Make sure, Laravel uses the `file` session driver in your `.env` file!\nOtherwise, the shopping basket content won't get stored correctly!\n\n```\nSESSION_DRIVER=file\n```\n\nIf your `./public` directory isn't writable by your web server, you have to create these\ndirectories:\n\n```\nmkdir public/aimeos public/vendor\nchmod 777 public/aimeos public/vendor\n```\n\nIn a production environment, you should be more specific about the granted permissions!\n\n## Test\n\nThen, you should be able to call the catalog list page in your browser. For a\nquick start, you can use the integrated web server. Simply execute this command\nin the base directory of your application:\n\n```\nphp artisan serve\n```\n\n### Frontend\n\nPoint your browser to the list page of the shop using:\n\nhttp://127.0.0.1:8000/shop/search\n\n**Note:** Integrating the Aimeos package adds some routes like `/shop` or `/admin` to your\nLaravel installation but the **home page stays untouched!** If you want to add Aimeos to\nthe home page as well, replace the route for \"/\" in `./routes/web.php` by this line:\n\n```php\nRoute::group(['middleware' => ['web']], function () {\n    Route::get('/', '\\Aimeos\\Shop\\Controller\\CatalogController@homeAction')->name('aimeos_home');\n});\n```\n\nFor multi-vendor setups, read the article about [multiple shops](https://aimeos.org/docs/latest/laravel/customize/#multiple-shops).\n\nThis will display the Aimeos catalog home component on the home page you you get a\nnice looking shop home page which will look like this:\n\n[![Aimeos frontend](https://aimeos.org/fileadmin/aimeos.org/images/aimeos-frontend.jpg?2021.07)](http://127.0.0.1:8000/)\n\n### Backend\n\nIf you've still started the internal PHP web server (`php artisan serve`)\nyou should now open this URL in your browser:\n\nhttp://127.0.0.1:8000/admin\n\nEnter the e-mail address and the password of the newly created user and press \"Login\".\nIf you don't get redirected to the admin interface (that depends on the authentication\ncode you've created according to the Laravel documentation), point your browser to the\n`/admin` URL again.\n\n**Caution:** Make sure that you aren't already logged in as a non-admin user! In this\ncase, login won't work because Laravel requires you to log out first.\n\n[![Aimeos backend](https://aimeos.org/fileadmin/aimeos.org/images/aimeos-backend.png)](http://127.0.0.1:8000/admin)\n\n## Hints\n\nTo simplify development, you should configure to use no content cache. You can\ndo this in the `config/shop.php` file of your Laravel application by adding\nthese lines at the bottom:\n\n```php\n    'madmin' => [\n        'cache' => [\n            'manager' => [\n                'name' => 'None',\n            ],\n        ],\n    ],\n```\n\n## License\n\nThe Aimeos Laravel package is licensed under the terms of the MIT license and\nis available for free.\n\n## Links\n\n* [Web site](https://aimeos.org/Laravel)\n* [Documentation](https://aimeos.org/docs/Laravel)\n* [Forum](https://aimeos.org/help/laravel-package-f18/)\n* [Issue tracker](https://github.com/aimeos/aimeos-laravel/issues)\n* [Composer packages](https://packagist.org/packages/aimeos/aimeos-laravel)\n* [Source code](https://github.com/aimeos/aimeos-laravel)\n"
  },
  {
    "path": "build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<project name=\"Aimeos Laravel package\" default=\"update\">\n\n\t<target name=\"update\" description=\"Updates the bundle\">\n\t\t<exec command=\"git pull https://github.com/aimeos/aimeos-laravel.git\" checkreturn=\"true\" logoutput=\"true\" />\n\t\t<exec command=\"composer update --working-dir ${project.basedir}\" checkreturn=\"true\" logoutput=\"true\" />\n\t</target>\n\n\n\t<target name=\"release\" description=\"Creates new release\">\n\t\t<propertyprompt propertyName=\"version\" promptText=\"Enter release version\" promptCharacter=\":\" useExistingValue=\"true\"/>\n\t\t<exec command=\"git branch ${version}\" checkreturn=\"true\" logoutput=\"true\" />\n\t\t<exec command=\"git tag -a aimeos-laravel_${version} -m 'Release ${version}'\" checkreturn=\"true\" logoutput=\"true\" />\n\t\t<exec command=\"git push origin ${version}\" checkreturn=\"true\" logoutput=\"true\" />\n\t\t<exec command=\"git push --tags\" checkreturn=\"true\" logoutput=\"true\" />\n\t</target>\n\n</project>\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"aimeos/aimeos-laravel\",\n    \"description\": \"Cloud native, API first Laravel eCommerce package with integrated AI for ultra-fast online shops, marketplaces and complex B2B projects\",\n    \"homepage\": \"https://aimeos.org/Laravel\",\n    \"type\": \"laravel-package\",\n    \"license\": \"MIT\",\n    \"keywords\": [\"aimeos\", \"laravel\", \"e-commerce\", \"ecommerce\", \"B2B\", \"shop\", \"portal\", \"marketplace\", \"API\", \"JSON\", \"GraphQL\"],\n    \"support\": {\n        \"source\": \"https://github.com/Aimeos/aimeos-laravel\",\n        \"issues\": \"https://github.com/Aimeos/aimeos-laravel/issues\",\n        \"forum\": \"https://aimeos.org/help\",\n        \"wiki\": \"https://aimeos.org/docs\"\n    },\n    \"prefer-stable\": true,\n    \"minimum-stability\": \"dev\",\n    \"require\": {\n        \"composer-runtime-api\": \"^2.1\",\n        \"laravel/framework\": \"^10.0||^11.0||^12.0||^13.0\",\n        \"symfony/psr-http-message-bridge\": \"~6.0||~7.0\",\n        \"laminas/laminas-diactoros\": \"~2.5||~3.0\",\n        \"nyholm/psr7\": \"~1.2\",\n        \"aimeos/aimeos-core\": \"dev-master\",\n        \"aimeos/ai-laravel\": \"dev-master\",\n        \"aimeos/ai-admin-graphql\": \"dev-master\",\n        \"aimeos/ai-admin-jqadm\": \"dev-master\",\n        \"aimeos/ai-admin-jsonadm\": \"dev-master\",\n        \"aimeos/ai-client-html\": \"dev-master\",\n        \"aimeos/ai-client-jsonapi\": \"dev-master\",\n        \"aimeos/ai-cms-grapesjs\": \"dev-master\",\n        \"aimeos/ai-controller-jobs\": \"dev-master\",\n        \"aimeos/ai-controller-frontend\": \"dev-master\"\n    },\n    \"require-dev\": {\n        \"phpunit/phpunit\": \"^10.0||^11.0||^12.0\",\n        \"orchestra/testbench\": \"~8.0||~9.0||~10.0||~11.0\",\n        \"orchestra/testbench-browser-kit\": \"~8.0||~9.0||~10.0||~11.0\",\n        \"php-coveralls/php-coveralls\": \"~2.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Aimeos\\\\Shop\\\\\": \"src/\"\n        },\n        \"files\": [\n            \"src/helpers.php\"\n        ]\n    },\n    \"autoload-dev\": {\n        \"classmap\": [\n            \"tests/AimeosTestAbstract.php\",\n            \"tests/HelpersTest.php\"\n        ]\n    },\n    \"extra\": {\n        \"laravel\": {\n            \"providers\": [\n                \"Aimeos\\\\Shop\\\\ShopServiceProvider\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "config/default.php",
    "content": "<?php\n\nswitch( config( 'database.default', 'mysql' ) ) {\n\tcase 'pgsql': $aimeosIndexManagerName = 'PgSQL'; break;\n\tcase 'sqlsrv': $aimeosIndexManagerName = 'SQLSrv'; break;\n\tdefault: $aimeosIndexManagerName = 'MySQL';\n}\n\n\nreturn [\n\n\t'apc_enabled' => false,\n\t'apc_prefix' => 'laravel:',\n\t'pcntl_max' => 4,\n\t'pcntl_priority' => 19,\n\n\t'page' => [\n\t\t'account-index' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'account/profile', 'account/review', 'account/subscription', 'account/basket', 'account/history', 'account/favorite', 'account/watch', 'catalog/session'],\n\t\t'basket-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'basket/standard', 'basket/bulk', 'basket/related'],\n\t\t'catalog-count' => ['catalog/count'],\n\t\t'catalog-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/stage', 'catalog/detail', 'catalog/session'],\n\t\t'catalog-home' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/home'],\n\t\t'catalog-list' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],\n\t\t'catalog-session' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/session'],\n\t\t'catalog-stock' => ['catalog/stock'],\n\t\t'catalog-suggest' => ['catalog/suggest'],\n\t\t'catalog-tree' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],\n\t\t'checkout-confirm' => ['catalog/tree', 'catalog/search', 'checkout/confirm'],\n\t\t'checkout-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'checkout/standard'],\n\t\t'checkout-update' => ['checkout/update'],\n\t\t'supplier-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'supplier/detail', 'catalog/lists'],\n\t],\n\n\t'admin' => [\n\t\t'graphql' => [\n\t\t\t'url' => [\n\t\t\t\t'target' => 'aimeos_shop_graphql_post',\n\t\t\t\t'config' => [\n\t\t\t\t\t'absoluteUri' => true,\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t\t'jqadm' => [\n\t\t\t'url' => [\n\t\t\t\t'batch' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_batch'\n\t\t\t\t],\n\t\t\t\t'copy' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_copy'\n\t\t\t\t],\n\t\t\t\t'create' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_create'\n\t\t\t\t],\n\t\t\t\t'delete' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_delete'\n\t\t\t\t],\n\t\t\t\t'export' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_export'\n\t\t\t\t],\n\t\t\t\t'get' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_get'\n\t\t\t\t],\n\t\t\t\t'import' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_import'\n\t\t\t\t],\n\t\t\t\t'save' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_save'\n\t\t\t\t],\n\t\t\t\t'search' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jqadm_search'\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t\t'jsonadm' => [\n\t\t\t'url' => [\n\t\t\t\t'target' => 'aimeos_shop_jsonadm_get',\n\t\t\t\t'config' => [\n\t\t\t\t\t'absoluteUri' => true,\n\t\t\t\t],\n\t\t\t\t'options' => [\n\t\t\t\t\t'target' => 'aimeos_shop_jsonadm_options',\n\t\t\t\t\t'config' => [\n\t\t\t\t\t\t'absoluteUri' => true,\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t],\n\t'client' => [\n\t\t'html' => [\n\t\t\t'account' => [\n\t\t\t\t'index' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'basket' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'review' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'profile' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'subscription' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'history' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'favorite' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account_favorite',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'watch' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account_watch',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'download' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_account_download',\n\t\t\t\t\t],\n\t\t\t\t\t'error' => [\n\t\t\t\t\t\t'url' => [\n\t\t\t\t\t\t\t'target' => 'aimeos_shop_account',\n\t\t\t\t\t\t],\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t\t'cms' => [\n\t\t\t\t'page' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_page',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t\t'catalog' => [\n\t\t\t\t'count' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_count',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'detail' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_detail',\n\t\t\t\t\t\t'filter' => ['path', 'd_prodid'],\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'home' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_home',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'lists' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_list',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'session' => [\n\t\t\t\t\t'pinned' => [\n\t\t\t\t\t\t'url' => [\n\t\t\t\t\t\t\t'target' => 'aimeos_shop_session_pinned',\n\t\t\t\t\t\t],\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'stock' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_stock',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'suggest' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_suggest',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'tree' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_tree',\n\t\t\t\t\t\t'filter' => ['path'],\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t\t'common' => [\n\t\t\t\t'template' => [\n\t\t\t\t\t'baseurl' => public_path( 'vendor/shop/themes/default' ),\n\t\t\t\t],\n\t\t\t],\n\t\t\t'basket' => [\n\t\t\t\t'standard' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_basket',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t\t'checkout' => [\n\t\t\t\t'confirm' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_confirm',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'standard' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_checkout',\n\t\t\t\t\t],\n\t\t\t\t\t'summary' => [\n\t\t\t\t\t\t'option' => [\n\t\t\t\t\t\t\t'terms' => [\n\t\t\t\t\t\t\t\t'url' => [\n\t\t\t\t\t\t\t\t\t'target' => 'aimeos_page',\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t'privacy' => [\n\t\t\t\t\t\t\t\t\t'url' => [\n\t\t\t\t\t\t\t\t\t\t'target' => 'aimeos_page',\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t'cancel' => [\n\t\t\t\t\t\t\t\t\t'url' => [\n\t\t\t\t\t\t\t\t\t\t'target' => 'aimeos_page',\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t],\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t\t'update' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_update',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t\t'locale' => [\n\t\t\t\t'select' => [\n\t\t\t\t\t'currency' => [\n\t\t\t\t\t\t'param-name' => 'currency',\n\t\t\t\t\t],\n\t\t\t\t\t'language' => [\n\t\t\t\t\t\t'param-name' => 'locale',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t\t'supplier' => [\n\t\t\t\t'detail' => [\n\t\t\t\t\t'url' => [\n\t\t\t\t\t\t'target' => 'aimeos_shop_supplier',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t]\n\t\t],\n\t\t'jsonapi' => [\n\t\t\t'url' => [\n\t\t\t\t'target' => 'aimeos_shop_jsonapi_options',\n\t\t\t\t'config' => [\n\t\t\t\t\t'absoluteUri' => true,\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t],\n\n\t'controller' => [\n\t\t'jobs' => [\n\t\t\t'to-email' => config( 'mail.from.address' ),\n\t\t]\n\t],\n\n\t'mshop' => [\n\t\t'customer' => [\n\t\t\t'manager' => [\n\t\t\t\t'name' => 'Laravel',\n\t\t\t\t'password' => [\n\t\t\t\t\t'name' => 'Bcrypt',\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t\t'index' => [\n\t\t\t'manager' => [\n\t\t\t\t'name' => $aimeosIndexManagerName,\n\t\t\t],\n\t\t],\n\t],\n];\n"
  },
  {
    "path": "config/shop.php",
    "content": "<?php\n\nreturn [\n\n\t'apc_enabled' => false, // enable for maximum performance if APCu is available\n\t'apc_prefix' => 'laravel:', // prefix for caching config and translation in APCu\n\t'num_formatter' => 'Locale', // locale based number formatter (alternative: \"Standard\")\n\t'pcntl_max' => 4, // maximum number of parallel command line processes when starting jobs\n\t'version' => env( 'APP_VERSION', 1 ), // shop CSS/JS file version\n\t'roles' => ['admin', 'editor'], // user groups allowed to access the admin backend\n\t'panel' => 'dashboard', // panel shown in admin backend after login\n\n\t'routes' => [\n\t\t// Docs: https://aimeos.org/docs/latest/laravel/extend/#custom-routes\n\t\t// Multi-sites: https://aimeos.org/docs/latest/laravel/customize/#multiple-shops\n\t\t// 'admin' => ['prefix' => 'admin', 'middleware' => ['web']],\n\t\t// 'jqadm' => ['prefix' => 'admin/{site}/jqadm', 'middleware' => ['web', 'auth']],\n\t\t// 'graphql' => ['prefix' => 'admin/{site}/graphql', 'middleware' => ['web', 'auth']],\n\t\t// 'jsonadm' => ['prefix' => 'admin/{site}/jsonadm', 'middleware' => ['web', 'auth']],\n\t\t// 'jsonapi' => ['prefix' => 'jsonapi', 'middleware' => ['web', 'api']],\n\t\t// 'account' => ['prefix' => 'profile', 'middleware' => ['web', 'auth']],\n\t\t// 'default' => ['prefix' => 'shop', 'middleware' => ['web']],\n\t\t// 'basket' => ['prefix' => 'shop', 'middleware' => ['web']],\n\t\t// 'checkout' => ['prefix' => 'shop', 'middleware' => ['web']],\n\t\t// 'confirm' => ['prefix' => 'shop', 'middleware' => ['web']],\n\t\t// 'supplier' => ['prefix' => 's', 'middleware' => ['web']],\n\t\t// 'page' => ['prefix' => 'p', 'middleware' => ['web']],\n\t\t// 'home' => ['middleware' => ['web']],\n\t\t// 'update' => [],\n\t],\n\n\t'page' => [\n\t\t'account-index' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'account/profile', 'account/review', 'account/subscription', 'account/basket', 'account/history', 'account/favorite', 'account/watch', 'catalog/session'],\n\t\t'basket-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'basket/standard', 'basket/bulk', 'basket/related'],\n\t\t'catalog-count' => ['catalog/count'],\n\t\t'catalog-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/stage', 'catalog/detail', 'catalog/session'],\n\t\t'catalog-home' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/home'],\n\t\t'catalog-list' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],\n\t\t'catalog-session' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/session'],\n\t\t'catalog-stock' => ['catalog/stock'],\n\t\t'catalog-suggest' => ['catalog/suggest'],\n\t\t'catalog-tree' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],\n\t\t'checkout-confirm' => ['catalog/tree', 'catalog/search', 'checkout/confirm'],\n\t\t'checkout-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'checkout/standard'],\n\t\t'checkout-update' => ['checkout/update'],\n\t\t'supplier-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'supplier/detail', 'catalog/lists'],\n\t\t'cms' => ['cms/page', 'catalog/tree', 'basket/mini'],\n\t],\n\n\t'resource' => [\n\t\t'db' => [\n\t\t\t'adapter' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.driver', 'mysql' ),\n\t\t\t'host' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.host', '127.0.0.1' ),\n\t\t\t'port' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.port', '3306' ),\n\t\t\t'socket' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.unix_socket', '' ),\n\t\t\t'database' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.database', 'forge' ),\n\t\t\t'username' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.username', 'forge' ),\n\t\t\t'password' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.password', '' ),\n\t\t\t'stmt' => config( 'database.default', 'mysql' ) === 'mysql' ? [\"SET SESSION sort_buffer_size=2097144; SET NAMES 'utf8mb4'; SET SESSION sql_mode='ANSI'\"] : [],\n\t\t\t'limit' => 3, // maximum number of concurrent database connections\n\t\t\t'defaultTableOptions' => [\n\t\t\t\t'charset' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.charset' ),\n\t\t\t\t'collate' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.collation' ),\n\t\t\t],\n\t\t\t'driverOptions' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.options' ),\n\t\t],\n\t\t'fs' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => public_path(),\n\t\t\t'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/'),\n\t\t],\n\t\t'fs-media' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => public_path( 'aimeos' ),\n\t\t\t'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/') . '/aimeos',\n\t\t],\n\t\t'fs-mimeicon' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => public_path( 'vendor/shop/mimeicons' ),\n\t\t\t'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/') . '/vendor/shop/mimeicons',\n\t\t],\n\t\t'fs-theme' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => public_path( 'vendor/shop/themes' ),\n\t\t\t'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/') . '/vendor/shop/themes',\n\t\t],\n\t\t'fs-admin' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => storage_path( 'admin' ),\n\t\t],\n\t\t'fs-export' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => storage_path( 'export' ),\n\t\t],\n\t\t'fs-import' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => storage_path( 'import' ),\n\t\t],\n\t\t'fs-secure' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => storage_path( 'secure' ),\n\t\t],\n\t\t'mq' => [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'db' => 'db',\n\t\t],\n\t\t'email' => [\n\t\t\t'from-email' => config( 'mail.from.address' ),\n\t\t\t'from-name' => config( 'mail.from.name' ),\n\t\t],\n\t],\n\n\t'admin' => [],\n\n\t'client' => [\n\t\t'html' => [\n\t\t\t'basket' => [\n\t\t\t\t'cache' => [\n\t\t\t\t\t// 'enable' => false, // Disable basket content caching for development\n\t\t\t\t],\n\t\t\t],\n\t\t\t'common' => [\n\t\t\t\t'cache' => [\n\t\t\t\t\t// 'force' => true // enforce caching for logged in users\n\t\t\t\t],\n\t\t\t],\n\t\t\t'catalog' => [\n\t\t\t\t'lists' => [\n\t\t\t\t\t'basket-add' => true, // shows add to basket in list views\n\t\t\t\t\t// 'infinite-scroll' => true, // load more products in list view\n\t\t\t\t\t// 'size' => 48, // number of products per page\n\t\t\t\t],\n\t\t\t\t'selection' => [\n\t\t\t\t\t'type' => [// how variant attributes are displayed\n\t\t\t\t\t\t'color' => 'radio',\n\t\t\t\t\t\t'length' => 'radio',\n\t\t\t\t\t\t'width' => 'radio',\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t],\n\n\t'controller' => [\n\t\t'frontend' => [\n\t\t\t'catalog' => [\n\t\t\t\t'levels-always' => 3 // number of category levels for mega menu\n\t\t\t]\n\t\t]\n\t],\n\n\t'i18n' => [\n\t],\n\n\t'madmin' => [\n\t\t'cache' => [\n\t\t\t'manager' => [\n\t\t\t\t// 'name' => 'None', // Disable caching for development\n\t\t\t],\n\t\t],\n\t\t'log' => [\n\t\t\t'manager' => [\n\t\t\t\t// 'loglevel' => 7, // Enable debug logging into madmin_log table\n\t\t\t],\n\t\t],\n\t],\n\n\t'mshop' => [\n\t\t'locale' => [\n\t\t\t// 'site' => '<custom site code>', // used instead of \"default\"\n\t\t]\n\t],\n\n\n\t'command' => [\n\t],\n\n\t'frontend' => [\n\t],\n\n\t'backend' => [\n\t],\n\n];\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" bootstrap=\"vendor/autoload.php\" xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/10.0/phpunit.xsd\" cacheDirectory=\".phpunit.cache\">\n  <source>\n    <include>\n      <directory suffix=\".php\">./src</directory>\n    </include>\n  </source>\n  <coverage>\n    <report>\n      <clover outputFile=\"coverage.xml\"/>\n    </report>\n  </coverage>\n  <php>\n    <env name=\"APP_ENV\" value=\"testing\"/>\n    <env name=\"CACHE_DRIVER\" value=\"array\"/>\n    <env name=\"SESSION_DRIVER\" value=\"array\"/>\n    <env name=\"XDEBUG_MODE\" value=\"coverage\"/>\n  </php>\n  <testsuites>\n    <testsuite name=\"command\">\n      <file>./tests/Command/SetupCommandTest.php</file>\n      <file>./tests/Command/ClearCommandTest.php</file>\n      <file>./tests/Command/JobsCommandTest.php</file>\n      <file>./tests/Command/AccountCommandTest.php</file>\n    </testsuite>\n    <testsuite name=\"base\">\n      <directory>./tests/Base</directory>\n    </testsuite>\n    <testsuite name=\"controller\">\n      <directory>./tests/Controller</directory>\n    </testsuite>\n    <testsuite name=\"helpers\">\n      <file>./tests/HelpersTest.php</file>\n    </testsuite>\n  </testsuites>\n</phpunit>\n"
  },
  {
    "path": "routes/aimeos.php",
    "content": "<?php\n\nif( ( $conf = config( 'shop.routes.admin', ['prefix' => 'admin', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET' ), '', array(\n\t\t\t'as' => 'aimeos_shop_admin',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\AdminController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.jqadm', ['prefix' => 'admin/{site}/jqadm', 'middleware' => ['web', 'auth']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET' ), 'file/{name}/{locale}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_file',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@fileAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'POST' ), 'batch/{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_batch',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@batchAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'copy/{resource}/{id}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_copy',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@copyAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'create/{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_create',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@createAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'POST' ), 'delete/{resource}/{id?}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_delete',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@deleteAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'export/{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_export',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@exportAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'GET' ), 'get/{resource}/{id}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_get',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@getAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'POST' ), 'import/{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_import',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@importAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'POST' ), 'save/{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_save',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@saveAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'search/{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jqadm_search',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JqadmController@searchAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.graphql', ['prefix' => 'admin/{site}/graphql', 'middleware' => ['web', 'auth']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'POST' ), '', array(\n\t\t\t'as' => 'aimeos_shop_graphql_post',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\GraphqlController@indexAction'\n\t\t) )->where( ['site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.jsonadm', ['prefix' => 'admin/{site}/jsonadm', 'middleware' => ['web', 'auth']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'DELETE' ), '{resource}/{id?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonadm_delete',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonadmController@deleteAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'GET' ), '{resource}/{id?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonadm_get',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonadmController@getAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'PATCH' ), '{resource}/{id?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonadm_patch',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonadmController@patchAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'POST' ), '{resource}/{id?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonadm_post',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonadmController@postAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'PUT' ), '{resource}/{id?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonadm_put',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonadmController@putAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t\tRoute::match( array( 'OPTIONS' ), '{resource?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonadm_options',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonadmController@optionsAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'resource' => '[a-z\\/]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.jsonapi', ['prefix' => 'jsonapi', 'middleware' => ['web', 'api']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'DELETE' ), '{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jsonapi_delete',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonapiController@deleteAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET' ), '{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jsonapi_get',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonapiController@getAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'PATCH' ), '{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jsonapi_patch',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonapiController@patchAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'POST' ), '{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jsonapi_post',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonapiController@postAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'PUT' ), '{resource}', array(\n\t\t\t'as' => 'aimeos_shop_jsonapi_put',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonapiController@putAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'OPTIONS' ), '{resource?}', array(\n\t\t\t'as' => 'aimeos_shop_jsonapi_options',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\JsonapiController@optionsAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.account', ['prefix' => 'profile', 'middleware' => ['web', 'auth']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'favorite/{fav_action?}/{fav_id?}/{d_name?}/{d_pos?}', array(\n\t\t\t'as' => 'aimeos_shop_account_favorite',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\AccountController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'watch/{wat_action?}/{wat_id?}/{d_name?}/{d_pos?}', array(\n\t\t\t'as' => 'aimeos_shop_account_watch',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\AccountController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'download/{dl_id}', array(\n\t\t\t'as' => 'aimeos_shop_account_download',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\AccountController@downloadAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), '', array(\n\t\t\t'as' => 'aimeos_shop_account',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\AccountController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.supplier', ['prefix' => 'brand', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), '{s_name}/{f_supid}', array(\n\t\t\t'as' => 'aimeos_shop_supplier',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\SupplierController@detailAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t} );\n}\n\n\nif( ( $conf = config( 'shop.routes.update', [] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'update', array(\n\t\t\t'as' => 'aimeos_shop_update',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CheckoutController@updateAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.confirm', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'confirm/{code?}', array(\n\t\t\t'as' => 'aimeos_shop_confirm',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CheckoutController@confirmAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.checkout', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'checkout/{c_step?}', array(\n\t\t\t'as' => 'aimeos_shop_checkout',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CheckoutController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.basket', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'basket', array(\n\t\t\t'as' => 'aimeos_shop_basket',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\BasketController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.default', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'count', array(\n\t\t\t'as' => 'aimeos_shop_count',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@countAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'suggest', array(\n\t\t\t'as' => 'aimeos_shop_suggest',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@suggestAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'stock', array(\n\t\t\t'as' => 'aimeos_shop_stock',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@stockAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'pin', array(\n\t\t\t'as' => 'aimeos_shop_session_pinned',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@sessionAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), 'search', array(\n\t\t\t'as' => 'aimeos_shop_list',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@listAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), '{f_name}~{f_catid}/{l_page?}', array(\n\t\t\t'as' => 'aimeos_shop_tree',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@treeAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'f_name' => '[^~]*', 'l_page' => '[0-9]+'] );\n\n\t\tRoute::match( array( 'GET', 'POST' ), '{d_name}/{d_pos?}/{d_prodid?}', array(\n\t\t\t'as' => 'aimeos_shop_detail',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@detailAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'd_pos' => '[0-9]*'] );\n\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.page', ['prefix' => 'p', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match(['GET', 'POST'], '{path?}', [\n\t\t\t'as' => 'aimeos_page',\n\t\t\t'uses' => '\\Aimeos\\Shop\\Controller\\PageController@indexAction'\n\t\t] )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\t});\n}\n\n\nif( ( $conf = config( 'shop.routes.home', ['middleware' => ['web']] ) ) !== false ) {\n\n\tRoute::group( $conf, function() {\n\n\t\tRoute::match( array( 'GET', 'POST' ), '/', array(\n\t\t\t'as' => 'aimeos_home',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\CatalogController@homeAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+'] );\n\n\t});\n}\n"
  },
  {
    "path": "src/Base/Aimeos.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\n/**\n * Service providing the Aimeos object\n */\nclass Aimeos\n{\n\t/**\n\t * @var \\Illuminate\\Contracts\\Config\\Repository\n\t */\n\tprivate $config;\n\n\t/**\n\t * @var \\Aimeos\\Bootstrap\n\t */\n\tprivate $object;\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Illuminate\\Contracts\\Config\\Repository $config Configuration object\n\t */\n\tpublic function __construct( \\Illuminate\\Contracts\\Config\\Repository $config )\n\t{\n\t\t$this->config = $config;\n\t}\n\n\n\t/**\n\t * Returns the Aimeos object.\n\t *\n\t * @return \\Aimeos\\Bootstrap Aimeos bootstrap object\n\t */\n\tpublic function get() : \\Aimeos\\Bootstrap\n\t{\n\t\tif( $this->object === null )\n\t\t{\n\t\t\t$dir = base_path( 'ext' );\n\n\t\t\tif( !is_dir( $dir ) ) {\n\t\t\t\t$dir = dirname( __DIR__, 4 ) . DIRECTORY_SEPARATOR . 'ext';\n\t\t\t}\n\n\t\t\t$extDirs = (array) $this->config->get( 'shop.extdir', $dir );\n\t\t\t$this->object = new \\Aimeos\\Bootstrap( $extDirs, false );\n\t\t}\n\n\t\treturn $this->object;\n\t}\n\n\n\t/**\n\t * Returns the version of the Aimeos package\n\t *\n\t * @return string Version string\n\t */\n\tpublic function getVersion() : string\n\t{\n\t\tif( ( $content = @file_get_contents( base_path( 'composer.lock' ) ) ) !== false\n\t\t\t&& ( $content = json_decode( $content, true ) ) !== null && isset( $content['packages'] )\n\t\t) {\n\t\t\tforeach( (array) $content['packages'] as $item )\n\t\t\t{\n\t\t\t\tif( $item['name'] === 'aimeos/aimeos-laravel' ) {\n\t\t\t\t\treturn $item['version'];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn '';\n\t}\n}"
  },
  {
    "path": "src/Base/Config.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\n/**\n * Service providing the config object\n */\nclass Config\n{\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Config[]\n\t */\n\tprivate $objects = [];\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Aimeos\n\t */\n\tprivate $aimeos;\n\n\t/**\n\t * @var \\Illuminate\\Contracts\\Config\\Repository\n\t */\n\tprivate $config;\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Illuminate\\Contracts\\Config\\Repository $config Configuration object\n\t * @param \\Aimeos\\Shop\\Base\\Aimeos $aimeos Aimeos object\n\t */\n\tpublic function __construct( \\Illuminate\\Contracts\\Config\\Repository $config, \\Aimeos\\Shop\\Base\\Aimeos $aimeos )\n\t{\n\t\t$this->aimeos = $aimeos;\n\t\t$this->config = $config;\n\t}\n\n\n\t/**\n\t * Creates a new configuration object.\n\t *\n\t * @param string $type Configuration type (\"frontend\" or \"backend\")\n\t * @return \\Aimeos\\Base\\Config\\Iface Configuration object\n\t */\n\tpublic function get( string $type = 'frontend' ) : \\Aimeos\\Base\\Config\\Iface\n\t{\n\t\tif( !isset( $this->objects[$type] ) )\n\t\t{\n\t\t\t$configPaths = $this->aimeos->get()->getConfigPaths();\n\t\t\t$cfgfile = dirname( dirname( __DIR__ ) ) . '/config/default.php';\n\n\t\t\t$config = new \\Aimeos\\Base\\Config\\PHPArray( require $cfgfile, $configPaths );\n\n\t\t\tif( $this->config->get( 'shop.apc_enabled', false ) == true ) {\n\t\t\t\t$config = new \\Aimeos\\Base\\Config\\Decorator\\APC( $config, $this->config->get( 'shop.apc_prefix', 'laravel:' ) );\n\t\t\t}\n\n\t\t\t$config = new \\Aimeos\\Base\\Config\\Decorator\\Memory( $config, $this->config->get( 'shop' ) );\n\n\t\t\tif( ( $conf = $this->config->get( 'shop.' . $type, [] ) ) !== [] ) {\n\t\t\t\t$config = new \\Aimeos\\Base\\Config\\Decorator\\Memory( $config, $conf );\n\t\t\t}\n\n\t\t\t$this->objects[$type] = $config;\n\t\t}\n\n\t\treturn $this->objects[$type];\n\t}\n}\n"
  },
  {
    "path": "src/Base/Context.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Session;\n\n\n/**\n * Service providing the context objects\n */\nclass Context\n{\n\t/**\n\t * @var \\Aimeos\\MShop\\ContextIface\n\t */\n\tprivate $context;\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Config\n\t */\n\tprivate $config;\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\I18n\n\t */\n\tprivate $i18n;\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Locale\n\t */\n\tprivate $locale;\n\n\t/**\n\t * @var \\Illuminate\\Session\\Store\n\t */\n\tprivate $session;\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Illuminate\\Session\\Store $session Laravel session object\n\t * @param \\Aimeos\\Shop\\Base\\Config $config Configuration object\n\t * @param \\Aimeos\\Shop\\Base\\Locale $locale Locale object\n\t * @param \\Aimeos\\Shop\\Base\\I18n $i18n Internationalisation object\n\t */\n\tpublic function __construct( \\Illuminate\\Session\\Store $session, \\Aimeos\\Shop\\Base\\Config $config, \\Aimeos\\Shop\\Base\\Locale $locale, \\Aimeos\\Shop\\Base\\I18n $i18n )\n\t{\n\t\t$this->session = $session;\n\t\t$this->config = $config;\n\t\t$this->locale = $locale;\n\t\t$this->i18n = $i18n;\n\t}\n\n\n\t/**\n\t * Returns the current context\n\t *\n\t * @param bool $locale True to add locale object to context, false if not (deprecated, use \\Aimeos\\Shop\\Base\\Locale)\n\t * @param string $type Configuration type, i.e. \"frontend\" or \"backend\" (deprecated, use \\Aimeos\\Shop\\Base\\Config)\n\t * @return \\Aimeos\\MShop\\ContextIface Context object\n\t */\n\tpublic function get( bool $locale = true, string $type = 'frontend' ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$config = $this->config->get( $type );\n\n\t\tif( $this->context === null )\n\t\t{\n\t\t\t$context = new \\Aimeos\\MShop\\Context();\n\t\t\t$context->setConfig( $config );\n\n\t\t\t$this->addDataBaseManager( $context );\n\t\t\t$this->addFilesystemManager( $context );\n\t\t\t$this->addMessageQueueManager( $context );\n\t\t\t$this->addLogger( $context );\n\t\t\t$this->addCache( $context );\n\t\t\t$this->addMailer( $context );\n\t\t\t$this->addNonce( $context );\n\t\t\t$this->addPassword( $context );\n\t\t\t$this->addProcess( $context );\n\t\t\t$this->addSession( $context );\n\t\t\t$this->addToken( $context );\n\t\t\t$this->addUserGroups( $context );\n\n\t\t\t$this->context = $context;\n\t\t}\n\n\t\t$this->context->setConfig( $config );\n\n\t\tif( $locale === true )\n\t\t{\n\t\t\t$localeItem = $this->locale->get( $this->context );\n\t\t\t$this->context->setLocale( $localeItem );\n\t\t\t$this->context->setI18n( $this->i18n->get( array( $localeItem->getLanguageId() ) ) );\n\n\t\t\t$config->apply( $localeItem->getSiteItem()->getConfig() );\n\t\t}\n\n\t\treturn $this->context;\n\t}\n\n\n\t/**\n\t * Adds the cache object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object including config\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addCache( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$cache = \\Aimeos\\MAdmin::create( $context, 'cache' )->getCache();\n\n\t\treturn $context->setCache( $cache );\n\t}\n\n\n\t/**\n\t * Adds the database manager object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addDatabaseManager( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$dbm = new \\Aimeos\\Base\\DB\\Manager\\Standard( $context->config()->get( 'resource' ), 'DBAL' );\n\n\t\treturn $context->setDatabaseManager( $dbm );\n\t}\n\n\n\t/**\n\t * Adds the filesystem manager object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addFilesystemManager( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$config = $context->config()->get( 'resource' );\n\t\t$fs = new \\Aimeos\\Base\\Filesystem\\Manager\\Laravel( app( 'filesystem' ), $config, storage_path( 'aimeos' ) );\n\n\t\treturn $context->setFilesystemManager( $fs );\n\t}\n\n\n\t/**\n\t * Adds the logger object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addLogger( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$logger = \\Aimeos\\MAdmin::create( $context, 'log' );\n\n\t\treturn $context->setLogger( $logger );\n\t}\n\n\n\n\t/**\n\t * Adds the mailer object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addMailer( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$mail = new \\Aimeos\\Base\\Mail\\Manager\\Laravel( app( 'mail.manager' ) );\n\n\t\treturn $context->setMail( $mail );\n\t}\n\n\n\t/**\n\t * Adds the message queue manager object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addMessageQueueManager( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$mq = new \\Aimeos\\Base\\MQueue\\Manager\\Standard( $context->config()->get( 'resource' ) );\n\n\t\treturn $context->setMessageQueueManager( $mq );\n\t}\n\n\n\t/**\n\t * Adds the nonce value for inline JS to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addNonce( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\treturn $context->setNonce( base64_encode( random_bytes( 16 ) ) );\n\t}\n\n\n\t/**\n\t * Adds the password hasher object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addPassword( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\treturn $context->setPassword( new \\Aimeos\\Base\\Password\\Standard() );\n\t}\n\n\n\t/**\n\t * Adds the process object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addProcess( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$config = $context->config();\n\t\t$max = $config->get( 'pcntl_max', 4 );\n\t\t$prio = $config->get( 'pcntl_priority', 19 );\n\n\t\t$process = new \\Aimeos\\Base\\Process\\Pcntl( $max, $prio );\n\t\t$process = new \\Aimeos\\Base\\Process\\Decorator\\Check( $process );\n\n\t\treturn $context->setProcess( $process );\n\t}\n\n\n\t/**\n\t * Adds the session object to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addSession( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$session = new \\Aimeos\\Base\\Session\\Laravel( $this->session );\n\n\t\treturn $context->setSession( $session );\n\t}\n\n\n\t/**\n\t * Adds the session token to the context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addToken( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\tif( ( $token = Session::get( 'token' ) ) === null ) {\n\t\t\tSession::put( 'token', $token = Session::getId() );\n\t\t}\n\n\t\treturn $context->setToken( $token );\n\t}\n\n\n\t/**\n\t * Adds the user and groups if available\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\ContextIface Modified context object\n\t */\n\tprotected function addUserGroups( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$key = collect( config( 'shop.routes' ) )\n\t\t\t->where( 'prefix', optional( Route::getCurrentRoute() )->getPrefix() )\n\t\t\t->keys()->first();\n\t\t$gname = data_get( config( 'shop.guards' ), $key, Auth::getDefaultDriver() );\n\n\t\tif( ( $guard = Auth::guard( $gname ) ) && ( $userid = $guard->id() ) )\n\t\t{\n\t\t\t$context->setUser( function() use ( $context, $userid ) {\n\t\t\t\ttry {\n\t\t\t\t\treturn \\Aimeos\\MShop::create( $context, 'customer' )->get( $userid, ['group'] );\n\t\t\t\t} catch( \\Aimeos\\MShop\\Exception $e ) { // avoid errors if user is assigned to another site\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t$context->setGroups( function() use ( $context ) {\n\t\t\t\treturn $context->user()?->getGroups() ?? [];\n\t\t\t} );\n\n\t\t\t$context->setEditor( $guard->user()?->email ?: \\Request::ip() );\n\t\t}\n\t\telseif( $ip = \\Request::ip() )\n\t\t{\n\t\t\t$context->setEditor( $ip );\n\t\t}\n\n\t\treturn $context;\n\t}\n}\n"
  },
  {
    "path": "src/Base/I18n.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\n/**\n * Service providing the internationalization objects\n */\nclass I18n\n{\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Aimeos\n\t */\n\tprivate $aimeos;\n\n\t/**\n\t * @var \\Illuminate\\Contracts\\Config\\Repository\n\t */\n\tprivate $config;\n\n\t/**\n\t * @var array\n\t */\n\tprivate $i18n = [];\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Illuminate\\Contracts\\Config\\Repository $config Configuration object\n\t * @param \\Aimeos\\Shop\\Base\\Aimeos $aimeos Aimeos object\n\t */\n\tpublic function __construct( \\Illuminate\\Contracts\\Config\\Repository $config, \\Aimeos\\Shop\\Base\\Aimeos $aimeos )\n\t{\n\t\t$this->aimeos = $aimeos;\n\t\t$this->config = $config;\n\t}\n\n\n\t/**\n\t * Creates new translation objects.\n\t *\n\t * @param array $languageIds List of two letter ISO language IDs\n\t * @return \\Aimeos\\Base\\Translation\\Iface[] List of translation objects\n\t */\n\tpublic function get( array $languageIds ) : array\n\t{\n\t\t$i18nPaths = $this->aimeos->get()->getI18nPaths();\n\n\t\tforeach( $languageIds as $langid )\n\t\t{\n\t\t\tif( !isset( $this->i18n[$langid] ) )\n\t\t\t{\n\t\t\t\t$i18n = new \\Aimeos\\Base\\Translation\\Gettext( $i18nPaths, $langid );\n\n\t\t\t\tif( $this->config->get( 'shop.apc_enabled', false ) == true ) {\n\t\t\t\t\t$i18n = new \\Aimeos\\Base\\Translation\\Decorator\\APC( $i18n, $this->config->get( 'shop.apc_prefix', 'laravel:' ) );\n\t\t\t\t}\n\n\t\t\t\tif( $this->config->has( 'shop.i18n.' . $langid ) ) {\n\t\t\t\t\t$i18n = new \\Aimeos\\Base\\Translation\\Decorator\\Memory( $i18n, $this->config->get( 'shop.i18n.' . $langid ) );\n\t\t\t\t}\n\n\t\t\t\t$this->i18n[$langid] = $i18n;\n\t\t\t}\n\t\t}\n\n\t\treturn $this->i18n;\n\t}\n}"
  },
  {
    "path": "src/Base/Locale.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Support\\Facades\\Route;\n\n\n/**\n * Service providing the context objects\n */\nclass Locale\n{\n\t/**\n\t * @var \\Illuminate\\Contracts\\Config\\Repository\n\t */\n\tprivate $config;\n\n\t/**\n\t * @var \\Aimeos\\MShop\\Locale\\Item\\Iface\n\t */\n\tprivate $locale;\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Illuminate\\Contracts\\Config\\Repository $config Configuration object\n\t */\n\tpublic function __construct( \\Illuminate\\Contracts\\Config\\Repository $config )\n\t{\n\t\t$this->config = $config;\n\t}\n\n\n\t/**\n\t * Returns the locale item for the current request\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\MShop\\Locale\\Item\\Iface Locale item object\n\t */\n\tpublic function get( \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\MShop\\Locale\\Item\\Iface\n\t{\n\t\tif( $this->locale === null )\n\t\t{\n\t\t\t$site = config( 'shop.mshop.locale.site', 'default' );\n\t\t\t$lang = app()->getLocale();\n\t\t\t$currency = '';\n\n\t\t\tif( Route::current() )\n\t\t\t{\n\t\t\t\t$site = Request::route( 'site', $site );\n\t\t\t\t$lang = Request::route( 'locale', $lang );\n\t\t\t\t$currency = Request::route( 'currency', $currency );\n\t\t\t}\n\n\t\t\t$site = Request::input( 'site', $site );\n\t\t\t$lang = Request::input( 'locale', $lang );\n\t\t\t$currency = Request::input( 'currency', $currency );\n\n\t\t\t$localeManager = \\Aimeos\\MShop::create( $context, 'locale' );\n\t\t\t$disableSites = $this->config->get( 'shop.disableSites', true );\n\n\t\t\t$this->locale = $localeManager->bootstrap( $site, $lang, $currency, $disableSites );\n\t\t}\n\n\t\treturn $this->locale;\n\t}\n\n\n\t/**\n\t * Returns the locale item for the current request\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @param string $site Unique site code\n\t * @return \\Aimeos\\MShop\\Locale\\Item\\Iface Locale item object\n\t */\n\tpublic function getBackend( \\Aimeos\\MShop\\ContextIface $context, string $site ) : \\Aimeos\\MShop\\Locale\\Item\\Iface\n\t{\n\t\t$localeManager = \\Aimeos\\MShop::create( $context, 'locale' );\n\n\t\ttry {\n\t\t\t$localeItem = $localeManager->bootstrap( $site, '', '', false, null, true );\n\t\t} catch( \\Aimeos\\MShop\\Exception $e ) {\n\t\t\t$localeItem = $localeManager->create();\n\t\t}\n\n\t\treturn $localeItem->setCurrencyId( null )->setLanguageId( null );\n\t}\n}\n"
  },
  {
    "path": "src/Base/Shop.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\n/**\n * Service providing the shop object\n */\nclass Shop\n{\n\t/**\n\t * @var \\Aimeos\\MShop\\ContextIface\n\t */\n\tprivate $context;\n\n\t/**\n\t * @var \\Aimeos\\Base\\View\\Iface\n\t */\n\tprivate $view;\n\n\t/**\n\t * @var array\n\t */\n\tprivate $objects = [];\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Aimeos\\Shop\\Base\\Aimeos $aimeos Aimeos object\n\t * @param \\Aimeos\\Shop\\Base\\Context $context Context object\n\t * @param \\Aimeos\\Shop\\Base\\View $view View object\n\t */\n\tpublic function __construct( \\Aimeos\\Shop\\Base\\Aimeos $aimeos,\n\t\t\\Aimeos\\Shop\\Base\\Context $context, \\Aimeos\\Shop\\Base\\View $view )\n\t{\n\t\t$this->context = $context->get();\n\t\t$locale = $this->context->locale();\n\n\t\t$tmplPaths = $aimeos->get()->getTemplatePaths( 'client/html/templates', $locale->getSiteItem()->getTheme() );\n\t\t$langid = $locale->getLanguageId();\n\n\t\t$this->view = $view->create( $this->context, $tmplPaths, $langid );\n\t\t$this->context->setView( $this->view );\n\t}\n\n\n\t/**\n\t * Returns the HTML client for the given name\n\t *\n\t * @param string $name Name of the shop component\n\t * @return \\Aimeos\\Client\\Html\\Iface HTML client\n\t */\n\tpublic function get( string $name ) : \\Aimeos\\Client\\Html\\Iface\n\t{\n\t\tif( !isset( $this->objects[$name] ) )\n\t\t{\n\t\t\t$client = \\Aimeos\\Client\\Html::create( $this->context, $name );\n\t\t\t$client->setView( clone $this->view );\n\t\t\t$client->init();\n\n\t\t\t$this->objects[$name] = $client;\n\t\t}\n\n\t\treturn $this->objects[$name];\n\t}\n\n\n\t/** Returns the view template for the given name\n\t *\n\t * @param string $name View name, e.g. \"account.index\"\n\t * @return string Template name, e.g. \"shop::account.indx\"\n\t */\n\tpublic function template( string $name ) : string\n\t{\n\t\t$theme = $this->context->locale()->getSiteItem()->getTheme();\n\t\treturn \\Illuminate\\Support\\Facades\\View::exists( $theme . '::' . $name ) ? $theme . '::' . $name : 'shop::' . $name;\n\t}\n\n\n\t/**\n\t * Returns the used view object\n\t *\n\t * @return \\Aimeos\\Base\\View\\Iface View object\n\t */\n\tpublic function view() : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\treturn $this->view;\n\t}\n}\n"
  },
  {
    "path": "src/Base/Support.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Support\\Facades\\Route;\n\n\n/**\n * Service providing the supporting functionality\n */\nclass Support\n{\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Context\n\t */\n\tprivate $context;\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Locale\n\t */\n\tprivate $locale;\n\n\t/**\n\t * @var array\n\t */\n\tprivate $access = [];\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Aimeos\\Shop\\Base\\Context $context Context provider\n\t * @param \\Aimeos\\Shop\\Base\\Locale $locale Locale provider\n\t */\n\tpublic function __construct( \\Aimeos\\Shop\\Base\\Context $context, \\Aimeos\\Shop\\Base\\Locale $locale )\n\t{\n\t\t$this->context = $context;\n\t\t$this->locale = $locale;\n\t}\n\n\n\t/**\n\t * Checks if the user is in the specified group and associatied to the site\n\t *\n\t * @param \\Illuminate\\Foundation\\Auth\\User $user Authenticated user\n\t * @param string|array $groupcodes Unique user/customer group codes that are allowed\n\t * @return bool True if user is part of the group, false if not\n\t */\n\tpublic function checkUserGroup( \\Illuminate\\Foundation\\Auth\\User $user, $groupcodes ) : bool\n\t{\n\t\t$groups = ( is_array( $groupcodes ) ? implode( ',', $groupcodes ) : $groupcodes );\n\n\t\tif( isset( $this->access[$user->id][$groups] ) ) {\n\t\t\treturn $this->access[$user->id][$groups];\n\t\t}\n\n\t\t$this->access[$user->id][$groups] = false;\n\n\t\t$context = $this->context->get( false );\n\t\t$siteid = current( array_reverse( explode( '.', trim( $user->siteid, '.' ) ) ) );\n\n\t\tif( $siteid ) {\n\t\t\t$site = \\Aimeos\\MShop::create( $context, 'locale/site' )->get( $siteid )->getCode();\n\t\t} else {\n\t\t\t$site = config( 'shop.mshop.locale.site', 'default' );\n\t\t}\n\n\t\t$site = ( Route::current() ? Route::input( 'site', Request::get( 'site', $site ) ) : $site );\n\t\t$context->setLocale( $this->locale->getBackend( $context, $site ) );\n\n\t\tforeach( array_reverse( $context->locale()->getSitePath() ) as $siteid )\n\t\t{\n\t\t\tif( $user->siteid === '' || $user->siteid === $siteid ) {\n\t\t\t\t$this->access[$user->id][$groups] = $this->checkGroups( $context, $user->id, $groupcodes );\n\t\t\t}\n\t\t}\n\n\t\treturn $this->access[$user->id][$groups];\n\t}\n\n\n\t/**\n\t * Checks if one of the groups is associated to the given user ID\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context item\n\t * @param string $userid ID of the logged in user\n\t * @param string[]|string $groupcodes List of group codes to check against\n\t * @return bool True if the user is in one of the groups, false if not\n\t */\n\tprotected function checkGroups( \\Aimeos\\MShop\\ContextIface $context, string $userid, $groupcodes ) : bool\n\t{\n\t\t$manager = \\Aimeos\\MShop::create( $context, 'group' );\n\n\t\t$search = $manager->filter();\n\t\t$search->setConditions( $search->compare( '==', 'group.code', (array) $groupcodes ) );\n\t\t$groupIds = $manager->search( $search )->keys()->toArray();\n\n\t\t$manager = \\Aimeos\\MShop::create( $context, 'customer/lists' );\n\n\t\t$search = $manager->filter()->slice( 0, 1 );\n\t\t$expr = array(\n\t\t\t$search->compare( '==', 'customer.lists.parentid', $userid ),\n\t\t\t$search->compare( '==', 'customer.lists.refid', $groupIds ),\n\t\t\t$search->compare( '==', 'customer.lists.domain', 'group' ),\n\t\t);\n\t\t$search->setConditions( $search->combine( '&&', $expr ) );\n\n\t\treturn !$manager->search( $search )->isEmpty();\n\t}\n}\n"
  },
  {
    "path": "src/Base/View.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespace Aimeos\\Shop\\Base;\n\n\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Service providing the view objects\n */\nclass View\n{\n\t/**\n\t * @var \\Illuminate\\Contracts\\Config\\Repository\n\t */\n\tprivate $config;\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\I18n\n\t */\n\tprivate $i18n;\n\n\t/**\n\t * @var \\Aimeos\\Shop\\Base\\Support\n\t */\n\tprivate $support;\n\n\n\t/**\n\t * Initializes the object\n\t *\n\t * @param \\Illuminate\\Contracts\\Config\\Repository $config Configuration object\n\t * @param \\Aimeos\\Shop\\Base\\I18n $i18n I18n object\n\t * @param \\Aimeos\\Shop\\Base\\Support $support Support object\n\t */\n\tpublic function __construct( \\Illuminate\\Contracts\\Config\\Repository $config,\n\t\t\\Aimeos\\Shop\\Base\\I18n $i18n, \\Aimeos\\Shop\\Base\\Support $support )\n\t{\n\t\t$this->i18n = $i18n;\n\t\t$this->config = $config;\n\t\t$this->support = $support;\n\t}\n\n\n\t/**\n\t * Creates the view object for the HTML client.\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @param array $templatePaths List of base path names with relative template paths as key/value pairs\n\t * @param string|null $locale Code of the current language or null for no translation\n\t * @return \\Aimeos\\Base\\View\\Iface View object\n\t */\n\tpublic function create( \\Aimeos\\MShop\\ContextIface $context, array $templatePaths,\n\t\tstring $locale = null ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$engine = new \\Aimeos\\Base\\View\\Engine\\Blade( app( 'Illuminate\\Contracts\\View\\Factory' ) );\n\t\t$view = new \\Aimeos\\Base\\View\\Standard( $templatePaths, array( '.blade.php' => $engine ) );\n\n\t\t$config = $context->config();\n\t\t$session = $context->session();\n\n\t\t$this->addCsrf( $view );\n\t\t$this->addAccess( $view, $context );\n\t\t$this->addConfig( $view, $config );\n\t\t$this->addNumber( $view, $config, $locale );\n\t\t$this->addParam( $view );\n\t\t$this->addRequest( $view );\n\t\t$this->addResponse( $view );\n\t\t$this->addSession( $view, $session );\n\t\t$this->addTranslate( $view, $locale );\n\t\t$this->addUrl( $view );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"access\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addAccess( \\Aimeos\\Base\\View\\Iface $view, \\Aimeos\\MShop\\ContextIface $context ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\tif( $this->config->get( 'shop.accessControl', true ) === false\n\t\t\t|| ( ( $user = \\Illuminate\\Support\\Facades\\Auth::user() ) !== null && $user->superuser )\n\t\t) {\n\t\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Access\\All( $view );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Access\\Standard( $view, function() use ( $context ) {\n\t\t\t\t$manager = \\Aimeos\\MShop::create( $context, 'group' );\n\t\t\t\t$filter = $manager->filter( true )->add( 'group.id', '==', $context->groups() );\n\t\t\t\treturn $manager->search( $filter )->col( 'group.code' )->all();\n\t\t\t} );\n\t\t}\n\n\t\t$view->addHelper( 'access', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"config\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @param \\Aimeos\\Base\\Config\\Iface $config Configuration object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addConfig( \\Aimeos\\Base\\View\\Iface $view, \\Aimeos\\Base\\Config\\Iface $config ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$config = new \\Aimeos\\Base\\Config\\Decorator\\Protect( clone $config, ['resource/*/baseurl'], ['resource'] );\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Config\\Standard( $view, $config );\n\t\t$view->addHelper( 'config', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"access\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addCsrf( \\Aimeos\\Base\\View\\Iface $view ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Csrf\\Standard( $view, '_token', csrf_token() );\n\t\t$view->addHelper( 'csrf', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"number\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @param \\Aimeos\\Base\\Config\\Iface $config Configuration object\n\t * @param string|null $locale Code of the current language or null for no translation\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addNumber( \\Aimeos\\Base\\View\\Iface $view, \\Aimeos\\Base\\Config\\Iface $config,\n\t\tstring $locale = null ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\tif( config( 'shop.num_formatter', 'Locale' ) === 'Locale' )\n\t\t{\n\t\t\t$pattern = $config->get( 'client/html/common/format/pattern' );\n\t\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Number\\Locale( $view, $locale, $pattern );\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$sep1000 = $config->get( 'client/html/common/format/separator1000', '' );\n\t\t\t$decsep = $config->get( 'client/html/common/format/separatorDecimal', '.' );\n\t\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Number\\Standard( $view, $decsep, $sep1000 );\n\t\t}\n\n\t\treturn $view->addHelper( 'number', $helper );\n\t}\n\n\n\t/**\n\t * Adds the \"param\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addParam( \\Aimeos\\Base\\View\\Iface $view ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$params = ( Route::current() ? Route::current()->parameters() : array() ) + Request::all();\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Param\\Standard( $view, $params );\n\t\t$view->addHelper( 'param', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"request\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addRequest( \\Aimeos\\Base\\View\\Iface $view ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Request\\Laravel( $view, Request::instance() );\n\t\t$view->addHelper( 'request', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"response\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addResponse( \\Aimeos\\Base\\View\\Iface $view ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Response\\Laravel( $view );\n\t\t$view->addHelper( 'response', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"session\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @param \\Aimeos\\Base\\Session\\Iface $session Session object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addSession( \\Aimeos\\Base\\View\\Iface $view, \\Aimeos\\Base\\Session\\Iface $session ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Session\\Standard( $view, $session );\n\t\t$view->addHelper( 'session', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"translate\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @param string|null $locale ISO language code, e.g. \"de\" or \"de_CH\"\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addTranslate( \\Aimeos\\Base\\View\\Iface $view, ?string $locale = null ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\tif( $locale !== null )\n\t\t{\n\t\t\t$i18n = $this->i18n->get( array( $locale ) );\n\t\t\t$translation = $i18n[$locale];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$translation = new \\Aimeos\\Base\\Translation\\None( 'en' );\n\t\t}\n\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Translate\\Standard( $view, $translation );\n\t\t$view->addHelper( 'translate', $helper );\n\n\t\treturn $view;\n\t}\n\n\n\t/**\n\t * Adds the \"url\" helper to the view object\n\t *\n\t * @param \\Aimeos\\Base\\View\\Iface $view View object\n\t * @return \\Aimeos\\Base\\View\\Iface Modified view object\n\t */\n\tprotected function addUrl( \\Aimeos\\Base\\View\\Iface $view ) : \\Aimeos\\Base\\View\\Iface\n\t{\n\t\t$fixed = [\n\t\t\t'site' => env( 'SHOP_MULTISHOP' ) ? config( 'shop.mshop.locale.site', 'default' ) : '',\n\t\t\t'locale' => env( 'SHOP_MULTILOCALE' ) ? app()->getLocale() : '',\n\t\t\t'currency' => ''\n\t\t];\n\n\t\tif( Route::current() )\n\t\t{\n\t\t\t$fixed['site'] = Request::route( 'site', $fixed['site'] );\n\t\t\t$fixed['locale'] = Request::route( 'locale', $fixed['locale'] );\n\t\t\t$fixed['currency'] = Request::route( 'currency', $fixed['currency'] );\n\t\t}\n\n\t\t$fixed['site'] = Request::input( 'site', $fixed['site'] );\n\t\t$fixed['locale'] = Request::input( 'locale', $fixed['locale'] );\n\t\t$fixed['currency'] = Request::input( 'currency', $fixed['currency'] );\n\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Url\\Laravel( $view, app( 'url' ), array_filter( $fixed ) );\n\t\t$view->addHelper( 'url', $helper );\n\n\t\treturn $view;\n\t}\n}"
  },
  {
    "path": "src/Command/AbstractCommand.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Command;\n\nuse Illuminate\\Console\\Command;\n\n\n/**\n * Common base class for all commands\n */\nabstract class AbstractCommand extends Command\n{\n\t/**\n\t * Adds the configuration options from the input object to the given context\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $ctx Context object\n\t */\n\tprotected function addConfig( \\Aimeos\\MShop\\ContextIface $ctx ) : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$config = $ctx->config();\n\n\t\tforeach( (array) $this->option( 'option' ) as $option )\n\t\t{\n\t\t\tlist( $name, $value ) = explode( ':', $option );\n\t\t\t$config->set( $name, $value );\n\t\t}\n\n\t\treturn $ctx;\n\t}\n\n\n\t/**\n\t * Executes the function for all given sites\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @param \\Closure $fcn Function to execute\n\t * @param array|string|null $sites Site codes\n\t */\n\tprotected function exec( \\Aimeos\\MShop\\ContextIface $context, \\Closure $fcn, $sites )\n\t{\n\t\t$process = $context->process();\n\t\t$aimeos = $this->getLaravel()->make( 'aimeos' )->get();\n\n\t\t$siteManager = \\Aimeos\\MShop::create( $context, 'locale/site' );\n\t\t$localeManager = \\Aimeos\\MShop::create( $context, 'locale' );\n\t\t$filter = $siteManager->filter();\n\t\t$start = 0;\n\n\t\tif( !empty( $sites ) ) {\n\t\t\t$filter->add( ['locale.site.code' => !is_array( $sites ) ? explode( ' ', (string) $sites ) : $sites] );\n\t\t}\n\n\t\tdo\n\t\t{\n\t\t\t$siteItems = $siteManager->search( $filter->slice( $start ) );\n\n\t\t\tforeach( $siteItems as $siteItem )\n\t\t\t{\n\t\t\t\t\\Aimeos\\MShop::cache( true );\n\t\t\t\t\\Aimeos\\MAdmin::cache( true );\n\n\t\t\t\t$localeItem = $localeManager->bootstrap( $siteItem->getCode(), '', '', false );\n\t\t\t\t$localeItem->setLanguageId( null );\n\t\t\t\t$localeItem->setCurrencyId( null );\n\n\t\t\t\t$lcontext = clone $context;\n\t\t\t\t$lcontext->setLocale( $localeItem );\n\n\t\t\t\t$tmplPaths = $aimeos->getTemplatePaths( 'controller/jobs/templates', $siteItem->getTheme() );\n\t\t\t\t$view = $this->getLaravel()->make( 'aimeos.view' )->create( $lcontext, $tmplPaths );\n\t\t\t\t$lcontext->setView( $view );\n\n\t\t\t\t$config = $lcontext->config();\n\t\t\t\t$config->apply( $siteItem->getConfig() );\n\n\t\t\t\t$process->start( $fcn, [$lcontext, $aimeos], false );\n\t\t\t}\n\n\t\t\t$count = count( $siteItems );\n\t\t\t$start += $count;\n\t\t}\n\t\twhile( $count === $filter->getLimit() );\n\n\t\t$process->wait();\n\t}\n}"
  },
  {
    "path": "src/Command/AccountCommand.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Command;\n\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\n\n\n/**\n * Creates new accounts or resets their passwords\n */\nclass AccountCommand extends AbstractCommand\n{\n\t/**\n\t * The name and signature of the console command.\n\t *\n\t * @var string\n\t */\n\tprotected $signature = 'aimeos:account\n\t\t{email? : E-Mail adress of the (admin) user (will ask for if not given)}\n\t\t{site? : Site to create account for (will use default value if not given}\n\t\t{--password= : Secret password for the account (will ask for if not given)}\n\t\t{--super : If account should have super user privileges for all sites}\n\t\t{--admin : If account should have site administrator privileges}\n\t\t{--editor : If account should have limited editor privileges}\n\t';\n\n\t/**\n\t * The console command description.\n\t *\n\t * @var string\n\t */\n\tprotected $description = 'Creates new (admin) accounts';\n\n\n\t/**\n\t * Execute the console command.\n\t *\n\t * @return mixed\n\t */\n\tpublic function handle()\n\t{\n\t\t$site = $this->argument( 'site' ) ?: config( 'shop.mshop.locale.site', 'default' );\n\n\t\tif( ( $email = $this->argument( 'email' ) ) === null ) {\n\t\t\t$email = $this->ask( 'E-Mail' );\n\t\t}\n\n\t\tif( ( $password = $this->option( 'password' ) ) === null ) {\n\t\t\t$password = $this->secret( 'Password' );\n\t\t}\n\n\t\t$context = $this->getLaravel()->make( 'aimeos.context' )->get( false, 'command' );\n\t\t$context->setEditor( 'aimeos:account' );\n\n\t\t$localeManager = \\Aimeos\\MShop::create( $context, 'locale' );\n\t\t$localeItem = $localeManager->bootstrap( $site, '', '', false, null, true );\n\t\t$context->setLocale( $localeItem );\n\n\t\t$manager = \\Aimeos\\MShop::create( $context, 'customer' );\n\n\t\ttry {\n\t\t\t$item = $manager->find( $email );\n\t\t} catch( \\Aimeos\\MShop\\Exception $e ) {\n\t\t\t$item = $manager->create();\n\t\t}\n\n\t\t$item = $item->setCode( $email )->setLabel( $email )->setPassword( $password )->setStatus( 1 );\n\t\t$item->getPaymentAddress()->setEmail( $email );\n\n\t\t$item = $manager->save( $this->addGroups( $context, $item ) );\n\n\t\t\\Illuminate\\Foundation\\Auth\\User::findOrFail( $item->getId() )\n\t\t\t->forceFill( [\n\t\t\t\t'siteid' => $this->option( 'super' ) ? '' : $item->getSiteId(),\n\t\t\t\t'superuser' => ( $this->option( 'super' ) ? 1 : 0 ),\n\t\t\t\t'email_verified_at' => now(),\n\t\t\t] )->save();\n\t}\n\n\n\t/**\n\t * Adds the group to the given user\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Aimeos context object\n\t * @param \\Aimeos\\MShop\\Customer\\Item\\Iface $user Aimeos customer object\n\t * @return \\Aimeos\\MShop\\Customer\\Item\\Iface Updated customer object\n\t */\n\tprotected function addGroups( \\Aimeos\\MShop\\ContextIface $context,\n\t\t\\Aimeos\\MShop\\Customer\\Item\\Iface $user ) : \\Aimeos\\MShop\\Customer\\Item\\Iface\n\t{\n\t\tif( $this->option( 'admin' ) ) {\n\t\t\t$user = $this->addGroup( $context, $user, 'admin' );\n\t\t}\n\n\t\tif( $this->option( 'editor' ) ) {\n\t\t\t$user = $this->addGroup( $context, $user, 'editor' );\n\t\t}\n\n\t\treturn $user;\n\t}\n\n\n\t/**\n\t * Adds the group to the given user\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Aimeos context object\n\t * @param \\Aimeos\\MShop\\Customer\\Item\\Iface $user Aimeos customer object\n\t * @param string $group Unique customer group code\n\t */\n\tprotected function addGroup( \\Aimeos\\MShop\\ContextIface $context, \\Aimeos\\MShop\\Customer\\Item\\Iface $user,\n\t\tstring $group ) : \\Aimeos\\MShop\\Customer\\Item\\Iface\n\t{\n\t\t$msg = 'Add \"%1$s\" group to user \"%2$s\" for site \"%3$s\"';\n\t\t$site = $this->argument( 'site' ) ?: config( 'shop.mshop.locale.site', 'default' );\n\t\t$this->info( sprintf( $msg, $group, $user->getCode(), $site ) );\n\n\t\t$item = $this->getGroupItem( $context, $group );\n\t\treturn $user->setGroups( array_merge( $user->getGroups(), [$item->getId()] ) );\n\t}\n\n\n\t/**\n\t * Returns the customer group item for the given code\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Aimeos context object\n\t * @param string $code Unique customer group code\n\t * @return \\Aimeos\\MShop\\Group\\Item\\Iface Aimeos customer group item object\n\t */\n\tprotected function getGroupItem( \\Aimeos\\MShop\\ContextIface $context, string $code ) : \\Aimeos\\MShop\\Group\\Item\\Iface\n\t{\n\t\t$manager = \\Aimeos\\MShop::create( $context, 'group' );\n\n\t\ttry\n\t\t{\n\t\t\t$item = $manager->find( $code );\n\t\t}\n\t\tcatch( \\Aimeos\\MShop\\Exception $e )\n\t\t{\n\t\t\t$item = $manager->create();\n\t\t\t$item->setLabel( $code );\n\t\t\t$item->setCode( $code );\n\n\t\t\t$manager->save( $item );\n\t\t}\n\n\t\treturn $item;\n\t}\n}\n"
  },
  {
    "path": "src/Command/ClearCommand.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Command;\n\n\n/**\n * Command for clearing the content cache\n */\nclass ClearCommand extends AbstractCommand\n{\n\t/**\n\t * The name and signature of the console command.\n\t *\n\t * @var string\n\t */\n\tprotected $signature = 'aimeos:clear';\n\n\t/**\n\t * The console command description.\n\t *\n\t * @var string\n\t */\n\tprotected $description = 'Clears the content cache';\n\n\n\t/**\n\t * Execute the console command.\n\t *\n\t * @return mixed\n\t */\n\tpublic function handle()\n\t{\n\t\t$this->info( 'Clearing Aimeos cache', 'v' );\n\n\t\t$context = $this->getLaravel()->make( 'aimeos.context' )->get( false, 'command' );\n\t\t$context->setEditor( 'aimeos:clear' );\n\n\t\t\\Aimeos\\MAdmin::create( $context, 'cache' )->getCache()->clear();\n\t}\n}\n"
  },
  {
    "path": "src/Command/JobsCommand.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Command;\n\nuse Illuminate\\Console\\Command;\n\n\n/**\n * Command for executing the Aimeos job controllers\n */\nclass JobsCommand extends AbstractCommand\n{\n\t/**\n\t * The name and signature of the console command.\n\t *\n\t * @var string\n\t */\n\tprotected $signature = 'aimeos:jobs\n\t\t{jobs : One or more job controller names like \"admin/job customer/email/watch\"}\n\t\t{site? : Site codes to execute the jobs for like \"default unittest\" (none for all)}\n\t\t{--option= : Setup configuration, name and value are separated by colon like \"setup/default/demo:1\"}\n\t';\n\n\t/**\n\t * The console command description.\n\t *\n\t * @var string\n\t */\n\tprotected $description = 'Executes the job controllers';\n\n\n\t/**\n\t * Execute the console command.\n\t *\n\t * @return mixed\n\t */\n\tpublic function handle()\n\t{\n\t\t$jobs = $this->argument( 'jobs' );\n\t\t$jobs = !is_array( $jobs ) ? explode( ' ', (string) $jobs ) : $jobs;\n\n\t\t$fcn = function( \\Aimeos\\MShop\\ContextIface $lcontext, \\Aimeos\\Bootstrap $aimeos ) use ( $jobs )\n\t\t{\n\t\t\t$jobfcn = function( $context, $aimeos, $jobname ) {\n\t\t\t\t\\Aimeos\\Controller\\Jobs::create( $context, $aimeos, $jobname )->run();\n\t\t\t};\n\n\t\t\t$process = $lcontext->process();\n\t\t\t$site = $lcontext->locale()->getSiteItem()->getCode();\n\n\t\t\tforeach( $jobs as $jobname )\n\t\t\t{\n\t\t\t\t$this->info( sprintf( 'Executing Aimeos jobs \"%s\" for \"%s\"', $jobname, $site ), 'v' );\n\t\t\t\t$process->start( $jobfcn, [$lcontext, $aimeos, $jobname], false );\n\t\t\t}\n\n\t\t\t$process->wait();\n\t\t};\n\n\t\t$this->exec( $this->context(), $fcn, $this->argument( 'site' ) );\n\t}\n\n\n\t/**\n\t * Returns a context object\n\t *\n\t * @return \\Aimeos\\MShop\\ContextIface Context object\n\t */\n\tprotected function context() : \\Aimeos\\MShop\\ContextIface\n\t{\n\t\t$lv = $this->getLaravel();\n\t\t$context = $lv->make( 'aimeos.context' )->get( false, 'command' );\n\n\t\t$langManager = \\Aimeos\\MShop::create( $context, 'locale/language' );\n\t\t$langids = $langManager->search( $langManager->filter( true ) )->keys()->toArray();\n\t\t$i18n = $lv->make( 'aimeos.i18n' )->get( $langids );\n\n\t\t$context->setSession( new \\Aimeos\\Base\\Session\\None() );\n\t\t$context->setCache( new \\Aimeos\\Base\\Cache\\None() );\n\n\t\t$context->setEditor( 'aimeos:jobs' );\n\t\t$context->setI18n( $i18n );\n\n\t\treturn $this->addConfig( $context );\n\t}\n}\n"
  },
  {
    "path": "src/Command/SetupCommand.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Command;\n\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Console\\Input\\InputArgument;\n\n\n/**\n * Command for initializing or updating the Aimeos database tables\n */\nclass SetupCommand extends AbstractCommand\n{\n\t/**\n\t * The name and signature of the console command.\n\t *\n\t * @var string\n\t */\n\tprotected $signature = 'aimeos:setup\n\t\t{site? : Site for updating database entries}\n\t\t{tplsite=default : Site used as template for creating the new one}\n\t\t{--q : Quiet}\n\t\t{--v=v : Verbosity level}\n\t\t{--option=* : Setup configuration, name and value are separated by colon like \"setup/default/demo:1\"}\n\t';\n\n\t/**\n\t * The console command description.\n\t *\n\t * @var string\n\t */\n\tprotected $description = 'Initialize or update the Aimeos database tables';\n\n\n\t/**\n\t * Execute the console command.\n\t *\n\t * @return mixed\n\t */\n\tpublic function handle()\n\t{\n\t\t\\Aimeos\\MShop::cache( false );\n\t\t\\Aimeos\\MAdmin::cache( false );\n\n\t\t$template = $this->argument( 'tplsite' );\n\n\t\tif( ( $site = $this->argument( 'site' ) ) === null ) {\n\t\t\t$site = config( 'shop.mshop.locale.site', 'default' );\n\t\t}\n\n\t\t$boostrap = $this->getLaravel()->make( 'aimeos' )->get();\n\t\t$ctx = $this->getLaravel()->make( 'aimeos.context' )->get( false, 'command' );\n\n\t\t$this->info( sprintf( 'Initializing or updating the Aimeos database tables for site \"%1$s\"', $site ) );\n\n\t\t\\Aimeos\\Setup::use( $boostrap )\n\t\t\t->verbose( $this->option( 'q' ) ? '' : $this->option( 'v' ) )\n\t\t\t->context( $this->addConfig( $ctx->setEditor( 'aimeos:setup' ) ) )\n\t\t\t->up( $site, $template );\n\t}\n}\n"
  },
  {
    "path": "src/Composer.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2020-2023\n */\n\n\nnamespace Aimeos\\Shop;\n\n\n/**\n * Performs setup during composer installs\n */\nclass Composer\n{\n\t/**\n\t * @param \\Composer\\Script\\Event $event Event instance\n\t * @throws \\RuntimeException If an error occured\n\t */\n\tpublic static function join( \\Composer\\Script\\Event $event )\n\t{\n\t\ttry\n\t\t{\n\t\t\t$options = [\n\t\t\t\t'http' => [\n\t\t\t\t\t'method' => 'POST',\n\t\t\t\t\t'header' => ['Content-Type: application/json'],\n\t\t\t\t\t'content' => json_encode( ['query' => 'mutation{\n                        _1: addStar(input:{clientMutationId:\"_1\",starrableId:\"R_kgDOBiPing\"}){clientMutationId}\n                        _2: addStar(input:{clientMutationId:\"_2\",starrableId:\"R_kgDOAeFH2g\"}){clientMutationId}\n                        _3: addStar(input:{clientMutationId:\"_3\",starrableId:\"R_kgDOAZou5Q\"}){clientMutationId}\n                        _4: addStar(input:{clientMutationId:\"_4\",starrableId:\"R_kgDODUDlmg\"}){clientMutationId}\n                        _5: addStar(input:{clientMutationId:\"_5\",starrableId:\"R_kgDODqs9PA\"}){clientMutationId}\n                        _6: addStar(input:{clientMutationId:\"_6\",starrableId:\"R_kgDOGcKL7A\"}){clientMutationId}\n                        _7: addStar(input:{clientMutationId:\"_7\",starrableId:\"R_kgDOGeAkvw\"}){clientMutationId}\n                        _8: addStar(input:{clientMutationId:\"_8\",starrableId:\"R_kgDOG1PAJw\"}){clientMutationId}\n                        _9: addStar(input:{clientMutationId:\"_9\",starrableId:\"MDEwOlJlcG9zaXRvcnkyNDU0MjQyNw==\"}){clientMutationId}\n                        _10: addStar(input:{clientMutationId:\"_10\",starrableId:\"MDEwOlJlcG9zaXRvcnkyODc0MzEyNg==\"}){clientMutationId}\n                        _11: addStar(input:{clientMutationId:\"_11\",starrableId:\"MDEwOlJlcG9zaXRvcnkyNDE2MjI1Ng==\"}){clientMutationId}\n\t\t\t\t\t\t}'\n\t\t\t\t\t] )\n\t\t\t\t]\n\t\t\t];\n\t\t\t$config = $event->getComposer()->getConfig();\n\n\t\t\tif( method_exists( '\\Composer\\Factory', 'createHttpDownloader' ) )\n\t\t\t{\n\t\t\t\t\\Composer\\Factory::createHttpDownloader( $event->getIO(), $config )\n\t\t\t\t\t->get( 'https://api.github.com/graphql', $options );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t\\Composer\\Factory::createRemoteFilesystem( $event->getIO(), $config )\n\t\t\t\t\t->getContents( 'github.com', 'https://api.github.com/graphql', false, $options );\n\t\t\t}\n\t\t}\n\t\tcatch( \\Exception $e ) {}\n\t}\n}\n"
  },
  {
    "path": "src/Controller/AccountController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Aimeos controller for account related functionality.\n */\nclass AccountController extends Controller\n{\n\t/**\n\t * Returns the html for the \"My account\" page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function indexAction()\n\t{\n\t\t$params = ['page' => 'page-account-index'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.account-index' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'account.index' ), $params )\n\t\t\t->header( 'Cache-Control', 'no-store, max-age=0' );\n\t}\n\n\n\t/**\n\t * Returns the html for the \"My account\" download page.\n\t *\n\t * @return \\Illuminate\\Contracts\\View\\View View for rendering the output\n\t */\n\tpublic function downloadAction()\n\t{\n\t\t$response = Shop::get( 'account/download' )->response();\n\t\treturn Response::make( (string) $response->getBody(), $response->getStatusCode(), $response->getHeaders() );\n\t}\n}"
  },
  {
    "path": "src/Controller/AdminController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2014-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\View;\nuse Illuminate\\Foundation\\Auth\\Access\\AuthorizesRequests;\n\n\n/**\n * Controller providing the administration interface\n */\nclass AdminController extends Controller\n{\n\tuse AuthorizesRequests;\n\n\n\t/**\n\t * Returns the initial HTML view for the admin interface.\n\t *\n\t * @param \\Illuminate\\Http\\Request $request Laravel request object\n\t * @return \\Illuminate\\Contracts\\View\\View View for rendering the output\n\t */\n\tpublic function indexAction( \\Illuminate\\Http\\Request $request )\n\t{\n\t\tif( Auth::check() === false\n\t\t\t|| $request->user()->can( 'admin', [AdminController::class, config( 'shop.roles', ['admin', 'editor'] )] ) === false\n\t\t) {\n\t\t\treturn redirect()->guest( airoute( 'login', ['locale' => app()->getLocale()] ) );\n\t\t}\n\n\t\t$context = app( 'aimeos.context' )->get( false );\n\t\t$siteManager = \\Aimeos\\MShop::create( $context, 'locale/site' );\n\t\t$siteId = current( array_reverse( explode( '.', trim( $request->user()->siteid, '.' ) ) ) );\n\t\t$siteCode = ( $siteId ? $siteManager->get( $siteId )->getCode() : config( 'shop.mshop.locale.site', 'default' ) );\n\t\t$locale = $request->user()->langid ?: config( 'app.locale', 'en' );\n\n\t\t$param = array(\n\t\t\t'resource' => config( 'shop.panel', 'dashboard' ),\n\t\t\t'site' => Route::input( 'site', Request::get( 'site', $siteCode ) ),\n\t\t\t'locale' => Route::input( 'locale', Request::get( 'locale', $locale ) )\n\t\t);\n\n\t\treturn redirect()->route( 'aimeos_shop_jqadm_search', $param );\n\t}\n}\n"
  },
  {
    "path": "src/Controller/BasketController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Aimeos controller for basket related functionality.\n */\nclass BasketController extends Controller\n{\n\t/**\n\t * Returns the html for the standard basket page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function indexAction()\n\t{\n\t\t$params = ['page' => 'page-basket-index'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.basket-index' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'basket.index' ), $params )\n\t\t\t->header( 'Cache-Control', 'no-store, , max-age=0' );\n\t}\n}"
  },
  {
    "path": "src/Controller/CatalogController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Aimeos controller for catalog related functionality.\n */\nclass CatalogController extends Controller\n{\n\t/**\n\t * Returns the view for the XHR response with the counts for the facetted search.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function countAction()\n\t{\n\t\t$params = ['page' => 'page-catalog-count'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-count' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.count' ), $params )\n\t\t\t->header( 'Content-Type', 'application/javascript' )\n\t\t\t->header( 'Cache-Control', 'public, max-age=300' );\n\t}\n\n\n\t/**\n\t * Returns the html for the catalog detail page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function detailAction()\n\t{\n\t\ttry\n\t\t{\n\t\t\t$params = ['page' => 'page-catalog-detail'];\n\n\t\t\tforeach( app( 'config' )->get( 'shop.page.catalog-detail' ) as $name )\n\t\t\t{\n\t\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t\t}\n\n\t\t\treturn Response::view( Shop::template( 'catalog.detail' ), $params )\n\t\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t\t}\n\t\tcatch( \\Exception $e )\n\t\t{\n\t\t\tif( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }\n\t\t\tthrow $e;\n\t\t}\n\t}\n\n\n\t/**\n\t * Returns the html for the catalog home page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function homeAction()\n\t{\n\t\t$params = ['page' => 'page-catalog-home'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-home' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.home' ), $params )\n\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t}\n\n\n\t/**\n\t * Returns the html for the catalog list page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function listAction()\n\t{\n\t\ttry\n\t\t{\n\t\t\t$params = ['page' => 'page-catalog-list'];\n\n\t\t\tforeach( app( 'config' )->get( 'shop.page.catalog-list' ) as $name )\n\t\t\t{\n\t\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t\t}\n\n\t\t\treturn Response::view( Shop::template( 'catalog.list' ), $params )\n\t\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t\t}\n\t\tcatch( \\Exception $e )\n\t\t{\n\t\t\tif( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }\n\t\t\tthrow $e;\n\t\t}\n\t}\n\n\n\t/**\n\t * Returns the html for the catalog session page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function sessionAction()\n\t{\n\t\t$params = ['page' => 'page-catalog-session'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-session' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.session' ), $params )\n\t\t\t->header( 'Cache-Control', 'no-cache' );\n\t}\n\n\n\t/**\n\t * Returns the html body part for the catalog stock page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function stockAction()\n\t{\n\t\t$params = ['page' => 'page-catalog-stock'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-stock' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.stock' ), $params )\n\t\t\t->header( 'Content-Type', 'application/javascript' )\n\t\t\t->header( 'Cache-Control', 'public, max-age=30' );\n\t}\n\n\n\t/**\n\t * Returns the view for the XHR response with the product information for the search suggestion.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function suggestAction()\n\t{\n\t\t$params = ['page' => 'page-catalog-suggest'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-suggest' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.suggest' ), $params )\n\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) )\n\t\t\t->header( 'Content-Type', 'application/json' );\n\t}\n\n\n\t/**\n\t * Returns the html for the catalog tree page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function treeAction()\n\t{\n\t\ttry\n\t\t{\n\t\t\t$params = ['page' => 'page-catalog-tree'];\n\n\t\t\tforeach( app( 'config' )->get( 'shop.page.catalog-tree' ) as $name )\n\t\t\t{\n\t\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t\t}\n\n\t\t\treturn Response::view( Shop::template( 'catalog.tree' ), $params )\n\t\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t\t}\n\t\tcatch( \\Exception $e )\n\t\t{\n\t\t\tif( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }\n\t\t\tthrow $e;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/Controller/CheckoutController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Aimeos controller for checkout related functionality.\n */\nclass CheckoutController extends Controller\n{\n\t/**\n\t * Returns the html for the checkout confirmation page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function confirmAction()\n\t{\n\t\t$params = ['page' => 'page-checkout-confirm'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.checkout-confirm' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'checkout.confirm' ), $params )\n\t\t\t->header( 'Cache-Control', 'no-store, max-age=0' );\n\t}\n\n\n\t/**\n\t * Returns the html for the standard checkout page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function indexAction()\n\t{\n\t\t$params = ['page' => 'page-checkout-index'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.checkout-index' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'checkout.index' ), $params )\n\t\t\t->header( 'Cache-Control', 'no-store, max-age=0' );\n\t}\n\n\n\t/**\n\t * Returns the view for the order update page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function updateAction()\n\t{\n\t\t$params = ['page' => 'page-checkout-update'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.checkout-update' ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'checkout.update' ), $params )\n\t\t\t->header( 'Cache-Control', 'no-store, max-age=0' );\n\t}\n}"
  },
  {
    "path": "src/Controller/GraphqlController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2022-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Foundation\\Auth\\Access\\AuthorizesRequests;\nuse Psr\\Http\\Message\\ServerRequestInterface;\n\n\n/**\n * Aimeos controller for the GraphQL Admin API\n */\nclass GraphqlController extends Controller\n{\n\tuse AuthorizesRequests;\n\n\n\t/**\n\t * Creates a new resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function indexAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [GraphqlController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\t$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );\n\t\t$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );\n\n\t\t$context = app( 'aimeos.context' )->get( false, 'backend' );\n\t\t$context->setI18n( app( 'aimeos.i18n' )->get( array( $lang, 'en' ) ) );\n\t\t$context->setLocale( app( 'aimeos.locale' )->getBackend( $context, $site ) );\n\t\t$context->setView( app( 'aimeos.view' )->create( $context, [], $lang ) );\n\n\t\treturn \\Aimeos\\Admin\\Graphql::execute( $context, $request );\n\t}\n}\n"
  },
  {
    "path": "src/Controller/JqadmController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Illuminate\\Support\\Facades\\View;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Foundation\\Auth\\Access\\AuthorizesRequests;\n\n\n/**\n * Aimeos controller for the JQuery admin interface\n */\nclass JqadmController extends AdminController\n{\n\tuse AuthorizesRequests;\n\n\n\t/**\n\t * Returns the JS file content\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object containing the generated output\n\t */\n\tpublic function fileAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$files = [];\n\t\t$aimeos = app( 'aimeos' )->get();\n\t\t$name = Route::input( 'name', Request::get( 'name' ) );\n\n\t\tforeach( $aimeos->getCustomPaths( 'admin/jqadm' ) as $base => $paths )\n\t\t{\n\t\t\tforeach( $paths as $path ) {\n\t\t\t\t$files[] = $base . '/' . $path;\n\t\t\t}\n\t\t}\n\n\t\t$response = response( \\Aimeos\\Admin\\JQAdm\\Bundle::get( $files, $name ) );\n\n\t\tif( str_ends_with( $name, 'js' ) ) {\n\t\t\t$response->header( 'Content-Type', 'application/javascript' );\n\t\t} elseif( str_ends_with( $name, 'css' ) ) {\n\t\t\t$response->header( 'Content-Type', 'text/css' );\n\t\t}\n\n\t\treturn $response->header( 'Cache-Control', 'public, max-age=3600' );\n\t}\n\n\n\t/**\n\t * Returns the HTML code for batch operations on a resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function batchAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->batch() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Returns the HTML code for a copy of a resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function copyAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->copy() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Returns the HTML code for a new resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function createAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->create() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Deletes the resource object or a list of resource objects\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function deleteAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->delete() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Exports the data for a resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function exportAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->export() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Returns the HTML code for the requested resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function getAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->get() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Imports the data for a resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function importAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->import() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Saves a new resource object\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function saveAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->save() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Returns the HTML code for a list of resource objects\n\t *\n\t * @return string Generated output\n\t */\n\tpublic function searchAction()\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );\n\t\t}\n\n\t\t$cntl = $this->createAdmin();\n\n\t\tif( ( $html = $cntl->search() ) == '' ) {\n\t\t\treturn $cntl->response();\n\t\t}\n\n\t\treturn $this->getHtml( (string) $html );\n\t}\n\n\n\t/**\n\t * Returns the resource controller\n\t *\n\t * @return \\Aimeos\\Admin\\JQAdm\\Iface JQAdm client\n\t */\n\tprotected function createAdmin() : \\Aimeos\\Admin\\JQAdm\\Iface\n\t{\n\t\t$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );\n\t\t$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );\n\t\t$resource = Route::input( 'resource' );\n\n\t\t$aimeos = app( 'aimeos' )->get();\n\n\t\t$context = app( 'aimeos.context' )->get( false, 'backend' );\n\t\t$context->setI18n( app( 'aimeos.i18n' )->get( array( $lang, 'en' ) ) );\n\t\t$context->setLocale( app( 'aimeos.locale' )->getBackend( $context, $site )->setLanguageId( $lang ) );\n\n\t\t$siteManager = \\Aimeos\\MShop::create( $context, 'locale/site' );\n\t\t$context->config()->apply( $siteManager->find( $site )->getConfig() );\n\n\t\t$paths = $aimeos->getTemplatePaths( 'admin/jqadm/templates', $context->locale()->getSiteItem()->getTheme() );\n\t\t$view = app( 'aimeos.view' )->create( $context, $paths, $lang );\n\n\t\t$view->aimeosType = 'Laravel';\n\t\t$view->aimeosVersion = app( 'aimeos' )->getVersion();\n\t\t$view->aimeosExtensions = implode( ',', $aimeos->getExtensions() );\n\n\t\t$context->setView( $view );\n\n\t\treturn \\Aimeos\\Admin\\JQAdm::create( $context, $aimeos, $resource );\n\t}\n\n\n\t/**\n\t * Returns the generated HTML code\n\t *\n\t * @param string $content Content from admin client\n\t * @return \\Illuminate\\Contracts\\View\\View View for rendering the output\n\t */\n\tprotected function getHtml( string $content )\n\t{\n\t\t$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );\n\t\t$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );\n\n\t\treturn View::make( 'shop::jqadm.index', [\n\t\t\t'content' => $content,\n\t\t\t'site' => $site,\n\t\t\t'locale' => $lang,\n\t\t\t'localeDir' => in_array( $lang, ['ar', 'az', 'dv', 'fa', 'he', 'ku', 'ur'] ) ? 'rtl' : 'ltr',\n\t\t\t'theme' => ( $_COOKIE['aimeos_backend_theme'] ?? '' ) == 'dark' ? 'dark' : 'light'\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/Controller/JsonadmController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Foundation\\Auth\\Access\\AuthorizesRequests;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Nyholm\\Psr7\\Factory\\Psr17Factory;\n\n\n/**\n * Aimeos controller for the JSON REST API\n */\nclass JsonadmController extends Controller\n{\n\tuse AuthorizesRequests;\n\n\n\t/**\n\t * Deletes the resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function deleteAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\treturn $this->createAdmin()->delete( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Returns the requested resource object or list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function getAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\treturn $this->createAdmin()->get( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Updates a resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function patchAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\treturn $this->createAdmin()->patch( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Creates a new resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function postAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\treturn $this->createAdmin()->post( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Creates or updates a single resource object\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function putAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\treturn $this->createAdmin()->put( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Returns the available HTTP verbs and the resource URLs\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function optionsAction( ServerRequestInterface $request )\n\t{\n\t\tif( config( 'shop.authorize', true ) ) {\n\t\t\t$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );\n\t\t}\n\n\t\treturn $this->createAdmin()->options( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Returns the JsonAdm client\n\t *\n\t * @return \\Aimeos\\Admin\\JsonAdm\\Iface JsonAdm client\n\t */\n\tprotected function createAdmin() : \\Aimeos\\Admin\\JsonAdm\\Iface\n\t{\n\t\t$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );\n\t\t$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );\n\t\t$resource = Route::input( 'resource', '' );\n\n\t\t$aimeos = app( 'aimeos' )->get();\n\t\t$context = app( 'aimeos.context' )->get( false, 'backend' );\n\n\t\t$context->setI18n( app( 'aimeos.i18n' )->get( array( $lang, 'en' ) ) );\n\t\t$context->setLocale( app( 'aimeos.locale' )->getBackend( $context, $site ) );\n\n\t\t$templatePaths = $aimeos->getTemplatePaths( 'admin/jsonadm/templates', $context->locale()->getSiteItem()->getTheme() );\n\t\t$context->setView( app( 'aimeos.view' )->create( $context, $templatePaths, $lang ) );\n\n\t\treturn \\Aimeos\\Admin\\JsonAdm::create( $context, $aimeos, $resource );\n\t}\n}\n"
  },
  {
    "path": "src/Controller/JsonapiController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2017-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\Request;\nuse Psr\\Http\\Message\\ServerRequestInterface;\nuse Nyholm\\Psr7\\Factory\\Psr17Factory;\n\n\n/**\n * Aimeos controller for the JSON REST API\n */\nclass JsonapiController extends Controller\n{\n\t/**\n\t * Deletes the resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function deleteAction( ServerRequestInterface $request )\n\t{\n\t\treturn $this->createClient()->delete( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Returns the requested resource object or list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function getAction( ServerRequestInterface $request )\n\t{\n\t\treturn $this->createClient()->get( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Updates a resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function patchAction( ServerRequestInterface $request )\n\t{\n\t\treturn $this->createClient()->patch( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Creates a new resource object or a list of resource objects\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function postAction( ServerRequestInterface $request )\n\t{\n\t\treturn $this->createClient()->post( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Creates or updates a single resource object\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function putAction( ServerRequestInterface $request )\n\t{\n\t\treturn $this->createClient()->put( $request, ( new Psr17Factory )->createResponse() );\n\t}\n\n\n\t/**\n\t * Returns the available HTTP verbs and the resource URLs\n\t *\n\t * @param \\Psr\\Http\\Message\\ServerRequestInterface $request Request object\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function optionsAction( ServerRequestInterface $request )\n\t{\n\t\treturn $this->createClient()->options( $request, ( new Psr17Factory )->createResponse() )\n\t\t\t->withHeader( 'access-control-allow-headers', 'authorization,content-type' )\n\t\t\t->withHeader( 'access-control-allow-methods', 'DELETE, GET, OPTIONS, PATCH, POST, PUT' )\n\t\t\t->withHeader( 'access-control-allow-origin', $request->getHeaderLine( 'origin' ) );\n\t}\n\n\n\t/**\n\t * Returns the JsonAdm client\n\t *\n\t * @return \\Aimeos\\Client\\JsonApi\\Iface JsonApi client\n\t */\n\tprotected function createClient() : \\Aimeos\\Client\\JsonApi\\Iface\n\t{\n\t\t$resource = Route::input( 'resource' );\n\t\t$related = Route::input( 'related', Request::get( 'related' ) );\n\n\t\t$aimeos = app( 'aimeos' )->get();\n\t\t$context = app( 'aimeos.context' )->get();\n\t\t$tmplPaths = $aimeos->getTemplatePaths( 'client/jsonapi/templates', $context->locale()->getSiteItem()->getTheme() );\n\n\t\t$langid = $context->locale()->getLanguageId();\n\n\t\t$context->setView( app( 'aimeos.view' )->create( $context, $tmplPaths, $langid ) );\n\n\t\treturn \\Aimeos\\Client\\JsonApi::create( $context, $resource . '/' . $related );\n\t}\n}\n"
  },
  {
    "path": "src/Controller/PageController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\View;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Aimeos controller for support page request.\n */\nclass PageController extends Controller\n{\n\t/**\n\t * Returns the html for the content pages.\n\t *\n\t * @return \\Psr\\Http\\Message\\ResponseInterface Response object containing the generated output\n\t */\n\tpublic function indexAction()\n\t{\n\t\t$params = ['page' => 'page-index'];\n\n\t\tforeach( app( 'config' )->get( 'shop.page.cms', ['cms/page', 'catalog/tree', 'basket/mini'] ) as $name )\n\t\t{\n\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t}\n\n\t\tif( empty( $params['aibody']['cms/page'] ) ) {\n\t\t\tabort( 404 );\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'page.index' ), $params )\n\t\t\t->header( 'Cache-Control', 'private, max-age=10' );\n\t}\n}\n"
  },
  {
    "path": "src/Controller/ResolveController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Request;\nuse Illuminate\\Support\\Facades\\Response;\nuse Illuminate\\Support\\Facades\\Route;\nuse Illuminate\\Support\\Facades\\View;\n\n\n/**\n * Aimeos controller for dispatching requests.\n */\nclass ResolveController extends Controller\n{\n\tprivate static $fcn = [];\n\n\n\t/**\n\t * Register a new resolver function.\n\t *\n\t * @param string $name Name of the resolver function\n\t * @param \\Closure $fcn Resolver function\n\t */\n\tpublic static function register( string $name, \\Closure $fcn )\n\t{\n\t\tself::$fcn[$name] = $fcn;\n\t}\n\n\n\t/**\n\t * Initializes the object.\n\t */\n\tpublic function __construct()\n\t{\n\t\tself::$fcn['product'] = function( \\Aimeos\\MShop\\ContextIface $context, string $path ) {\n\t\t\treturn $this->product( $context, $path );\n\t\t};\n\n\t\tself::$fcn['catalog'] = function( \\Aimeos\\MShop\\ContextIface $context, string $path ) {\n\t\t\treturn $this->catalog( $context, $path );\n\t\t};\n\t}\n\n\n\t/**\n\t * Returns the html of the resolved URLs.\n\t *\n\t * @param \\Illuminate\\Http\\Request $request Laravel request object\n\t * @return \\Illuminate\\Http\\Response Laravel response object containing the generated output\n\t */\n\tpublic function indexAction( \\Illuminate\\Http\\Request $request )\n\t{\n\t\tif( ( $path = $request->route( 'path', $request->input( 'path' ) ) ) === null ) {\n\t\t\tabort( 404 );\n\t\t}\n\n\t\t$context = app( 'aimeos.context' )->get( true );\n\n\t\tforeach( array_reverse( self::$fcn ) as $name => $fcn )\n\t\t{\n\t\t\ttry {\n\t\t\t\treturn call_user_func_array( $fcn->bindTo( $this, static::class ), [$context, $path] );\n\t\t\t} catch( \\Exception $e ) {\n\t\t\t\tif( $e->getCode() !== 404 ) throw $e;\n\t\t\t}\n\t\t}\n\n\t\tabort( 404 );\n\t}\n\n\n\t/**\n\t * Returns the category page if the give path can be resolved to a category.\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @param string $path URL path to resolve\n\t * @return Response Response object\n\t */\n\tprotected function catalog( \\Aimeos\\MShop\\ContextIface $context, string $path ) : ?\\Illuminate\\Http\\Response\n\t{\n\t\t$item = \\Aimeos\\Controller\\Frontend::create( $context, 'catalog' )->resolve( $path );\n\t\t$view = Shop::view();\n\n\t\t$params = ( Route::current() ? Route::current()->parameters() : [] ) + Request::all();\n\t\t$params += ['path' => $path, 'f_name' => $path, 'f_catid' => $item->getId(), 'page' => 'page-catalog-tree'];\n\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Param\\Standard( $view, $params );\n\t\t$view->addHelper( 'param', $helper );\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-tree' ) as $name )\n\t\t{\n\t\t\t$client = Shop::get( $name );\n\n\t\t\t$params['aiheader'][$name] = $client->header();\n\t\t\t$params['aibody'][$name] = $client->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.tree' ), $params )\n\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t}\n\n\n\t/**\n\t * Returns the CMS page if the give path can be resolved to a CMS page.\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @param string $path URL path to resolve\n\t * @return Response Response object\n\t */\n\tprotected function cms( \\Aimeos\\MShop\\ContextIface $context, string $path ) : ?\\Illuminate\\Http\\Response\n\t{\n\t\t\\Aimeos\\Controller\\Frontend::create( $context, 'cms' )->resolve( $path );\n\t\t$view = Shop::view();\n\n\t\t$params = ( Route::current() ? Route::current()->parameters() : [] ) + Request::all();\n\t\t$params += ['path' => $path, 'page' => 'page-index'];\n\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Param\\Standard( $view, $params );\n\t\t$view->addHelper( 'param', $helper );\n\n\t\tforeach( app( 'config' )->get( 'shop.page.cms' ) as $name )\n\t\t{\n\t\t\t$client = Shop::get( $name );\n\n\t\t\t$params['aiheader'][$name] = $client->header();\n\t\t\t$params['aibody'][$name] = $client->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'page.index' ), $params )\n\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t}\n\n\n\t/**\n\t * Returns the product page if the give path can be resolved to a product.\n\t *\n\t * @param \\Aimeos\\MShop\\ContextIface $context Context object\n\t * @param string $path URL path to resolve\n\t * @return Response Response object\n\t */\n\tprotected function product( \\Aimeos\\MShop\\ContextIface $context, string $path ) : ?\\Illuminate\\Http\\Response\n\t{\n\t\t$item = \\Aimeos\\Controller\\Frontend::create( $context, 'product' )->resolve( $path );\n\t\t$view = Shop::view();\n\n\t\t$params = ( Route::current() ? Route::current()->parameters() : [] ) + Request::all();\n\t\t$params += ['path' => $path, 'd_name' => $path, 'd_prodid' => $item->getId(), 'page' => 'page-catalog-detail'];\n\n\t\t$helper = new \\Aimeos\\Base\\View\\Helper\\Param\\Standard( $view, $params );\n\t\t$view->addHelper( 'param', $helper );\n\n\t\tforeach( app( 'config' )->get( 'shop.page.catalog-detail' ) as $name )\n\t\t{\n\t\t\t$client = Shop::get( $name );\n\n\t\t\t$params['aiheader'][$name] = $client->header();\n\t\t\t$params['aibody'][$name] = $client->body();\n\t\t}\n\n\t\treturn Response::view( Shop::template( 'catalog.detail' ), $params )\n\t\t\t->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );\n\t}\n}"
  },
  {
    "path": "src/Controller/SupplierController.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop\\Controller;\n\nuse Aimeos\\Shop\\Facades\\Shop;\nuse Illuminate\\Routing\\Controller;\nuse Illuminate\\Support\\Facades\\Response;\n\n\n/**\n * Aimeos controller for supplier related functionality.\n */\nclass SupplierController extends Controller\n{\n\t/**\n\t * Returns the html for the supplier detail page.\n\t *\n\t * @return \\Illuminate\\Http\\Response Response object with output and headers\n\t */\n\tpublic function detailAction()\n\t{\n\t\ttry\n\t\t{\n\t\t\t$params = ['page' => 'page-supplier-detail'];\n\n\t\t\tforeach( app( 'config' )->get( 'shop.page.supplier-detail' ) as $name )\n\t\t\t{\n\t\t\t\t$params['aiheader'][$name] = Shop::get( $name )->header();\n\t\t\t\t$params['aibody'][$name] = Shop::get( $name )->body();\n\t\t\t}\n\n\t\t\treturn Response::view( Shop::template( 'supplier.detail' ), $params )\n\t\t\t\t->header( 'Cache-Control', 'private, max-age=10' );\n\t\t}\n\t\tcatch( \\Exception $e )\n\t\t{\n\t\t\tif( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }\n\t\t\tthrow $e;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/Facades/Attribute.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the attribute frontend controller\n */\nclass Attribute extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new attribute frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Attribute\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.attribute';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Basket.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the basket frontend controller\n */\nclass Basket extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new basket frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Basket\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.basket';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Catalog.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the catalog frontend controller\n */\nclass Catalog extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new catalog frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Catalog\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.catalog';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Cms.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the CMS frontend controller\n */\nclass Cms extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new CMS frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Product\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.cms';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Customer.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the customer frontend controller\n */\nclass Customer extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new customer frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Customer\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.customer';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Locale.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the locale frontend controller\n */\nclass Locale extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new locale frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Locale\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.locale';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Order.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the order frontend controller\n */\nclass Order extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new order frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Order\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.order';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Product.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the product frontend controller\n */\nclass Product extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new product frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Product\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.product';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Service.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the service frontend controller\n */\nclass Service extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new service frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Service\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.service';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Shop.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the HTML clients\n *\n * @method static \\Aimeos\\Client\\Html\\Iface get()\n */\nclass Shop extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Get the registered name of the component.\n\t *\n\t * @return string\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.shop';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Stock.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the stock frontend controller\n */\nclass Stock extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new stock frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Stock\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.stock';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Subscription.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the subscription frontend controller\n */\nclass Subscription extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new subscription frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Subscription\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.subscription';\n\t}\n}\n"
  },
  {
    "path": "src/Facades/Supplier.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamespace Aimeos\\Shop\\Facades;\n\n\n/**\n * Returns the supplier frontend controller\n */\nclass Supplier extends \\Illuminate\\Support\\Facades\\Facade\n{\n\t/**\n\t * Returns a new supplier frontend controller object\n\t *\n\t * @return \\Aimeos\\Controller\\Frontend\\Supplier\\Iface\n\t */\n\tprotected static function getFacadeAccessor()\n\t{\n\t\treturn 'aimeos.frontend.supplier';\n\t}\n}\n"
  },
  {
    "path": "src/ShopServiceProvider.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamespace Aimeos\\Shop;\n\nuse Illuminate\\Support\\ServiceProvider;\nuse Illuminate\\Support\\Facades\\Blade;\nuse Illuminate\\Support\\Facades\\Route;\n\n\n/**\n * Aimeos shop service provider for Laravel\n */\nclass ShopServiceProvider extends ServiceProvider {\n\n\t/**\n\t * Indicates if loading of the provider is deferred.\n\t *\n\t * @var bool\n\t */\n\tprotected $defer = false;\n\n\n\t/**\n\t * Bootstrap the application events.\n\t *\n\t * @return void\n\t */\n\tpublic function boot()\n\t{\n\t\t$this->loadViewsFrom( dirname( __DIR__ ) . '/views', 'shop' );\n\t\t$this->loadRoutesFrom( dirname( __DIR__ ) . '/routes/aimeos.php' );\n\n\t\t$this->publishes( [dirname( __DIR__ ) . '/config/shop.php' => config_path( 'shop.php' )], 'config' );\n\t\t$this->publishes( [dirname( __DIR__ ) . '/public' => public_path( 'vendor/shop' )], 'public' );\n\n\t\t$class = '\\Composer\\InstalledVersions';\n\n\t\tif( class_exists( $class ) && method_exists( $class, 'getInstalledPackagesByType' ) )\n\t\t{\n\t\t\tforeach( \\Composer\\InstalledVersions::getInstalledPackagesByType( 'aimeos-extension' ) as $package )\n\t\t\t{\n\t\t\t\t$path = realpath( \\Composer\\InstalledVersions::getInstallPath( $package ) );\n\n\t\t\t\tif( file_exists( $path . '/themes/client/html' ) ) {\n\t\t\t\t\t$this->publishes( [$path . '/themes/client/html' => public_path( 'vendor/shop/themes' )], 'public' );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/**\n\t * Register the service provider.\n\t *\n\t * @return void\n\t */\n\tpublic function register()\n\t{\n\t\t$this->mergeConfigFrom( dirname( __DIR__ ) . '/config/default.php', 'shop' );\n\n\t\t$this->app->scoped( 'aimeos', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\Aimeos( $app['config'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.config', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\Config( $app['config'], $app['aimeos'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.i18n', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\I18n( $this->app['config'], $app['aimeos'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.locale', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\Locale( $app['config'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.context', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\Context( $app['session.store'], $app['aimeos.config'], $app['aimeos.locale'], $app['aimeos.i18n'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.support', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\Support( $app['aimeos.context'], $app['aimeos.locale'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.view', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\View( $app['config'], $app['aimeos.i18n'], $app['aimeos.support'] );\n\t\t});\n\n\t\t$this->app->scoped( 'aimeos.shop', function( $app ) {\n\t\t\treturn new \\Aimeos\\Shop\\Base\\Shop( $app['aimeos'], $app['aimeos.context'], $app['aimeos.view'] );\n\t\t});\n\n\n\t\t$this->app->bind( 'aimeos.frontend.attribute', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'attribute' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.basket', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'basket' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.catalog', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'catalog' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.cms', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'cms' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.customer', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'customer' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.locale', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'locale' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.order', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'order' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.product', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'product' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.service', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'service' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.stock', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'stock' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.subscription', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'subscription' );\n\t\t});\n\n\t\t$this->app->bind( 'aimeos.frontend.supplier', function( $app ) {\n\t\t\treturn \\Aimeos\\Controller\\Frontend::create( $app['aimeos.context'], 'supplier' );\n\t\t});\n\n\n\t\t$this->commands( array(\n\t\t\t'Aimeos\\Shop\\Command\\AccountCommand',\n\t\t\t'Aimeos\\Shop\\Command\\ClearCommand',\n\t\t\t'Aimeos\\Shop\\Command\\SetupCommand',\n\t\t\t'Aimeos\\Shop\\Command\\JobsCommand',\n\t\t) );\n\t}\n\n\n\t/**\n\t * Get the services provided by the provider.\n\t *\n\t * @return array\n\t */\n\tpublic function provides()\n\t{\n\t\treturn array(\n\t\t\t'Aimeos\\Shop\\Base\\Aimeos', 'Aimeos\\Shop\\Base\\I18n', 'Aimeos\\Shop\\Base\\Context',\n\t\t\t'Aimeos\\Shop\\Base\\Config', 'Aimeos\\Shop\\Base\\Locale', 'Aimeos\\Shop\\Base\\View',\n\t\t\t'Aimeos\\Shop\\Base\\Support', 'Aimeos\\Shop\\Base\\Shop',\n\t\t\t'Aimeos\\Shop\\Command\\AccountCommand', 'Aimeos\\Shop\\Command\\ClearCommand',\n\t\t\t'Aimeos\\Shop\\Command\\SetupCommand', 'Aimeos\\Shop\\Command\\JobsCommand',\n\t\t);\n\t}\n\n}"
  },
  {
    "path": "src/helpers.php",
    "content": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2017-2023\n */\n\n\nif( !function_exists( 'airoute' ) )\n{\n\t/**\n\t * Generate the URL to a named route.\n\t *\n\t * @param  array|string  $name\n\t * @param  mixed  $parameters\n\t * @param  bool  $absolute\n\t * @return string\n\t */\n\tfunction airoute( $name, $parameters = [], $absolute = true )\n\t{\n\t\tif( $current = Route::current() )\n\t\t{\n\t\t\t$site = config( 'app.shop_multishop' ) ? config( 'shop.mshop.locale.site', 'default' ) : null;\n\n\t\t\t$parameters['site'] ??= $current->parameter( 'site', Request::get( 'site', $site ) );\n\t\t\t$parameters['locale'] ??= $current->parameter( 'locale', Request::get( 'locale' ) );\n\t\t\t$parameters['currency'] ??= $current->parameter( 'currency', Request::get( 'currency' ) );\n\t\t}\n\n\t\treturn app( 'url' )->route( $name, array_filter( $parameters ), $absolute );\n\t}\n}\n\n\nif( !function_exists( 'aiconfig' ) )\n{\n\t/**\n\t * Returns the configuration setting for the given key\n\t *\n\t * @param string $key Configuration key\n\t * @param mixed $default Default value if the configuration key isn't found\n\t * @return mixed Configuration value\n\t */\n\tfunction aiconfig( $key, $default = null )\n\t{\n\t\treturn app( 'aimeos.config' )->get()->get( $key, $default );\n\t}\n}\n\n\nif( !function_exists( 'aitrans' ) )\n{\n\t/**\n\t * Translates the given message\n\t *\n\t * @param string $singular Message to translate\n\t * @param array $params List of paramters for replacing the placeholders in that order\n\t * @param string $domain Translation domain\n\t * @param string $locale ISO language code, maybe combine with ISO currency code, e.g. \"en_US\"\n\t * @return string Translated string\n\t */\n\tfunction aitrans( $singular, array $params = array(), $domain = 'client', $locale = null )\n\t{\n\t\t$i18n = app( 'aimeos.context' )->get()->i18n( $locale );\n\n\t\treturn vsprintf( $i18n->dt( $domain, $singular ), $params );\n\t}\n}\n\n\nif( !function_exists( 'aitransplural' ) )\n{\n\t/**\n\t * Translates the given messages based on the number\n\t *\n\t * @param string $singular Message to translate\n\t * @param string $plural Message for plural translations\n\t * @param integer $number Count of items to chose the correct plural translation\n\t * @param array $params List of paramters for replacing the placeholders in that order\n\t * @param string $domain Translation domain\n\t * @param string $locale ISO language code, maybe combine with ISO currency code, e.g. \"en_US\"\n\t * @return string Translated string\n\t */\n\tfunction aitransplural( $singular, $plural, $number, array $params = array(), $domain = 'client', $locale = null )\n\t{\n\t\t$i18n = app( 'aimeos.context' )->get()->i18n( $locale );\n\n\t\treturn vsprintf( $i18n->dn( $domain, $singular, $plural, $number ), $params );\n\t}\n}\n"
  },
  {
    "path": "tests/AimeosTestAbstract.php",
    "content": "<?php\n\nabstract class AimeosTestAbstract extends Orchestra\\Testbench\\BrowserKit\\TestCase\n{\n\tprotected function getEnvironmentSetUp( $app )\n\t{\n\t\tputenv( 'APP_DEBUG=1' );\n\n\t\t$app['config']->set( 'app.key', 'SomeRandomStringWith32Characters' );\n\t\t$app['config']->set( 'app.cipher', 'AES-256-CBC' );\n\n\t\t$app['config']->set( 'database.default', 'mysql' );\n\t\t$app['config']->set( 'database.connections.mysql', [\n\t\t\t'driver' => 'mysql',\n\t\t\t'host' => env( 'DB_HOST', '127.0.0.1' ),\n\t\t\t'port' => env( 'DB_PORT', '3306' ),\n\t\t\t'database' => env( 'DB_DATABASE', 'laravel' ),\n\t\t\t'username' => env( 'DB_USERNAME', 'aimeos' ),\n\t\t\t'password' => env( 'DB_PASSWORD', 'aimeos' ),\n\t\t\t'unix_socket' => env( 'DB_SOCKET', '' ),\n\t\t\t'collation' => 'utf8_unicode_ci',\n\t\t] );\n\n\t\t$app['config']->set( 'shop.resource.db', [\n\t\t\t'adapter' => 'mysql',\n\t\t\t'host' => env( 'DB_HOST', '127.0.0.1' ),\n\t\t\t'database' => env( 'DB_DATABASE', 'laravel' ),\n\t\t\t'username' => env( 'DB_USERNAME', 'aimeos' ),\n\t\t\t'password' => env( 'DB_PASSWORD', 'aimeos' ),\n\t\t\t'stmt' => [\"SET SESSION sort_buffer_size=2097144; SET SESSION sql_mode='ANSI'; SET NAMES 'utf8'\"],\n\t\t\t'opt-persistent' => 0,\n\t\t\t'limit' => 3,\n\t\t\t'defaultTableOptions' => [\n\t\t\t\t'collate' => 'utf8_unicode_ci',\n\t\t\t\t'charset' => 'utf8',\n\t\t\t],\n\t\t] );\n\n\t\t$app['config']->set( 'shop.resource.fs', [\n\t\t\t'adapter' => 'Standard',\n\t\t\t'tempdir' => storage_path( 'tmp' ),\n\t\t\t'basedir' => storage_path( 'tmp' ),\n\t\t\t'baseurl' => '/aimeos',\n\t\t] );\n\n\t\t$app['config']->set( 'shop.authorize', false );\n\t\t$app['config']->set( 'shop.disableSites', false );\n\t\t$app['config']->set( 'shop.accessControl', false );\n\t\t$app['config']->set( 'shop.admin.graphql.debug', true );\n\t\t$app['config']->set( 'shop.routes.jqadm', ['prefix' => '{site}/jqadm'] );\n\t\t$app['config']->set( 'shop.routes.graphql', ['prefix' => '{site}/graphql'] );\n\t\t$app['config']->set( 'shop.routes.jsonadm', ['prefix' => '{site}/jsonadm'] );\n\t\t$app['config']->set( 'shop.routes.jsonapi', ['prefix' => '{site}/jsonapi'] );\n\t\t$app['config']->set( 'shop.routes.account', ['prefix' => '{site}/profile'] );\n\t\t$app['config']->set( 'shop.routes.default', ['prefix' => '{site}/shop'] );\n\t\t$app['config']->set( 'shop.routes.confirm', ['prefix' => '{site}/shop'] );\n\t\t$app['config']->set( 'shop.routes.update', ['prefix' => '{site}'] );\n\t\t$app['config']->set( 'shop.routes.page', ['prefix' => '{site}/p'] );\n\t\t$app['config']->set( 'shop.routes.resolve', ['prefix' => '{site}', 'middleware' => ['web']] );\n\t\t$app['config']->set( 'shop.routes.login', [] );\n\t\t$app['config']->set( 'shop.mshop.locale.site', 'unittest' );\n\t\t$app['config']->set( 'shop.resource.email.from-email', 'root@localhost' );\n\n\t\tRoute::any( 'login', ['as' => 'login'] );\n\t\tRoute::any( 'logout', ['as' => 'logout'] );\n\t\tRoute::match( array( 'GET', 'POST' ), '{site}/resolve/{path?}', array(\n\t\t\t'as' => 'aimeos_resolve',\n\t\t\t'uses' => 'Aimeos\\Shop\\Controller\\ResolveController@indexAction'\n\t\t) )->where( ['locale' => '[a-z]{2}(\\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\\.\\-]+', 'path', '.*'] );\n\t}\n\n\n\tprotected function getPackageProviders( $app )\n\t{\n\t\treturn ['Aimeos\\Shop\\ShopServiceProvider'];\n\t}\n}"
  },
  {
    "path": "tests/Base/AimeosTest.php",
    "content": "<?php\n\nclass AimeosTest extends AimeosTestAbstract\n{\n\tpublic function testGet()\n\t{\n\t\t$object = $this->app->make( '\\Aimeos\\Shop\\Base\\Aimeos' )->get();\n\t\t$this->assertInstanceOf( '\\Aimeos\\Bootstrap', $object );\n\t}\n\n\n\tpublic function testGetVersion()\n\t{\n\t\t$object = $this->app->make( '\\Aimeos\\Shop\\Base\\Aimeos' );\n\t\t$this->assertIsString( $object->getVersion() );\n\t}\n}\n"
  },
  {
    "path": "tests/Base/ConfigTest.php",
    "content": "<?php\n\nclass ConfigTest extends AimeosTestAbstract\n{\n\tpublic function testGet()\n\t{\n\t\t$aimeos = $this->app->make( '\\Aimeos\\Shop\\Base\\Aimeos' );\n\n\t\t$configMock = $this->getMockBuilder( '\\Illuminate\\Config\\Repository' )\n\t\t\t->onlyMethods( array( 'get' ) )->getMock();\n\n\t\t$configMock->expects( $this->exactly( 4 ) )->method( 'get' )\n\t\t\t->willReturnOnConsecutiveCalls( true, 'laravel:', array(), array() );\n\n\t\t$object = new \\Aimeos\\Shop\\Base\\Config( $configMock, $aimeos );\n\n\t\t$this->assertInstanceOf( '\\Aimeos\\Base\\Config\\Iface', $object->get() );\n\t}\n}\n"
  },
  {
    "path": "tests/Base/ContextTest.php",
    "content": "<?php\n\nclass ContextTest extends AimeosTestAbstract\n{\n\tpublic function testGetNoLocale()\n\t{\n\t\t$session = $this->getMockBuilder( '\\Illuminate\\Session\\Store' )->disableOriginalConstructor()->getMock();\n\t\t$config = $this->app->make( '\\Aimeos\\Shop\\Base\\Config' );\n\t\t$locale = $this->app->make( '\\Aimeos\\Shop\\Base\\Locale' );\n\t\t$i18n = $this->app->make( '\\Aimeos\\Shop\\Base\\I18n' );\n\n\t\t$object = new \\Aimeos\\Shop\\Base\\Context( $session, $config, $locale, $i18n );\n\t\t$ctx = $object->get( false );\n\n\t\t$this->assertInstanceOf( '\\Aimeos\\MShop\\ContextIface', $ctx );\n\t\t$this->assertIsArray( $ctx->groups() );\n\t}\n}\n"
  },
  {
    "path": "tests/Base/I18nTest.php",
    "content": "<?php\n\nclass I18nTest extends AimeosTestAbstract\n{\n\tpublic function testGet()\n\t{\n\t\t$aimeos = $this->app->make( '\\Aimeos\\Shop\\Base\\Aimeos' );\n\n\t\t$configMock = $this->getMockBuilder( '\\Illuminate\\Config\\Repository' )\n\t\t\t->onlyMethods( array( 'get', 'has' ) )->getMock();\n\n\t\t$configMock->expects( $this->once() )->method( 'has' )\n\t\t\t->willReturn( true );\n\n\t\t$configMock->expects( $this->exactly( 3 ) )->method( 'get' )\n\t\t\t->willReturnOnConsecutiveCalls( true, 'laravel:', array() );\n\n\t\t$object = new \\Aimeos\\Shop\\Base\\I18n( $configMock, $aimeos );\n\t\t$list = $object->get( array( 'en' ) );\n\n\t\t$this->assertInstanceOf( '\\Aimeos\\Base\\Translation\\Iface', $list['en'] );\n\t}\n}\n"
  },
  {
    "path": "tests/Base/LocaleTest.php",
    "content": "<?php\n\nclass LocaleTest extends AimeosTestAbstract\n{\n\tpublic function testGetBackend()\n\t{\n\t\t$mock = $this->getMockBuilder( '\\Illuminate\\Config\\Repository' )->getMock();\n\t\t$context = $this->app->make( '\\Aimeos\\Shop\\Base\\Context' )->get( false, 'backend' );\n\n\t\t$object = new \\Aimeos\\Shop\\Base\\Locale( $mock );\n\n\t\t$this->assertInstanceOf( '\\Aimeos\\MShop\\Locale\\Item\\Iface', $object->getBackend( $context, 'unittest' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Base/SupportTest.php",
    "content": "<?php\n\nclass SupportTest extends AimeosTestAbstract\n{\n\tpublic function testCheckUserGroup()\n\t{\n\t\t$context = $this->app->make( '\\Aimeos\\Shop\\Base\\Context' );\n\t\t$locale = $this->app->make( '\\Aimeos\\Shop\\Base\\Locale' );\n\n\t\t$object = new \\Aimeos\\Shop\\Base\\Support( $context, $locale );\n\t\t$user = new \\Illuminate\\Foundation\\Auth\\User();\n\t\t$user->siteid = '0';\n\n\t\t$this->assertFalse( $object->checkUserGroup( $user, 'admin' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Base/ViewTest.php",
    "content": "<?php\n\n\nclass ViewTest extends AimeosTestAbstract\n{\n\tpublic function testCreateNoLocale()\n\t{\n\t\t$config = $this->getMockBuilder( '\\Illuminate\\Config\\Repository' )->getMock();\n\n\t\t$i18n = $this->getMockBuilder( '\\Aimeos\\Shop\\Base\\I18n' )\n\t\t\t->disableOriginalConstructor()\n\t\t\t->getMock();\n\n\t\t$support = $this->getMockBuilder( '\\Aimeos\\Shop\\Base\\Support' )\n\t\t\t->disableOriginalConstructor()\n\t\t\t->getMock();\n\n\t\t$context = new \\Aimeos\\MShop\\Context();\n\t\t$context->setConfig( new \\Aimeos\\Base\\Config\\PHPArray() );\n\t\t$context->setSession( new \\Aimeos\\Base\\Session\\None() );\n\n\t\t$object = new \\Aimeos\\Shop\\Base\\View( $config, $i18n, $support );\n\n\t\t$this->assertInstanceOf( '\\Aimeos\\Base\\View\\Iface', $object->create( $context, array() ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Command/AccountCommandTest.php",
    "content": "<?php\n\nclass AccountCommandTest extends AimeosTestAbstract\n{\n\tpublic function testAccountCommandNew()\n\t{\n\t\t$args = array( 'site' => 'unittest', 'email' => 'unitCustomer@example.com', '--password' => 'test' );\n\t\t$this->assertEquals( 0, $this->artisan( 'aimeos:account', $args ) );\n\t}\n\n\n\tpublic function testAccountCommandAdmin()\n\t{\n\t\t$args = array( 'site' => 'unittest', 'email' => 'unitCustomer@example.com', '--password' => 'test', '--admin' => true );\n\t\t$this->assertEquals( 0, $this->artisan( 'aimeos:account', $args ) );\n\t}\n\n\n\tpublic function testAccountCommandEditor()\n\t{\n\t\t$args = array( 'site' => 'unittest', 'email' => 'unitCustomer@example.com', '--password' => 'test', '--editor' => true );\n\t\t$this->assertEquals( 0, $this->artisan( 'aimeos:account', $args ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Command/ClearCommandTest.php",
    "content": "<?php\n\nclass ClearCommandTest extends AimeosTestAbstract\n{\n\tpublic function testSetupCommand()\n\t{\n\t\t$this->assertEquals( 0, $this->artisan( 'aimeos:clear' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Command/JobsCommandTest.php",
    "content": "<?php\n\nclass JobsCommandTest extends AimeosTestAbstract\n{\n\tpublic function testJobsCommand()\n\t{\n\t\t$this->assertEquals( 0, $this->artisan( 'aimeos:jobs', array( 'jobs' => 'customer/email/watch', 'site' => 'unittest' ) ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Command/SetupCommandTest.php",
    "content": "<?php\n\nclass SetupCommandTest extends AimeosTestAbstract\n{\n\tpublic function testSetupCommand()\n\t{\n\t\t$args = array( 'site' => 'unittest', 'tplsite' => 'unittest', '--option' => 'setup/default/demo:0' );\n\t\t$this->assertEquals( 0, $this->artisan( 'aimeos:setup', $args ) );\n\t}\n}\n"
  },
  {
    "path": "tests/Controller/AccountControllerTest.php",
    "content": "<?php\n\nclass AccountControllerTest extends AimeosTestAbstract\n{\n\tpublic function testActions()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\AccountController@indexAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos account-profile\"', $response->getContent() );\n\t}\n\n\n\tpublic function testDownload()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\AccountController@downloadAction', ['site' => 'unittest', 'dl_id' => 0] );\n\n\t\t$this->assertEquals( 401, $response->getStatusCode() );\n\t}\n}"
  },
  {
    "path": "tests/Controller/AdminControllerTest.php",
    "content": "<?php\n\nclass AdminControllerTest extends AimeosTestAbstract\n{\n\tpublic function testIndexAction()\n\t{\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\AdminController@indexAction' );\n\n\t\t$this->assertEquals( '302', $response->getStatusCode() );\n\t}\n}"
  },
  {
    "path": "tests/Controller/BasketControllerTest.php",
    "content": "<?php\n\nclass BasketControllerTest extends AimeosTestAbstract\n{\n\tpublic function testActions()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\BasketController@indexAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos basket-standard\"', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos basket-related\"', $response->getContent() );\n\t}\n}"
  },
  {
    "path": "tests/Controller/CatalogControllerTest.php",
    "content": "<?php\n\nclass CatalogControllerTest extends AimeosTestAbstract\n{\n\tpublic function testCountAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@countAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringStartsWith( '{\"', $response->getContent() );\n\t}\n\n\n\tpublic function testDetailAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@detailAction', ['site' => 'unittest', 'd_name' => 'Cafe_Noire_Cappuccino'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-stage', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"aimeos catalog-detail', $response->getContent() );\n\t}\n\n\n\tpublic function testListAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@listAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-filter', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-list', $response->getContent() );\n\t}\n\n\n\tpublic function testSessionAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@sessionAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-session', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"section catalog-session-pinned', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"section catalog-session-seen', $response->getContent() );\n\t}\n\n\n\tpublic function testStockAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@stockAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '.aimeos .product .stock', $response->getContent() );\n\t}\n\n\n\tpublic function testSuggestAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@suggestAction', ['site' => 'unittest'], ['f_search' => 'Cafe'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertMatchesRegularExpression( '/[{.*}]/', $response->getContent() );\n\t}\n\n\n\tpublic function testTreeAction()\n\t{\n\t\t$root = \\Aimeos\\Controller\\Frontend::create( app( 'aimeos.context' )->get(), 'catalog' )->getTree( \\Aimeos\\Controller\\Frontend\\Catalog\\Iface::TREE );\n\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CatalogController@treeAction', ['site' => 'unittest', 'f_catid' => $root->getId(), 'f_name' => 'test'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-filter', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-list', $response->getContent() );\n\t}\n}"
  },
  {
    "path": "tests/Controller/CheckoutControllerTest.php",
    "content": "<?php\n\nclass CheckoutControllerTest extends AimeosTestAbstract\n{\n\tpublic function testConfirmAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CheckoutController@confirmAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t}\n\n\n\tpublic function testIndexAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CheckoutController@indexAction', ['site' => 'unittest'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section checkout-standard-address\"', $response->getContent() );\n\t}\n\n\n\tpublic function testUpdateAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\CheckoutController@updateAction', ['site' => 'unittest'], ['code' => 'paypalexpress'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertEquals( '', $response->getContent() );\n\t}\n}"
  },
  {
    "path": "tests/Controller/GraphqlControllerTest.php",
    "content": "<?php\n\nclass GraphqlControllerTest extends AimeosTestAbstract\n{\n\tpublic function testQuery()\n\t{\n\t\t$params = ['site' => 'unittest'];\n\t\t$body = '{\"query\":\"query {\\n  findProduct(code: \\\"CNC\\\") {\\n    id\\n    code\\n  }\\n}\\n\",\"variables\":{},\"operationName\":null}';\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\GraphqlController@indexAction', $params, [], [], [], [], $body );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t}\n}\n"
  },
  {
    "path": "tests/Controller/JqadmControllerTest.php",
    "content": "<?php\n\nclass JqadmControllerTest extends AimeosTestAbstract\n{\n\tpublic function testFileActionCss()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@fileAction', ['site' => 'unittest', 'name' => 'index-css', 'locale' => 'en'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '.aimeos', $response->getContent() );\n\t}\n\n\n\tpublic function testFileActionJs()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@fileAction', ['site' => 'unittest', 'name' => 'index-js', 'locale' => 'en'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( 'Aimeos = {', $response->getContent() );\n\t}\n\n\n\tpublic function testBatchAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product', 'id' => ['0', '1']];\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\JqadmController@batchAction', $params );\n\n\t\t$this->assertEquals( 302, $response->getStatusCode() );\n\t}\n\n\n\tpublic function testCopyAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product', 'id' => '0'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@copyAction', $params );\n\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertStringContainsString( 'item-product', $response->getContent() );\n\t}\n\n\n\tpublic function testCreateAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@createAction', $params );\n\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertStringContainsString( 'list-items', $response->getContent() );\n\t}\n\n\n\tpublic function testDeleteAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\t\t$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product', 'id' => '0'];\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\JqadmController@deleteAction', $params );\n\n\t\t$this->assertEquals( 302, $response->getStatusCode() );\n\t}\n\n\n\tpublic function testExportAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'order'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@exportAction', $params );\n\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertStringContainsString( 'list-items', $response->getContent() );\n\t}\n\n\n\tpublic function testGetAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product', 'id' => '0'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@getAction', $params );\n\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertStringContainsString( 'item-product', $response->getContent() );\n\t}\n\n\n\tpublic function testSaveAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\t\t$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product', 'item' => ['product.code' => 'jqadmSaveTest']];\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\JqadmController@saveAction', $params );\n\n\t\t$this->assertEquals( 302, $response->getStatusCode() );\n\t}\n\n\n\tpublic function testSearchAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@searchAction', $params );\n\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertStringContainsString( 'list-items', $response->getContent() );\n\t}\n\n\n\tpublic function testSearchActionSite()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'invalid', 'resource' => 'product'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JqadmController@searchAction', $params );\n\n\t\t$this->assertEquals( 500, $response->getStatusCode() );\n\t}\n}\n"
  },
  {
    "path": "tests/Controller/JsonadmControllerTest.php",
    "content": "<?php\n\nclass JsonadmControllerTest extends AimeosTestAbstract\n{\n\tpublic function testOptionsAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\t\t$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product'];\n\t\t$response = $this->action( 'OPTIONS', '\\Aimeos\\Shop\\Controller\\JsonadmController@optionsAction', $params );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertArrayHasKey( 'resources', $json['meta'] );\n\t\t$this->assertGreaterThan( 1, count( $json['meta']['resources'] ) );\n\n\n\t\t$params = ['site' => 'unittest'];\n\t\t$response = $this->action( 'OPTIONS', '\\Aimeos\\Shop\\Controller\\JsonadmController@optionsAction', $params );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t\t$this->assertArrayHasKey( 'resources', $json['meta'] );\n\t\t$this->assertGreaterThan( 1, count( $json['meta']['resources'] ) );\n\t}\n\n\n\tpublic function testActionsSingle()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\t\t$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type'];\n\t\t$content = '{\"data\":{\"type\":\"stock/type\",\"attributes\":{\"stock.type.code\":\"laravel\",\"stock.type.label\":\"laravel\"}}}';\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\JsonadmController@postAction', $params, [], [], [], [], $content );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertEquals( 201, $response->getStatusCode() );\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data']['attributes'] );\n\t\t$this->assertEquals( 'laravel', $json['data']['attributes']['stock.type.code'] );\n\t\t$this->assertEquals( 'laravel', $json['data']['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( 1, $json['meta']['total'] );\n\n\t\t$id = $json['data']['attributes']['stock.type.id'];\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type', 'id' => $id];\n\t\t$content = '{\"data\":{\"type\":\"stock/type\",\"attributes\":{\"stock.type.code\":\"laravel2\",\"stock.type.label\":\"laravel2\"}}}';\n\t\t$response = $this->action( 'PATCH', '\\Aimeos\\Shop\\Controller\\JsonadmController@patchAction', $params, [], [], [], [], $content );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data']['attributes'] );\n\t\t$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.code'] );\n\t\t$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( $id, $json['data']['attributes']['stock.type.id'] );\n\t\t$this->assertEquals( 1, $json['meta']['total'] );\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type', 'id' => $id];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JsonadmController@getAction', $params );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data']['attributes'] );\n\t\t$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.code'] );\n\t\t$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( $id, $json['data']['attributes']['stock.type.id'] );\n\t\t$this->assertEquals( 1, $json['meta']['total'] );\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type', 'id' => $id];\n\t\t$response = $this->action( 'DELETE', '\\Aimeos\\Shop\\Controller\\JsonadmController@deleteAction', $params );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 1, $json['meta']['total'] );\n\t}\n\n\n\tpublic function testActionsBulk()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\t\t$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type'];\n\t\t$content = '{\"data\":[\n\t\t\t{\"type\":\"stock/type\",\"attributes\":{\"stock.type.code\":\"laravel\",\"stock.type.label\":\"laravel\"}},\n\t\t\t{\"type\":\"stock/type\",\"attributes\":{\"stock.type.code\":\"laravel2\",\"stock.type.label\":\"laravel\"}}\n\t\t]}';\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\JsonadmController@postAction', $params, [], [], [], [], $content );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertEquals( 201, $response->getStatusCode() );\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 2, count( $json['data'] ) );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data'][0]['attributes'] );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data'][1]['attributes'] );\n\t\t$this->assertEquals( 'laravel', $json['data'][0]['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( 'laravel', $json['data'][1]['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( 2, $json['meta']['total'] );\n\n\t\t$ids = array( $json['data'][0]['attributes']['stock.type.id'], $json['data'][1]['attributes']['stock.type.id'] );\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type'];\n\t\t$content = '{\"data\":[\n\t\t\t{\"type\":\"stock/type\",\"id\":' . $ids[0] . ',\"attributes\":{\"stock.type.label\":\"laravel2\"}},\n\t\t\t{\"type\":\"stock/type\",\"id\":' . $ids[1] . ',\"attributes\":{\"stock.type.label\":\"laravel2\"}}\n\t\t]}';\n\t\t$response = $this->action( 'PATCH', '\\Aimeos\\Shop\\Controller\\JsonadmController@patchAction', $params, [], [], [], [], $content );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 2, count( $json['data'] ) );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data'][0]['attributes'] );\n\t\t$this->assertArrayHasKey( 'stock.type.id', $json['data'][1]['attributes'] );\n\t\t$this->assertEquals( 'laravel2', $json['data'][0]['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( 'laravel2', $json['data'][1]['attributes']['stock.type.label'] );\n\t\t$this->assertTrue( in_array( $json['data'][0]['attributes']['stock.type.id'], $ids ) );\n\t\t$this->assertTrue( in_array( $json['data'][1]['attributes']['stock.type.id'], $ids ) );\n\t\t$this->assertEquals( 2, $json['meta']['total'] );\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type'];\n\t\t$getParams = ['filter' => ['&&' => [\n\t\t\t['=~' => ['stock.type.code' => 'laravel']],\n\t\t\t['==' => ['stock.type.label' => 'laravel2']]\n\t\t\t]],\n\t\t\t'sort' => 'stock.type.code', 'page' => ['offset' => 0, 'limit' => 3]\n\t\t];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JsonadmController@getAction', $params, $getParams );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 2, count( $json['data'] ) );\n\t\t$this->assertEquals( 'laravel', $json['data'][0]['attributes']['stock.type.code'] );\n\t\t$this->assertEquals( 'laravel2', $json['data'][1]['attributes']['stock.type.code'] );\n\t\t$this->assertEquals( 'laravel2', $json['data'][0]['attributes']['stock.type.label'] );\n\t\t$this->assertEquals( 'laravel2', $json['data'][1]['attributes']['stock.type.label'] );\n\t\t$this->assertTrue( in_array( $json['data'][0]['attributes']['stock.type.id'], $ids ) );\n\t\t$this->assertTrue( in_array( $json['data'][1]['attributes']['stock.type.id'], $ids ) );\n\t\t$this->assertEquals( 2, $json['meta']['total'] );\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type'];\n\t\t$content = '{\"data\":[\n\t\t\t{\"type\":\"stock/type\",\"id\":' . $ids[0] . '},\n\t\t\t{\"type\":\"stock/type\",\"id\":' . $ids[1] . '}\n\t\t]}';\n\t\t$response = $this->action( 'DELETE', '\\Aimeos\\Shop\\Controller\\JsonadmController@deleteAction', $params, [], [], [], [], $content );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 2, $json['meta']['total'] );\n\t}\n\n\n\tpublic function testPutAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\t\t$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'stock/type'];\n\t\t$content = '{\"data\":[\n\t\t\t{\"type\":\"stock/type\",\"attributes\":{\"stock.type.code\":\"laravel\",\"stock.type.label\":\"laravel\"}},\n\t\t\t{\"type\":\"stock/type\",\"attributes\":{\"stock.type.code\":\"laravel2\",\"stock.type.label\":\"laravel\"}}\n\t\t]}';\n\t\t$response = $this->action( 'PUT', '\\Aimeos\\Shop\\Controller\\JsonadmController@postAction', $params, [], [], [], [], $content );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertEquals( 501, $response->getStatusCode() );\n\t\t$this->assertNotNull( $json );\n\t}\n}\n"
  },
  {
    "path": "tests/Controller/JsonapiControllerTest.php",
    "content": "<?php\n\nclass JsonapiControllerTest extends AimeosTestAbstract\n{\n\tpublic function testOptionsAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest'];\n\t\t$response = $this->action( 'OPTIONS', '\\Aimeos\\Shop\\Controller\\JsonapiController@optionsAction', $params );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertArrayHasKey( 'resources', $json['meta'] );\n\t\t$this->assertGreaterThan( 1, count( $json['meta']['resources'] ) );\n\t}\n\n\n\tpublic function testGetAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product'];\n\t\t$getParams = ['filter' => ['f_search' => 'Cafe Noire Cap'], 'sort' => 'product.code'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JsonapiController@getAction', $params, $getParams );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 3, $json['meta']['total'] );\n\t\t$this->assertEquals( 3, count( $json['data'] ) );\n\t\t$this->assertArrayHasKey( 'id', $json['data'][0] );\n\t\t$this->assertEquals( 'CNC', $json['data'][0]['attributes']['product.code'] );\n\n\t\t$id = $json['data'][0]['id'];\n\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'product', 'id' => $id];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JsonapiController@getAction', $params );\n\n\t\t$json = json_decode( $response->getContent(), true );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertNotNull( $json );\n\t\t$this->assertEquals( 1, $json['meta']['total'] );\n\t\t$this->assertArrayHasKey( 'id', $json['data'] );\n\t\t$this->assertEquals( 'CNC', $json['data']['attributes']['product.code'] );\n\t}\n\n\n\tpublic function testPostPatchDeleteAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t// get CNC product\n\t\t$params = ['site' => 'unittest', 'resource' => 'product'];\n\t\t$getParams = ['filter' => ['f_search' => 'Cafe Noire Cap', 'f_listtype' => 'unittype19'], 'sort' => 'product.code'];\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\JsonapiController@getAction', $params, $getParams );\n\n\t\t$this->assertResponseOk();\n\t\t$json = json_decode( $response->getContent(), true );\n\t\t$this->assertEquals( 'CNC', $json['data'][0]['attributes']['product.code'] );\n\n\t\t// add CNC product to basket\n\t\t$params = ['site' => 'unittest', 'resource' => 'basket', 'id' => 'default', 'related' => 'product'];\n\t\t$content = json_encode( ['data' => ['attributes' => ['product.id' => $json['data'][0]['id']]]] );\n\t\t$response = $this->action( 'POST', '\\Aimeos\\Shop\\Controller\\JsonapiController@postAction', $params, [], [], [], [], $content );\n\n\t\t$this->assertEquals( 201, $response->getStatusCode() );\n\t\t$json = json_decode( $response->getContent(), true );\n\t\t$this->assertEquals( 'CNC', $json['included'][0]['attributes']['order.product.prodcode'] );\n\n\t\t// change product quantity in basket\n\t\t$params = ['site' => 'unittest', 'resource' => 'basket', 'id' => 'default', 'related' => 'product', 'relatedid' => 0];\n\t\t$content = json_encode( ['data' => ['attributes' => ['quantity' => 2]]] );\n\t\t$response = $this->action( 'PATCH', '\\Aimeos\\Shop\\Controller\\JsonapiController@patchAction', $params, [], [], [], [], $content );\n\n\t\t$this->assertResponseOk();\n\t\t$json = json_decode( $response->getContent(), true );\n\t\t$this->assertEquals( 2, $json['included'][0]['attributes']['order.product.quantity'] );\n\n\t\t// delete product from basket\n\t\t$params = ['site' => 'unittest', 'resource' => 'basket', 'id' => 'default', 'related' => 'product', 'relatedid' => 0];\n\t\t$response = $this->action( 'DELETE', '\\Aimeos\\Shop\\Controller\\JsonapiController@deleteAction', $params );\n\n\t\t$this->assertResponseOk();\n\t\t$json = json_decode( $response->getContent(), true );\n\t\t$this->assertEquals( 0, count( $json['included'] ) );\n\t}\n\n\n\tpublic function testPutAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$params = ['site' => 'unittest', 'resource' => 'basket'];\n\t\t$response = $this->action( 'PUT', '\\Aimeos\\Shop\\Controller\\JsonapiController@putAction', $params );\n\n\t\t$this->assertEquals( 403, $response->getStatusCode() );\n\t\t$json = json_decode( $response->getContent(), true );\n\t\t$this->assertArrayHasKey( 'errors', $json );\n\t}\n}\n"
  },
  {
    "path": "tests/Controller/PageControllerTest.php",
    "content": "<?php\n\nclass PageControllerTest extends AimeosTestAbstract\n{\n\tpublic function testIndexAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\PageController@indexAction', ['site' => 'unittest', 'path' => 'contact'] );\n\n\t\t$this->assertEquals( 200, $response->getStatusCode() );\n\t}\n}"
  },
  {
    "path": "tests/Controller/ResolveControllerTest.php",
    "content": "<?php\n\nclass ResolveControllerTest extends AimeosTestAbstract\n{\n\tpublic function testCategory()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\ResolveController@indexAction', ['site' => 'unittest', 'path' => 'tee'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-filter', $response->getContent() );\n\t}\n\n\n\tpublic function testProduct()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\ResolveController@indexAction', ['site' => 'unittest', 'path' => 'Cafe_Noire_Cappuccino'] );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"aimeos catalog-detail', $response->getContent() );\n\t}\n\n\n\tpublic function testNotFound()\n\t{\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\ResolveController@indexAction', ['site' => 'unittest', 'path' => 'invalid'] );\n\n\t\t$response->assertStatus( 404 );\n\t}\n\n\n\tprotected function getEnvironmentSetUp( $app )\n\t{\n\t\tparent::getEnvironmentSetUp( $app );\n\n\t\t$app['config']->set( 'shop.client.html.catalog.detail.url.target', 'aimeos_resolve' );\n\t}\n}"
  },
  {
    "path": "tests/Controller/SupplierControllerTest.php",
    "content": "<?php\n\nclass SupplierControllerTest extends AimeosTestAbstract\n{\n\tpublic function testDetailAction()\n\t{\n\t\tView::addLocation( dirname( __DIR__ ) . '/fixtures/views' );\n\n\t\t$context = app( 'aimeos.context' )->get( true );\n\t\t$item = \\Aimeos\\Controller\\Frontend::create( $context, 'supplier' )->slice( 0, 1 )->search()->first();\n\t\t$params = ['site' => 'unittest', 's_name' => 'Test supplier', 'f_supid' => $item->getId()];\n\n\t\t$response = $this->action( 'GET', '\\Aimeos\\Shop\\Controller\\SupplierController@detailAction', $params );\n\n\t\t$this->assertResponseOk();\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos supplier-detail', $response->getContent() );\n\t\t$this->assertStringContainsString( '<div class=\"section aimeos catalog-list', $response->getContent() );\n\t}\n}"
  },
  {
    "path": "tests/FacadesTest.php",
    "content": "<?php\n\n\nclass FacadesTest extends AimeosTestAbstract\n{\n\tpublic function testAttribute()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Attribute::uses( [] ) );\n\t}\n\n\n\tpublic function testBasket()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Basket::clear() );\n\t}\n\n\n\tpublic function testCatalog()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Catalog::uses( [] ) );\n\t}\n\n\n\tpublic function testCms()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Cms::uses( [] ) );\n\t}\n\n\n\tpublic function testCustomer()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Customer::uses( [] ) );\n\t}\n\n\n\tpublic function testLocale()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Locale::compare( '==', 'locale.id', -1 ) );\n\t}\n\n\n\tpublic function testOrder()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Order::uses( [] ) );\n\t}\n\n\n\tpublic function testProduct()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Product::uses( [] ) );\n\t}\n\n\n\tpublic function testService()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Service::uses( [] ) );\n\t}\n\n\n\tpublic function testStock()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Stock::compare( '==', 'stock.id', -1 ) );\n\t}\n\n\n\tpublic function testSubscription()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Subscription::uses( [] ) );\n\t}\n\n\n\tpublic function testSupplier()\n\t{\n\t\t$this->assertInstanceOf( \\Aimeos\\Controller\\Frontend\\Iface::class, \\Aimeos\\Shop\\Facades\\Supplier::uses( [] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/HelpersTest.php",
    "content": "<?php\n\n\nclass HelpersTest extends AimeosTestAbstract\n{\n\tpublic function testAiconfig()\n\t{\n\t\t$this->assertEquals( 'notexisting', aiconfig( 'not/exists', 'notexisting' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/fixtures/views/app.blade.php",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta name=\"csrf-token\" content=\"{{ csrf_token() }}\" />\n\t\t@yield('aimeos_header')\n\t\t<title>Laravel</title>\n\t\t@yield('aimeos_styles')\n\t</head>\n\t<body>\n\t\t<nav class=\"navbar navbar-default\">\n\t\t\t@yield('aimeos_head')\n\t\t</nav>\n\t\t@yield('aimeos_nav')\n\t\t@yield('aimeos_stage')\n\t\t@yield('aimeos_body')\n\t\t@yield('aimeos_aside')\n\t\t@yield('content')\n\n\t\t@yield('aimeos_scripts')\n\t</body>\n</html>\n"
  },
  {
    "path": "views/account/index.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Profile') }}</title>\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['account/profile'] ?? '' ?>\n\t<?= $aiheader['account/review'] ?? '' ?>\n\t<?= $aiheader['account/subscription'] ?? '' ?>\n\t<?= $aiheader['account/basket'] ?? '' ?>\n\t<?= $aiheader['account/history'] ?? '' ?>\n\t<?= $aiheader['account/favorite'] ?? '' ?>\n\t<?= $aiheader['account/watch'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/session'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<?= $aibody['account/profile'] ?? '' ?>\n\t\t<?= $aibody['account/review'] ?? '' ?>\n\t\t<?= $aibody['account/subscription'] ?? '' ?>\n\t\t<?= $aibody['account/basket'] ?? '' ?>\n\t\t<?= $aibody['account/history'] ?? '' ?>\n\t\t<?= $aibody['account/favorite'] ?? '' ?>\n\t\t<?= $aibody['account/watch'] ?? '' ?>\n\t</div>\n@stop\n\n@section('aimeos_aside')\n\t<?= $aibody['catalog/session'] ?? '' ?>\n@stop\n"
  },
  {
    "path": "views/admin/index.blade.php",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t\t<title>Aimeos administration interface</title>\n\n\t\t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/font-awesome@4/css/font-awesome.min.css\">\n\t\t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css\">\n\n\t\t<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->\n\t\t<!--[if lt IE 9]>\n\t\t<script src=\"https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js\"></script>\n\t\t<script src=\"https://oss.maxcdn.com/respond/1.4.2/respond.min.js\"></script>\n\t\t<![endif]-->\n\n\t\t<style type=\"text/css\">\n\t\t\tform.login {\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: 50%;\n\t\t\t\ttop: 50%;\n\t\t\t\twidth: 20rem;\n\t\t\t\tmargin: -6rem -10rem;\n\t\t\t}\n\n\t\t\tform.login .input-group-addon {\n\t\t\t\tfont-size: 1.25rem;\n\t\t\t\tmin-width: 3.65rem;\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\n\t\t<form class=\"login\" method=\"POST\" action=\"{{ url('login') }}\" >\n\t\t\t{!! csrf_field() !!}\n\t\t\t<div class=\"form-group input-group input-group-lg\">\n\t\t\t\t<span class=\"input-group-addon fa fa-at\" id=\"email-addon\"></span>\n\t\t\t\t<input class=\"form-control\" type=\"email\" name=\"email\" required=\"required\" placeholder=\"Email\" value=\"{{ old('email') }}\" aria-describedby=\"email-addon\">\n\t\t\t</div>\n\t\t\t<div class=\"form-group input-group input-group-lg\">\n\t\t\t\t<span class=\"input-group-addon fa fa-lock\" id=\"password-addon\"></span>\n\t\t\t\t<input class=\"form-control\" type=\"password\" name=\"password\" required=\"required\" placeholder=\"Password\" aria-describedby=\"password-addon\">\n\t\t\t</div>\n\t\t\t<hr>\n\t\t\t<button class=\"btn btn-block btn-lg btn-primary\" type=\"submit\">Login</button>\n\t\t</form>\n\t\t<script src=\"https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js\"></script>\n\t\t<script src=\"https://cdn.jsdelivr.net/npm/bootstrap@4/dist/js/bootstrap.bundle.min.js\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "views/base.blade.php",
    "content": "<!DOCTYPE html>\n<html class=\"no-js\" lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\" dir=\"{{ in_array(app()->getLocale(), ['ar', 'az', 'dv', 'fa', 'he', 'ku', 'ur']) ? 'rtl' : 'ltr' }}\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t\t<meta name=\"csrf-token\" content=\"{{ csrf_token() }}\">\n\n\t\t@if( config('app.debug') !== true )\n\t\t\t<meta http-equiv=\"Content-Security-Policy\" content=\"base-uri 'self'; default-src 'self' 'nonce-{{ app( 'aimeos.context' )->get()->nonce() }}'; {{ config( 'shop.csp.frontend', 'style-src \\'unsafe-inline\\' \\'self\\'; img-src \\'self\\' data: https://aimeos.org; frame-src https://www.youtube.com https://player.vimeo.com' ) }}\">\n\t\t@endif\n\n\t\t@if( in_array(app()->getLocale(), ['ar', 'az', 'dv', 'fa', 'he', 'ku', 'ur']) )\n\t\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"{{ asset('vendor/shop/themes/default/app.rtl.css?v=' . config( 'shop.version', 1 ) ) }}\">\n\t\t@else\n\t\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"{{ asset('vendor/shop/themes/default/app.css?v=' . config( 'shop.version', 1 ) ) }}\">\n\t\t@endif\n\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"{{ asset('vendor/shop/themes/default/aimeos.css?v=' . config( 'shop.version', 1 ) ) }}\">\n\n\t\t@yield('aimeos_header')\n\n\t\t<style nonce=\"{{ app( 'aimeos.context' )->get()->nonce() }}\">\n\t\t\t:root {\n\t\t\t\t@foreach( app( 'aimeos.context' )->get()->locale()->getSiteItem()->getConfigValue( 'theme/default', [] ) as $key => $value )\n\t\t\t\t\t{{ $key }}: {{ $value }};\n\t\t\t\t@endforeach\n\t\t\t}\n\t\t</style>\n\n\t\t<link rel=\"icon\" href=\"{{ asset( app( 'aimeos.context' )->get()->config()->get( 'resource/fs-media/baseurl' ) . '/' . ( app( 'aimeos.context' )->get()->locale()->getSiteItem()->getIcon() ?: '../vendor/shop/themes/default/assets/icon.png' ) ) }}\">\n\n\t\t<link rel=\"preload\" href=\"{{ asset('vendor/shop/themes/default/assets/roboto-condensed-v19-latin-regular.woff2') }}\" as=\"font\" type=\"font/woff2\" crossorigin>\n\t\t<link rel=\"preload\" href=\"{{ asset('vendor/shop/themes/default/assets/roboto-condensed-v19-latin-700.woff2') }}\" as=\"font\" type=\"font/woff2\" crossorigin>\n\t\t<link rel=\"preload\" href=\"{{ asset('vendor/shop/themes/default/assets/bootstrap-icons.woff2') }}\" as=\"font\" type=\"font/woff2\" crossorigin>\n\t</head>\n\t<body class=\"{{ $page ?? '' }}\">\n\t\t<nav class=\"navbar navbar-expand-md navbar-top\">\n\t\t\t<a class=\"navbar-brand\" href=\"/\" title=\"{{ __('To the home page') }}\">\n\t\t\t\t<img src=\"{{ asset( app( 'aimeos.context' )->get()->config()->get( 'resource/fs-media/baseurl' ) . '/' . ( app( 'aimeos.context' )->get()->locale()->getSiteItem()->getLogo() ?: '../vendor/shop/themes/default/assets/logo.png' ) ) }}\" height=\"40\" alt=\"{{ __('To the home page') }}\">\n\t\t\t</a>\n\n\t\t\t<button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#navbar-top\" aria-controls=\"navbar-top\" aria-expanded=\"false\" aria-label=\"Toggle navigation\">\n\t\t\t\t<span class=\"navbar-toggler-icon\"></span>\n\t\t\t</button>\n\t\t\t<div class=\"collapse navbar-collapse\" id=\"navbar-top\">\n\t\t\t\t@yield('aimeos_head_nav')\n\t\t\t</div>\n\n\t\t\t@yield('aimeos_head_locale')\n\t\t\t@yield('aimeos_head_search')\n\n\t\t\t<ul class=\"navbar-nav\">\n\t\t\t\t@if (Auth::guest() && config('app.shop_registration'))\n\t\t\t\t\t<li class=\"nav-item register\"><a class=\"nav-link\" href=\"{{ airoute( 'register' ) }}\" title=\"{{ __( 'Register' ) }}\"><span class=\"name\">{{ __('Register') }}</span></a></li>\n\t\t\t\t@endif\n\t\t\t\t@if (Auth::guest())\n\t\t\t\t\t<li class=\"nav-item login\"><a class=\"nav-link\" href=\"{{ airoute( 'login' ) }}\" title=\"{{ __( 'Login' ) }}\"><span class=\"name\">{{ __( 'Login' ) }}</span></a></li>\n\t\t\t\t@else\n\t\t\t\t\t<li class=\"nav-item login profile dropdown\">\n\t\t\t\t\t\t<a href=\"#\" class=\"nav-link dropdown-toggle\" data-bs-toggle=\"dropdown\" role=\"button\" aria-expanded=\"false\" title=\"{{ __( 'Account' ) }}\"><span class=\"name\">{{ __( 'Account' ) }}</span> <span class=\"caret\"></span></a>\n\t\t\t\t\t\t<ul class=\"dropdown-menu dropdown-menu-end\" role=\"menu\">\n\t\t\t\t\t\t\t<li class=\"dropdown-item\"><a class=\"nav-link\" href=\"{{ airoute( 'aimeos_shop_account' ) }}\"><span class=\"name\">{{ __( 'Profile' ) }}</span></a></li>\n\t\t\t\t\t\t\t<li class=\"dropdown-item\"><form id=\"logout\" action=\"{{ airoute( 'logout' ) }}\" method=\"POST\">{{ csrf_field() }}<button class=\"nav-link\"><span class=\"name\">{{ __( 'Logout' ) }}</span></button></form></li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t@endif\n\t\t\t</ul>\n\n\t\t\t@yield('aimeos_head_basket')\n\t\t</nav>\n\n\t\t<div class=\"content\">\n\t\t\t@yield('aimeos_stage')\n\t\t\t<main>\n\t\t\t\t@yield('aimeos_body')\n\t\t\t\t@yield('content')\n\t\t\t</main>\n\t\t</div>\n\n\n\t\t<footer>\n\t\t\t<div class=\"container-fluid\">\n\t\t\t\t<div class=\"row\">\n\t\t\t\t\t<div class=\"col-md-8\">\n\t\t\t\t\t\t<div class=\"row\">\n\t\t\t\t\t\t\t<div class=\"col-sm-6 footer-left\">\n\t\t\t\t\t\t\t\t<div class=\"footer-block\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"pb-3\" aria-label=\"{{ __('Legal information') }}\">{{ __( 'LEGAL' ) }}</h2>\n\t\t\t\t\t\t\t\t\t<p><a href=\"{{ airoute(config('shop.client.html.cms.page.url.target', 'aimeos_page'), ['path' => 'terms']) }}\">{{ __( 'Terms & Conditions' ) }}</a></p>\n\t\t\t\t\t\t\t\t\t<p><a href=\"{{ airoute(config('shop.client.html.cms.page.url.target', 'aimeos_page'), ['path' => 'privacy']) }}\">{{ __( 'Privacy Notice' ) }}</a></p>\n\t\t\t\t\t\t\t\t\t<p><a href=\"{{ airoute(config('shop.client.html.cms.page.url.target', 'aimeos_page'), ['path' => 'cancel']) }}\">{{ __( 'Cancellation' ) }}</a></p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"col-sm-6 footer-center\">\n\t\t\t\t\t\t\t\t<div class=\"footer-block\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"pb-3\" aria-label=\"{{ __('About the company') }}\">{{ __( 'ABOUT US' ) }}</h2>\n\t\t\t\t\t\t\t\t\t<p><a href=\"{{ airoute(config('shop.client.html.cms.page.url.target', 'aimeos_page'), ['path' => 'contact']) }}\">{{ __( 'Contact us' ) }}</a></p>\n\t\t\t\t\t\t\t\t\t<p><a href=\"{{ airoute(config('shop.client.html.cms.page.url.target', 'aimeos_page'), ['path' => 'about']) }}\">{{ __( 'Company' ) }}</a></p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"col-md-4 footer-right\">\n\t\t\t\t\t\t<div class=\"footer-block\">\n\t\t\t\t\t\t\t<a class=\"logo\" href=\"/\" title=\"{{ __('To the home page') }}\">\n\t\t\t\t\t\t\t\t<img src=\"{{ asset( app( 'aimeos.context' )->get()->config()->get( 'resource/fs-media/baseurl' ) . '/' . ( app( 'aimeos.context' )->get()->locale()->getSiteItem()->getLogo() ?: '../vendor/shop/themes/default/assets/logo.png' ) ) }}\" height=\"40\" alt=\"{{ __('To the home page') }}\">\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t<div class=\"social\" aria-label=\"{{ __('Social media links') }}\">\n\t\t\t\t\t\t\t\t<p><a href=\"#\" class=\"sm facebook\" title=\"Facebook\" rel=\"noopener\">Facebook</a></p>\n\t\t\t\t\t\t\t\t<p><a href=\"#\" class=\"sm twitter\" title=\"Twitter\" rel=\"noopener\">Twitter</a></p>\n\t\t\t\t\t\t\t\t<p><a href=\"#\" class=\"sm instagram\" title=\"Instagram\" rel=\"noopener\">Instagram</a></p>\n\t\t\t\t\t\t\t\t<p><a href=\"#\" class=\"sm youtube\" title=\"Youtube\" rel=\"noopener\">Youtube</a></p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</footer>\n\n\n\n\t\t<a id=\"toTop\" class=\"back-to-top\" href=\"#\" title=\"{{ __( 'Back to top' ) }}\">\n\t\t\t<div class=\"top-icon\"></div>\n\t\t</a>\n\n\t\t<!-- Scripts -->\n\t\t<script src=\"{{ asset('vendor/shop/themes/default/app.js?v=' . config( 'shop.version', 1 ) ) }}\"></script>\n\t\t<script src=\"{{ asset('vendor/shop/themes/default/aimeos.js?v=' . config( 'shop.version', 1 ) ) }}\"></script>\n\t\t@yield('aimeos_scripts')\n\t</body>\n</html>\n"
  },
  {
    "path": "views/basket/index.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Basket') }}</title>\n\t<meta name=\"robots\" content=\"noindex\">\n\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['basket/bulk'] ?? '' ?>\n\t<?= $aiheader['basket/standard'] ?? '' ?>\n\t<?= $aiheader['basket/related'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<?= $aibody['basket/standard'] ?? '' ?>\n\t\t<?= $aibody['basket/related'] ?? '' ?>\n\t\t<?= $aibody['basket/bulk'] ?? '' ?>\n\t</div>\n@stop\n"
  },
  {
    "path": "views/catalog/count.blade.php",
    "content": "<?php echo $aibody['catalog/count']; ?>\n"
  },
  {
    "path": "views/catalog/detail.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/stage'] ?? '' ?>\n\t<?= $aiheader['catalog/detail'] ?? '' ?>\n\t<?= $aiheader['catalog/session'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_stage')\n\t<?= $aibody['catalog/stage'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<?= $aibody['catalog/detail'] ?? '' ?>\n\t<?= $aibody['cms/page'] ?? '' ?>\n@stop\n\n@section('aimeos_aside')\n\t<?= $aibody['catalog/session'] ?? '' ?>\n@stop\n"
  },
  {
    "path": "views/catalog/home.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['catalog/home'] ?? '' ?>\n\t<?= $aiheader['cms/page'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<?= $aibody['catalog/home'] ?? '' ?>\n\t<?= $aibody['cms/page'] ?? '' ?>\n@stop\n"
  },
  {
    "path": "views/catalog/list.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/filter'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['catalog/stage'] ?? '' ?>\n\t<?= $aiheader['catalog/session'] ?? '' ?>\n\t<?= $aiheader['catalog/lists'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<?= $aibody['catalog/stage'] ?? '' ?>\n\t<div class=\"container-fluid\">\n\t\t<div class=\"row\">\n\t\t\t<aside class=\"col-lg-3\">\n\t\t\t\t<?= $aibody['catalog/filter'] ?? '' ?>\n\t\t\t\t<?= $aibody['catalog/session'] ?? '' ?>\n\t\t\t</aside>\n\t\t\t<div class=\"col-lg-9\">\n\t\t\t\t<?= $aibody['catalog/lists'] ?>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n@stop\n"
  },
  {
    "path": "views/catalog/session.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<meta name=\"robots\" content=\"noindex\">\n\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/session'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<?= $aibody['catalog/session'] ?? '' ?>\n@stop\n"
  },
  {
    "path": "views/catalog/stock.blade.php",
    "content": "<?= $aibody['catalog/stock'] ?>"
  },
  {
    "path": "views/catalog/suggest.blade.php",
    "content": "<?= $aibody['catalog/suggest'] ?>"
  },
  {
    "path": "views/catalog/tree.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/filter'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['catalog/session'] ?? '' ?>\n\t<?= $aiheader['catalog/stage'] ?? '' ?>\n\t<?= $aiheader['catalog/lists'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_stage')\n\t<?= $aibody['catalog/stage'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<div class=\"row\">\n\t\t\t<aside class=\"col-lg-3\">\n\t\t\t\t<?= $aibody['catalog/filter'] ?? '' ?>\n\t\t\t\t<?= $aibody['catalog/session'] ?? '' ?>\n\t\t\t</aside>\n\t\t\t<div class=\"col-lg-9\">\n\t\t\t\t<?= $aibody['catalog/lists'] ?? '' ?>\n\t\t\t\t<?= $aibody['cms/page'] ?? '' ?>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n@stop\n"
  },
  {
    "path": "views/checkout/confirm.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Thank you') }}</title>\n\t<?= $aiheader['checkout/confirm'] ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<?= $aibody['checkout/confirm'] ?>\n\t</div>\n@stop\n"
  },
  {
    "path": "views/checkout/index.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Checkout') }}</title>\n\t<meta name=\"robots\" content=\"noindex\">\n\n\t<?= $aiheader['checkout/standard'] ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<?= $aibody['checkout/standard'] ?>\n\t</div>\n@stop\n"
  },
  {
    "path": "views/checkout/update.blade.php",
    "content": "<?php echo $aibody['checkout/update']; ?>\n"
  },
  {
    "path": "views/jqadm/index.blade.php",
    "content": "<!DOCTYPE html>\n<html lang=\"{{ $locale }}\" dir=\"{{ $localeDir }}\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\t\t<meta name=\"csrf-token\" content=\"{{ csrf_token() }}\" />\n\n\t\t@if( config('app.debug') !== true )\n\t\t\t<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self' data: blob:; {{ config( 'shop.csp.backend', 'style-src \\'unsafe-inline\\' \\'self\\' https://cdnjs.cloudflare.com; script-src \\'unsafe-eval\\' \\'self\\'; connect-src \\'self\\' https://*.deepl.com https://api.openai.com; img-src \\'self\\' data: blob: https://*.tile.openstreetmap.org https://aimeos.org; frame-src https://www.youtube.com https://player.vimeo.com' ) }}\">\n\t\t@endif\n\n\t\t<title>Aimeos administration interface</title>\n\n\t\t<link rel=\"stylesheet\" href=\"<?= airoute( 'aimeos_shop_jqadm_file', ['site' => $site, 'locale' => 'en', 'name' => 'vendor-css'] ) ?>\">\n\t\t@if( $localeDir == 'rtl' )\n\t\t\t<link rel=\"stylesheet\" href=\"<?= airoute( 'aimeos_shop_jqadm_file', ['site' => $site, 'locale' => 'en', 'name' => 'index-rtl-css'] ) ?>\">\n\t\t@else\n\t\t\t<link rel=\"stylesheet\" href=\"<?= airoute( 'aimeos_shop_jqadm_file', ['site' => $site, 'locale' => 'en', 'name' => 'index-ltr-css'] ) ?>\">\n\t\t@endif\n\t\t<link rel=\"stylesheet\" href=\"<?= airoute( 'aimeos_shop_jqadm_file', ['site' => $site, 'locale' => 'en', 'name' => 'index-css'] ) ?>\">\n\n\t\t<style nonce=\"{{ app( 'aimeos.context' )->get( false )->nonce() }}\">\n\t\t\tbody.dark .btn-theme.dark-mode {display:none}\n\t\t\tbody.light .btn-theme.light-mode {display:none}\n\t\t\t.app-menu .icon {cursor: pointer; padding: 0.75rem; height: 2.5rem; width: 2.5rem; margin: 0 0.25rem;}\n\t\t\t.app-menu button {border: none; color: var(--ai-bg); background-color: transparent; padding: 0}\n\t\t\t#logout-form {display: inline-block}\n\t\t</style>\n\t</head>\n\t<body class=\"{{ $theme }}\">\n\t\t<div class=\"app-menu\">\n\t\t\t<span class=\"menu\"></span>\n\t\t\t<div class=\"app-menu-end\">\n\t\t\t\t<form id=\"logout-form\" action=\"{{ airoute( 'logout', ['locale' => Request::get( 'locale', app()->getLocale() )] ) }}\" method=\"POST\">\n\t\t\t\t\t{{ csrf_field() }}\n\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-sun icon btn-theme light-mode\" viewBox=\"0 0 16 16\"><path d=\"M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708\"/></svg>\n\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-moon icon btn-theme dark-mode\" viewBox=\"0 0 16 16\"><path d=\"M6 .278a.77.77 0 0 1 .08.858 7.2 7.2 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277q.792-.001 1.533-.16a.79.79 0 0 1 .81.316.73.73 0 0 1-.031.893A8.35 8.35 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.75.75 0 0 1 6 .278M4.858 1.311A7.27 7.27 0 0 0 1.025 7.71c0 4.02 3.279 7.276 7.319 7.276a7.32 7.32 0 0 0 5.205-2.162q-.506.063-1.029.063c-4.61 0-8.343-3.714-8.343-8.29 0-1.167.242-2.278.681-3.286\"/></svg>\n\t\t\t\t\t<button><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-box-arrow-right icon logout\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0z\"/><path fill-rule=\"evenodd\" d=\"M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708z\"/></svg></button>\n\t\t\t\t</form>\n\t\t\t</div>\n\t\t</div>\n\n<?= $content ?>\n\n\t\t<script src=\"<?= airoute( 'aimeos_shop_jqadm_file', array( 'site' => $site, 'locale' => 'en', 'name' => 'vendor-js' ) ) ?>\"></script>\n\t\t<script src=\"<?= airoute( 'aimeos_shop_jqadm_file', array( 'site' => $site, 'locale' => 'en', 'name' => 'index-js' ) ) ?>\"></script>\n\t</body>\n</html>\n"
  },
  {
    "path": "views/page/index.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['cms/page'] ?? '' ?>\n@stop\n\n@section('aimeos_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<?= $aibody['cms/page'] ?? '' ?>\n\t</div>\n@stop\n"
  },
  {
    "path": "views/supplier/detail.blade.php",
    "content": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['supplier/detail'] ?>\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] ?? '' ?>\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['catalog/search'] ?? '' ?>\n\t<?= $aiheader['catalog/lists'] ?? '' ?>\n@stop\n\n@section('aimeos_head_basket')\n\t<?= $aibody['basket/mini'] ?? '' ?>\n@stop\n\n@section('aimeos_head_nav')\n\t<?= $aibody['catalog/tree'] ?? '' ?>\n@stop\n\n@section('aimeos_head_locale')\n\t<?= $aibody['locale/select'] ?? '' ?>\n@stop\n\n@section('aimeos_head_search')\n\t<?= $aibody['catalog/search'] ?? '' ?>\n@stop\n\n@section('aimeos_body')\n\t<div class=\"container-fluid\">\n\t\t<?= $aibody['supplier/detail'] ?>\n\t\t<?= $aibody['catalog/lists'] ?? '' ?>\n\t</div>\n@stop\n"
  }
]