diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..30ca42b --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,61 @@ +name: Telegram Mini App Shop Builder + +on: + push: + branches: + - master + +permissions: + contents: write + +jobs: + module-build: + if: github.ref == 'refs/heads/master' + name: Build module. + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build module + run: | + bash scripts/ci/build.sh "${GITHUB_WORKSPACE}" + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: oc_telegram_shop.ocmod.zip + path: ./build/oc_telegram_shop.ocmod.zip + retention-days: 1 + + release: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + needs: [module-build] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # to fetch tags + + - name: Extract tag and set filename + id: meta + run: | + TAG=${GITHUB_REF#refs/tags/} + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "filename=oc_telegram_shop_${TAG}.ocmod.zip" >> $GITHUB_OUTPUT + + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: oc_telegram_shop.ocmod.zip + path: ./build + + - name: Rename artifact file + run: mv ./build/oc_telegram_shop.ocmod.zip ./build/${{ steps.meta.outputs.filename }} + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.meta.outputs.tag }} + files: ./build/${{ steps.meta.outputs.filename }} + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index c381f44..f2c33cf 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ dist-ssr *.sw? src/* -spa/node_modules \ No newline at end of file +spa/node_modules +module/oc_telegram_shop/upload/oc_telegram_shop/vendor +module/oc_telegram_shop/upload/image \ No newline at end of file diff --git a/Makefile b/Makefile index 7e43888..808d5eb 100644 --- a/Makefile +++ b/Makefile @@ -22,15 +22,11 @@ start: docker compose up -d ssh: - docker compose exec web bash + docker compose exec -w /module/oc_telegram_shop/upload/oc_telegram_shop web bash link: docker compose exec web bash -c "php ./scripts/link.php" dev: $(MAKE) link && \ - cd frontend && npm run dev - -build: - docker build -t oc_layout_pro_build_prod -f docker/build.dockerfile . && \ - docker run --rm -v "./build":/build oc_layout_pro_build_prod \ No newline at end of file + cd spa && npm run dev \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 7636b50..9f55000 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ services: web: - image: webdevops/php-apache-dev:${PHP_VERSION} + image: webdevops/php-apache-dev:7.4 platform: linux/amd64 volumes: - "./src:/web" diff --git a/module/oc_telegram_shop/upload/catalog/controller/tgshop/handle.php b/module/oc_telegram_shop/upload/catalog/controller/tgshop/handle.php new file mode 100755 index 0000000..ad9a1f0 --- /dev/null +++ b/module/oc_telegram_shop/upload/catalog/controller/tgshop/handle.php @@ -0,0 +1,72 @@ + $this->config->get('config_tax'), + 'oc_currency' => $this->session->data['currency'], + 'timezone' => $this->config->get('config_timezone', 'UTC'), + 'lang' => $this->config->get('config_admin_language'), + 'language_id' => (int)$this->config->get('config_language_id'), + 'shop_base_url' => HTTPS_SERVER, + 'dir_image' => DIR_IMAGE, + 'db' => [ + 'host' => DB_HOSTNAME, + 'database' => DB_DATABASE, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'prefix' => DB_PREFIX, + ], + 'logs' => [ + 'path' => DIR_LOGS, + ], + ]); + + $app->bind(Url::class, function () { + return $this->url; + }); + + $app->bind(Currency::class, function () { return $this->currency; }); + $app->bind(Tax::class, function () { return $this->tax; }); + + $this->load->model('tool/image'); + $app->bind('image_resize', function () { + return function ($path, $width, $height) { + if (is_file(DIR_IMAGE . $path)) { + return $this->model_tool_image->resize($path, $width, $height); + } + + return $this->model_tool_image->resize('no_image.png', $width, $height); + }; + }); + + $this->load->model('checkout/order'); + + $app->bind('model_checkout_order', function () { + return $this->model_checkout_order; + }); + + $app->bootAndHandleRequest(); + } + + public function spa() + { + + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json new file mode 100755 index 0000000..40e9018 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json @@ -0,0 +1,27 @@ +{ + "name": "nikitakiselev/oc_telegram_shop", + "autoload": { + "psr-4": { + "Openguru\\OpenCartFramework\\": "framework/", + "App\\": "src/" + }, + "files": [ + "framework/Support/helpers.php" + ] + }, + "authors": [ + { + "name": "Nikita Kiselev", + "email": "mail@nikitakiselev.ru" + } + ], + "require": { + "php": "^7.4", + "ext-pdo": "*", + "psr/container": "^2.0", + "ext-json": "*" + }, + "require-dev": { + "roave/security-advisories": "dev-latest" + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock new file mode 100755 index 0000000..5712087 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock @@ -0,0 +1,1033 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "775a9a548899d07b9c007ecf3afe4e44", + "packages": [ + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + } + ], + "packages-dev": [ + { + "name": "roave/security-advisories", + "version": "dev-latest", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "c4ed86478b4c2004245ec9d3a470dbe1790586a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/c4ed86478b4c2004245ec9d3a470dbe1790586a3", + "reference": "c4ed86478b4c2004245ec9d3a470dbe1790586a3", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adaptcms/adaptcms": "<=1.3", + "admidio/admidio": "<4.3.12", + "adodb/adodb-php": "<=5.22.8", + "aheinze/cockpit": "<2.2", + "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", + "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", + "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", + "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", + "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", + "airesvsg/acf-to-rest-api": "<=3.1", + "akaunting/akaunting": "<2.1.13", + "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", + "alextselegidis/easyappointments": "<=1.5.1", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", + "ameos/ameos_tarteaucitron": "<1.2.23", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<=1.7.2|>=2,<=2.1", + "amphp/http-client": ">=4,<4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", + "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", + "aoe/restler": "<1.7.1", + "apache-solr-for-typo3/solr": "<2.8.3", + "apereo/phpcas": "<1.6", + "api-platform/core": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", + "api-platform/graphql": "<3.4.17|>=4.0.0.0-alpha1,<4.0.22", + "appwrite/server-ce": "<=1.2.1", + "arc/web": "<3", + "area17/twill": "<1.2.5|>=2,<2.5.3", + "artesaos/seotools": "<0.17.2", + "asymmetricrypt/asymmetricrypt": "<9.9.99", + "athlon1600/php-proxy": "<=5.1", + "athlon1600/php-proxy-app": "<=3", + "athlon1600/youtube-downloader": "<=4", + "austintoddj/canvas": "<=3.4.2", + "auth0/auth0-php": ">=8.0.0.0-beta1,<8.14", + "auth0/login": "<7.17", + "auth0/symfony": "<5.4", + "auth0/wordpress": "<5.3", + "automad/automad": "<2.0.0.0-alpha5", + "automattic/jetpack": "<9.8", + "awesome-support/awesome-support": "<=6.0.7", + "aws/aws-sdk-php": "<3.288.1", + "azuracast/azuracast": "<0.18.3", + "b13/seo_basics": "<0.8.2", + "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", + "backpack/crud": "<3.4.9", + "backpack/filemanager": "<2.0.2|>=3,<3.0.9", + "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", + "badaso/core": "<2.7", + "bagisto/bagisto": "<2.1", + "barrelstrength/sprout-base-email": "<1.2.7", + "barrelstrength/sprout-forms": "<3.9", + "barryvdh/laravel-translation-manager": "<0.6.8", + "barzahlen/barzahlen-php": "<2.0.1", + "baserproject/basercms": "<=5.1.1", + "bassjobsen/bootstrap-3-typeahead": ">4.0.2", + "bbpress/bbpress": "<2.6.5", + "bcit-ci/codeigniter": "<3.1.3", + "bcosca/fatfree": "<3.7.2", + "bedita/bedita": "<4", + "bednee/cooluri": "<1.0.30", + "bigfork/silverstripe-form-capture": ">=3,<3.1.1", + "billz/raspap-webgui": "<3.3.6", + "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "blueimp/jquery-file-upload": "==6.4.4", + "bmarshall511/wordpress_zero_spam": "<5.2.13", + "bolt/bolt": "<3.7.2", + "bolt/core": "<=4.2", + "born05/craft-twofactorauthentication": "<3.3.4", + "bottelet/flarepoint": "<2.2.1", + "bref/bref": "<2.1.17", + "brightlocal/phpwhois": "<=4.2.5", + "brotkrueml/codehighlight": "<2.7", + "brotkrueml/schema": "<1.13.1|>=2,<2.5.1", + "brotkrueml/typo3-matomo-integration": "<1.3.2", + "buddypress/buddypress": "<7.2.1", + "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bvbmedia/multishop": "<2.0.39", + "bytefury/crater": "<6.0.2", + "cachethq/cachet": "<2.5.1", + "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", + "cakephp/database": ">=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", + "cardgate/magento2": "<2.0.33", + "cardgate/woocommerce": "<=3.1.15", + "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cart2quote/module-quotation-encoded": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cartalyst/sentry": "<=2.1.6", + "catfan/medoo": "<1.7.5", + "causal/oidc": "<4", + "cecil/cecil": "<7.47.1", + "centreon/centreon": "<22.10.15", + "cesnet/simplesamlphp-module-proxystatistics": "<3.1", + "chriskacerguis/codeigniter-restserver": "<=2.7.1", + "chrome-php/chrome": "<1.14", + "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", + "ckeditor/ckeditor": "<4.25", + "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", + "co-stack/fal_sftp": "<0.2.6", + "cockpit-hq/cockpit": "<2.11.4", + "codeception/codeception": "<3.1.3|>=4,<4.1.22", + "codeigniter/framework": "<3.1.10", + "codeigniter4/framework": "<4.5.8", + "codeigniter4/shield": "<1.0.0.0-beta8", + "codiad/codiad": "<=2.8.4", + "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", + "commerceteam/commerce": ">=0.9.6,<0.9.9", + "components/jquery": ">=1.0.3,<3.5", + "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", + "concrete5/concrete5": "<9.4.0.0-RC2-dev", + "concrete5/core": "<8.5.8|>=9,<9.1", + "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.9.40|>=4.10,<4.11.7|>=4.13,<4.13.21|>=5.1,<5.1.4", + "contao/core": "<3.5.39", + "contao/core-bundle": "<4.13.54|>=5,<5.3.30|>=5.4,<5.5.6", + "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", + "contao/managed-edition": "<=1.5", + "corveda/phpsandbox": "<1.3.5", + "cosenary/instagram": "<=2.3", + "couleurcitron/tarteaucitron-wp": "<0.3", + "craftcms/cms": "<4.15.3|>=5,<5.7.5", + "croogo/croogo": "<4", + "cuyz/valinor": "<0.12", + "czim/file-handling": "<1.5|>=2,<2.3", + "czproject/git-php": "<4.0.3", + "damienharper/auditor-bundle": "<5.2.6", + "dapphp/securimage": "<3.6.6", + "darylldoyle/safe-svg": "<1.9.10", + "datadog/dd-trace": ">=0.30,<0.30.2", + "datatables/datatables": "<1.10.10", + "david-garcia/phpwhois": "<=4.3.1", + "dbrisinajumi/d2files": "<1", + "dcat/laravel-admin": "<=2.1.3|==2.2.0.0-beta|==2.2.2.0-beta", + "derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3", + "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", + "desperado/xml-bundle": "<=0.1.7", + "dev-lancer/minecraft-motd-parser": "<=1.0.5", + "devgroup/dotplant": "<2020.09.14-dev", + "digimix/wp-svg-upload": "<=1", + "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", + "dl/yag": "<3.0.1", + "dmk/webkitpdf": "<1.1.4", + "dnadesign/silverstripe-elemental": "<5.3.12", + "doctrine/annotations": "<1.2.7", + "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", + "doctrine/common": "<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", + "doctrine/doctrine-bundle": "<1.5.2", + "doctrine/doctrine-module": "<0.7.2", + "doctrine/mongodb-odm": "<1.0.2", + "doctrine/mongodb-odm-bundle": "<3.0.1", + "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", + "dolibarr/dolibarr": "<19.0.2|==21.0.0.0-beta", + "dompdf/dompdf": "<2.0.4", + "doublethreedigital/guest-entries": "<3.1.2", + "drupal/admin_audit_trail": "<1.0.5", + "drupal/ai": "<1.0.5", + "drupal/alogin": "<2.0.6", + "drupal/cache_utility": "<1.2.1", + "drupal/commerce_alphabank_redirect": "<1.0.3", + "drupal/commerce_eurobank_redirect": "<2.1.1", + "drupal/config_split": "<1.10|>=2,<2.0.2", + "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.3.14|>=10.4,<10.4.5|>=11,<11.0.13|>=11.1,<11.1.5", + "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/formatter_suite": "<2.1", + "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", + "drupal/google_tag": "<1.8|>=2,<2.0.8", + "drupal/ignition": "<1.0.4", + "drupal/lightgallery": "<1.6", + "drupal/link_field_display_mode_formatter": "<1.6", + "drupal/matomo": "<1.24", + "drupal/oauth2_client": "<4.1.3", + "drupal/oauth2_server": "<2.1", + "drupal/obfuscate": "<2.0.1", + "drupal/quick_node_block": "<2", + "drupal/rapidoc_elements_field_formatter": "<1.0.1", + "drupal/spamspan": "<3.2.1", + "drupal/tfa": "<1.10", + "duncanmcclean/guest-entries": "<3.1.2", + "dweeves/magmi": "<=0.7.24", + "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", + "ecodev/newsletter": "<=4", + "ectouch/ectouch": "<=2.7.2", + "egroupware/egroupware": "<23.1.20240624", + "elefant/cms": "<2.0.7", + "elgg/elgg": "<3.3.24|>=4,<4.0.5", + "elijaa/phpmemcacheadmin": "<=1.3", + "elmsln/haxcms": "<11", + "encore/laravel-admin": "<=1.8.19", + "endroid/qr-code-bundle": "<3.4.2", + "enhavo/enhavo-app": "<=0.13.1", + "enshrined/svg-sanitize": "<0.15", + "erusev/parsedown": "<1.7.2", + "ether/logs": "<3.0.4", + "evolutioncms/evolution": "<=3.2.3", + "exceedone/exment": "<4.4.3|>=5,<5.0.3", + "exceedone/laravel-admin": "<2.2.3|==3", + "ezsystems/demobundle": ">=5.4,<5.4.6.1-dev", + "ezsystems/ez-support-tools": ">=2.2,<2.2.3", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", + "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", + "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.38|>=3.3,<3.3.39", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1|>=5.3.0.0-beta1,<5.3.5", + "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", + "ezsystems/ezplatform-http-cache": "<2.3.16", + "ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.26|>=3.3,<3.3.40", + "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", + "ezsystems/ezplatform-user": ">=1,<1.0.1", + "ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31", + "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1", + "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", + "ezyang/htmlpurifier": "<=4.2", + "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", + "facturascripts/facturascripts": "<=2022.08", + "fastly/magento2": "<1.2.26", + "feehi/cms": "<=2.1.1", + "feehi/feehicms": "<=2.1.1", + "fenom/fenom": "<=2.12.1", + "filament/actions": ">=3.2,<3.2.123", + "filament/infolists": ">=3,<3.2.115", + "filament/tables": ">=3,<3.2.115", + "filegator/filegator": "<7.8", + "filp/whoops": "<2.1.13", + "fineuploader/php-traditional-server": "<=1.2.2", + "firebase/php-jwt": "<6", + "fisharebest/webtrees": "<=2.1.18", + "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", + "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", + "flarum/core": "<1.8.10", + "flarum/flarum": "<0.1.0.0-beta8", + "flarum/framework": "<1.8.10", + "flarum/mentions": "<1.6.3", + "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", + "flarum/tags": "<=0.1.0.0-beta13", + "floriangaerber/magnesium": "<0.3.1", + "fluidtypo3/vhs": "<5.1.1", + "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", + "fof/upload": "<1.2.3", + "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", + "fooman/tcpdf": "<6.2.22", + "forkcms/forkcms": "<5.11.1", + "fossar/tcpdf-parser": "<6.2.22", + "francoisjacquet/rosariosis": "<=11.5.1", + "frappant/frp-form-answers": "<3.1.2|>=4,<4.0.2", + "friendsofsymfony/oauth2-php": "<1.3", + "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", + "friendsofsymfony/user-bundle": ">=1,<1.3.5", + "friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5", + "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", + "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", + "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", + "froala/wysiwyg-editor": "<=4.3", + "froxlor/froxlor": "<=2.2.5", + "frozennode/administrator": "<=5.0.12", + "fuel/core": "<1.8.1", + "funadmin/funadmin": "<=5.0.2", + "gaoming13/wechat-php-sdk": "<=1.10.2", + "genix/cms": "<=1.1.11", + "georgringer/news": "<1.3.3", + "geshi/geshi": "<=1.0.9.1", + "getformwork/formwork": "<1.13.1|>=2.0.0.0-beta1,<2.0.0.0-beta4", + "getgrav/grav": "<1.7.46", + "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "getkirby/panel": "<2.5.14", + "getkirby/starterkit": "<=3.7.0.2", + "gilacms/gila": "<=1.15.4", + "gleez/cms": "<=1.3|==2", + "globalpayments/php-sdk": "<2", + "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", + "gogentooss/samlbase": "<1.2.7", + "google/protobuf": "<3.15", + "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gree/jose": "<2.2.1", + "gregwar/rst": "<1.0.3", + "grumpydictator/firefly-iii": "<6.1.17", + "gugoan/economizzer": "<=0.9.0.0-beta1", + "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", + "guzzlehttp/oauth-subscriber": "<0.8.1", + "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", + "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", + "handcraftedinthealps/goodby-csv": "<1.4.3", + "harvesthq/chosen": "<1.8.7", + "helloxz/imgurl": "<=2.31", + "hhxsv5/laravel-s": "<3.7.36", + "hillelcoren/invoice-ninja": "<5.3.35", + "himiklab/yii2-jqgrid-widget": "<1.0.8", + "hjue/justwriting": "<=1", + "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "httpsoft/http-message": "<1.0.12", + "hyn/multi-tenant": ">=5.6,<5.7.2", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.21", + "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", + "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", + "ibexa/fieldtype-richtext": ">=4.6,<4.6.21", + "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", + "ibexa/http-cache": ">=4.6,<4.6.14", + "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", + "ibexa/solr": ">=4.5,<4.5.4", + "ibexa/user": ">=4,<4.4.3", + "icecoder/icecoder": "<=8.1", + "idno/known": "<=1.3.1", + "ilicmiljan/secure-props": ">=1.2,<1.2.2", + "illuminate/auth": "<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4", + "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "imdbphp/imdbphp": "<=5.1.1", + "impresscms/impresscms": "<=1.4.5", + "impresspages/impresspages": "<1.0.13", + "in2code/femanager": "<5.5.5|>=6,<6.4.1|>=7,<7.4.2|>=8,<8.2.2", + "in2code/ipandlanguageredirect": "<5.1.2", + "in2code/lux": "<17.6.1|>=18,<24.0.2", + "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.4.1", + "innologi/typo3-appointments": "<2.0.6", + "intelliants/subrion": "<4.2.2", + "inter-mediator/inter-mediator": "==5.5", + "ipl/web": "<0.10.1", + "islandora/crayfish": "<4.1", + "islandora/islandora": ">=2,<2.4.1", + "ivankristianto/phpwhois": "<=4.3", + "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "jambagecom/div2007": "<0.10.2", + "james-heinrich/getid3": "<1.9.21", + "james-heinrich/phpthumb": "<1.7.12", + "jasig/phpcas": "<1.3.3", + "jbartels/wec-map": "<3.0.3", + "jcbrand/converse.js": "<3.3.3", + "joelbutcher/socialstream": "<5.6|>=6,<6.2", + "johnbillion/wp-crontrol": "<1.16.2", + "joomla/application": "<1.0.13", + "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/database": ">=1,<2.2|>=3,<3.4", + "joomla/filesystem": "<1.6.2|>=2,<2.0.1", + "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", + "joomla/input": ">=2,<2.0.2", + "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", + "joomla/joomla-platform": "<1.5.4", + "joomla/session": "<1.3.1", + "joyqi/hyper-down": "<=2.4.27", + "jsdecena/laracom": "<2.0.9", + "jsmitty12/phpwhois": "<5.1", + "juzaweb/cms": "<=3.4.2", + "jweiland/events2": "<8.3.8|>=9,<9.0.6", + "jweiland/kk-downloader": "<1.2.2", + "kazist/phpwhois": "<=4.2.6", + "kelvinmo/simplexrd": "<3.1.1", + "kevinpapst/kimai2": "<1.16.7", + "khodakhah/nodcms": "<=3", + "kimai/kimai": "<=2.20.1", + "kitodo/presentation": "<3.2.3|>=3.3,<3.3.4", + "klaviyo/magento2-extension": ">=1,<3", + "knplabs/knp-snappy": "<=1.4.2", + "kohana/core": "<3.3.3", + "koillection/koillection": "<1.6.12", + "krayin/laravel-crm": "<=1.3", + "kreait/firebase-php": ">=3.2,<3.8.1", + "kumbiaphp/kumbiapp": "<=1.1.1", + "la-haute-societe/tcpdf": "<6.2.22", + "laminas/laminas-diactoros": "<2.18.1|==2.19|==2.20|==2.21|==2.22|==2.23|>=2.24,<2.24.2|>=2.25,<2.25.2", + "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", + "laminas/laminas-http": "<2.14.2", + "lara-zeus/artemis": ">=1,<=1.0.6", + "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", + "laravel/fortify": "<1.11.1", + "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", + "laravel/laravel": ">=5.4,<5.4.22", + "laravel/pulse": "<1.3.1", + "laravel/reverb": "<1.4", + "laravel/socialite": ">=1,<2.0.10", + "latte/latte": "<2.10.8", + "lavalite/cms": "<=9|==10.1", + "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", + "league/commonmark": "<2.7", + "league/flysystem": "<1.1.4|>=2,<2.1.1", + "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", + "leantime/leantime": "<3.3", + "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", + "libreform/libreform": ">=2,<=2.0.8", + "librenms/librenms": "<2017.08.18", + "liftkit/database": "<2.13.2", + "lightsaml/lightsaml": "<1.3.5", + "limesurvey/limesurvey": "<6.5.12", + "livehelperchat/livehelperchat": "<=3.91", + "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.5.2", + "livewire/volt": "<1.7", + "lms/routes": "<2.1.1", + "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "lomkit/laravel-rest-api": "<2.13", + "luracast/restler": "<3.1", + "luyadev/yii-helpers": "<1.2.1", + "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", + "maestroerror/php-heic-to-jpg": "<1.0.5", + "magento/community-edition": "<2.4.5.0-patch13|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch11|>=2.4.7.0-beta1,<2.4.7.0-patch6|>=2.4.8.0-beta1,<2.4.8.0-patch1", + "magento/core": "<=1.9.4.5", + "magento/magento1ce": "<1.9.4.3-dev", + "magento/magento1ee": ">=1,<1.14.4.3-dev", + "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", + "magento/project-community-edition": "<=2.0.2", + "magneto/core": "<1.9.4.4-dev", + "maikuolan/phpmussel": ">=1,<1.6", + "mainwp/mainwp": "<=4.4.3.3", + "mantisbt/mantisbt": "<=2.26.3", + "marcwillmann/turn": "<0.3.3", + "matomo/matomo": "<1.11", + "matyhtf/framework": "<3.0.6", + "mautic/core": "<5.2.6|>=6.0.0.0-alpha,<6.0.2", + "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", + "maximebf/debugbar": "<1.19", + "mdanter/ecc": "<2", + "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", + "mediawiki/cargo": "<3.6.1", + "mediawiki/core": "<1.39.5|==1.40", + "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", + "mediawiki/matomo": "<2.4.3", + "mediawiki/semantic-media-wiki": "<4.0.2", + "mehrwert/phpmyadmin": "<3.2", + "melisplatform/melis-asset-manager": "<5.0.1", + "melisplatform/melis-cms": "<5.0.1", + "melisplatform/melis-front": "<5.0.1", + "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", + "mgallegos/laravel-jqgrid": "<=1.3", + "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", + "microsoft/microsoft-graph-beta": "<2.0.1", + "microsoft/microsoft-graph-core": "<2.0.2", + "microweber/microweber": "<=2.0.19", + "mikehaertl/php-shellcommand": "<1.6.1", + "miniorange/miniorange-saml": "<1.4.3", + "mittwald/typo3_forum": "<1.2.1", + "mobiledetect/mobiledetectlib": "<2.8.32", + "modx/revolution": "<=3.1", + "mojo42/jirafeau": "<4.4", + "mongodb/mongodb": ">=1,<1.9.2", + "monolog/monolog": ">=1.8,<1.12", + "moodle/moodle": "<4.3.12|>=4.4,<4.4.8|>=4.5.0.0-beta,<4.5.4", + "mos/cimage": "<0.7.19", + "movim/moxl": ">=0.8,<=0.10", + "movingbytes/social-network": "<=1.2.1", + "mpdf/mpdf": "<=7.1.7", + "munkireport/comment": "<4.1", + "munkireport/managedinstalls": "<2.6", + "munkireport/munki_facts": "<1.5", + "munkireport/munkireport": ">=2.5.3,<5.6.3", + "munkireport/reportdata": "<3.5", + "munkireport/softwareupdate": "<1.6", + "mustache/mustache": ">=2,<2.14.1", + "mwdelaney/wp-enable-svg": "<=0.2", + "namshi/jose": "<2.2", + "nasirkhan/laravel-starter": "<11.11", + "nategood/httpful": "<1", + "neoan3-apps/template": "<1.1.1", + "neorazorx/facturascripts": "<2022.04", + "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", + "neos/media-browser": "<7.3.19|>=8,<8.0.16|>=8.1,<8.1.11|>=8.2,<8.2.11|>=8.3,<8.3.9", + "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2", + "neos/swiftmailer": "<5.4.5", + "nesbot/carbon": "<2.72.6|>=3,<3.8.4", + "netcarver/textile": "<=4.1.2", + "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", + "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", + "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "nilsteampassnet/teampass": "<3.1.3.1-dev", + "nitsan/ns-backup": "<13.0.1", + "nonfiction/nterchange": "<4.1.1", + "notrinos/notrinos-erp": "<=0.7", + "noumo/easyii": "<=0.9", + "novaksolutions/infusionsoft-php-sdk": "<1", + "nukeviet/nukeviet": "<4.5.02", + "nyholm/psr7": "<1.6.1", + "nystudio107/craft-seomatic": "<3.4.12", + "nzedb/nzedb": "<0.8", + "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", + "october/backend": "<1.1.2", + "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", + "october/october": "<3.7.5", + "october/rain": "<1.0.472|>=1.1,<1.1.2", + "october/system": "<3.7.5", + "oliverklee/phpunit": "<3.5.15", + "omeka/omeka-s": "<4.0.3", + "onelogin/php-saml": "<2.10.4", + "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", + "open-web-analytics/open-web-analytics": "<1.7.4", + "opencart/opencart": ">=0", + "openid/php-openid": "<2.3", + "openmage/magento-lts": "<20.12.3", + "opensolutions/vimbadmin": "<=3.0.15", + "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", + "orchid/platform": ">=8,<14.43", + "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", + "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", + "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", + "oro/crm-call-bundle": ">=4.2,<=4.2.5|>=5,<5.0.4|>=5.1,<5.1.1", + "oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3", + "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3", + "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3", + "oxid-esales/oxideshop-ce": "<=7.0.5", + "oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1", + "packbackbooks/lti-1-3-php-library": "<5", + "padraic/humbug_get_contents": "<1.1.2", + "pagarme/pagarme-php": "<3", + "pagekit/pagekit": "<=1.0.18", + "paragonie/ecc": "<2.0.1", + "paragonie/random_compat": "<2", + "passbolt/passbolt_api": "<4.6.2", + "paypal/adaptivepayments-sdk-php": "<=3.9.2", + "paypal/invoice-sdk-php": "<=3.9", + "paypal/merchant-sdk-php": "<3.12", + "paypal/permissions-sdk-php": "<=3.9.1", + "pear/archive_tar": "<1.4.14", + "pear/auth": "<1.2.4", + "pear/crypt_gpg": "<1.6.7", + "pear/http_request2": "<2.7", + "pear/pear": "<=1.10.1", + "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", + "personnummer/personnummer": "<3.0.2", + "phanan/koel": "<5.1.4", + "phenx/php-svg-lib": "<0.5.2", + "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", + "php-mod/curl": "<2.3.2", + "phpbb/phpbb": "<3.3.11", + "phpems/phpems": ">=6,<=6.1.3", + "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", + "phpmailer/phpmailer": "<6.5", + "phpmussel/phpmussel": ">=1,<1.6", + "phpmyadmin/phpmyadmin": "<5.2.2", + "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5|>=3.2.10,<=4.0.1", + "phpoffice/common": "<0.2.9", + "phpoffice/math": "<=0.2", + "phpoffice/phpexcel": "<=1.8.2", + "phpoffice/phpspreadsheet": "<1.29.9|>=2,<2.1.8|>=2.2,<2.3.7|>=3,<3.9", + "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", + "phpservermon/phpservermon": "<3.6", + "phpsysinfo/phpsysinfo": "<3.4.3", + "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpwhois/phpwhois": "<=4.2.5", + "phpxmlrpc/extras": "<0.6.1", + "phpxmlrpc/phpxmlrpc": "<4.9.2", + "pi/pi": "<=2.5", + "pimcore/admin-ui-classic-bundle": "<1.7.6", + "pimcore/customer-management-framework-bundle": "<4.2.1", + "pimcore/data-hub": "<1.2.4", + "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", + "pimcore/demo": "<10.3", + "pimcore/ecommerce-framework-bundle": "<1.0.10", + "pimcore/perspective-editor": "<1.5.1", + "pimcore/pimcore": "<11.5.4", + "piwik/piwik": "<1.11", + "pixelfed/pixelfed": "<0.12.5", + "plotly/plotly.js": "<2.25.2", + "pocketmine/bedrock-protocol": "<8.0.2", + "pocketmine/pocketmine-mp": "<5.25.2", + "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", + "pressbooks/pressbooks": "<5.18", + "prestashop/autoupgrade": ">=4,<4.10.1", + "prestashop/blockreassurance": "<=5.1.3", + "prestashop/blockwishlist": ">=2,<2.1.1", + "prestashop/contactform": ">=1.0.1,<4.3", + "prestashop/gamification": "<2.3.2", + "prestashop/prestashop": "<8.1.6", + "prestashop/productcomments": "<5.0.2", + "prestashop/ps_contactinfo": "<=3.3.2", + "prestashop/ps_emailsubscription": "<2.6.1", + "prestashop/ps_facetedsearch": "<3.4.1", + "prestashop/ps_linklist": "<3.1", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", + "processwire/processwire": "<=3.0.229", + "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", + "propel/propel1": ">=1,<=1.7.1", + "pterodactyl/panel": "<=1.11.10", + "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", + "ptrofimov/beanstalk_console": "<1.7.14", + "pubnub/pubnub": "<6.1", + "punktde/pt_extbase": "<1.5.1", + "pusher/pusher-php-server": "<2.2.1", + "pwweb/laravel-core": "<=0.3.6.0-beta", + "pxlrbt/filament-excel": "<1.1.14|>=2.0.0.0-alpha,<2.3.3", + "pyrocms/pyrocms": "<=3.9.1", + "qcubed/qcubed": "<=3.1.1", + "quickapps/cms": "<=2.0.0.0-beta2", + "rainlab/blog-plugin": "<1.4.1", + "rainlab/debugbar-plugin": "<3.1", + "rainlab/user-plugin": "<=1.4.5", + "rankmath/seo-by-rank-math": "<=1.0.95", + "rap2hpoutre/laravel-log-viewer": "<0.13", + "react/http": ">=0.7,<1.9", + "really-simple-plugins/complianz-gdpr": "<6.4.2", + "redaxo/source": "<5.18.3", + "remdex/livehelperchat": "<4.29", + "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", + "reportico-web/reportico": "<=8.1", + "rhukster/dom-sanitizer": "<1.0.7", + "rmccue/requests": ">=1.6,<1.8", + "robrichards/xmlseclibs": ">=1,<3.0.4", + "roots/soil": "<4.1", + "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", + "rudloff/alltube": "<3.0.3", + "rudloff/rtmpdump-bin": "<=2.3.1", + "s-cart/core": "<6.9", + "s-cart/s-cart": "<6.9", + "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", + "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", + "samwilson/unlinked-wikibase": "<1.42", + "scheb/two-factor-bundle": "<3.26|>=4,<4.11", + "sensiolabs/connect": "<4.2.3", + "serluck/phpwhois": "<=4.2.6", + "sfroemken/url_redirect": "<=1.2.1", + "sheng/yiicms": "<1.2.1", + "shopware/core": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", + "shopware/platform": "<6.5.8.18-dev|>=6.6,<6.6.10.3-dev|>=6.7.0.0-RC1-dev,<6.7.0.0-RC2-dev", + "shopware/production": "<=6.3.5.2", + "shopware/shopware": "<=5.7.17", + "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopxo/shopxo": "<=6.4", + "showdoc/showdoc": "<2.10.4", + "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", + "silverstripe-australia/advancedreports": ">=1,<=2", + "silverstripe/admin": "<1.13.19|>=2,<2.1.8", + "silverstripe/assets": ">=1,<1.11.1", + "silverstripe/cms": "<4.11.3", + "silverstripe/comments": ">=1.3,<3.1.1", + "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", + "silverstripe/framework": "<5.3.23", + "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", + "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", + "silverstripe/recipe-cms": ">=4.5,<4.5.3", + "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", + "silverstripe/reports": "<5.2.3", + "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2", + "silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1", + "silverstripe/subsites": ">=2,<2.6.1", + "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", + "silverstripe/userforms": "<3|>=5,<5.4.2", + "silverstripe/versioned-admin": ">=1,<1.11.1", + "simple-updates/phpwhois": "<=1", + "simplesamlphp/saml2": "<=4.16.15|>=5.0.0.0-alpha1,<=5.0.0.0-alpha19", + "simplesamlphp/saml2-legacy": "<=4.16.15", + "simplesamlphp/simplesamlphp": "<1.18.6", + "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "simplesamlphp/simplesamlphp-module-openid": "<1", + "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", + "simplesamlphp/xml-common": "<1.20", + "simplesamlphp/xml-security": "==1.6.11", + "simplito/elliptic-php": "<1.0.6", + "sitegeist/fluid-components": "<3.5", + "sjbr/sr-feuser-register": "<2.6.2|>=5.1,<12.5", + "sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3", + "sjbr/static-info-tables": "<2.3.1", + "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", + "slim/slim": "<2.6", + "slub/slub-events": "<3.0.3", + "smarty/smarty": "<4.5.3|>=5,<5.1.1", + "snipe/snipe-it": "<8.1", + "socalnick/scn-social-auth": "<1.15.2", + "socialiteproviders/steam": "<1.1", + "spatie/browsershot": "<5.0.5", + "spatie/image-optimizer": "<1.7.3", + "spencer14420/sp-php-email-handler": "<1", + "spipu/html2pdf": "<5.2.8", + "spoon/library": "<1.4.1", + "spoonity/tcpdf": "<6.2.22", + "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", + "ssddanbrown/bookstack": "<24.05.1", + "starcitizentools/citizen-skin": ">=1.9.4,<3.4", + "starcitizentools/short-description": ">=4,<4.0.1", + "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", + "statamic/cms": "<=5.16", + "stormpath/sdk": "<9.9.99", + "studio-42/elfinder": "<=2.1.64", + "studiomitte/friendlycaptcha": "<0.1.4", + "subhh/libconnect": "<7.0.8|>=8,<8.1", + "sukohi/surpass": "<1", + "sulu/form-bundle": ">=2,<2.5.3", + "sulu/sulu": "<1.6.44|>=2,<2.5.25|>=2.6,<2.6.9|>=3.0.0.0-alpha1,<3.0.0.0-alpha3", + "sumocoders/framework-user-bundle": "<1.4", + "superbig/craft-audit": "<3.0.2", + "svewap/a21glossary": "<=0.4.10", + "swag/paypal": "<5.4.4", + "swiftmailer/swiftmailer": "<6.2.5", + "swiftyedit/swiftyedit": "<1.2", + "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", + "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": "<1.10.1", + "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", + "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", + "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", + "symbiote/silverstripe-multivaluefield": ">=3,<3.1", + "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", + "symbiote/silverstripe-seed": "<6.0.3", + "symbiote/silverstripe-versionedfiles": "<=2.0.3", + "symfont/process": ">=0", + "symfony/cache": ">=3.1,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8", + "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", + "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", + "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", + "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", + "symfony/mime": ">=4.3,<4.3.8", + "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/polyfill": ">=1,<1.10", + "symfony/polyfill-php55": ">=1,<1.10", + "symfony/process": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/routing": ">=2,<2.0.19", + "symfony/runtime": ">=5.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", + "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.4.10|>=7,<7.0.10|>=7.1,<7.1.3", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", + "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", + "symfony/symfony": "<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/translation": ">=2,<2.0.17", + "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", + "symfony/ux-autocomplete": "<2.11.2", + "symfony/ux-live-component": "<2.25.1", + "symfony/ux-twig-component": "<2.25.1", + "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4", + "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", + "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", + "symfony/webhook": ">=6.3,<6.3.8", + "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7|>=2.2.0.0-beta1,<2.2.0.0-beta2", + "symphonycms/symphony-2": "<2.6.4", + "t3/dce": "<0.11.5|>=2.2,<2.6.2", + "t3g/svg-sanitizer": "<1.0.3", + "t3s/content-consent": "<1.0.3|>=2,<2.0.2", + "tastyigniter/tastyigniter": "<4", + "tcg/voyager": "<=1.8", + "tecnickcom/tc-lib-pdf-font": "<2.6.4", + "tecnickcom/tcpdf": "<6.8", + "terminal42/contao-tablelookupwizard": "<3.3.5", + "thelia/backoffice-default-template": ">=2.1,<2.1.2", + "thelia/thelia": ">=2.1,<2.1.3", + "theonedemon/phpwhois": "<=4.2.5", + "thinkcmf/thinkcmf": "<6.0.8", + "thorsten/phpmyfaq": "<=4.0.1", + "tikiwiki/tiki-manager": "<=17.1", + "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", + "tinymce/tinymce": "<7.2", + "tinymighty/wiki-seo": "<1.2.2", + "titon/framework": "<9.9.99", + "tltneon/lgsl": "<7", + "tobiasbg/tablepress": "<=2.0.0.0-RC1", + "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", + "topthink/think": "<=6.1.1", + "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", + "torrentpier/torrentpier": "<=2.4.3", + "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", + "tribalsystems/zenario": "<=9.7.61188", + "truckersmp/phpwhois": "<=4.3.1", + "ttskch/pagination-service-provider": "<1", + "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", + "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", + "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<10.4.46|>=11,<11.5.40|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-beuser": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-core": "<=8.7.56|>=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-dashboard": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", + "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-felogin": ">=4.2,<4.2.3", + "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1", + "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", + "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", + "typo3/cms-lowlevel": ">=11,<=11.5.41", + "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", + "typo3/cms-scheduler": ">=11,<=11.5.41", + "typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", + "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", + "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", + "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", + "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", + "ua-parser/uap-php": "<3.8", + "uasoft-indonesia/badaso": "<=2.9.7", + "unisharp/laravel-filemanager": "<2.9.1", + "unopim/unopim": "<0.1.5", + "userfrosting/userfrosting": ">=0.3.1,<4.6.3", + "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", + "uvdesk/community-skeleton": "<=1.1.1", + "uvdesk/core-framework": "<=1.1.1", + "vanilla/safecurl": "<0.9.2", + "verbb/comments": "<1.5.5", + "verbb/formie": "<=2.1.43", + "verbb/image-resizer": "<2.0.9", + "verbb/knock-knock": "<1.2.8", + "verot/class.upload.php": "<=2.1.6", + "vertexvaar/falsftp": "<0.2.6", + "villagedefrance/opencart-overclocked": "<=1.11.1", + "vova07/yii2-fileapi-widget": "<0.1.9", + "vrana/adminer": "<4.8.1", + "vufind/vufind": ">=2,<9.1.1", + "waldhacker/hcaptcha": "<2.1.2", + "wallabag/tcpdf": "<6.2.22", + "wallabag/wallabag": "<2.6.11", + "wanglelecc/laracms": "<=1.0.3", + "wapplersystems/a21glossary": "<=0.4.10", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", + "web-auth/webauthn-lib": ">=4.5,<4.9", + "web-feet/coastercms": "==5.5", + "web-tp3/wec_map": "<3.0.3", + "webbuilders-group/silverstripe-kapost-bridge": "<0.4", + "webcoast/deferred-image-processing": "<1.0.2", + "webklex/laravel-imap": "<5.3", + "webklex/php-imap": "<5.3", + "webpa/webpa": "<3.1.2", + "wikibase/wikibase": "<=1.39.3", + "wikimedia/parsoid": "<0.12.2", + "willdurand/js-translation-bundle": "<2.1.1", + "winter/wn-backend-module": "<1.2.4", + "winter/wn-cms-module": "<1.0.476|>=1.1,<1.1.11|>=1.2,<1.2.7", + "winter/wn-dusk-plugin": "<2.1", + "winter/wn-system-module": "<1.2.4", + "wintercms/winter": "<=1.2.3", + "wireui/wireui": "<1.19.3|>=2,<2.1.3", + "woocommerce/woocommerce": "<6.6|>=8.8,<8.8.5|>=8.9,<8.9.3", + "wp-cli/wp-cli": ">=0.12,<2.5", + "wp-graphql/wp-graphql": "<=1.14.5", + "wp-premium/gravityforms": "<2.4.21", + "wpanel/wpanel4-cms": "<=4.3.1", + "wpcloud/wp-stateless": "<3.2", + "wpglobus/wpglobus": "<=1.9.6", + "wwbn/avideo": "<14.3", + "xataface/xataface": "<3", + "xpressengine/xpressengine": "<3.0.15", + "yab/quarx": "<2.4.5", + "yeswiki/yeswiki": "<4.5.4", + "yetiforce/yetiforce-crm": "<6.5", + "yidashi/yii2cmf": "<=2", + "yii2mod/yii2-cms": "<1.9.2", + "yiisoft/yii": "<1.1.31", + "yiisoft/yii2": "<2.0.52", + "yiisoft/yii2-authclient": "<2.2.15", + "yiisoft/yii2-bootstrap": "<2.0.4", + "yiisoft/yii2-dev": "<=2.0.45", + "yiisoft/yii2-elasticsearch": "<2.0.5", + "yiisoft/yii2-gii": "<=2.2.4", + "yiisoft/yii2-jui": "<2.0.4", + "yiisoft/yii2-redis": "<2.0.20", + "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", + "yoast-seo-for-typo3/yoast_seo": "<7.2.3", + "yourls/yourls": "<=1.8.2", + "yuan1994/tpadmin": "<=1.3.12", + "zencart/zencart": "<=1.5.7.0-beta", + "zendesk/zendesk_api_client_php": "<2.2.11", + "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", + "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-db": "<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", + "zendframework/zend-diactoros": "<1.8.4", + "zendframework/zend-feed": "<2.10.3", + "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-http": "<2.8.1", + "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", + "zendframework/zend-mail": "<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-session": ">=2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-validator": ">=2.3,<2.3.6", + "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zendframework": "<=3", + "zendframework/zendframework1": "<1.12.20", + "zendframework/zendopenid": "<2.0.2", + "zendframework/zendrest": "<2.0.2", + "zendframework/zendservice-amazon": "<2.0.3", + "zendframework/zendservice-api": "<1", + "zendframework/zendservice-audioscrobbler": "<2.0.2", + "zendframework/zendservice-nirvanix": "<2.0.2", + "zendframework/zendservice-slideshare": "<2.0.2", + "zendframework/zendservice-technorati": "<2.0.2", + "zendframework/zendservice-windowsazure": "<2.0.2", + "zendframework/zendxml": ">=1,<1.0.1", + "zenstruck/collection": "<0.2.1", + "zetacomponents/mail": "<1.8.2", + "zf-commons/zfc-user": "<1.2.2", + "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", + "zfr/zfr-oauth2-server-module": "<0.1.2", + "zoujingli/thinkadmin": "<=6.1.53" + }, + "default-branch": true, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2025-07-09T19:05:36+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "roave/security-advisories": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.4", + "ext-pdo": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php new file mode 100755 index 0000000..1266c65 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php @@ -0,0 +1,113 @@ +singleton(ExecutionTimeProfiler::class, function () { + return new ExecutionTimeProfiler(); + }); + + $this->profiler = $this->get(ExecutionTimeProfiler::class); + $this->profiler->start(); + + $this->singleton(Container::class, function (Container $container) { + return $container; + }); + + $this->singleton(Logger::class, function (Container $container) { + $path = $container->getConfigValue('logs.path') . '/oc_telegram_shop.log'; + return new Logger($path, Logger::LEVEL_INFO); + }); + + $this->singleton(Settings::class, function (Container $container) { + return new Settings($container->getConfigValue()); + }); + + $errorHandler = new ErrorHandler( + $this->get(Logger::class) + ); + + $errorHandler->register(); + } + + public function boot(): Application + { + $this->bootKernelServices(); + + $this->initializeEventDispatcher(static::$events); + + DependencyRegistration::register($this, $this->serviceProviders); + + static::$instance = $this; + + $this->profiler->addCheckpoint('Bootstrap Application'); + + return $this; + } + + public function handleRequest(Request $request): JsonResponse + { + $this->bind(Request::class, function () use ($request) { + return $request; + }); + + $router = $this->get(Router::class); + $apiAction = $request->get('api_action'); + $action = $router->resolve($apiAction); + + [$controller, $method] = $action; + + if (!class_exists($controller) || !method_exists($controller, $method)) { + throw new InvalidArgumentException('Invalid action: ' . $controller . '->' . $method); + } + + $this->profiler->addCheckpoint('Handle Router.'); + + $response = $this->call($controller, $method); + + $this->profiler->addCheckpoint('Handle HTTP request.'); + + return $response; + } + + public function bootAndHandleRequest(): void + { + $this->boot(); + $request = Request::createFromGlobals(); + $response = $this->handleRequest($request); + $response->send(); + } + + public function withServiceProviders(array $serviceProviders): Application + { + $this->serviceProviders = $serviceProviders; + + return $this; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheInterface.php new file mode 100755 index 0000000..59e8e4e --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheInterface.php @@ -0,0 +1,11 @@ +container->singleton(CacheInterface::class, function (Container $container) { + return new DatabaseCache( + $container->get(ConnectionInterface::class), + $container->get(Settings::class)->get('tables.cache'), + ); + }); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/DatabaseCache.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/DatabaseCache.php new file mode 100755 index 0000000..020b78d --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/DatabaseCache.php @@ -0,0 +1,63 @@ +connection = $connection; + $this->table = $table; + } + + public function get(string $key) + { + $cache = $this->connection->select( + " + SELECT result + FROM $this->table + WHERE cache_key = :key AND (expires_at IS NULL OR expires_at > NOW()) + ", + [':key' => $key] + ); + + return $cache ? unserialize($cache[0]['result'], ['allowed_classes' => true]) : null; + } + + public function set(string $key, $value, ?int $ttlSeconds = null): void + { + $this->connection->statement("DELETE FROM $this->table WHERE expires_at IS NOT NULL AND expires_at <= NOW()"); + + $expiresAt = $ttlSeconds ? date('Y-m-d H:i:s', time() + $ttlSeconds) : null; + + $sql = <<table (cache_key, result, expires_at) + VALUES (:key, :result, :expiresAt) + ON DUPLICATE KEY UPDATE + result = VALUES(result), + expires_at = VALUES(expires_at) + SQL; + + $this->connection->statement($sql, [ + ':key' => $key, + ':result' => serialize($value), + ':expiresAt' => $expiresAt, + ]); + } + + public function delete(string $key): void + { + $this->connection->statement("DELETE FROM $this->table WHERE cache_key = :key", [':key' => $key]); + } + + public function clear(): void + { + $this->connection->truncateTable($this->table); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Config/Settings.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Config/Settings.php new file mode 100755 index 0000000..3421f05 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Config/Settings.php @@ -0,0 +1,45 @@ +settings = $initialSettings; + } + + public function get(string $key, $default = null) + { + return Arr::get($this->settings, $key, $default); + } + + public function set(string $key, $value): void + { + Arr::set($this->settings, $key, $value); + } + + public function has(string $key): bool + { + return Arr::get($this->settings, $key) !== null; + } + + public function remove(string $key): void + { + Arr::unset($this->settings, $key); + } + + public function getAll(): array + { + return $this->settings; + } + + public function setAll(array $settings): void + { + $this->settings = $settings; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Container/Container.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Container/Container.php new file mode 100755 index 0000000..4ff468f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Container/Container.php @@ -0,0 +1,177 @@ +config = $config; + } + + public function getConfigValue(?string $key = null, $default = null) + { + if ($key === null) { + return $this->config; + } + + return Arr::get($this->config, $key, $default); + } + + public function has(string $id): bool + { + return array_key_exists($id, $this->factories); + } + + public function bind(string $abstract, callable $concrete, bool $singleton = false): void + { + $this->factories[$abstract] = [ + 'concrete' => $concrete, + 'singleton' => $singleton, + ]; + } + + public function singleton(string $abstract, callable $concrete): void + { + $this->bind($abstract, $concrete, true); + } + + /** + * @template T + * @param string $id + * @return T + * @psalm-param class-string|string $id + * @psalm-suppress MoreSpecificImplementedParamType + */ + public function get(string $id) + { + try { + if ($this->has($id)) { + if ($this->factories[$id]['singleton']) { + if (!array_key_exists($id, $this->instances)) { + $this->instances[$id] = $this->factories[$id]['concrete']($this); + } + + return $this->instances[$id]; + } + + return $this->factories[$id]['concrete']($this); + } + + if (class_exists($id)) { + $reflection = new ReflectionClass($id); + $constructor = $reflection->getConstructor(); + + $arguments = []; + + if ($constructor && $parameters = $constructor->getParameters()) { + foreach ($parameters as $parameter) { + if ($parameter->getType() instanceof ReflectionNamedType) { + $parameterType = $parameter->getType()->getName(); + /** @psalm-suppress ArgumentTypeCoercion */ + $arguments[] = $this->get($parameterType); + } else { + $arguments[] = null; + } + } + } + + /** @var T $instance */ + $instance = $reflection->newInstanceArgs($arguments); + + return $instance; + } + } catch (ReflectionException $exception) { + throw new ContainerDependencyResolutionException( + 'Could not resolve the concrete: ' . $id . '. ' . $exception->getMessage() + ); + } + + throw new ContainerDependencyResolutionException( + 'Could not resolve the concrete: ' . $id . ' (class does not exist)' + ); + } + + /** + * @param class-string $abstract + * @throws ReflectionException + */ + public function call(string $abstract, string $method): JsonResponse + { + if (!class_exists($abstract)) { + throw new ContainerDependencyResolutionException('Could not resolve the concrete: ' . $abstract); + } + + if (!method_exists($abstract, $method)) { + throw new ContainerDependencyResolutionException('Method not found: ' . $abstract . '@' . $method); + } + + $instance = $this->get($abstract); + + $reflection = new ReflectionMethod($instance, $method); + + $arguments = []; + foreach ($reflection->getParameters() as $parameter) { + if ($parameter->getType() instanceof ReflectionNamedType) { + $parameterType = $parameter->getType()->getName(); + /** @psalm-suppress ArgumentTypeCoercion */ + $arguments[] = $this->get($parameterType); + } else { + $arguments[] = null; + } + } + + return $instance->{$method}(...$arguments); + } + + public function registerEventListener(string $eventClass, string $listenerClass): void + { + $dispatcher = $this->get(EventDispatcher::class); + + $dispatcher->registerListener($eventClass, $listenerClass); + } + + public function initializeEventDispatcher(array $listeners): void + { + $this->singleton(EventDispatcher::class, function (Container $container) { + return new EventDispatcher($container); + }); + + foreach ($listeners as $eventClass => $listenerClasses) { + foreach ($listenerClasses as $listenerClass) { + $this->registerEventListener($eventClass, $listenerClass); + } + } + } + + public function tag(string $tag, array $abstracts): void + { + if (array_key_exists($tag, $this->taggedAbstracts)) { + throw new RuntimeException('Tag already exists: ' . $tag); + } + + $this->taggedAbstracts[$tag] = $abstracts; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Container/ServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Container/ServiceProvider.php new file mode 100755 index 0000000..8cf43f2 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Container/ServiceProvider.php @@ -0,0 +1,16 @@ +container = $container; + } + + abstract public function register(): void; +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Contracts/Arrayable.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Contracts/Arrayable.php new file mode 100755 index 0000000..239a268 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Contracts/Arrayable.php @@ -0,0 +1,13 @@ +singleton(UserSettingsInterface::class, function (Container $container) { + return $container->get(DatabaseUserSettings::class); + }); + + static::registerServiceProviders($container, $serviceProviders); + } + + private static function registerServiceProviders(Container $container, array $serviceProviders): void + { + foreach ($serviceProviders as $serviceProvider) { + $provider = $container->get($serviceProvider); + if (!$provider instanceof ServiceProvider) { + throw new RuntimeException('ServiceProvider must extend ServiceProvider'); + } + + $provider->register(); + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/ErrorHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/ErrorHandler.php new file mode 100755 index 0000000..e13a728 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/ErrorHandler.php @@ -0,0 +1,93 @@ +logger = $logger; + } + + public function register(): void + { + set_error_handler([$this, 'handleError']); + set_exception_handler([$this, 'handleException']); + register_shutdown_function([$this, 'handleShutdown']); + } + + public function handleError(int $severity, string $message, string $file, int $line): bool + { + $this->logger->error('Handled PHP error: ' . implode(', ', compact('severity', 'message', 'file', 'line'))); + + // Convert warnings and notices to ErrorException + if (!(error_reporting() & $severity)) { + return true; + } + + throw new ErrorException($message, 0, $severity, $file, $line); + } + + public function handleException(Throwable $exception): void + { + if (!$exception instanceof NonLoggableExceptionInterface) { + $this->logger->logException($exception); + } + + if (PHP_SAPI === 'cli') { + echo $exception->getMessage() . PHP_EOL; + } else { + (new JsonResponse([ + 'exception' => get_class($exception), + 'message' => $exception->getMessage(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTrace(), + ], Response::HTTP_INTERNAL_SERVER_ERROR))->send(); + } + + + exit(1); + } + + public function handleShutdown(): void + { + $error = error_get_last(); + + if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR], true)) { + $message = sprintf( + "[%s] Fatal error: %s in %s:%d", + date('Y-m-d H:i:s'), + $error['message'], + $error['file'], + $error['line'] + ); + + $this->logger->error($message); + + if (PHP_SAPI === 'cli') { + echo $message . PHP_EOL; + } else { + (new JsonResponse([ + 'message' => $message, + 'file' => $error['file'], + 'line' => $error['line'], + ], Response::HTTP_INTERNAL_SERVER_ERROR))->send(); + } + + exit(1); + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Events/Event.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Events/Event.php new file mode 100755 index 0000000..141b5fe --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Events/Event.php @@ -0,0 +1,7 @@ +container = $container; + } + + public function registerListener(string $eventClass, string $listenerClass): void + { + $this->listeners[$eventClass][] = $listenerClass; + } + + public function dispatch(Event $event): void + { + $eventClass = get_class($event); + if (isset($this->listeners[$eventClass])) { + foreach ($this->listeners[$eventClass] as $listenerClass) { + $listener = $this->container->get($listenerClass); + $listener->handle($event); + } + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Events/Listener.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Events/Listener.php new file mode 100755 index 0000000..8f3fea9 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Events/Listener.php @@ -0,0 +1,7 @@ +data = $data; + $this->code = $code; + } + + public function getData(): array + { + return $this->data; + } + + public function getCode(): int + { + return $this->code; + } + + public function send(): void + { + $this->sendHeaders(); + $this->sendContent(); + } + + private function sendContent(): void + { + if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(204); + exit; + } + + echo json_encode($this->getData(), JSON_THROW_ON_ERROR); + } + + private function sendHeaders(): void + { + http_response_code($this->getCode()); + header('Content-Type: application/json'); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php new file mode 100755 index 0000000..394ef31 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php @@ -0,0 +1,72 @@ +query = $query; + $this->request = $request; + $this->cookies = $cookies; + $this->files = $files; + $this->server = $server; + $this->content = $content; + } + + public static function createFromGlobals(): Request + { + return new static($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } + + public function getContent(): string + { + if ($this->content === null || $this->content === '') { + $this->content = (string)file_get_contents('php://input'); + } + + return $this->content; + } + + public function json(string $key = null, $default = null) + { + $content = $this->getContent(); + + if (!$content) { + return $default; + } + + $decoded = Utils::safeJsonDecode($content); + + if ($key === null) { + return $decoded; + } + + return Arr::get($decoded, $key, $default); + } + + public function get(string $key = null, $default = null) + { + if ($key === null) { + return $this->query; + } + + return $this->query[$key] ?? $default; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Response.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Response.php new file mode 100755 index 0000000..2517155 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Response.php @@ -0,0 +1,70 @@ +logFile = $logFile; + $this->logLevel = $logLevel; + $this->maxFileSize = $maxFileSize; + } + + public function log($message, $level = self::LEVEL_INFO) + { + if ($level < $this->logLevel) { + return; // Не логируем, если уровень ниже установленного + } + + $this->rotateLogs(); + + $timestamp = date('Y-m-d H:i:s'); + $levelStr = $this->getLevelString($level); + $logMessage = "[$timestamp] [$levelStr] $message\n"; + + file_put_contents($this->logFile, $logMessage, FILE_APPEND); + } + + private function getLevelString($level): string + { + switch ($level) { + case self::LEVEL_INFO: + return 'INFO'; + case self::LEVEL_WARNING: + return 'WARNING'; + case self::LEVEL_ERROR: + return 'ERROR'; + default: + return 'UNKNOWN'; + } + } + + public function info(string $message): void + { + $this->log($message, self::LEVEL_INFO); + } + + public function warning(string $message): void + { + $this->log($message, self::LEVEL_WARNING); + } + + public function error(string $message): void + { + $this->log($message, self::LEVEL_ERROR); + } + + private function rotateLogs(): void + { + if (is_file($this->logFile) && filesize($this->logFile) >= $this->maxFileSize) { + $newLogFile = $this->logFile . '.' . date('YmdHis'); + rename($this->logFile, $newLogFile); + } + } + + public function logException(Throwable $exception): void + { + $this->error( + sprintf( + "Fatal error %s in %s on line %d\n%s", + $exception->getMessage(), + $exception->getFile(), + $exception->getLine(), + $exception->getTraceAsString() + ) + ); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php new file mode 100755 index 0000000..a1c3f6a --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Builder.php @@ -0,0 +1,429 @@ + [], + 'from' => [], + 'join' => [], + 'where' => [], + 'order' => [], + ]; + + public function __construct(ConnectionInterface $connection, Grammar $grammar) + { + $this->connection = $connection; + $this->grammar = $grammar; + } + + public function newQuery(): Builder + { + return new self($this->getConnection(), $this->getGrammar()); + } + + public function select(array $columns): Builder + { + $this->columns = []; + $this->bindings['select'] = []; + + if (!$columns) { + throw new InvalidArgumentException('Select columns not provided'); + } + + foreach ($columns as $key => $value) { + if ($value instanceof RawExpression) { + $this->columns[] = $value; + continue; + } + + if (is_string($key)) { + $this->columns[] = [ + 'column' => $key, + 'as' => $value, + ]; + continue; + } + + $this->columns[] = [ + 'column' => $value, + 'as' => null, + ]; + } + + return $this; + } + + public function from(string $table, ?string $as = null): Builder + { + $this->from = compact('table', 'as'); + + return $this; + } + + public function whereNull($column, $boolean = 'and'): Builder + { + $this->wheres[] = [ + 'type' => 'Null', + 'column' => $column, + 'boolean' => $boolean, + ]; + + return $this; + } + + public function whereNotNull($column, $boolean = 'and'): Builder + { + $this->wheres[] = [ + 'type' => 'NotNull', + 'column' => $column, + 'boolean' => $boolean, + ]; + + return $this; + } + + public function whereNested(Closure $callback, $boolean = 'and'): Builder + { + $callback($nestedQuery = $this->newQuery()); + + $this->wheres[] = [ + 'type' => 'Nested', + 'query' => $nestedQuery, + 'boolean' => $boolean, + ]; + + $this->bindings['where'] = array_merge( + $this->bindings['where'], + $nestedQuery->getBindings('where') + ); + + return $this; + } + + public function whereRaw(string $expression, $boolean = 'and'): Builder + { + $this->wheres[] = [ + 'type' => 'Raw', + 'expression' => $expression, + 'boolean' => $boolean, + ]; + + return $this; + } + + public function where($column, $operator, $value, $boolean = 'and'): Builder + { + $this->wheres[] = [ + 'type' => 'Basic', + 'column' => $column, + 'operator' => $operator, + 'value' => $value, + 'boolean' => $boolean, + ]; + + $this->addBinding($value); + + return $this; + } + + public function whereIn($column, array $values, $not = false, $boolean = 'and'): Builder + { + $this->wheres[] = [ + 'type' => 'In', + 'column' => $column, + 'operator' => $not ? 'NOT IN' : 'IN', + 'value' => $values, + 'boolean' => $boolean, + ]; + + $this->addBinding($values); + + return $this; + } + + public function whereNotIn($column, array $values, $boolean = 'and'): Builder + { + $this->wheres[] = [ + 'type' => 'In', + 'column' => $column, + 'operator' => 'NOT IN', + 'value' => $values, + 'boolean' => $boolean, + ]; + + $this->addBinding($values); + + return $this; + } + + public function whereBetween(string $column, array $values, $boolean = 'and'): Builder + { + if (count($values) !== 2) { + throw new InvalidArgumentException('Invalid number of values provided.'); + } + + $this->wheres[] = [ + 'type' => 'Between', + 'column' => $column, + 'values' => $values, + 'boolean' => $boolean, + ]; + + $this->addBinding($values[0]); + $this->addBinding($values[1]); + + return $this; + } + + public function orWhere($column, $operator, $value): Builder + { + return $this->where($column, $operator, $value, 'or'); + } + + public function limit(int $limit): Builder + { + $this->limit = $limit; + + return $this; + } + + public function offset(int $offset): Builder + { + $this->offset = $offset; + + return $this; + } + + public function forPage(int $page = 0, int $perPage = 10): Builder + { + $offset = ($page - 1) * $perPage; + + return $this->offset($offset)->limit($perPage); + } + + public function orderBy(string $column, string $direction = 'ASC'): Builder + { + $direction = strtoupper($direction); + + if (!in_array($direction, ['ASC', 'DESC'], true)) { + throw new InvalidArgumentException('Order direction must be "ASC" or "DESC".'); + } + + $this->orders[] = [ + 'column' => $column, + 'direction' => $direction, + ]; + + return $this; + } + + public function toSql(): string + { + return $this->grammar->compileComponents($this); + } + + public function toRawSql(): string + { + return $this->substituteBindingsIntoRawSql( + $this->toSql(), + $this->getBindings() + ); + } + + public function substituteBindingsIntoRawSql(string $sql, array $bindings): string + { + $bindings = array_map(function ($value) { + return $this->connection->escape($value); + }, $bindings); + + $query = ''; + + $isStringLiteral = false; + + $sqlLen = strlen($sql); + + for ($i = 0; $i < $sqlLen; $i++) { + $char = $sql[$i]; + $nextChar = $sql[$i + 1] ?? null; + + // Single quotes can be escaped as '' according to the SQL standard while + // MySQL uses \'. Postgres has operators like ?| that must get encoded + // in PHP like ??|. We should skip over the escaped characters here. + if (in_array($char . $nextChar, ["\'", "''", '??'])) { + $query .= $char . $nextChar; + ++$i; + } elseif ($char === "'") { // Starting / leaving string literal... + $query .= $char; + $isStringLiteral = !$isStringLiteral; + } elseif ($char === '?' && !$isStringLiteral) { // Substitutable binding... + $query .= array_shift($bindings) ?? '?'; + } else { // Normal character... + $query .= $char; + } + } + + return $query; + } + + public function get(): array + { + return $this->runSelect(); + } + + /** + * @return array|null + */ + public function firstOrNull(): ?array + { + $rows = $this->limit(1)->get(); + + return $rows[0] ?? null; + } + + public function value(string $field) + { + $rows = $this->limit(1)->get(); + + return $rows[0][$field] ?? null; + } + + public function pluck(string $column, string $index = null): array + { + $items = $this->runSelect(); + + return array_column($items, $column, $index); + } + + public function storeResultsToTable(string $destinationTable): bool + { + $sql = "INSERT INTO `$destinationTable` {$this->toSql()}"; + + $this->connection->truncateTable($destinationTable); + + return $this->connection->statement($sql, $this->getBindings()); + } + + public function runSelect(): array + { + return $this->connection->select( + $this->toSql(), + $this->getBindings() + ); + } + + public function getBindings(string $type = null): array + { + if ($type === null) { + return Utils::arrayFlatten($this->bindings); + } + + return $this->bindings[$type]; + } + + public function addBinding($value, $type = 'where'): Builder + { + if (!array_key_exists($type, $this->bindings)) { + throw new InvalidArgumentException("Invalid binding type: {$type}."); + } + + $this->bindings[$type][] = $value; + + return $this; + } + + public function dd(): void + { + Utils::dd($this->toSql(), $this->getBindings()); + } + + public function join(string $table, Closure $joinClosure, $type = 'inner'): Builder + { + $joinClause = new JoinClause($this, $type, $table); + + $joinClosure($joinClause); + + $this->joins[] = $joinClause; + $this->bindings['join'][] = $joinClause->getBindings(); + + return $this; + } + + public function leftJoin(string $table, Closure $joinClosure): Builder + { + return $this->join($table, $joinClosure, 'left'); + } + + public function getConnection(): ConnectionInterface + { + return $this->connection; + } + + public function getGrammar(): Grammar + { + return $this->grammar; + } + + public function count(): int + { + $sql = 'SELECT COUNT(*) AS total_count FROM (' . $this->toSql() . ') AS x'; + + $result = $this->connection->select($sql, $this->getBindings()); + + return $result[0]['total_count'] ?? 0; + } + + public function distinct(): Builder + { + $this->distinct = true; + + return $this; + } + + /** + * Apply a callback to the query if the given condition is true. + * + * @param bool $condition + * @param Closure $callback + * @param Closure|null $default + * @return $this + */ + public function when(bool $condition, Closure $callback, Closure $default = null): Builder + { + if ($condition) { + $callback($this); + } elseif ($default) { + $default($this); + } + + return $this; + } + + public function hasJoinAlias(string $joinAlias): bool + { + $join = array_filter($this->joins, static function (JoinClause $join) use ($joinAlias) { + return strpos($join->table, $joinAlias) !== false; + }); + + return count($join) > 0; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php new file mode 100755 index 0000000..3480ad1 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php @@ -0,0 +1,34 @@ +pdo = $pdo; + + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $this->pdo->exec("SET NAMES 'utf8'"); + $this->pdo->exec("SET CHARACTER SET utf8"); + $this->pdo->exec("SET CHARACTER_SET_CONNECTION=utf8"); + $this->pdo->exec("SET SQL_MODE = ''"); + } + + public function select(string $sql, array $bindings = []): array + { + $statement = $this->pdo->prepare($sql); + $statement->execute($bindings); + + return $statement->fetchAll(PDO::FETCH_ASSOC); + } + + public function escape($value): string + { + switch (true) { + case $value === null: + return 'null'; + + case is_int($value) || is_float($value): + return (string)$value; + + case is_bool($value): + return $value ? '1' : '0'; + + case is_array($value): + throw new RuntimeException('The database connection does not support escaping arrays.'); + + default: + if (Utils::strContains($value, "\00")) { + throw new RuntimeException( + 'Strings with null bytes cannot be escaped. Use the binary escape option.' + ); + } + + if (preg_match('//u', $value) === false) { + throw new RuntimeException('Strings with invalid UTF-8 byte sequences cannot be escaped.'); + } + + return $this->pdo->quote($value); + } + } + + public function createTableFromSql(string $table, string $sql, array $bindings = []): bool + { + $createSql = "CREATE TABLE IF NOT EXISTS `$table` AS ($sql)"; + + return $this->pdo->prepare($createSql)->execute($bindings); + } + + public function dropTable(string $table): bool + { + return $this->pdo->prepare("DROP TABLE `$table`")->execute(); + } + + public function dropTableIfExists(string $table): bool + { + return $this->pdo->prepare("DROP TABLE IF EXISTS `$table`")->execute(); + } + + public function tableExists(string $table): bool + { + $statement = $this->pdo->prepare("SHOW TABLES LIKE ?;"); + $statement->execute([$table]); + + return $statement->rowCount() > 0; + } + + public function statement(string $sql, array $bindings = []): bool + { + return $this->pdo->prepare($sql)->execute($bindings); + } + + public function beginTransaction(): bool + { + return $this->pdo->beginTransaction(); + } + + public function commitTransaction(): bool + { + return $this->pdo->commit(); + } + + public function rollBackTransaction(): bool + { + return $this->pdo->rollBack(); + } + + public function insertIntoTableFromSql(string $table, string $sql, array $bindings = []): bool + { + $createSql = "INSERT INTO `{$table}` {$sql}"; + + return $this->pdo->prepare($createSql)->execute($bindings); + } + + public function truncateTable(string $table): bool + { + return $this->pdo->prepare("TRUNCATE TABLE {$table}")->execute(); + } + + public function lastInsertId($name = null): int + { + return $this->pdo->lastInsertId($name); + } + + public function getVersion(): string + { + return $this->pdo + ->query('SELECT VERSION()') + ->fetch(PDO::FETCH_COLUMN); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php new file mode 100755 index 0000000..0459373 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/Grammar.php @@ -0,0 +1,188 @@ + [], + 'from' => [], + 'joins' => [], + 'wheres' => [], + 'orders' => [], + 'limit' => [], + 'offset' => [], + ]; + + private function resetCompiled(): void + { + $this->compiled = [ + 'columns' => [], + 'from' => [], + 'joins' => [], + 'wheres' => [], + 'orders' => [], + 'limit' => [], + 'offset' => [], + ]; + } + + public function compileComponents(Builder $builder): string + { + $this->resetCompiled(); + + foreach (array_keys($this->compiled) as $component) { + $method = 'compile' . ucfirst($component); + + if (!method_exists($this, $method)) { + throw new BadMethodCallException("Method '{$method}' does not exist."); + } + + if (! empty($builder->{$component})) { + $this->compiled[$component] = $this->{$method}( + $builder, + $builder->{$component}, + ); + } + } + + return $this->concatCompiled($this->compiled); + } + + public function concatCompiled(array $compiled): string + { + $array = []; + + foreach (array_keys($this->compiled) as $component) { + $array[] = $compiled[$component] ?? null; + } + + return implode(' ', array_filter($array)); + } + + public function compileColumns(Builder $builder, $columns): string + { + if ($columns === '*' || count($columns) === 0) { + return 'SELECT *'; + } + + $columns = array_map(static function ($item) { + if ($item instanceof RawExpression) { + return $item->getValue(); + } + + return $item['column'] . ($item['as'] !== null ? ' AS ' . $item['as'] : ''); + }, $columns); + + $distinct = $builder->distinct ? 'DISTINCT ' : ''; + + return "SELECT {$distinct}" . implode(', ', $columns); + } + + public function compileWheres(Builder $builder, $wheres, string $prefix = 'WHERE '): string + { + if (!$wheres) { + return ''; + } + + $compiledConditions = []; + + $counter = 0; + foreach ($wheres as $condition) { + $compiledConditions[] = ($counter > 0 ? mb_strtoupper($condition['boolean']) . ' ' : '') + . $this->{'where' . $condition['type']}($condition); + $counter++; + } + + return $prefix . implode(' ', $compiledConditions); + } + + public function whereBasic($condition): string + { + return $condition['column'] . ' ' . $condition['operator'] . ' ?'; + } + + public function whereRaw($condition): string + { + return $condition['expression']; + } + + public function whereNull($condition): string + { + return $condition['column'] . ' IS NULL'; + } + + public function whereNotNull($condition): string + { + return $condition['column'] . ' IS NOT NULL'; + } + + public function whereNested($condition): string + { + return '(' . $this->compileWheres($condition['query'], $condition['query']->wheres, '') . ')'; + } + + public function whereBetween($condition): string + { + return $condition['column'] . ' BETWEEN ? AND ?'; + } + + public function compileOrders(Builder $builder, array $orders): string + { + return 'ORDER BY ' . implode( + ', ', + array_map( + static function ($order) { + return $order['column'] . ' ' . $order['direction']; + }, + $orders + ) + ); + } + + public function compileFrom(Builder $builder, array $from): string + { + return 'FROM ' . $from['table'] . ($from['as'] !== null ? ' AS ' . $from['as'] : ''); + } + + public function compileLimit(Builder $builder, $limit): string + { + return "LIMIT $limit"; + } + + public function compileOffset(Builder $builder, $offset): string + { + return "OFFSET $offset"; + } + + public function compileJoins(Builder $builder, $joins): string + { + return implode(' ', array_map(function (JoinClause $join) use ($builder) { + $conditions = ''; + if ($join->wheres) { + $conditions = ' AND ' . $this->compileWheres($builder, $join->wheres, ''); + } + + return mb_strtoupper( + $join->type + ) . " JOIN $join->table ON $join->first $join->operator $join->second" . $conditions; + }, $joins)); + } + + public function whereIn($condition): string + { + if (! $condition['value']) { + return ''; + } + + $inValues = str_repeat('?, ', count($condition['value']) - 1) . '?'; + $inOperator = $condition['operator']; + + return $condition['column'] . " $inOperator (" . $inValues . ')'; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/MySqlGrammar.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/MySqlGrammar.php new file mode 100755 index 0000000..2f2125f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Grammars/MySqlGrammar.php @@ -0,0 +1,7 @@ + [], + ]; + + public function __construct(Builder $parent, string $type, string $table) + { + $this->type = $type; + $this->table = $table; + + parent::__construct($parent->getConnection(), $parent->getGrammar()); + } + + public function on($first, $operator, $second): JoinClause + { + $this->first = $first; + $this->operator = $operator; + $this->second = $second; + + return $this; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/QueryBuilderException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/QueryBuilderException.php new file mode 100755 index 0000000..d30be66 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/QueryBuilderException.php @@ -0,0 +1,9 @@ +container->bind(ConnectionInterface::class, function (Container $container) { + $host = $container->getConfigValue('db.host'); + $username = $container->getConfigValue('db.username'); + $password = $container->getConfigValue('db.password'); + $port = $container->getConfigValue('db.port'); + $dbName = $container->getConfigValue('db.database'); + + $dsn = "mysql:host=$host;port=$port;dbname=$dbName"; + + return new MySqlConnection( + new PDO($dsn, $username, $password) + ); + }); + + $this->container->bind(Builder::class, function (Container $container) { + return new Builder( + $container->get(ConnectionInterface::class), + $container->get(MySqlGrammar::class) + ); + }); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/QueryResult.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/QueryResult.php new file mode 100755 index 0000000..e31ae41 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/QueryResult.php @@ -0,0 +1,44 @@ +row = $row; + $this->rows = $rows; + $this->numRows = $numRows; + } + + /** + * @return mixed + */ + public function getRow() + { + return $this->row; + } + + /** + * @return mixed + */ + public function getRows() + { + return $this->rows; + } + + /** + * @return mixed + */ + public function getNumRows(): int + { + return $this->numRows; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/RawExpression.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/RawExpression.php new file mode 100755 index 0000000..116a394 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/RawExpression.php @@ -0,0 +1,18 @@ +value = $value; + } + + public function getValue(): string + { + return $this->value; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php new file mode 100755 index 0000000..3a295b0 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php @@ -0,0 +1,16 @@ +container->singleton(Router::class, function (Container $container) { + return new Router(); + }); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/Router.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/Router.php new file mode 100755 index 0000000..c66a74b --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/Router.php @@ -0,0 +1,38 @@ +routes = $routes; + } + + public function loadRoutesFromFile(string $file): void + { + $this->routes = include $file; + } + + /** + * @param $action + * @return string[] + * @throws ActionNotFoundException + */ + public function resolve($action): array + { + if (!$action) { + throw new ActionNotFoundException('No action provided'); + } + + if (isset($this->routes[$action])) { + return $this->routes[$action]; + } + + throw new ActionNotFoundException('Action "' . $action . '" not found.'); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Settings/DatabaseUserSettings.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Settings/DatabaseUserSettings.php new file mode 100755 index 0000000..6f8c428 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Settings/DatabaseUserSettings.php @@ -0,0 +1,90 @@ +builder = $builder; + $this->database = $database; + } + + public function getAll(): array + { + if ($this->allSettings) { + return $this->allSettings; + } + + $row = $this->builder + ->select(['settings_json']) + ->from(config('tables.settings')) + ->where('id', '=', 1) + ->firstOrNull(); + + if (! $row || empty($row['settings_json'])) { + throw new RuntimeException('Database settings not found. Please reinstall the module.'); + } + + try { + $decoded = json_decode($row['settings_json'], true, 512, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw new RuntimeException('Database settings invalid format: ' . $e->getMessage()); + } + + $this->allSettings = $decoded; + + return $decoded; + } + + public function update(array $userSettings): void + { + $settingsTable = config('tables.settings'); + try { + $currentSettings = $this->getAll(); + $updatedSettings = array_replace_recursive($currentSettings, $userSettings); + + $encoded = json_encode($updatedSettings, JSON_THROW_ON_ERROR); + + $sql = <<database->statement($sql, [$encoded]); + } catch (Exception $e) { + throw new RuntimeException('Could not update settings: ' . $e->getMessage()); + } + } + + public function get(string $key, $default = null) + { + $all = $this->getAll(); + + return Arr::get($all, $key, $default); + } + + public function safeGet(string $key, $default = null) + { + try { + return $this->get($key, $default); + } catch (Exception $e) { + return $default; + } + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Settings/UserSettingsInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Settings/UserSettingsInterface.php new file mode 100755 index 0000000..e05260d --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Settings/UserSettingsInterface.php @@ -0,0 +1,11 @@ + 1) { + $k = array_shift($keys); + if (!isset($array[$k]) || !is_array($array[$k])) { + return; + } + $array = &$array[$k]; + } + unset($array[array_shift($keys)]); + } + + public static function find(array $array, callable $callback) + { + foreach ($array as $key => $value) { + if ($callback($value, $key) === true) { + return $value; + } + } + + return null; + } + + public static function pluck(array $value, string $field): array + { + return array_map( + static function ($item) use ($field) { + if (is_object($item)) { + return $item->{$field}; + } + + return $item[$field]; + }, + $value + ); + } + + public static function mergeArraysRecursively(array $base, array $override): array + { + foreach ($override as $key => $value) { + if (isset($base[$key]) && is_array($base[$key]) && is_array($value)) { + $base[$key] = static::mergeArraysRecursively($base[$key], $value); + } else { + $base[$key] = $value; + } + } + return $base; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/ExecutionTimeProfiler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/ExecutionTimeProfiler.php new file mode 100755 index 0000000..a80e216 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/ExecutionTimeProfiler.php @@ -0,0 +1,49 @@ +startTime = microtime(true); + $this->checkpoints = [['label' => 'start', 'time' => $this->startTime]]; + } + + public function addCheckpoint(string $label): void + { + $currentTime = microtime(true); + $this->checkpoints[] = [ + 'label' => $label, + 'time' => $currentTime + ]; + } + + public function stop(): array + { + $endTime = microtime(true); + $this->checkpoints[] = ['label' => 'End', 'time' => $endTime]; + + $result = [ + 'total_execution_time_ms' => round(($endTime - $this->startTime) * 1000, 2), + 'checkpoints' => [] + ]; + + $total = count($this->checkpoints); + + for ($i = 1; $i < $total; $i++) { + $prev = $this->checkpoints[$i - 1]; + $current = $this->checkpoints[$i]; + + $result['checkpoints'][] = [ + 'label' => $current['label'], + 'elapsed_since_previous_ms' => round(($current['time'] - $prev['time']) * 1000, 2) + ]; + } + + return $result; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/PaginationHelper.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/PaginationHelper.php new file mode 100755 index 0000000..524ef9a --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/PaginationHelper.php @@ -0,0 +1,11 @@ + + */ + public static function arrayFlatten(array $array, $depth = INF): array + { + $result = []; + + foreach ($array as $item) { + if (! is_array($item)) { + $result[] = $item; + } else { + $values = $depth === 1 + ? array_values($item) + : static::arrayFlatten($item, $depth - 1); + + foreach ($values as $value) { + $result[] = $value; + } + } + } + + return $result; + } + + /** + * @return never + */ + public static function dd() + { + $args = func_get_args(); + echo '
';
+        /** @psalm-suppress ForbiddenCode */
+        var_dump(...$args);
+        echo '
'; + die(); + } + + public static function strContains(string $haystack, string $needle): bool + { + return $needle === '' || strpos($haystack, $needle) !== false; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/helpers.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/helpers.php new file mode 100755 index 0000000..0d01cf1 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Support/helpers.php @@ -0,0 +1,59 @@ +getConfigValue('db.prefix'); + + return $prefix . $name; + } +} + +if (!function_exists('column')) { + function db_column($column): string + { + if (strpos($column, '.') !== false) { + [$table, $column] = explode('.', $column, 2); + + if ($table === '' || $column === '') { + throw new InvalidArgumentException('Invalid column reference: ' . $column); + } + + return db_table($table) . '.' . $column; + } + + return $column; + } +} + +if (!function_exists('resources_path')) { + function resources_path(string $path = ''): string + { + return BP_BASE_PATH . '/resources/' . $path; + } +} + +if (!function_exists('base_path')) { + function base_path(string $path = ''): string + { + return BP_BASE_PATH . '/' . $path; + } +} + +if (!function_exists('config')) { + function config(string $key, $default = null) + { + return Application::getInstance()->get(Settings::class)->get($key, $default); + } +} + +if (! function_exists('dd')) { + function dd(): void + { + Utils::dd(func_get_args()); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Translator/Translator.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Translator/Translator.php new file mode 100755 index 0000000..a7d1837 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Translator/Translator.php @@ -0,0 +1,64 @@ +language = $language; + $this->translations = $translations; + } + + public function loadTranslationsFromFolder(string $directory): void + { + if (!is_dir($directory)) { + throw new RuntimeException('Translations folder not found.'); + } + + $filename = $directory . "/{$this->language}.php"; + if (!is_file($filename)) { + throw new RuntimeException("Translation file for language '{$this->language}' not found."); + } + + $translations = include $filename; + $this->translations = array_merge($this->translations, $translations); + } + + public function translate(string $key, array $params = []): string + { + if (isset($this->translations[$key])) { + return $this->substitute($this->translations[$key], $params); + } + + return $key; + } + + public function getTranslations(): array + { + return $this->translations; + } + + public function getLanguage(): string + { + return $this->language; + } + + private function substitute($text, array $parameters): string + { + if (!$parameters) { + return $text; + } + + $search = array_map(static function ($param) { + return '{' . $param . '}'; + }, array_keys($parameters)); + + return str_replace($search, array_values($parameters), $text); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Translator/TranslatorInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Translator/TranslatorInterface.php new file mode 100755 index 0000000..346ef4e --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Translator/TranslatorInterface.php @@ -0,0 +1,12 @@ +container->singleton(TranslatorInterface::class, function (Container $container) { + $language = $container->getConfigValue('lang'); + $translator = new Translator($language); + $translator->loadTranslationsFromFolder(resources_path('/translations')); + return $translator; + }); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/Validator.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/Validator.php new file mode 100755 index 0000000..600039f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Validator/Validator.php @@ -0,0 +1,24 @@ +input = $input; + $this->rules = $rules; + } + + public function validate(): bool + { + foreach ($this->rules as $name => $rule) { + $components = explode('|', $rule); + } + + return true; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php new file mode 100755 index 0000000..9d472f2 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php @@ -0,0 +1,26 @@ +withServiceProviders([ + QueryBuilderServiceProvider::class, + CacheServiceProvider::class, + RouteServiceProvider::class, + AppServiceProvider::class + ]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/HelloWorldHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/HelloWorldHandler.php new file mode 100755 index 0000000..7c613e7 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/HelloWorldHandler.php @@ -0,0 +1,50 @@ +queryBuilder = $queryBuilder; + } + + public function handle(): JsonResponse + { + $languageId = 1; + + $products = $this->queryBuilder->newQuery() + ->select([ + 'products.product_id' => 'product_id', + 'products.quantity' => 'product_quantity', + 'product_description.name' => 'product_name', + 'products.price' => 'product_price', + ]) + ->from(db_table('product'), 'products') + ->join( + db_table('product_description') . ' AS product_description', + function (JoinClause $join) use ($languageId) { + $join->on('products.product_id', '=', 'product_description.product_id') + ->where('product_description.language_id', '=', $languageId); + } + ) + ->get(); + + return new JsonResponse([ + 'data' => array_map(function ($product) { + return [ + 'product_id' => (int) $product['product_id'], + 'product_quantity' => (int) $product['product_quantity'], + 'product_name' => $product['product_name'], + 'product_price' => (float) $product['product_price'], + ]; + }, $products), + ]); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php new file mode 100755 index 0000000..befd57c --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php @@ -0,0 +1,60 @@ +database = $database; + } + + public function handle(Request $request): JsonResponse + { + $now = date('Y-m-d H:i:s'); + $storeId = 0; + $storeName = 'Ваш магазин'; + + $sql = <<database->statement($sql); + + + return new JsonResponse([]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php new file mode 100755 index 0000000..e212e95 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php @@ -0,0 +1,86 @@ +queryBuilder = $queryBuilder; + $this->currency = $currency; + $this->tax = $tax; + $this->settings = $settings; + } + + public function handle(Request $request): JsonResponse + { + $languageId = 1; + $page = $request->get('page', 1); + $perPage = $request->get('perPage', 20); + + $products = $this->queryBuilder->newQuery() + ->select([ + 'products.product_id' => 'product_id', + 'products.quantity' => 'product_quantity', + 'product_description.name' => 'product_name', + 'products.price' => 'price', + 'products.image' => 'product_image', + 'products.tax_class_id' => 'tax_class_id', + ]) + ->from(db_table('product'), 'products') + ->join( + db_table('product_description') . ' AS product_description', + function (JoinClause $join) use ($languageId) { + $join->on('products.product_id', '=', 'product_description.product_id') + ->where('product_description.language_id', '=', $languageId); + } + ) + ->forPage($page, $perPage) + ->get(); + + /** @var Closure $resize */ + $resize = Application::getInstance()->get('image_resize'); + + return new JsonResponse([ + 'data' => array_map(function ($product) use ($resize) { + if ($product['product_image']) { + $image = $resize($product['product_image'], 500, 500); + } else { + $image = $resize('placeholder.png', 500, 500); + } + + $price = $this->currency->format( + $this->tax->calculate( + $product['price'], + $product['tax_class_id'], + $this->settings->get('oc_config_tax'), + ), + $this->settings->get('oc_currency'), + ); + + return [ + 'id' => (int)$product['product_id'], + 'product_quantity' => (int)$product['product_quantity'], + 'name' => $product['product_name'], + 'price' => $price, + 'image' => $image, + ]; + }, $products), + ]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php new file mode 100755 index 0000000..6a3a2d0 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php @@ -0,0 +1,14 @@ +container->get(Router::class)->loadRoutesFromFile(__DIR__ . '/../routes.php'); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php new file mode 100755 index 0000000..a8fce00 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php @@ -0,0 +1,42 @@ + 'UTC', + + 'lang' => 'en-gb', + 'language_id' => 1, + 'auth_user_id' => 0, + + 'base_url' => 'http://localhost', + + 'search_cache_seconds' => 60, + + 'chunk_size' => 100, + + 'retry_count' => 3, + + 'activity_log_retention_limit' => 5000, + + 'tables' => [ + 'selected_products' => 'bp_selected_products', + 'simulation_results' => 'bp_simulation_results', + 'activity_logs' => 'bp_activity_logs', + 'cache' => 'bp_cache', + 'task_progress' => 'bp_task_progress', + 'task_steps' => 'bp_task_steps', + 'search_results' => 'bp_search_results', + 'settings' => 'bp_settings', + ], + + 'db' => [ + 'host' => 'localhost', + 'database' => 'not_set', + 'username' => 'not_set', + 'password' => 'not_set', + ], + + 'logs' => [ + 'path' => 'not_set', + ], +]; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php new file mode 100755 index 0000000..f100806 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php @@ -0,0 +1,10 @@ + [ProductsHandler::class, 'handle'], + 'order_create' => [OrderCreateHandler::class, 'handle'], +]; diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index f83e962..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -set -e - -VERSION="1.0.0" -MODULE_NAME="oc_telegram_shop" -CURRENT_DIR="$(dirname "$0")" - -echo "Current dir: ${CURRENT_DIR}" -node -v -npm -v - -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -echo -e "${BLUE}🔨 Starting build process...${NC}" - -echo -e "${BLUE}Build frontend...${NC}" -rm -rf /app/module/oc_telegram_shop/upload/admin/view/javascript -rm -rf /app/module/oc_telegram_shop/upload/admin/view/integration.js -cd /app/frontend -npm install -NODE_ENV=production npm run build -rm -rf /app/module/oc_telegram_shop/upload/admin/view/javascript/oc_telegram_shop/.vite -rm -rf /app/module/oc_telegram_shop/upload/admin/view/javascript/oc_telegram_shop/oc_telegram_shop.js.map -rm -rf /app/module/oc_telegram_shop/upload/admin/view/javascript/oc_telegram_shop/favicon.ico - -echo -e "${BLUE}📦 Packaging into .ocmod.zip...${NC}" -cd /app/module/oc_telegram_shop -zip -rqq "${MODULE_NAME}.ocmod.zip" . -mv "${MODULE_NAME}.ocmod.zip" /build - -FINAL_FILE="/build/${MODULE_NAME}.ocmod.zip" - -if [ -f "$FINAL_FILE" ]; then - FILE_SIZE=$(du -h "$FINAL_FILE" | cut -f1) - echo -e "${GREEN}✅ Build completed successfully!${NC}" - echo -e "${GREEN}🎉 Created: $FINAL_FILE ($FILE_SIZE)${NC}" -else - echo "❌ Error: Final build file not found: $FINAL_FILE" - exit 1 -fi - -echo -e "${BLUE}🎯 Build artifact ready for installation!${NC}" diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh new file mode 100755 index 0000000..11c2ed5 --- /dev/null +++ b/scripts/ci/build.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -e + +MODULE_NAME="oc_telegram_shop" +CURRENT_DIR="$(dirname "$0")" + +echo "Current dir: ${CURRENT_DIR}" +composer --version +node -v +npm -v + +SRC_PATH="$1" +BUILD_PATH="${CURRENT_DIR}/build" + +echo "🔨 Starting build process..." +echo "ℹ️ Source path: ${SRC_PATH}" +echo "ℹ️ Build path: ${BUILD_PATH}" + +if [ -z "$BUILD_PATH" ]; then + echo "❌ Error: BUILD_PATH is required" + exit 1 +fi + +if [ -z "$SRC_PATH" ]; then + echo "❌ Error: SRC_PATH is required" + exit 1 +fi + +echo "Build SPA..." +cd "${SRC_PATH}/spa" +npm install +npm run build +cd - > /dev/null + +echo "Install Composer dependencies." +cd "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop" +composer install \ + --prefer-dist \ + --no-dev \ + --optimize-autoloader \ + --no-interaction \ + --no-cache +cd - > /dev/null + +echo "📜 Creating Phar archive..." +rm -rf \ + "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop/tests" \ + "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop/phpunit.xml" \ + "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop/psalm.xml" \ + "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop/psalm-classes.php" \ + "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop/psalm-stubs.php" + +php -d phar.readonly=0 scripts/ci/create-phar.php "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop" "${MODULE_NAME}.phar" + +echo "📦 Packaging into .ocmod.zip..." +mkdir -p "${SRC_PATH}/module/oc_telegram_shop/upload/system/library/oc_telegram_shop" +mv "${MODULE_NAME}.phar" "${SRC_PATH}/module/oc_telegram_shop/upload/system/library/oc_telegram_shop" + +rm -rf \ + "${SRC_PATH}/module/oc_telegram_shop/upload/oc_telegram_shop" + +cd "${SRC_PATH}/module/oc_telegram_shop/" +zip -rqq "${MODULE_NAME}.ocmod.zip" . +mv "${MODULE_NAME}.ocmod.zip" "${BUILD_PATH}" + +FINAL_FILE="${BUILD_PATH}/${MODULE_NAME}.ocmod.zip" + +if [ -f "$FINAL_FILE" ]; then + FILE_SIZE=$(du -h "$FINAL_FILE" | cut -f1) + echo -e "✅ Build completed successfully!" + echo -e "🎉 Created: $FINAL_FILE ($FILE_SIZE)" +else + echo "❌ Error: Final build file not found: $FINAL_FILE" + exit 1 +fi + +echo -e "🎯 Build artifact ready for installation!" \ No newline at end of file diff --git a/scripts/ci/create-phar.php b/scripts/ci/create-phar.php new file mode 100644 index 0000000..72455f7 --- /dev/null +++ b/scripts/ci/create-phar.php @@ -0,0 +1,37 @@ +startBuffering(); + +// Add files from source directory + +$phar->buildFromDirectory($sourcePath); + +// (Optional) Set stub file — main entry point +// $phar->setStub($phar->createDefaultStub('index.php')); + +$phar->compressFiles(Phar::GZ); + +// Stop buffering and write +$phar->stopBuffering(); + +echo "Phar created: $pharFile\n"; diff --git a/scripts/link.php b/scripts/link.php index 032221b..adf9cbc 100644 --- a/scripts/link.php +++ b/scripts/link.php @@ -1,6 +1,6 @@ =15.0.1" } }, + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -1784,6 +1797,17 @@ "node": ">=0.10.0" } }, + "node_modules/ofetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", + "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.3", + "node-fetch-native": "^1.6.4", + "ufo": "^1.5.4" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1933,6 +1957,12 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", diff --git a/spa/package.json b/spa/package.json index 269ef7d..871ff51 100644 --- a/spa/package.json +++ b/spa/package.json @@ -11,6 +11,7 @@ "dependencies": { "@heroicons/vue": "^2.2.0", "@tailwindcss/vite": "^4.1.11", + "ofetch": "^1.4.1", "vue": "^3.5.17" }, "devDependencies": { diff --git a/spa/src/App.vue b/spa/src/App.vue index 74d9f88..bd6768c 100644 --- a/spa/src/App.vue +++ b/spa/src/App.vue @@ -1,63 +1,7 @@ - - diff --git a/spa/src/components/ProductCard.vue b/spa/src/components/ProductCard.vue index d654a2b..a57e8b5 100644 --- a/spa/src/components/ProductCard.vue +++ b/spa/src/components/ProductCard.vue @@ -1,8 +1,10 @@ diff --git a/spa/src/components/ProductList.vue b/spa/src/components/ProductList.vue new file mode 100644 index 0000000..c02f5e5 --- /dev/null +++ b/spa/src/components/ProductList.vue @@ -0,0 +1,28 @@ + + + \ No newline at end of file diff --git a/spa/vite.config.js b/spa/vite.config.js index 0d744eb..e6bb310 100644 --- a/spa/vite.config.js +++ b/spa/vite.config.js @@ -4,4 +4,12 @@ import vue from "@vitejs/plugin-vue"; export default defineConfig({ plugins: [tailwindcss(), vue()], -}); \ No newline at end of file + + base: '/telegram_shop_spa/', + build: { + outDir: '../module/oc_telegram_shop/upload/image/catalog/tgshopspa', + emptyOutDir: true, + sourcemap: true, + manifest: true, + }, +});