├── .env-example
├── .env.panther
├── .env.test
├── .eslintrc.js
├── .gitignore
├── .php-cs-fixer.dist.php
├── LICENSE.md
├── Makefile
├── README.md
├── assets
├── css
│ └── section
│ │ ├── admin
│ │ ├── libs.scss
│ │ ├── main.scss
│ │ ├── sb-admin-2.css
│ │ └── styles.css
│ │ └── front
│ │ ├── _alert.css
│ │ ├── _banner.css
│ │ ├── _base.css
│ │ ├── _button.css
│ │ ├── _cart.css
│ │ ├── _cart_dropdown.css
│ │ ├── _footer.css
│ │ ├── _form.css
│ │ ├── _header.css
│ │ ├── _header_menu.css
│ │ ├── _pagination.css
│ │ ├── _product.css
│ │ ├── _product_list.css
│ │ ├── _spinner.css
│ │ ├── _typography.css
│ │ ├── email.scss
│ │ ├── libs.scss
│ │ └── main.scss
├── images
│ ├── banner
│ │ └── collection
│ │ │ ├── banner_1.jpg
│ │ │ ├── banner_2.jpg
│ │ │ ├── banner_3.jpg
│ │ │ ├── banner_4.jpg
│ │ │ └── banner_5.jpg
│ ├── favicon
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── mstile-150x150.png
│ │ ├── safari-pinned-tab.svg
│ │ └── site.webmanifest
│ └── icons
│ │ ├── favicon.ico
│ │ ├── logo_366x79.png
│ │ ├── logo_366x79.svg
│ │ └── short-logo_115x115.svg
├── js
│ ├── section
│ │ ├── admin
│ │ │ ├── admin-order
│ │ │ │ ├── App.vue
│ │ │ │ ├── app.js
│ │ │ │ ├── components
│ │ │ │ │ ├── OrderProductAdd.vue
│ │ │ │ │ ├── OrderProductItem.vue
│ │ │ │ │ └── TotalPriceBlock.vue
│ │ │ │ └── store
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── modules
│ │ │ │ │ └── products.js
│ │ │ └── theme
│ │ │ │ ├── filters-feature.js
│ │ │ │ └── sb-admin-2.js
│ │ └── front
│ │ │ ├── front-cart-show
│ │ │ ├── App.vue
│ │ │ ├── app.js
│ │ │ ├── components
│ │ │ │ ├── Alert.vue
│ │ │ │ ├── CartProductItem.vue
│ │ │ │ ├── CartProductList.vue
│ │ │ │ └── CartTotalPrice.vue
│ │ │ └── store
│ │ │ │ ├── index.js
│ │ │ │ └── modules
│ │ │ │ └── cart.js
│ │ │ ├── menu-cart
│ │ │ ├── App.vue
│ │ │ ├── app.js
│ │ │ ├── components
│ │ │ │ ├── CartActions.vue
│ │ │ │ ├── CartProductItem.vue
│ │ │ │ ├── CartProductList.vue
│ │ │ │ └── CartTotalPrice.vue
│ │ │ └── store
│ │ │ │ ├── index.js
│ │ │ │ └── modules
│ │ │ │ └── cart.js
│ │ │ └── utils
│ │ │ └── menu.js
│ └── utils
│ │ ├── changed-locale.js
│ │ ├── cookie-manager.js
│ │ ├── settings.js
│ │ ├── title-formatter.js
│ │ └── url-generator.js
├── section-admin.js
└── section-front.js
├── bin
├── console
├── phpunit
├── run-tests.sh
├── selenium-server-4.22.0.jar
└── selenium-server-standalone-3.141.59.jar
├── composer.json
├── composer.lock
├── config
├── bundles.php
├── packages
│ ├── api_platform.yaml
│ ├── assets.yaml
│ ├── cache.yaml
│ ├── dev
│ │ ├── debug.yaml
│ │ ├── hautelook_alice.yaml
│ │ ├── monolog.yaml
│ │ ├── nelmio_alice.yaml
│ │ ├── php_translation.yaml
│ │ └── web_profiler.yaml
│ ├── doctrine.yaml
│ ├── doctrine_migrations.yaml
│ ├── framework.yaml
│ ├── knp_paginator.yaml
│ ├── knpu_oauth2_client.yaml
│ ├── mailer.yaml
│ ├── messenger.yaml
│ ├── nelmio_cors.yaml
│ ├── notifier.yaml
│ ├── php_translation.yaml
│ ├── prod
│ │ ├── deprecations.yaml
│ │ ├── doctrine.yaml
│ │ ├── monolog.yaml
│ │ └── webpack_encore.yaml
│ ├── reset_password.yaml
│ ├── routing.yaml
│ ├── security.yaml
│ ├── sensio_framework_extra.yaml
│ ├── stof_doctrine_extensions.yaml
│ ├── test
│ │ ├── dama_doctrine_test_bundle.yaml
│ │ ├── doctrine.yaml
│ │ ├── hautelook_alice.yaml
│ │ ├── monolog.yaml
│ │ ├── nelmio_alice.yaml
│ │ ├── validator.yaml
│ │ ├── web_profiler.yaml
│ │ └── webpack_encore.yaml
│ ├── translation.yaml
│ ├── twig.yaml
│ ├── validator.yaml
│ └── webpack_encore.yaml
├── preload.php
├── routes.yaml
├── routes
│ ├── annotations.yaml
│ ├── api_platform.yaml
│ ├── dev
│ │ ├── php_translation.yaml
│ │ └── web_profiler.yaml
│ ├── framework.yaml
│ └── php_translation.yaml
└── services.yaml
├── deploy-example.php
├── deployer7
├── docs
├── conf
│ ├── nginx
│ │ └── s-shop.conf
│ └── supervisor
│ │ └── messenger-worker.conf
├── img
│ ├── chromedriver-not-found.png
│ ├── deployer7-deploy.png
│ ├── install-openjdk-21-jdk.png
│ └── selenium-server-not-work.png
└── langs
│ ├── README_de.md
│ ├── README_en.md
│ ├── README_es.md
│ ├── README_fr.md
│ └── README_zh.md
├── drivers
├── chromedriver
└── geckodriver
├── fixtures
├── category.yaml
├── order.yaml
├── order_product.yaml
├── product.yaml
└── user.yaml
├── migrations
├── Version20210923235603.php
├── Version20210924222154.php
├── Version20210924223952.php
├── Version20210927185157.php
├── Version20210927185503.php
├── Version20210927190540.php
├── Version20211001104011.php
├── Version20211002150834.php
├── Version20211002222439.php
├── Version20211002223343.php
├── Version20211003091352.php
├── Version20211003203107.php
├── Version20211004063505.php
├── Version20211004205602.php
├── Version20211004210150.php
├── Version20211006193743.php
├── Version20211110214414.php
├── Version20211118200708.php
├── Version20211119072129.php
├── Version20211125173610.php
├── Version20220325174952.php
├── Version20220326090356.php
├── Version20220326141147.php
├── Version20220326151023.php
└── Version20240714084855.php
├── package-lock.json
├── package.json
├── phpunit.xml.dist
├── phpunit.xml.dist.bak
├── public
└── index.php
├── src
├── Command
│ ├── AddUserCommand.php
│ └── UpdateSlugProduct.php
├── Controller
│ ├── Admin
│ │ ├── BaseAdminController.php
│ │ ├── CategoryController.php
│ │ ├── DashboardController.php
│ │ ├── OrderController.php
│ │ ├── ProductController.php
│ │ ├── ProductImageController.php
│ │ ├── SecurityController.php
│ │ └── UserController.php
│ └── Front
│ │ ├── AuthGithubEnController.php
│ │ ├── AuthGithubRusController.php
│ │ ├── AuthGoogleController.php
│ │ ├── AuthVkontakteController.php
│ │ ├── AuthYandexController.php
│ │ ├── CartApiController.php
│ │ ├── CartController.php
│ │ ├── CategoryController.php
│ │ ├── DefaultController.php
│ │ ├── EmbedController.php
│ │ ├── ProductController.php
│ │ ├── ProfileController.php
│ │ ├── RegistrationController.php
│ │ ├── ResetPasswordController.php
│ │ ├── RobotsTxtController.php
│ │ ├── SecurityController.php
│ │ └── SitemapController.php
├── Entity
│ ├── Cart.php
│ ├── CartProduct.php
│ ├── Category.php
│ ├── Order.php
│ ├── OrderProduct.php
│ ├── Product.php
│ ├── ProductImage.php
│ ├── ResetPasswordRequest.php
│ ├── StaticStorage
│ │ ├── OrderStaticStorage.php
│ │ └── UserStaticStorage.php
│ └── User.php
├── Event
│ ├── OrderCreatedFromCartEvent.php
│ └── UserLoggedInViaSocialNetworkEvent.php
├── EventSubscriber
│ ├── LocaleSubscriber.php
│ ├── OrderCreatedFromCartSendNotificationSubscriber.php
│ └── UserLoggedInViaSocialNetworkSendNotificationSubscriber.php
├── Exception
│ └── Security
│ │ └── EmptyUserPlainPasswordException.php
├── Form
│ ├── Admin
│ │ ├── EditCategoryFormType.php
│ │ ├── EditOrderFormType.php
│ │ ├── EditProductFormType.php
│ │ ├── EditUserFormType.php
│ │ └── FilterType
│ │ │ ├── OrderFilterFormType.php
│ │ │ └── ProductFilterFormType.php
│ ├── DTO
│ │ ├── EditCategoryModel.php
│ │ ├── EditOrderModel.php
│ │ ├── EditProductModel.php
│ │ └── EditUserModel.php
│ ├── Front
│ │ ├── ChangePasswordFormType.php
│ │ ├── ProfileEditFormType.php
│ │ ├── RegistrationFormType.php
│ │ └── ResetPasswordRequestFormType.php
│ ├── Handler
│ │ ├── CategoryFormHandler.php
│ │ ├── OrderFormHandler.php
│ │ ├── ProductFormHandler.php
│ │ └── UserFormHandler.php
│ └── Validator
│ │ ├── GreaterThanOrEqualPrice.php
│ │ └── GreaterThanOrEqualPriceValidator.php
├── Kernel.php
├── Messenger
│ ├── Message
│ │ ├── Command
│ │ │ └── ResetUserPasswordCommand.php
│ │ └── Event
│ │ │ └── EventUserRegisteredEvent.php
│ └── MessageHandler
│ │ ├── Command
│ │ └── ResetUserPasswordHandler.php
│ │ └── Event
│ │ └── UserRegisteredHandler.php
├── Repository
│ ├── CartProductRepository.php
│ ├── CartRepository.php
│ ├── CategoryRepository.php
│ ├── OrderProductRepository.php
│ ├── OrderRepository.php
│ ├── ProductImageRepository.php
│ ├── ProductRepository.php
│ ├── ResetPasswordRequestRepository.php
│ └── UserRepository.php
├── Security
│ ├── Authenticator
│ │ ├── Admin
│ │ │ └── LoginFormAuthenticator.php
│ │ └── Front
│ │ │ ├── GithubEnAuthenticator.php
│ │ │ ├── GithubRusAuthenticator.php
│ │ │ ├── GoogleAuthenticator.php
│ │ │ ├── LoginFormAuthenticator.php
│ │ │ ├── VkontakteAuthenticator.php
│ │ │ └── YandexAuthenticator.php
│ ├── EntryPoint
│ │ ├── AuthenticationAdminEntryPoint.php
│ │ └── AuthenticationFrontEntryPoint.php
│ ├── Handler
│ │ ├── AccessAdminDeniedHandler.php
│ │ └── AccessFrontDeniedHandler.php
│ ├── Verifier
│ │ └── EmailVerifier.php
│ └── Voters
│ │ ├── AdminOrderEditVoter.php
│ │ ├── CartProductVoter.php
│ │ └── CartVoter.php
└── Utils
│ ├── ApiPlatform
│ ├── Event
│ │ └── Subscriber
│ │ │ ├── MakeOrderFromCartSubscriber.php
│ │ │ └── SetCartTokenSubscriber.php
│ └── Extension
│ │ ├── FilterCartQueryExtension.php
│ │ └── FilterProductQueryExtension.php
│ ├── Authenticator
│ └── CheckingUserSocialNetworkBeforeAuthorization.php
│ ├── Event
│ └── Subscriber
│ │ └── MigrationEventSubscriber.php
│ ├── Factory
│ └── UserFactory.php
│ ├── File
│ ├── FileSaver.php
│ └── ImageResizer.php
│ ├── FileSystem
│ └── FilesystemWorker.php
│ ├── Generator
│ ├── PasswordGenerator.php
│ └── TokenGenerator.php
│ ├── Mailer
│ ├── DTO
│ │ └── MailerOptionModel.php
│ ├── MailerSender.php
│ └── Sender
│ │ ├── BaseSender.php
│ │ ├── OrderCreatedFromCartEmailSender.php
│ │ ├── ResetUserPasswordEmailSender.php
│ │ ├── UserLoggedInViaSocialNetworkEmailSender.php
│ │ └── UserRegisteredEmailSender.php
│ ├── Manager
│ ├── AbstractBaseManager.php
│ ├── CartManager.php
│ ├── CategoryManager.php
│ ├── OrderManager.php
│ ├── ProductImageManager.php
│ ├── ProductManager.php
│ └── UserManager.php
│ └── Oauth2
│ └── Vk
│ ├── Vk.php
│ └── VkUser.php
├── symfony.lock
├── templates
├── admin
│ ├── _embed
│ │ ├── _card
│ │ │ ├── _breadcrumbs.html.twig
│ │ │ ├── _form_buttons.html.twig
│ │ │ └── _header_action.html.twig
│ │ ├── _filters
│ │ │ └── _header.html.twig
│ │ ├── _main
│ │ │ ├── _sidebar.html.twig
│ │ │ └── _topbar.html.twig
│ │ └── _utils
│ │ │ ├── _error_authenticator.html.twig
│ │ │ ├── _flash_message.html.twig
│ │ │ └── _modal.html.twig
│ ├── base.html.twig
│ ├── category
│ │ ├── edit.html.twig
│ │ └── list.html.twig
│ ├── layout
│ │ ├── layout_empty_page.html.twig
│ │ └── layout_main.html.twig
│ ├── order
│ │ ├── _embed
│ │ │ ├── _table_actions_btn.html.twig
│ │ │ └── _table_filtration.html.twig
│ │ ├── edit.html.twig
│ │ └── list.html.twig
│ ├── pages
│ │ └── dashboard.html.twig
│ ├── product
│ │ ├── _embed
│ │ │ ├── _table_actions_btn.html.twig
│ │ │ └── _table_filtration.html.twig
│ │ ├── edit.html.twig
│ │ └── list.html.twig
│ ├── security
│ │ └── login.html.twig
│ └── user
│ │ ├── edit.html.twig
│ │ └── list.html.twig
├── bundles
│ └── TwigBundle
│ │ └── Exception
│ │ └── error.html.twig
└── front
│ ├── _embed
│ ├── _footer.html.twig
│ ├── _header.html.twig
│ ├── _menu
│ │ ├── _desktop_menu.html.twig
│ │ ├── _login_via_social_network.html.twig
│ │ ├── _menu_cart.html.twig
│ │ ├── _menu_nav_item.twig
│ │ └── _mobile_menu.html.twig
│ ├── _similar_products.html.twig
│ └── _utils
│ │ ├── _error_authenticator.html.twig
│ │ ├── _flash_message.html.twig
│ │ └── _social_network_link_unlink_btn.html.twig
│ ├── base.html.twig
│ ├── cart
│ └── show.html.twig
│ ├── category
│ └── show.html.twig
│ ├── default
│ └── index.html.twig
│ ├── email
│ ├── base.html.twig
│ ├── client
│ │ ├── created_order_from_cart.html.twig
│ │ └── user_logged_in_via_social_network.html.twig
│ ├── manager
│ │ └── created_order_from_cart.html.twig
│ └── security
│ │ ├── confirmation_email.html.twig
│ │ └── reset_password.html.twig
│ ├── product
│ └── show.html.twig
│ ├── profile
│ ├── edit.html.twig
│ └── index.html.twig
│ ├── reset_password
│ ├── check_email.html.twig
│ ├── request.html.twig
│ └── reset.html.twig
│ ├── robots.txt.twig
│ ├── security
│ ├── login.html.twig
│ └── registration.html.twig
│ └── sitemap.xml.twig
├── tests
├── Functional
│ ├── ApiPlatform
│ │ ├── ProductResourceTest.php
│ │ └── ResourceTestUtils.php
│ └── Controller
│ │ └── Front
│ │ ├── AuthLoginControllerTest.php
│ │ ├── DefaultControllerTest.php
│ │ └── RegistrationControllerTest.php
├── Integration
│ └── Security
│ │ └── Verifier
│ │ └── EmailVerifierTest.php
├── SymfonyPanther
│ └── BasePantherTestCase.php
├── TestUtils
│ └── Fixtures
│ │ └── UserFixtures.php
├── Unit
│ └── Utils
│ │ └── Generator
│ │ └── PasswordGeneratorTest.php
└── bootstrap.php
├── translations
├── LexikFormFilterBundle.en.xlf
├── LexikFormFilterBundle.en.yml
├── LexikFormFilterBundle.ru.xlf
├── LexikFormFilterBundle.ru.yml
├── messages.en.xlf
├── messages.en.yml
├── messages.ru.xlf
├── messages.ru.yml
├── security.ru.xlf
├── security.ru.yml
├── validators.en.xlf
├── validators.en.yml
├── validators.ru.xlf
└── validators.ru.yml
└── webpack.config.js
/.env.panther:
--------------------------------------------------------------------------------
1 | # define your env variables for the test env here
2 | APP_ENV=test
3 | KERNEL_CLASS='App\Kernel'
4 | APP_SECRET='$ecretf0rt3st'
5 | SYMFONY_DEPRECATIONS_HELPER=999999
6 | PANTHER_APP_ENV=panther
7 | PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
8 |
9 | MAILER_DSN=null://null
10 | MESSENGER_TRANSPORT_DSN=doctrine://default
11 | ADMIN_EMAIL=test@test.test
12 | DATABASE_URL="sqlite:///%kernel.project_dir%/var/db_for_test.db"
13 |
14 | SITE_BASE_SCHEME=https
15 | SITE_BASE_HOST=127.0.0.1:8000
16 |
17 | CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
18 |
19 | APP_TIMEZONE=Europe/Moscow
--------------------------------------------------------------------------------
/.env.test:
--------------------------------------------------------------------------------
1 | # define your env variables for the test env here
2 | APP_ENV=test
3 | KERNEL_CLASS='App\Kernel'
4 | APP_SECRET='$ecretf0rt3st'
5 | SYMFONY_DEPRECATIONS_HELPER=999999
6 | PANTHER_APP_ENV=panther
7 | PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
8 |
9 | MAILER_DSN=null://null
10 | MESSENGER_TRANSPORT_DSN=doctrine://default
11 | ADMIN_EMAIL=test@test.test
12 | DATABASE_URL="sqlite:///%kernel.project_dir%/var/db_for_test.db"
13 |
14 | SITE_BASE_SCHEME=https
15 | SITE_BASE_HOST=127.0.0.1:8000
16 |
17 | CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
18 |
19 | APP_TIMEZONE=Europe/Moscow
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'eslint:recommended',
4 | 'plugin:vue/recommended',
5 | 'plugin:prettier/recommended',
6 | ],
7 | env: {
8 | node: true,
9 | "jquery": true
10 | },
11 | };
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | in(__DIR__)
5 | ->exclude('var')
6 | ->exclude('vendor');
7 |
8 | return (new PhpCsFixer\Config())
9 | ->setRules([
10 | '@Symfony' => true,
11 | 'no_superfluous_phpdoc_tags' => true,
12 | 'global_namespace_import' => false,
13 | 'phpdoc_separation' => false,
14 | ])
15 | ->setFinder($finder);
16 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Александр Юрченко
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | NODE_MODULES = ./node_modules
2 | VENDOR = ./vendor
3 |
4 | ##
5 | ## UTILS
6 | ## ----------
7 | del-log:
8 | rm -rf ./var/log
9 |
10 | del-cache:
11 | rm -rf ./var/cache
12 |
13 | watch:
14 | npm run watch
15 |
16 | deploy:
17 | php deployer7 deploy
18 |
19 | ##
20 | ## REFACTORING
21 | ## -----------
22 |
23 | check:
24 | make refactoring --keep-going
25 |
26 | refactoring: eslint php-cs-fixer
27 |
28 | eslint:
29 | ${NODE_MODULES}/.bin/eslint assets/js/ --ext .js,.vue --fix
30 |
31 | php-cs-fixer:
32 | ${VENDOR}/bin/php-cs-fixer fix src/ --verbose
33 |
34 | phpstan:
35 | ${VENDOR}/bin/phpstan analyse src --level 4
36 |
37 | ##
38 | ## TESTING
39 | ## -----------
40 |
41 | run-test:
42 | sh ./bin/run-tests.sh
--------------------------------------------------------------------------------
/assets/css/section/admin/libs.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Nunito:200,300,400,600,700,800,900');
2 |
3 | @import "~@fortawesome/fontawesome-free/css/all";
4 |
--------------------------------------------------------------------------------
/assets/css/section/admin/main.scss:
--------------------------------------------------------------------------------
1 | .select-language {
2 | width: auto;
3 | &:focus {
4 | outline: none;
5 | text-decoration: none;
6 | box-shadow: none;
7 | border: 1px solid #ced4da;
8 | }
9 | }
--------------------------------------------------------------------------------
/assets/css/section/front/_alert.css:
--------------------------------------------------------------------------------
1 | .alert {
2 | font-weight: 300;
3 | letter-spacing: -.01em;
4 | border-radius: 0;
5 | font-size: 1.4rem;
6 | line-height: 1.43;
7 | margin: 0;
8 | padding: 1rem 1.5rem;
9 | border: none;
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/assets/css/section/front/_button.css:
--------------------------------------------------------------------------------
1 | .btn {
2 | cursor: pointer;
3 | }
4 |
5 | .btn-link, .btn-success, .btn-cancel {
6 | font-size: 1.2rem;
7 | text-transform: uppercase;
8 | padding: 10px !important;
9 | border: none;
10 | color: white;
11 | }
12 |
13 | .btn-link:hover, .btn-success:hover {
14 | color: white !important;
15 | text-decoration: none;
16 | }
17 |
18 | .btn-link {
19 | border-bottom: 4px solid #ff59bd !important;
20 | background-color: transparent;
21 | }
22 |
23 | .btn-link:hover {
24 | background: linear-gradient(to right, #da2893 2%,#93002F 82%);
25 | }
26 |
27 | .btn-success {
28 | border-bottom: 4px solid #799647 !important;
29 | background-color: #a6c76c;
30 | }
31 |
32 | .btn-success:hover {
33 | background: linear-gradient(to right, #a6c76c 2%, #799647 82%);
34 | }
35 |
36 | .btn-cancel {
37 | border-bottom: 4px solid #909090 !important;
38 | background-color: #f7f7f7;
39 | }
40 |
41 | .btn-cancel:hover {
42 | color: #333 !important;
43 | text-decoration: none;
44 | background: linear-gradient(to right, #f7f7f7 2%, #dedede 82%);
45 | }
46 |
47 | .btn-dark {
48 | color: #333;
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/assets/css/section/front/_cart.css:
--------------------------------------------------------------------------------
1 | .table-cart .product-title {
2 | font-size: 1.4rem;
3 | text-decoration: none;
4 | }
5 |
6 | .table-cart .product-title a {
7 | color: #212529;
8 | }
9 |
10 | .table-cart .total-col {
11 | width: 80px;
12 | color: #dc143c;
13 | font-size: 1.6rem;
14 | }
15 |
16 | .table-cart .price-col {
17 | width: 120px;
18 | }
19 |
20 | .table-cart .quantity-col {
21 | width: 135px;
22 | }
23 |
24 | .table-cart .quantity-col .form-control {
25 | height: 40px;
26 | text-align: center;
27 | }
28 |
29 | .table-cart .product-col figure {
30 | #margin: 15px 40px;
31 | }
32 |
33 | .table-cart figure img {
34 | margin-left: auto;
35 | margin-right: auto;
36 | }
37 |
38 | .table-cart td, .table-cart th {
39 | vertical-align: middle;
40 | }
41 |
--------------------------------------------------------------------------------
/assets/css/section/front/_footer.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | width: 100%;
3 | padding: 4rem 0 1rem;
4 | border-top: .1rem solid #ebebeb;
5 | font-size: 1.4rem;
6 | color: #777;
7 | text-align: center;
8 | background: #333;
9 | background: -webkit-linear-gradient(left, #565656 2%,#333 82%);
10 | background: -moz-linear-gradient(left, #565656 2%, #333 82%);
11 | background: linear-gradient(to right, #565656 2%,#333 82%);
12 | }
13 |
14 | .footer a {
15 | color: inherit;
16 | }
17 |
18 | .footer .widget-title {
19 | margin-top: 0;
20 | margin-bottom: 1rem;
21 | font-weight: 400;
22 | letter-spacing: .1em;
23 | color: #fff;
24 | text-transform: uppercase;
25 | font-size: 1.2rem;
26 | }
27 |
28 | .footer .widget-copyright {
29 | font-size: 1rem;
30 | }
31 |
32 | .widget-list {
33 | margin-bottom: 0;
34 | padding: 0;
35 | }
36 |
37 | .widget-list li:not(:last-child) {
38 | margin-bottom: .4rem;
39 | }
40 |
41 | .widget-list li {
42 | display: inline;
43 | margin-right: 5px;
44 | font-size: 1rem;
45 | }
46 |
47 | .widget-list li:after {
48 | content: " \00b7";
49 | font-weight: 700;
50 | vertical-align: middle;
51 | padding: 0 10px;
52 | }
53 |
54 | .widget-list li:last-child:after {
55 | content: none;
56 | }
57 |
--------------------------------------------------------------------------------
/assets/css/section/front/_header.css:
--------------------------------------------------------------------------------
1 | .header {
2 | width: 100%;
3 | border-bottom: none;
4 | height: auto;
5 | top: 0;
6 | z-index: 99;
7 | background: #ffffff;
8 | -webkit-box-shadow: 0 10px 10px -10px rgba(0, 0, 0, 0.35);
9 | box-shadow: 0 10px 10px -10px rgba(0, 0, 0, 0.35);
10 | }
11 |
12 | .header .container-fluid {
13 | display: flex;
14 | align-items: center;
15 | justify-content: center;
16 | }
17 |
18 | .logotype {
19 | display: block;
20 | min-height: 30px;
21 | }
22 |
23 | .logotype img {
24 | display: block;
25 | width: 100%;
26 | min-height: 30px;
27 | }
28 |
29 | .mobile-menu-toggler {
30 | border: none;
31 | background: transparent;
32 | color: #666666;
33 | padding: .2rem .25rem;
34 | font-size: 2.8rem;
35 | line-height: 1;
36 | display: flex;
37 | align-items: center;
38 | justify-content: center;
39 | cursor: pointer;
40 | margin-left: 1rem;
41 | margin-right: 1rem;
42 | }
43 |
44 | @media screen and (min-width: 1100px) {
45 | .header {
46 | height: 110px;
47 | }
48 | .mobile-menu-toggler {
49 | display: none;
50 | }
51 | .logotype img {
52 | display: block;
53 | max-width: 100%;
54 | height: auto;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/assets/css/section/front/_pagination.css:
--------------------------------------------------------------------------------
1 | .page-item {
2 | margin: 0 .2rem;
3 | }
4 |
5 | .page-item.active .btn-nav {
6 | color: #f13c76;
7 | border-color: #e8e8e8;
8 | }
9 |
10 | .btn-nav {
11 | display: inline-flex;
12 | align-items: center;
13 | justify-content: center;
14 | min-width: 3rem;
15 | height: 3rem;
16 | font-weight: 400;
17 | font-size: 1rem;
18 | color: #706f6c;
19 | border-radius: .3rem;
20 | background-color: transparent;
21 | border: .1rem solid transparent;
22 | }
23 |
24 | .btn-nav.btn-nav-prev i {
25 | margin-right: 1rem;
26 | }
27 |
28 | .btn-nav.btn-nav-next i {
29 | margin-left: 1rem;
30 | }
31 |
32 | .btn-nav.btn-nav-prev, .btn-nav.btn-nav-next {
33 | padding: 0 1rem;
34 | min-width: 60px;
35 | }
36 |
37 | .btn-nav:hover, .btn-nav:focus {
38 | box-shadow: none;
39 | color: #f13c76;
40 | border-color: #e3e3e3;
41 | text-decoration: none;
42 | outline: none !important;
43 | }
44 |
45 | .page-item-total {
46 | color: #cccccc;
47 | font-size: 1.4rem;
48 | font-weight: 400;
49 | margin: 0 .2rem;
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/assets/css/section/front/_spinner.css:
--------------------------------------------------------------------------------
1 | .spinner-dual-ring {
2 | position: absolute;
3 | display: inline-block;
4 | width: 80px;
5 | height: 80px;
6 | padding-left: 50%;
7 | padding-top: 50px;
8 | transform: translateY(-50%);
9 | }
10 | .spinner-dual-ring:after {
11 | content: " ";
12 | display: block;
13 | width: 64px;
14 | height: 64px;
15 | margin: 8px;
16 | border-radius: 50%;
17 | border: 6px solid #fff;
18 | border-color: #ff59bd transparent #ff59bd transparent;
19 | animation: spinner-dual-ring 1.2s linear infinite;
20 | }
21 | @keyframes spinner-dual-ring {
22 | 0% {
23 | transform: rotate(0deg);
24 | }
25 | 100% {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/assets/css/section/front/email.scss:
--------------------------------------------------------------------------------
1 | @import "~foundation-emails";
2 | @import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700');
3 |
4 | body {
5 | margin: 0;
6 | padding: 0;
7 | font-family: 'Roboto', sans-serif;
8 | background-color: #f8f9fa;
9 | }
10 | .header {
11 | text-align: center;
12 | background-color: #f8f9fa;
13 | }
14 | .footer {
15 | background-color: #f8f9fa;
16 | color: #ababab;
17 | }
18 | .footer p {
19 | padding: 20px;
20 | }
21 | .data-table {
22 | width: 100%;
23 | }
24 | .data-table thead {
25 | border-bottom: 1px solid #ececec;
26 | }
27 | .data-table td {
28 | padding: 20px;
29 | }
30 |
--------------------------------------------------------------------------------
/assets/css/section/front/libs.scss:
--------------------------------------------------------------------------------
1 | @import "~bootstrap/scss/bootstrap";
2 | @import "~@fortawesome/fontawesome-free/css/all";
3 | @import "main";
4 |
--------------------------------------------------------------------------------
/assets/css/section/front/main.scss:
--------------------------------------------------------------------------------
1 | #select-language {
2 | width: auto;
3 |
4 | &:focus {
5 | outline: none;
6 | text-decoration: none;
7 | box-shadow: none;
8 | border: 1px solid #ced4da;
9 | }
10 | }
11 |
12 | .social-group {
13 | display: inline-block;
14 | font-weight: 400;
15 | color: #212529;
16 | text-align: center;
17 | vertical-align: middle;
18 | user-select: none;
19 | background-color: transparent;
20 | border: 1px solid transparent;
21 | padding: 0.375rem 0.75rem;
22 | font-size: 1rem;
23 | line-height: 1.5;
24 | border-radius: 0.25rem;
25 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
26 |
27 | &--unlink {
28 | color: #dc3545;
29 | background-color: #f4f4f4;
30 | border-color: #dc3545;
31 | }
32 |
33 | &--link {
34 | color: #fff;
35 | border-color: #28a745;
36 | background-color: #28a745;
37 |
38 | &:hover {
39 | color: #fff;
40 | cursor: pointer;
41 | text-decoration: none;
42 | }
43 |
44 | &:focus {
45 | color: #fff;
46 | text-decoration: none;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/assets/images/banner/collection/banner_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/banner/collection/banner_1.jpg
--------------------------------------------------------------------------------
/assets/images/banner/collection/banner_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/banner/collection/banner_2.jpg
--------------------------------------------------------------------------------
/assets/images/banner/collection/banner_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/banner/collection/banner_3.jpg
--------------------------------------------------------------------------------
/assets/images/banner/collection/banner_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/banner/collection/banner_4.jpg
--------------------------------------------------------------------------------
/assets/images/banner/collection/banner_5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/banner/collection/banner_5.jpg
--------------------------------------------------------------------------------
/assets/images/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/assets/images/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/assets/images/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/assets/images/favicon/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #da532c
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/assets/images/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/assets/images/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/assets/images/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/favicon.ico
--------------------------------------------------------------------------------
/assets/images/favicon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/favicon/mstile-150x150.png
--------------------------------------------------------------------------------
/assets/images/favicon/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/assets/images/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/icons/favicon.ico
--------------------------------------------------------------------------------
/assets/images/icons/logo_366x79.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yaleksandr89/symfony-shop/c18698d9fc881cac80270695ddf88a6888c4a9a4/assets/images/icons/logo_366x79.png
--------------------------------------------------------------------------------
/assets/images/icons/short-logo_115x115.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/assets/js/section/admin/admin-order/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
36 |
--------------------------------------------------------------------------------
/assets/js/section/admin/admin-order/app.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App";
3 | import store from "./store";
4 |
5 | if (document.getElementById("app")) {
6 | new Vue({
7 | el: "#app",
8 | store,
9 | render: (h) => h(App),
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/assets/js/section/admin/admin-order/components/TotalPriceBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Total price: ${{ totalPrice }}
5 |
6 |
7 |
8 |
9 |
30 |
--------------------------------------------------------------------------------
/assets/js/section/admin/admin-order/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 | import products from "./modules/products";
4 |
5 | Vue.use(Vuex);
6 |
7 | const debug = process.env.NODE_ENV !== "production";
8 |
9 | export default new Vuex.Store({
10 | modules: {
11 | products,
12 | },
13 | strict: debug,
14 | });
15 |
--------------------------------------------------------------------------------
/assets/js/section/admin/theme/filters-feature.js:
--------------------------------------------------------------------------------
1 | import { getCookie, setCookie } from "../../../utils/cookie-manager";
2 |
3 | window.toggleFiltersVisibility = function toggleFiltersVisibility(section) {
4 | const filtersKey = "filtersVisible_" + section;
5 | const filtersSaveValue = getCookie(filtersKey);
6 |
7 | const visibleValue = filtersSaveValue === "false";
8 |
9 | setCookie(filtersKey, visibleValue, {
10 | secure: true,
11 | "max-age": 3600,
12 | });
13 | };
14 |
15 | window.changeFiltersBlockView = function changeFiltersBlockView(
16 | filterSection,
17 | element
18 | ) {
19 | const filtersKey = "filtersVisible_" + filterSection;
20 | const filtersSaveValue = getCookie(filtersKey);
21 |
22 | element.style.display = filtersSaveValue === "false" ? "block" : "none";
23 | };
24 |
--------------------------------------------------------------------------------
/assets/js/section/front/front-cart-show/app.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App";
3 | import store from "./store";
4 |
5 | if (document.getElementById("app")) {
6 | new Vue({
7 | el: "#app",
8 | store,
9 | render: (h) => h(App),
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/assets/js/section/front/front-cart-show/components/Alert.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ alert.message }}
4 |
5 |
6 |
7 |
20 |
--------------------------------------------------------------------------------
/assets/js/section/front/front-cart-show/components/CartProductList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ staticStore.localization.product }}
6 | {{ staticStore.localization.price }}
7 | {{ staticStore.localization.quantity }}
8 | {{ staticStore.localization.total }}
9 | {{ staticStore.localization.actions }}
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
34 |
--------------------------------------------------------------------------------
/assets/js/section/front/front-cart-show/components/CartTotalPrice.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ staticStore.localization.total_price }}:
4 | ${{ totalPrice }}
5 |
6 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/assets/js/section/front/front-cart-show/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 | import cart from "./modules/cart";
4 |
5 | Vue.use(Vuex);
6 |
7 | const debug = process.env.NODE_ENV !== "production";
8 |
9 | export default new Vuex.Store({
10 | modules: {
11 | cart,
12 | },
13 | strict: debug,
14 | });
15 |
--------------------------------------------------------------------------------
/assets/js/section/front/menu-cart/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
45 |
--------------------------------------------------------------------------------
/assets/js/section/front/menu-cart/app.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App";
3 | import store from "./store";
4 |
5 | if (document.getElementById("appFrontMenuCart")) {
6 | const vueMenuCartInstance = new Vue({
7 | el: "#appFrontMenuCart",
8 | store,
9 | render: (h) => h(App),
10 | });
11 |
12 | window.vueMenuCartInstance = {};
13 | window.vueMenuCartInstance.addCartProduct = (productData) =>
14 | vueMenuCartInstance.$store.dispatch("cart/addCartProduct", productData);
15 | window.vueMenuCartInstance.setCart = () =>
16 | vueMenuCartInstance.$store.commit("cart/setCart", {});
17 | }
18 |
--------------------------------------------------------------------------------
/assets/js/section/front/menu-cart/components/CartActions.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
25 |
--------------------------------------------------------------------------------
/assets/js/section/front/menu-cart/components/CartProductList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
23 |
--------------------------------------------------------------------------------
/assets/js/section/front/menu-cart/components/CartTotalPrice.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ staticStore.localization.total_price }}
4 | ${{ totalPrice }}
5 |
6 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/assets/js/section/front/menu-cart/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 | import cart from "./modules/cart";
4 |
5 | Vue.use(Vuex);
6 |
7 | const debug = process.env.NODE_ENV !== "production";
8 |
9 | export default new Vuex.Store({
10 | modules: {
11 | cart,
12 | },
13 | strict: debug,
14 | });
15 |
--------------------------------------------------------------------------------
/assets/js/section/front/utils/menu.js:
--------------------------------------------------------------------------------
1 | document.getElementById("mobile_menu_toggler").addEventListener("click", () => {
2 | document.getElementById("mobile_menu_container").classList.add("show");
3 | });
4 | document
5 | .getElementById("mobile_menu_close_btn")
6 | .addEventListener("click", () => {
7 | document.getElementById("mobile_menu_container").classList.remove("show");
8 | });
9 |
--------------------------------------------------------------------------------
/assets/js/utils/changed-locale.js:
--------------------------------------------------------------------------------
1 | let $desktopSelect = $("#desktop-select-language");
2 | let $mobileSelect = $("#mobile-select-language");
3 | changedLanguageSite($desktopSelect, $desktopSelect.find("option"));
4 | changedLanguageSite($mobileSelect, $mobileSelect.find("option"));
5 |
6 | function changedLanguageSite(selector, childEl) {
7 | let locale = location.pathname;
8 |
9 | if ("undefined" !== typeof locale || "" !== locale) {
10 | childEl.each(function () {
11 | let $this = $(this);
12 | if (locale.includes($this.attr("value"))) {
13 | this.selected = true;
14 | }
15 | });
16 | }
17 |
18 | selector.on("change", function (event) {
19 | event.preventDefault();
20 | let selectedOptionsIndex = event.target.options.selectedIndex;
21 |
22 | childEl.each(function (key, value) {
23 | if (key.toString() === selectedOptionsIndex.toString()) {
24 | let currentLocale = $(value).attr("value").replace(/\//g, "");
25 | let preparedUrl = $(value).attr("data-url").split("/");
26 | preparedUrl[1] = currentLocale;
27 |
28 | location.href = preparedUrl.join("/");
29 | }
30 | });
31 | return false;
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/assets/js/utils/cookie-manager.js:
--------------------------------------------------------------------------------
1 | /** @see https://learn.javascript.ru/cookie */
2 | export function setCookie(name, value, options = {}) {
3 | options = {
4 | path: "/",
5 | ...options,
6 | };
7 |
8 | if (options.expires instanceof Date) {
9 | options.expires = options.expires.toUTCString();
10 | }
11 |
12 | let updatedCookie =
13 | encodeURIComponent(name) + "=" + encodeURIComponent(value);
14 |
15 | for (let optionKey in options) {
16 | updatedCookie += "; " + optionKey;
17 | let optionValue = options[optionKey];
18 | if (optionValue !== true) {
19 | updatedCookie += "=" + optionValue;
20 | }
21 | }
22 |
23 | document.cookie = updatedCookie;
24 | }
25 |
26 | export function getCookie(name) {
27 | let matches = document.cookie.match(
28 | /* eslint-disable */
29 | new RegExp(
30 | "(?:^|; )" +
31 | name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1") +
32 | "=([^;]*)"
33 | )
34 | );
35 | return matches ? decodeURIComponent(matches[1]) : undefined;
36 | }
37 |
--------------------------------------------------------------------------------
/assets/js/utils/settings.js:
--------------------------------------------------------------------------------
1 | export const apiConfig = {
2 | headers: {
3 | accept: "application/ld+json",
4 | "Content-Type": "application/json",
5 | },
6 | };
7 |
8 | export const apiConfigPatch = {
9 | headers: {
10 | accept: "application/ld+json",
11 | "Content-Type": "application/merge-patch+json",
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/assets/js/utils/title-formatter.js:
--------------------------------------------------------------------------------
1 | export function getProductInformativeTitle(product) {
2 | return (
3 | "#" +
4 | product.id +
5 | " " +
6 | product.title +
7 | " / P: $" +
8 | product.price +
9 | " / Q: " +
10 | product.quantity
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/assets/js/utils/url-generator.js:
--------------------------------------------------------------------------------
1 | export function getUrlViewProduct(viewUrl, productId) {
2 | return (
3 | window.location.protocol +
4 | "//" +
5 | window.location.host +
6 | viewUrl +
7 | "/" +
8 | productId
9 | );
10 | }
11 |
12 | export function concatUrlByParams(...params) {
13 | return params.join("/");
14 | }
15 |
16 | export function getUrlProductsByCategory(
17 | defaultUrl,
18 | categoryId,
19 | page,
20 | itemsPerPage
21 | ) {
22 | return (
23 | defaultUrl +
24 | "?category=/api/categories/" +
25 | categoryId +
26 | "&isPublished=true" +
27 | "&page=" +
28 | page +
29 | "&itemsPerPage=" +
30 | itemsPerPage
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/assets/section-admin.js:
--------------------------------------------------------------------------------
1 | const $ = require('jquery');
2 | require('bootstrap');
3 |
4 | global.$ = global.jQuery = $;
5 |
6 | require('jquery.easing');
7 | require('chart.js');
8 |
9 | require('./js/section/admin/theme/sb-admin-2');
10 | require('./js/utils/changed-locale');
11 | require('./js/section/admin/theme/filters-feature');
12 |
13 | import './css/section/admin/libs.scss';
14 | import './css/section/admin/sb-admin-2.css';
15 | import './css/section/admin/styles.css';
16 | import './css/section/admin/main.scss';
17 |
--------------------------------------------------------------------------------
/assets/section-front.js:
--------------------------------------------------------------------------------
1 | const $ = require('jquery');
2 | require('bootstrap');
3 |
4 | global.$ = global.jQuery = $;
5 |
6 | import './css/section/front/libs.scss';
7 |
8 | import './css/section/front/_alert.css';
9 | import './css/section/front/_banner.css';
10 | import './css/section/front/_base.css';
11 | import './css/section/front/_button.css';
12 | import './css/section/front/_cart.css';
13 | import './css/section/front/_cart_dropdown.css';
14 | import './css/section/front/_footer.css';
15 | import './css/section/front/_form.css';
16 | import './css/section/front/_header.css';
17 | import './css/section/front/_header_menu.css';
18 | import './css/section/front/_pagination.css';
19 | import './css/section/front/_product.css';
20 | import './css/section/front/_product_list.css';
21 | import './css/section/front/_spinner.css';
22 | import './css/section/front/_typography.css';
23 |
24 | require('./js/section/front/utils/menu');
25 | require('./js/utils/changed-locale');
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | '
10 | dsn: '%env(MAILER_DSN)%'
11 |
--------------------------------------------------------------------------------
/config/packages/messenger.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | messenger:
3 | # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
4 | # failure_transport: failed
5 | default_bus: command.bus
6 | buses:
7 | command.bus: ~
8 | event.bus: ~
9 |
10 | transports:
11 | # https://symfony.com/doc/current/messenger.html#transport-configuration
12 | async:
13 | dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
14 | retry_strategy:
15 | delay: 500
16 | # failed: 'doctrine://default?queue_name=failed'
17 | # sync: 'sync://'
18 |
19 | routing:
20 | # Route your messages to the transports
21 | 'App\Messenger\Message\Event\EventUserRegisteredEvent': async
22 | 'App\Messenger\Message\Command\ResetUserPasswordCommand': async
23 |
--------------------------------------------------------------------------------
/config/packages/nelmio_cors.yaml:
--------------------------------------------------------------------------------
1 | nelmio_cors:
2 | defaults:
3 | origin_regex: true
4 | allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
5 | allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
6 | allow_headers: ['Content-Type', 'Authorization']
7 | expose_headers: ['Link']
8 | max_age: 3600
9 | paths:
10 | '^/': null
11 |
--------------------------------------------------------------------------------
/config/packages/notifier.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | notifier:
3 | #chatter_transports:
4 | # slack: '%env(SLACK_DSN)%'
5 | # telegram: '%env(TELEGRAM_DSN)%'
6 | #texter_transports:
7 | # twilio: '%env(TWILIO_DSN)%'
8 | # nexmo: '%env(NEXMO_DSN)%'
9 | channel_policy:
10 | # use chat/slack, chat/telegram, sms/twilio or sms/nexmo
11 | urgent: [ 'email' ]
12 | high: [ 'email' ]
13 | medium: [ 'email' ]
14 | low: [ 'email' ]
15 | admin_recipients:
16 | - { email: admin@example.com }
17 |
--------------------------------------------------------------------------------
/config/packages/php_translation.yaml:
--------------------------------------------------------------------------------
1 | translation:
2 | locales: ['ru', 'en']
3 | edit_in_place:
4 | enabled: false
5 | config_name: app
6 | configs:
7 | app:
8 | dirs: ["%kernel.project_dir%/templates", "%kernel.project_dir%/src"]
9 | output_dir: "%kernel.project_dir%/translations"
10 | excluded_names: ["*TestCase.php", "*Test.php"]
11 | excluded_dirs: [cache, data, logs]
12 |
--------------------------------------------------------------------------------
/config/packages/prod/deprecations.yaml:
--------------------------------------------------------------------------------
1 | # As of Symfony 5.1, deprecations are logged in the dedicated "deprecation" channel when it exists
2 | #monolog:
3 | # channels: [deprecation]
4 | # handlers:
5 | # deprecation:
6 | # type: stream
7 | # channels: [deprecation]
8 | # path: php://stderr
9 |
--------------------------------------------------------------------------------
/config/packages/prod/doctrine.yaml:
--------------------------------------------------------------------------------
1 | doctrine:
2 | orm:
3 | auto_generate_proxy_classes: false
4 | query_cache_driver:
5 | type: pool
6 | pool: doctrine.system_cache_pool
7 | result_cache_driver:
8 | type: pool
9 | pool: doctrine.result_cache_pool
10 |
11 | framework:
12 | cache:
13 | pools:
14 | doctrine.result_cache_pool:
15 | adapter: cache.app
16 | doctrine.system_cache_pool:
17 | adapter: cache.system
18 |
--------------------------------------------------------------------------------
/config/packages/prod/monolog.yaml:
--------------------------------------------------------------------------------
1 | monolog:
2 | handlers:
3 | main:
4 | type: fingers_crossed
5 | action_level: error
6 | handler: nested
7 | excluded_http_codes: [ 404, 405 ]
8 | buffer_size: 50 # How many messages should be saved? Prevent memory leaks
9 | nested:
10 | type: rotating_file
11 | path: "%kernel.logs_dir%/%kernel.environment%.log"
12 | level: debug
13 | max_files: 14
14 | formatter: monolog.formatter.json
15 | console:
16 | type: console
17 | process_psr_3_messages: false
18 | channels: [ "!event", "!doctrine" ]
19 |
--------------------------------------------------------------------------------
/config/packages/prod/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | #webpack_encore:
2 | # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
3 | # Available in version 1.2
4 | #cache: true
5 |
--------------------------------------------------------------------------------
/config/packages/reset_password.yaml:
--------------------------------------------------------------------------------
1 | symfonycasts_reset_password:
2 | request_password_repository: App\Repository\ResetPasswordRequestRepository
3 |
--------------------------------------------------------------------------------
/config/packages/routing.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | router:
3 | utf8: true
4 |
5 | # Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
6 | # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
7 | #default_uri: http://localhost
8 |
9 | when@prod:
10 | framework:
11 | router:
12 | strict_requirements: null
13 |
--------------------------------------------------------------------------------
/config/packages/sensio_framework_extra.yaml:
--------------------------------------------------------------------------------
1 | sensio_framework_extra:
2 | router:
3 | annotations: false
4 |
--------------------------------------------------------------------------------
/config/packages/stof_doctrine_extensions.yaml:
--------------------------------------------------------------------------------
1 | # Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
2 | # See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/
3 | stof_doctrine_extensions:
4 | default_locale: en_US
5 | orm:
6 | default:
7 | sluggable: true
8 |
--------------------------------------------------------------------------------
/config/packages/test/dama_doctrine_test_bundle.yaml:
--------------------------------------------------------------------------------
1 | dama_doctrine_test:
2 | enable_static_connection: true
3 | enable_static_meta_data_cache: true
4 | enable_static_query_cache: true
5 |
--------------------------------------------------------------------------------
/config/packages/test/doctrine.yaml:
--------------------------------------------------------------------------------
1 | doctrine:
2 | dbal:
3 | # "TEST_TOKEN" is typically set by ParaTest
4 | dbname_suffix: '_test%env(default::TEST_TOKEN)%'
5 | url: "sqlite:///%kernel.project_dir%/var/db_for_test.db"
--------------------------------------------------------------------------------
/config/packages/test/hautelook_alice.yaml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: ../dev/hautelook_alice.yaml }
3 |
--------------------------------------------------------------------------------
/config/packages/test/monolog.yaml:
--------------------------------------------------------------------------------
1 | monolog:
2 | handlers:
3 | main:
4 | type: fingers_crossed
5 | action_level: error
6 | handler: nested
7 | excluded_http_codes: [ 404, 405 ]
8 | channels: [ "!event" ]
9 | nested:
10 | type: stream
11 | path: "%kernel.logs_dir%/%kernel.environment%.log"
12 | level: debug
13 |
--------------------------------------------------------------------------------
/config/packages/test/nelmio_alice.yaml:
--------------------------------------------------------------------------------
1 | imports:
2 | - { resource: ../dev/nelmio_alice.yaml }
3 |
--------------------------------------------------------------------------------
/config/packages/test/validator.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | validation:
3 | not_compromised_password: false
4 |
--------------------------------------------------------------------------------
/config/packages/test/web_profiler.yaml:
--------------------------------------------------------------------------------
1 | web_profiler:
2 | toolbar: false
3 | intercept_redirects: false
4 |
5 | framework:
6 | profiler: { collect: false }
7 |
--------------------------------------------------------------------------------
/config/packages/test/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | #webpack_encore:
2 | # strict_mode: false
3 |
--------------------------------------------------------------------------------
/config/packages/translation.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | default_locale: ru
3 | translator:
4 | default_path: '%kernel.project_dir%/translations'
5 | fallbacks:
6 | - ru
7 | # providers:
8 | # crowdin:
9 | # dsn: '%env(CROWDIN_DSN)%'
10 | # loco:
11 | # dsn: '%env(LOCO_DSN)%'
12 | # lokalise:
13 | # dsn: '%env(LOKALISE_DSN)%'
14 |
--------------------------------------------------------------------------------
/config/packages/twig.yaml:
--------------------------------------------------------------------------------
1 | twig:
2 | paths:
3 | 'assets/images': images
4 | 'public': public
5 | default_path: '%kernel.project_dir%/templates'
6 |
7 | when@test:
8 | twig:
9 | strict_variables: true
10 |
--------------------------------------------------------------------------------
/config/packages/validator.yaml:
--------------------------------------------------------------------------------
1 | framework:
2 | validation:
3 | email_validation_mode: html5
4 |
5 | # Enables validator auto-mapping support.
6 | # For instance, basic validation constraints will be inferred from Doctrine's metadata.
7 | #auto_mapping:
8 | # App\Entity\: []
9 |
--------------------------------------------------------------------------------
/config/packages/webpack_encore.yaml:
--------------------------------------------------------------------------------
1 | webpack_encore:
2 | # The path where Encore is building the assets - i.e. Encore.setOutputPath()
3 | output_path: '%kernel.project_dir%/public/build'
4 | # If multiple builds are defined (as shown below), you can disable the default build:
5 | # output_path: false
6 |
7 | # Set attributes that will be rendered on all script and link tags
8 | script_attributes:
9 | defer: true
10 | # Uncomment (also under link_attributes) if using Turbo Drive
11 | # https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change
12 | # 'data-turbo-track': reload
13 | # link_attributes:
14 | # Uncomment if using Turbo Drive
15 | # 'data-turbo-track': reload
16 |
17 | # If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
18 | # crossorigin: 'anonymous'
19 |
20 | # Preload all rendered script and link tags automatically via the HTTP/2 Link header
21 | # preload: true
22 |
23 | # Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
24 | # strict_mode: false
25 |
26 | # If you have multiple builds:
27 | # builds:
28 | # pass "frontend" as the 3rg arg to the Twig functions
29 | # {{ encore_entry_script_tags('entry1', null, 'frontend') }}
30 |
31 | # frontend: '%kernel.project_dir%/public/frontend/build'
32 |
33 | # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
34 | # Put in config/packages/prod/webpack_encore.yaml
35 | # cache: true
36 |
--------------------------------------------------------------------------------
/config/preload.php:
--------------------------------------------------------------------------------
1 |
4 | price:
5 | quantity:
6 | category: '@category_sneakers'
7 | isPublished: 1
--------------------------------------------------------------------------------
/fixtures/user.yaml:
--------------------------------------------------------------------------------
1 | App\Entity\User:
2 | user_1:
3 | email: test1@test.com
4 | # password decoded: test1test1 | add "\" before "$"
5 | password: '\$2y\$13\$FGkTIKTGahgHHQjXst/KPenIr1bsLjXbyTlYN8vShWtZD4D8cEPCK'
6 | isVerified: 1
7 | roles: '[ROLE_SUPER_ADMIN]'
8 | user_2:
9 | email: test2@test.com
10 | # password decoded: test2test2 | add "\" before "$"
11 | password: '\$2y\$13\$aYxXLEQWaZ7NSFVUhUIlIeV9bdmrs6pr5mAb.mvS0WH7yHFoen13G'
12 | isVerified: 1
13 | roles: '[ROLE_ADMIN]'
14 | user_3:
15 | email: test3@test.com
16 | # password decoded: test3test3 | add "\" before "$"
17 | password: '\$2y\$13\$FtG2KFbS5Oue3sr2xVi98OCYeLO00vuR10WoKu5nxa1cDEaa5wSkO'
18 | isVerified: 1
19 | roles: '[ROLE_USER]'
20 |
--------------------------------------------------------------------------------
/migrations/Version20210923235603.php:
--------------------------------------------------------------------------------
1 | addSql('CREATE SEQUENCE product_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
24 | $this->addSql('CREATE TABLE product (id INT NOT NULL, title VARCHAR(255) NOT NULL, price NUMERIC(6, 2) NOT NULL, quantity INT NOT NULL, create_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, description TEXT DEFAULT NULL, is_published BOOLEAN NOT NULL, is_deleted BOOLEAN NOT NULL, PRIMARY KEY(id))');
25 | $this->addSql('COMMENT ON COLUMN product.create_at IS \'(DC2Type:datetime_immutable)\'');
26 | }
27 |
28 | public function down(Schema $schema): void
29 | {
30 | // this down() migration is auto-generated, please modify it to your needs
31 | $this->addSql('DROP SEQUENCE product_id_seq CASCADE');
32 | $this->addSql('DROP TABLE product');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/migrations/Version20210924222154.php:
--------------------------------------------------------------------------------
1 | addSql('CREATE SEQUENCE "user_id_seq" INCREMENT BY 1 MINVALUE 1 START 1');
24 | $this->addSql('CREATE TABLE "user" (id INT NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
25 | $this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649E7927C74 ON "user" (email)');
26 | }
27 |
28 | public function down(Schema $schema): void
29 | {
30 | // this down() migration is auto-generated, please modify it to your needs
31 | $this->addSql('DROP SEQUENCE "user_id_seq" CASCADE');
32 | $this->addSql('DROP TABLE "user"');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/migrations/Version20210924223952.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD is_verified BOOLEAN NOT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('CREATE SCHEMA public');
30 | $this->addSql('ALTER TABLE "user" DROP is_verified');
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/migrations/Version20210927185157.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD full_name VARCHAR(255) DEFAULT NULL');
24 | $this->addSql('ALTER TABLE "user" ADD phone VARCHAR(30) DEFAULT NULL');
25 | $this->addSql('ALTER TABLE "user" ADD address VARCHAR(255) DEFAULT NULL');
26 | $this->addSql('ALTER TABLE "user" ADD zip_code INT DEFAULT NULL');
27 | $this->addSql('ALTER TABLE "user" ADD is_deleted BOOLEAN DEFAULT NULL');
28 | }
29 |
30 | public function down(Schema $schema): void
31 | {
32 | // this down() migration is auto-generated, please modify it to your needs
33 | $this->addSql('ALTER TABLE "user" DROP full_name');
34 | $this->addSql('ALTER TABLE "user" DROP phone');
35 | $this->addSql('ALTER TABLE "user" DROP address');
36 | $this->addSql('ALTER TABLE "user" DROP zip_code');
37 | $this->addSql('ALTER TABLE "user" DROP is_deleted');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/migrations/Version20210927185503.php:
--------------------------------------------------------------------------------
1 | addSql('UPDATE "user" SET is_deleted=\'0\' WHERE is_deleted IS NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('UPDATE "user" SET is_deleted=NULL WHERE is_deleted IS NOT NULL');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20210927190540.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ALTER is_deleted SET NOT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" ALTER is_deleted DROP NOT NULL');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211001104011.php:
--------------------------------------------------------------------------------
1 | addSql('CREATE SEQUENCE product_image_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
24 | $this->addSql('CREATE TABLE product_image (id INT NOT NULL, product_id INT NOT NULL, filename_big VARCHAR(255) NOT NULL, filename_middle VARCHAR(255) NOT NULL, filename_small VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
25 | $this->addSql('CREATE INDEX IDX_64617F034584665A ON product_image (product_id)');
26 | $this->addSql('ALTER TABLE product_image ADD CONSTRAINT FK_64617F034584665A FOREIGN KEY (product_id) REFERENCES product (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
27 | $this->addSql('ALTER TABLE "user" ALTER zip_code TYPE DOUBLE PRECISION');
28 | $this->addSql('ALTER TABLE "user" ALTER zip_code DROP DEFAULT');
29 | }
30 |
31 | public function down(Schema $schema): void
32 | {
33 | // this down() migration is auto-generated, please modify it to your needs
34 | $this->addSql('DROP SEQUENCE product_image_id_seq CASCADE');
35 | $this->addSql('DROP TABLE product_image');
36 | $this->addSql('ALTER TABLE "user" ALTER zip_code TYPE INT');
37 | $this->addSql('ALTER TABLE "user" ALTER zip_code DROP DEFAULT');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/migrations/Version20211002150834.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE product ADD slug VARCHAR(128) DEFAULT NULL');
24 | $this->addSql('CREATE UNIQUE INDEX UNIQ_D34A04AD989D9B62 ON product (slug)');
25 | }
26 |
27 | public function down(Schema $schema): void
28 | {
29 | // this down() migration is auto-generated, please modify it to your needs
30 | $this->addSql('DROP INDEX UNIQ_D34A04AD989D9B62');
31 | $this->addSql('ALTER TABLE product DROP slug');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/migrations/Version20211002222439.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE product ADD uuid UUID DEFAULT NULL');
24 | $this->addSql('COMMENT ON COLUMN product.uuid IS \'(DC2Type:uuid)\'');
25 | $this->addSql('UPDATE product SET uuid=uuid_generate_v4() WHERE uuid IS NULL'); // ONLY postgresql
26 | }
27 |
28 | public function down(Schema $schema): void
29 | {
30 | // this down() migration is auto-generated, please modify it to your needs
31 | $this->addSql('ALTER TABLE product DROP uuid');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/migrations/Version20211002223343.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE product ALTER uuid SET NOT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE product ALTER uuid DROP NOT NULL');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211003091352.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE product ALTER price TYPE NUMERIC(15, 2)');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE product ALTER price TYPE NUMERIC(6, 2)');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211003203107.php:
--------------------------------------------------------------------------------
1 | addSql('CREATE SEQUENCE category_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
24 | $this->addSql('CREATE TABLE category (id INT NOT NULL, title VARCHAR(100) DEFAULT NULL, slug VARCHAR(120) NOT NULL, PRIMARY KEY(id))');
25 | $this->addSql('CREATE UNIQUE INDEX UNIQ_64C19C1989D9B62 ON category (slug)');
26 | $this->addSql('ALTER TABLE product ADD category_id INT DEFAULT NULL');
27 | $this->addSql('ALTER TABLE product ADD CONSTRAINT FK_D34A04AD12469DE2 FOREIGN KEY (category_id) REFERENCES category (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
28 | $this->addSql('CREATE INDEX IDX_D34A04AD12469DE2 ON product (category_id)');
29 | }
30 |
31 | public function down(Schema $schema): void
32 | {
33 | // this down() migration is auto-generated, please modify it to your needs
34 | $this->addSql('ALTER TABLE product DROP CONSTRAINT FK_D34A04AD12469DE2');
35 | $this->addSql('DROP SEQUENCE category_id_seq CASCADE');
36 | $this->addSql('DROP TABLE category');
37 | $this->addSql('DROP INDEX IDX_D34A04AD12469DE2');
38 | $this->addSql('ALTER TABLE product DROP category_id');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/migrations/Version20211004063505.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE category ADD is_deleted BOOLEAN DEFAULT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE category DROP is_deleted');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211004210150.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE product RENAME COLUMN create_at TO created_at');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE product RENAME COLUMN created_at TO create_at');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211118200708.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD facebook_id VARCHAR(50) DEFAULT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" DROP facebook_id');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211119072129.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD google_id VARCHAR(50) DEFAULT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" DROP google_id');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20211125173610.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE cart ADD token VARCHAR(255) DEFAULT NULL');
24 | $this->addSql('ALTER TABLE cart DROP session_id');
25 | }
26 |
27 | public function down(Schema $schema): void
28 | {
29 | // this down() migration is auto-generated, please modify it to your needs
30 | $this->addSql('ALTER TABLE cart DROP token');
31 | $this->addSql('ALTER TABLE cart ADD session_id VARCHAR(255) NOT NULL');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/migrations/Version20220325174952.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" DROP facebook_id');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" ADD facebook_id VARCHAR(50) DEFAULT NULL');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20220326090356.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD yandex_id VARCHAR(50) DEFAULT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" DROP yandex_id');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20220326141147.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD vkontakte_id VARCHAR(50) DEFAULT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" DROP vkontakte_id');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20220326151023.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ADD github_id VARCHAR(50) DEFAULT NULL');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" DROP github_id');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/migrations/Version20240714084855.php:
--------------------------------------------------------------------------------
1 | addSql('ALTER TABLE "user" ALTER zip_code TYPE INT');
24 | }
25 |
26 | public function down(Schema $schema): void
27 | {
28 | // this down() migration is auto-generated, please modify it to your needs
29 | $this->addSql('ALTER TABLE "user" ALTER zip_code TYPE DOUBLE PRECISION');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "@symfony/stimulus-bridge": "^2.0.0",
4 | "@symfony/webpack-encore": "^1.0.0",
5 | "core-js": "^3.0.0",
6 | "eslint": "^8.4.1",
7 | "eslint-config-prettier": "^8.3.0",
8 | "eslint-plugin-prettier": "^4.0.0",
9 | "eslint-plugin-vue": "^8.2.0",
10 | "file-loader": "^6.2.0",
11 | "regenerator-runtime": "^0.13.2",
12 | "sass": "1.32.13",
13 | "sass-loader": "^10.1.1",
14 | "stimulus": "^2.0.0",
15 | "webpack-notifier": "^1.6.0"
16 | },
17 | "license": "UNLICENSED",
18 | "private": true,
19 | "scripts": {
20 | "dev-server": "encore dev-server",
21 | "dev": "encore dev",
22 | "watch": "encore dev --watch",
23 | "build": "encore production --progress"
24 | },
25 | "dependencies": {
26 | "@fortawesome/fontawesome-free": "^5.15.4",
27 | "axios": "^0.21.1",
28 | "bootstrap": "^4.3.1",
29 | "chart.js": "^2.9.4",
30 | "foundation-emails": "^2.3.1",
31 | "http-status-codes": "^2.1.4",
32 | "jquery": "^3.5.1",
33 | "jquery.easing": "^1.4.1",
34 | "popper.js": "^1.16.1",
35 | "vue": "^2.6.12",
36 | "vue-loader": "^15.9.8",
37 | "vue-template-compiler": "^2.6.12",
38 | "vuex": "^3.6.2"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | tests
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | src
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/phpunit.xml.dist.bak:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | tests
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | src
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | getUser();
16 |
17 | if (false === $user->isVerified()) {
18 | $this->addFlash('danger', 'You don\'t have enough rights! Contact the administrator.');
19 |
20 | return false;
21 | }
22 |
23 | return true;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Controller/Admin/DashboardController.php:
--------------------------------------------------------------------------------
1 | render('admin/pages/dashboard.html.twig');
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Controller/Admin/ProductImageController.php:
--------------------------------------------------------------------------------
1 | getProduct();
27 |
28 | if (!$this->checkTheAccessLevel()) {
29 | return $this->redirect($request->server->get('HTTP_REFERER'));
30 | }
31 |
32 | $productImageDir = $productManager->getProductImagesDir($product);
33 | $imgId = $productImage->getId();
34 |
35 | $productImageManager->removeImageFromProduct($productImage, $productImageDir);
36 | $this->addFlash('warning', "The image (ID: $imgId) was successfully deleted!");
37 |
38 | return $this->redirectToRoute('admin_product_edit', [
39 | 'id' => $product->getId(),
40 | ]);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Controller/Admin/SecurityController.php:
--------------------------------------------------------------------------------
1 | getLastAuthenticationError();
21 | // last username entered by the user
22 | $lastUsername = $authenticationUtils->getLastUsername();
23 |
24 | return $this->render('admin/security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
25 | }
26 |
27 | #[Route('/logout', name: 'admin_security_logout')]
28 | public function logout(): RedirectResponse
29 | {
30 | return $this->redirectToRoute(LoginFormAuthenticator::LOGIN_ROUTE);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Controller/Front/AuthGithubEnController.php:
--------------------------------------------------------------------------------
1 | getClient('github_en')
19 | ->redirect([], []);
20 | }
21 |
22 | #[Route('/connect/github-en/check', name: 'connect_github_en_check')]
23 | public function connectCheckAction(): void
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Controller/Front/AuthGithubRusController.php:
--------------------------------------------------------------------------------
1 | getClient('github_ru')
19 | ->redirect([], []);
20 | }
21 |
22 | #[Route('/connect/github-ru/check', name: 'connect_github_ru_check')]
23 | public function connectCheckAction(): void
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Controller/Front/AuthGoogleController.php:
--------------------------------------------------------------------------------
1 | getClient('google_main')
19 | ->redirect([], []);
20 | }
21 |
22 | #[Route('/connect/google/check', name: 'connect_google_check')]
23 | public function connectCheckAction(): void
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Controller/Front/AuthVkontakteController.php:
--------------------------------------------------------------------------------
1 | getClient('vkontakte_main')
19 | ->redirect([], []);
20 | }
21 |
22 | #[Route('/connect/vkontakte/check', name: 'connect_vkontakte_check')]
23 | public function connectCheckAction(): void
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Controller/Front/AuthYandexController.php:
--------------------------------------------------------------------------------
1 | getClient('yandex_main')
19 | ->redirect([], []);
20 | }
21 |
22 | #[Route('/connect/yandex/check', name: 'connect_yandex_check')]
23 | public function connectCheckAction(): void
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Controller/Front/CartController.php:
--------------------------------------------------------------------------------
1 | cookies->get('CART_TOKEN');
22 | $cart = $cartRepository->findOneBy(['token' => $cartToken]);
23 |
24 | return $this->render('front/cart/show.html.twig', [
25 | 'cart' => $cart,
26 | ]);
27 | }
28 |
29 | #[Route('/cart/create', name: 'main_cart_create')]
30 | public function create(Request $request, OrderManager $orderManager): Response
31 | {
32 | $cartToken = $request->cookies->get('CART_TOKEN');
33 |
34 | /** @var User $user */
35 | $user = $this->getUser();
36 |
37 | $orderManager->createOrderFromCartByToken($cartToken, $user);
38 |
39 | $redirectUrl = $this->generateUrl('main_cart_show');
40 |
41 | // Пример удаления куки 'CART_TOKEN' через контроллер
42 | $response = new RedirectResponse($redirectUrl);
43 | $response->headers->clearCookie('CART_TOKEN', '/', null);
44 |
45 | return $response;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Controller/Front/CategoryController.php:
--------------------------------------------------------------------------------
1 | getIsDeleted()) {
24 | $this->addFlash('warning', "The category {$category->getTitle()} not found!");
25 |
26 | return $this->redirectToRoute('main_homepage');
27 | }
28 |
29 | $products = $productRepository->findByCategoryAndCount($category->getId());
30 |
31 | return $this->render('front/category/show.html.twig', [
32 | 'category' => $category,
33 | 'products' => $products,
34 | ]);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Controller/Front/SecurityController.php:
--------------------------------------------------------------------------------
1 | getSession()->set('HTTP_REFERER', $request->server->get('HTTP_REFERER'));
20 |
21 | if ($this->getUser()) {
22 | return $this->redirectToRoute('main_profile_index');
23 | }
24 |
25 | // get the login error if there is one
26 | $error = $authenticationUtils->getLastAuthenticationError();
27 | // last username entered by the user
28 | $lastUsername = $authenticationUtils->getLastUsername();
29 |
30 | return $this->render('front/security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
31 | }
32 |
33 | #[Route('/logout', name: 'main_logout')]
34 | public function logout(): RedirectResponse
35 | {
36 | return $this->redirectToRoute('main_profile_index');
37 | // throw new LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Controller/Front/SitemapController.php:
--------------------------------------------------------------------------------
1 | $this->generateUrl('main_homepage', [], UrlGeneratorInterface::ABSOLUTE_URL),
20 | 'lastmod' => (new DateTimeImmutable())->format('Y-m-d'),
21 | 'changefreq' => 'weekly',
22 | 'priority' => 1,
23 | ];
24 |
25 | return $this->render('front/sitemap.xml.twig', [
26 | 'data' => $mainPageInfo,
27 | ]);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Entity/ResetPasswordRequest.php:
--------------------------------------------------------------------------------
1 | id = null;
37 | $this->user = $user;
38 | $this->initialize($expiresAt, $selector, $hashedToken);
39 | }
40 |
41 | public function getId(): ?int
42 | {
43 | return $this->id;
44 | }
45 |
46 | public function getUser(): object
47 | {
48 | return $this->user;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Entity/StaticStorage/OrderStaticStorage.php:
--------------------------------------------------------------------------------
1 | 'Created',
22 | self::ORDER_STATUS_PROCESSED => 'Processed',
23 | self::ORDER_STATUS_COMPLECTED => 'Complected',
24 | self::ORDER_STATUS_DELIVERED => 'Delivered',
25 | self::ORDER_STATUS_DENIED => 'Denied',
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Entity/StaticStorage/UserStaticStorage.php:
--------------------------------------------------------------------------------
1 | 'User',
20 | self::USER_ROLE_ADMIN => 'Admin',
21 | self::USER_ROLE_SUPER_ADMIN => 'Super Admin',
22 | ];
23 | }
24 |
25 | public static function getUserRoleHasAccessToAdminSection(): array
26 | {
27 | return [
28 | self::USER_ROLE_ADMIN,
29 | self::USER_ROLE_SUPER_ADMIN,
30 | ];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Event/OrderCreatedFromCartEvent.php:
--------------------------------------------------------------------------------
1 | order;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Event/UserLoggedInViaSocialNetworkEvent.php:
--------------------------------------------------------------------------------
1 | user;
22 | }
23 |
24 | public function getPlainPassword(): string
25 | {
26 | return $this->plainPassword;
27 | }
28 |
29 | public function getVerifyEmail(): array
30 | {
31 | return $this->verifyEmail;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/EventSubscriber/LocaleSubscriber.php:
--------------------------------------------------------------------------------
1 | getRequest();
20 |
21 | if (!$request->hasPreviousSession()) {
22 | return;
23 | }
24 |
25 | $locale = $request->attributes->get('_locale');
26 |
27 | if ($locale) {
28 | $request->getSession()->set('_locale', $locale);
29 | } else {
30 | $request->setLocale(
31 | $request->getSession()->get('_locale', $this->defaultLocale)
32 | );
33 | }
34 | }
35 |
36 | public static function getSubscribedEvents(): array
37 | {
38 | return [
39 | KernelEvents::REQUEST => [
40 | [
41 | 'onKernelRequest',
42 | 20,
43 | ],
44 | ],
45 | ];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/EventSubscriber/OrderCreatedFromCartSendNotificationSubscriber.php:
--------------------------------------------------------------------------------
1 | orderCreatedFromCartEmailSender = $orderCreatedFromCartEmailSender;
20 |
21 | return $this;
22 | }
23 |
24 | public function onOrderCreatedFromCartEvent(OrderCreatedFromCartEvent $event): void
25 | {
26 | $order = $event->getOrder();
27 |
28 | $this->orderCreatedFromCartEmailSender->sendEmailToClient($order);
29 | $this->orderCreatedFromCartEmailSender->sendEmailToManager($order);
30 | }
31 |
32 | public static function getSubscribedEvents(): array
33 | {
34 | return [
35 | OrderCreatedFromCartEvent::class => 'onOrderCreatedFromCartEvent',
36 | ];
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/EventSubscriber/UserLoggedInViaSocialNetworkSendNotificationSubscriber.php:
--------------------------------------------------------------------------------
1 | mailerSender = $mailerSender;
20 |
21 | return $this;
22 | }
23 |
24 | public function onUserLoggedInViaSocialNetworkEvent(UserLoggedInViaSocialNetworkEvent $event): void
25 | {
26 | $user = $event->getUser();
27 | $plainPassword = $event->getPlainPassword();
28 | $verifyEmail = $event->getVerifyEmail();
29 |
30 | $this->mailerSender->sendEmailToClient($user, $plainPassword, $verifyEmail);
31 | }
32 |
33 | public static function getSubscribedEvents(): array
34 | {
35 | return [
36 | UserLoggedInViaSocialNetworkEvent::class => 'onUserLoggedInViaSocialNetworkEvent',
37 | ];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Exception/Security/EmptyUserPlainPasswordException.php:
--------------------------------------------------------------------------------
1 | add('title', TextType::class, [
21 | 'label' => 'Title',
22 | 'required' => true,
23 | 'trim' => true,
24 | 'attr' => [
25 | 'class' => 'form-control',
26 | ],
27 | 'constraints' => [
28 | new NotBlank(message: 'Please enter a title'),
29 | ],
30 | ])
31 | ->add('submit', SubmitType::class, [
32 | 'label' => 'Save changes',
33 | ]);
34 | }
35 |
36 | public function configureOptions(OptionsResolver $resolver): void
37 | {
38 | $resolver->setDefaults([
39 | 'data_class' => EditCategoryModel::class,
40 | ]);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Form/DTO/EditCategoryModel.php:
--------------------------------------------------------------------------------
1 | id = $category->getId();
26 | $model->title = $category->getTitle();
27 |
28 | return $model;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Form/DTO/EditOrderModel.php:
--------------------------------------------------------------------------------
1 | id = $order->getId();
32 | $model->owner = $order->getOwner();
33 | $model->status = $order->getStatus();
34 | $model->totalPrice = $order->getTotalPrice();
35 | $model->createdAt = $order->getCreatedAt();
36 | $model->isDeleted = $order->getIsDeleted();
37 |
38 | return $model;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Form/DTO/EditProductModel.php:
--------------------------------------------------------------------------------
1 | id = $product->getId();
37 | $model->title = $product->getTitle();
38 | $model->price = $product->getPrice();
39 | $model->quantity = $product->getQuantity();
40 | $model->description = $product->getDescription();
41 | $model->category = $product->getCategory();
42 | $model->isPublished = $product->getIsPublished();
43 | $model->isDeleted = $product->getIsDeleted();
44 | $model->createdAt = $product->getCreatedAt();
45 |
46 | return $model;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Form/Front/ProfileEditFormType.php:
--------------------------------------------------------------------------------
1 | add('fullName', TextType::class, [
20 | 'label' => 'personal_account.edit.labels.full_name',
21 | 'trim' => true,
22 | ])
23 | ->add('phone', TextType::class, [
24 | 'label' => 'personal_account.edit.labels.phone',
25 | 'trim' => true,
26 | 'required' => false,
27 | ])
28 | ->add('address', TextType::class, [
29 | 'label' => 'personal_account.edit.labels.address',
30 | 'trim' => true,
31 | ])
32 | ->add('zipCode', IntegerType::class, [
33 | 'label' => 'personal_account.edit.labels.zipcode',
34 | 'required' => false,
35 | 'trim' => true,
36 | ]);
37 | }
38 |
39 | public function configureOptions(OptionsResolver $resolver): void
40 | {
41 | $resolver->setDefaults([
42 | 'data_class' => User::class,
43 | ]);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Form/Front/ResetPasswordRequestFormType.php:
--------------------------------------------------------------------------------
1 | add('email', EmailType::class, [
19 | 'attr' => [
20 | 'autocomplete' => 'email',
21 | 'class' => 'form-control',
22 | ],
23 | 'constraints' => [
24 | new NotBlank([
25 | 'message' => 'Please enter your email',
26 | ]),
27 | ],
28 | ]);
29 | }
30 |
31 | public function configureOptions(OptionsResolver $resolver): void
32 | {
33 | $resolver->setDefaults([]);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Form/Handler/CategoryFormHandler.php:
--------------------------------------------------------------------------------
1 | id) {
22 | $category = $this->categoryManager->find($editCategoryModel->id);
23 | }
24 |
25 | $this->categoryManager->persist($category);
26 | $category = $this->fillingCategoryData($category, $editCategoryModel);
27 | $this->categoryManager->flush();
28 |
29 | return $category;
30 | }
31 |
32 | private function fillingCategoryData(Category $category, EditCategoryModel $editCategoryModel): Category
33 | {
34 | $title = (!is_string($editCategoryModel->title))
35 | ? (string) $editCategoryModel->title
36 | : $editCategoryModel->title;
37 |
38 | $category->setTitle($title);
39 |
40 | return $category;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Form/Validator/GreaterThanOrEqualPrice.php:
--------------------------------------------------------------------------------
1 | context->buildViolation($constraint->message)
35 | ->addViolation();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Kernel.php:
--------------------------------------------------------------------------------
1 | getContainer()->getParameter('timezone');
21 | if (empty($timezone)) {
22 | $timezone = date_default_timezone_get();
23 | }
24 | date_default_timezone_set($timezone);
25 | }
26 |
27 | protected function configureContainer(ContainerConfigurator $container): void
28 | {
29 | $container->import('../config/{packages}/*.yaml');
30 | $container->import('../config/{packages}/'.$this->environment.'/*.yaml');
31 |
32 | if (is_file(dirname(__DIR__).'/config/services.yaml')) {
33 | $container->import('../config/services.yaml');
34 | $container->import('../config/{services}_'.$this->environment.'.yaml');
35 | } else {
36 | $container->import('../config/{services}.php');
37 | }
38 | }
39 |
40 | protected function configureRoutes(RoutingConfigurator $routes): void
41 | {
42 | $routes->import('../config/{routes}/'.$this->environment.'/*.yaml');
43 | $routes->import('../config/{routes}/*.yaml');
44 |
45 | if (is_file(dirname(__DIR__).'/config/routes.yaml')) {
46 | $routes->import('../config/routes.yaml');
47 | } else {
48 | $routes->import('../config/{routes}.php');
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Messenger/Message/Command/ResetUserPasswordCommand.php:
--------------------------------------------------------------------------------
1 | email;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Messenger/Message/Event/EventUserRegisteredEvent.php:
--------------------------------------------------------------------------------
1 | userId;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Messenger/MessageHandler/Command/ResetUserPasswordHandler.php:
--------------------------------------------------------------------------------
1 | getEmail();
28 |
29 | /** @var User|null $user */
30 | $user = $this->userManager->getRepository()->findOneBy(['email' => $email]);
31 |
32 | if (!$user) {
33 | return;
34 | }
35 |
36 | try {
37 | $resetToken = $this->resetPasswordHelper->generateResetToken($user);
38 | $this->userPasswordEmailSender->sendEmailToClient($user, $resetToken);
39 | } catch (ResetPasswordExceptionInterface $e) {
40 | // ...
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Messenger/MessageHandler/Event/UserRegisteredHandler.php:
--------------------------------------------------------------------------------
1 | getUserId();
27 |
28 | /** @var User|null $user */
29 | $user = $this->userManager->find($userId);
30 |
31 | if (!$user) {
32 | return;
33 | }
34 |
35 | $emailSignature = $this->emailVerifier
36 | ->generateEmailSignature('main_verify_email', $user);
37 |
38 | $this->emailSender->sendEmailToClient($user, $emailSignature);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Repository/CartProductRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('c')
31 | ->andWhere('c.exampleField = :val')
32 | ->setParameter('val', $value)
33 | ->orderBy('c.id', 'ASC')
34 | ->setMaxResults(10)
35 | ->getQuery()
36 | ->getResult()
37 | ;
38 | }
39 | */
40 |
41 | /*
42 | public function findOneBySomeField($value): ?CartProduct
43 | {
44 | return $this->createQueryBuilder('c')
45 | ->andWhere('c.exampleField = :val')
46 | ->setParameter('val', $value)
47 | ->getQuery()
48 | ->getOneOrNullResult()
49 | ;
50 | }
51 | */
52 | }
53 |
--------------------------------------------------------------------------------
/src/Repository/CartRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('c')
31 | ->andWhere('c.exampleField = :val')
32 | ->setParameter('val', $value)
33 | ->orderBy('c.id', 'ASC')
34 | ->setMaxResults(10)
35 | ->getQuery()
36 | ->getResult()
37 | ;
38 | }
39 | */
40 |
41 | /*
42 | public function findOneBySomeField($value): ?Cart
43 | {
44 | return $this->createQueryBuilder('c')
45 | ->andWhere('c.exampleField = :val')
46 | ->setParameter('val', $value)
47 | ->getQuery()
48 | ->getOneOrNullResult()
49 | ;
50 | }
51 | */
52 | }
53 |
--------------------------------------------------------------------------------
/src/Repository/CategoryRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('c')
28 | ->andWhere('c.isDeleted = FALSE')
29 | ->getQuery()
30 | ->getResult();
31 | }
32 |
33 | public function forFormQueryBuilderFindActiveCategory(): QueryBuilder
34 | {
35 | return $this->createQueryBuilder('c')
36 | ->andWhere('c.isDeleted = FALSE');
37 | }
38 |
39 | public function findActiveCategoryWithJoinProduct(): ?array
40 | {
41 | return $this->createQueryBuilder('c')
42 | ->where('c.isDeleted = FALSE')
43 | ->join('c.products', 'p')
44 | ->andWhere('p.isDeleted = FALSE')
45 | ->andWhere('p.isPublished = TRUE')
46 | ->getQuery()
47 | ->getResult();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Repository/OrderProductRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('o')
31 | ->andWhere('o.exampleField = :val')
32 | ->setParameter('val', $value)
33 | ->orderBy('o.id', 'ASC')
34 | ->setMaxResults(10)
35 | ->getQuery()
36 | ->getResult()
37 | ;
38 | }
39 | */
40 |
41 | /*
42 | public function findOneBySomeField($value): ?OrderProduct
43 | {
44 | return $this->createQueryBuilder('o')
45 | ->andWhere('o.exampleField = :val')
46 | ->setParameter('val', $value)
47 | ->getQuery()
48 | ->getOneOrNullResult()
49 | ;
50 | }
51 | */
52 | }
53 |
--------------------------------------------------------------------------------
/src/Repository/OrderRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('o')
31 | ->andWhere('o.exampleField = :val')
32 | ->setParameter('val', $value)
33 | ->orderBy('o.id', 'ASC')
34 | ->setMaxResults(10)
35 | ->getQuery()
36 | ->getResult()
37 | ;
38 | }
39 | */
40 |
41 | /*
42 | public function findOneBySomeField($value): ?Order
43 | {
44 | return $this->createQueryBuilder('o')
45 | ->andWhere('o.exampleField = :val')
46 | ->setParameter('val', $value)
47 | ->getQuery()
48 | ->getOneOrNullResult()
49 | ;
50 | }
51 | */
52 | }
53 |
--------------------------------------------------------------------------------
/src/Repository/ProductImageRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('p')
31 | ->andWhere('p.exampleField = :val')
32 | ->setParameter('val', $value)
33 | ->orderBy('p.id', 'ASC')
34 | ->setMaxResults(10)
35 | ->getQuery()
36 | ->getResult()
37 | ;
38 | }
39 | */
40 |
41 | /*
42 | public function findOneBySomeField($value): ?ProductImage
43 | {
44 | return $this->createQueryBuilder('p')
45 | ->andWhere('p.exampleField = :val')
46 | ->setParameter('val', $value)
47 | ->getQuery()
48 | ->getOneOrNullResult()
49 | ;
50 | }
51 | */
52 | }
53 |
--------------------------------------------------------------------------------
/src/Repository/ResetPasswordRequestRepository.php:
--------------------------------------------------------------------------------
1 | urlGenerator = $urlGenerator;
22 | }
23 |
24 | public function start(Request $request, ?AuthenticationException $authException = null): RedirectResponse
25 | {
26 | /** @var Session $session */
27 | $session = $request->getSession();
28 |
29 | $session->getFlashBag()->add('warning', 'You have to login in order to access this page.');
30 |
31 | return new RedirectResponse($this->urlGenerator->generate(LoginFormAuthenticator::LOGIN_ROUTE));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Security/EntryPoint/AuthenticationFrontEntryPoint.php:
--------------------------------------------------------------------------------
1 | urlGenerator = $urlGenerator;
21 | }
22 |
23 | public function start(Request $request, ?AuthenticationException $authException = null): RedirectResponse
24 | {
25 | /** @var Session $session */
26 | $session = $request->getSession();
27 |
28 | $session->getFlashBag()->add('warning', 'You have to login in order to access this page.');
29 |
30 | return new RedirectResponse($this->urlGenerator->generate('main_login'));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Security/Handler/AccessAdminDeniedHandler.php:
--------------------------------------------------------------------------------
1 | urlGenerator = $urlGenerator;
23 | }
24 |
25 | public function handle(Request $request, AccessDeniedException $accessDeniedException): ?Response
26 | {
27 | /** @var Session $session */
28 | $session = $request->getSession();
29 |
30 | $session->getFlashBag()->add('warning', 'You have to login in order to access this page.');
31 |
32 | return new RedirectResponse($this->urlGenerator->generate(LoginFormAuthenticator::LOGIN_ROUTE));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Security/Handler/AccessFrontDeniedHandler.php:
--------------------------------------------------------------------------------
1 | urlGenerator = $urlGenerator;
22 | }
23 |
24 | public function handle(Request $request, AccessDeniedException $accessDeniedException): ?Response
25 | {
26 | /** @var Session $session */
27 | $session = $request->getSession();
28 |
29 | $session->getFlashBag()->add('warning', 'You have to login in order to access this page.');
30 |
31 | return new RedirectResponse($this->urlGenerator->generate('main_login'));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Security/Voters/AdminOrderEditVoter.php:
--------------------------------------------------------------------------------
1 | getUser();
25 | $isVerified = $user->isVerified();
26 | $isAdmin = $user->isVerified();
27 |
28 | return $user instanceof User && $isAdmin && $isVerified;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Utils/ApiPlatform/Event/Subscriber/SetCartTokenSubscriber.php:
--------------------------------------------------------------------------------
1 | [
22 | 'setCartTokenToCart', EventPriorities::PRE_WRITE,
23 | ],
24 | ];
25 | }
26 |
27 | /**
28 | * @throws Exception
29 | */
30 | public function setCartTokenToCart(ViewEvent $event): void
31 | {
32 | $cart = $event->getControllerResult();
33 | $method = $event->getRequest()->getMethod();
34 |
35 | if (!$cart instanceof Cart || Request::METHOD_POST !== $method) {
36 | return;
37 | }
38 |
39 | $cartToken = $event->getRequest()->cookies->get('CART_TOKEN');
40 |
41 | if (!$cartToken) {
42 | $cartToken = TokenGenerator::generateToken();
43 | }
44 |
45 | $cart->setToken($cartToken);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Utils/Authenticator/CheckingUserSocialNetworkBeforeAuthorization.php:
--------------------------------------------------------------------------------
1 | security = $security;
19 |
20 | return $this;
21 | }
22 |
23 | protected function checkingUserSocialNetworkBeforeAuthorization(string $socialNetworkUserEmail): bool
24 | {
25 | /** @var User|null $activeUser */
26 | $activeUser = $this->security->getUser();
27 |
28 | if ($activeUser) {
29 | $activeUserEmail = $activeUser->getEmail();
30 |
31 | if ($activeUserEmail !== $socialNetworkUserEmail) {
32 | return true;
33 | }
34 | }
35 |
36 | return false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Utils/Event/Subscriber/MigrationEventSubscriber.php:
--------------------------------------------------------------------------------
1 | getSchema();
28 |
29 | if (!$schema->hasNamespace('public')) {
30 | $schema->createNamespace('public');
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Utils/File/FileSaver.php:
--------------------------------------------------------------------------------
1 | getClientOriginalName(), PATHINFO_FILENAME);
28 | $saveFileName = $this->slugger->slug($originalFileName);
29 | $filename = sprintf('%s-%s.%s', $saveFileName, str_replace('.', '', uniqid('', true)), $uploadedFile->guessExtension());
30 | $this->filesystemWorker->createFolderIfNotExist($this->uploadsTempDir);
31 |
32 | try {
33 | $uploadedFile->move($this->uploadsTempDir, $filename);
34 | } catch (FileException $exception) {
35 | return null;
36 | }
37 |
38 | return $filename;
39 | }
40 |
41 | public function getUploadsTempDir(): string
42 | {
43 | return $this->uploadsTempDir;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Utils/File/ImageResizer.php:
--------------------------------------------------------------------------------
1 | filesystemWorker->generatePathToFile($originalFileFolder, $originalFilename);
20 | [$imageWidth, $imageHeight] = getimagesize($originalFilePath);
21 |
22 | $ratio = $imageWidth / $imageHeight;
23 | $targetWidth = $targetParams['width'];
24 | $targetHeight = $targetParams['height'];
25 |
26 | if ($targetHeight && ($targetWidth / $targetHeight) > $ratio) {
27 | $targetWidth = $targetHeight * $ratio;
28 | } else {
29 | $targetHeight = $targetWidth / $ratio;
30 | }
31 |
32 | $targetFolder = $targetParams['newFolder'];
33 | $targetFilename = $targetParams['newFilename'];
34 | $targetFilePath = sprintf('%s/%s', $targetFolder, $targetFilename);
35 |
36 | $imagineFile = $this->imagine->open($originalFilePath);
37 | $imagineFile
38 | ->resize(new Box($targetWidth, $targetHeight))
39 | ->save($targetFilePath);
40 |
41 | return $targetFilename;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Utils/FileSystem/FilesystemWorker.php:
--------------------------------------------------------------------------------
1 | filesystem->exists($folder)) {
19 | $this->filesystem->mkdir($folder);
20 | }
21 | }
22 |
23 | public function remove(string $item): void
24 | {
25 | if ($this->filesystem->exists($item)) {
26 | $this->filesystem->remove($item);
27 | }
28 | }
29 |
30 | public function removeFolderIfEmpty(string $pathToDir): void
31 | {
32 | if (is_dir($pathToDir)) {
33 | $iterator = new FilesystemIterator($pathToDir);
34 | if (!$iterator->valid()) {
35 | $this->filesystem->remove($pathToDir);
36 | }
37 | }
38 | }
39 |
40 | public function generatePathToFile(string $dir, string $filename): string
41 | {
42 | return $dir.DIRECTORY_SEPARATOR.$filename;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Utils/Generator/PasswordGenerator.php:
--------------------------------------------------------------------------------
1 | mailerSender = $mailerSender;
19 |
20 | return $this;
21 | }
22 |
23 | protected UrlGeneratorInterface $urlGenerator;
24 |
25 | #[Required]
26 | public function setUrlGenerator(UrlGeneratorInterface $urlGenerator): BaseSender
27 | {
28 | $this->urlGenerator = $urlGenerator;
29 |
30 | return $this;
31 | }
32 |
33 | protected ParameterBagInterface $parameterBag;
34 |
35 | public function __construct(ParameterBagInterface $parameterBag)
36 | {
37 | $this->parameterBag = $parameterBag;
38 | }
39 |
40 | protected function getMailerOptions(): MailerOptionModel
41 | {
42 | return new MailerOptionModel();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Utils/Mailer/Sender/OrderCreatedFromCartEmailSender.php:
--------------------------------------------------------------------------------
1 | getOwner();
17 | $mailerOptions = $this->getMailerOptions()
18 | ->setRecipient($user->getEmail())
19 | ->setCc($this->parameterBag->get('admin_email'))
20 | ->setSubject('Symfony shop - Thank you for your purchase!')
21 | ->setHtmlTemplate('front/email/client/created_order_from_cart.html.twig')
22 | ->setContext([
23 | 'order' => $order,
24 | 'profileUrl' => $this->urlGenerator->generate('main_profile_index', [], UrlGeneratorInterface::ABSOLUTE_URL),
25 | ]);
26 |
27 | $this->mailerSender->sendTemplatedEmail($mailerOptions);
28 | }
29 |
30 | public function sendEmailToManager(Order $order): void
31 | {
32 | /** @var User $user */
33 | $user = $order->getOwner();
34 | $mailerOptions = $this->getMailerOptions()
35 | ->setRecipient($this->parameterBag->get('admin_email'))
36 | ->setSubject("Client created order (ID: {$order->getId()})")
37 | ->setHtmlTemplate('front/email/manager/created_order_from_cart.html.twig')
38 | ->setContext([
39 | 'order' => $order,
40 | ]);
41 |
42 | $this->mailerSender->sendTemplatedEmail($mailerOptions);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Utils/Mailer/Sender/ResetUserPasswordEmailSender.php:
--------------------------------------------------------------------------------
1 | urlGenerator->generate('main_profile_index', [], UrlGeneratorInterface::ABSOLUTE_URL);
20 |
21 | $mailerOptions = $this->getMailerOptions()
22 | ->setRecipient($user->getEmail())
23 | ->setSubject('Symfony shop - You password reset request!!')
24 | ->setHtmlTemplate('front/email/security/reset_password.html.twig')
25 | ->setContext($emailContext);
26 |
27 | $this->mailerSender->sendTemplatedEmail($mailerOptions);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Utils/Mailer/Sender/UserLoggedInViaSocialNetworkEmailSender.php:
--------------------------------------------------------------------------------
1 | getMailerOptions()
15 | ->setRecipient($user->getEmail())
16 | ->setSubject('Symfony shop - You new password!')
17 | ->setHtmlTemplate('front/email/client/user_logged_in_via_social_network.html.twig')
18 | ->setContext([
19 | 'user' => $user,
20 | 'plainPassword' => $plainPassword,
21 | 'profileUrl' => $this->urlGenerator->generate('main_profile_index', [], UrlGeneratorInterface::ABSOLUTE_URL),
22 | 'signedUrl' => $verifyEmail['signedUrl'],
23 | 'expiresAtMessageKey' => $verifyEmail['expiresAtMessageKey'],
24 | 'expiresAtMessageData' => $verifyEmail['expiresAtMessageData'],
25 | ]);
26 |
27 | $this->mailerSender->sendTemplatedEmail($mailerOptions);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Utils/Mailer/Sender/UserRegisteredEmailSender.php:
--------------------------------------------------------------------------------
1 | getSignedUrl();
18 | $emailContext['expiresAtMessageKey'] = $signatureComponents->getExpirationMessageKey();
19 | $emailContext['expiresAtMessageData'] = $signatureComponents->getExpirationMessageData();
20 | $emailContext['user'] = $user;
21 | $emailContext['profileUrl'] = $this->urlGenerator->generate('main_profile_index', [], UrlGeneratorInterface::ABSOLUTE_URL);
22 |
23 | $mailerOptions = $this->getMailerOptions()
24 | ->setRecipient($user->getEmail())
25 | ->setSubject('Symfony shop - Please confirm your email!')
26 | ->setHtmlTemplate('front/email/security/confirmation_email.html.twig')
27 | ->setContext($emailContext);
28 |
29 | $this->mailerSender->sendTemplatedEmail($mailerOptions);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Utils/Manager/AbstractBaseManager.php:
--------------------------------------------------------------------------------
1 | getRepository()->find($id);
21 | }
22 |
23 | public function persist(object $entity): void
24 | {
25 | $this->em->persist($entity);
26 | }
27 |
28 | public function flush(): void
29 | {
30 | $this->em->flush();
31 | }
32 |
33 | public function remove(object $entity): void
34 | {
35 | $this->em->remove($entity);
36 | $this->em->flush();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Utils/Manager/CartManager.php:
--------------------------------------------------------------------------------
1 | em->getRepository(Cart::class);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Utils/Manager/CategoryManager.php:
--------------------------------------------------------------------------------
1 | em->getRepository(Category::class);
16 | }
17 |
18 | public function remove(object $entity): void
19 | {
20 | /** @var Category $category */
21 | $category = $entity;
22 |
23 | /** @var Product[] $linkedProducts */
24 | $linkedProducts = $category->getProducts()->getValues();
25 |
26 | $this->em->persist($category);
27 |
28 | $category->setIsDeleted(true);
29 | foreach ($linkedProducts as $linkedProduct) {
30 | $linkedProduct->setIsDeleted(true);
31 | }
32 |
33 | $this->em->flush();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Utils/Manager/UserManager.php:
--------------------------------------------------------------------------------
1 | userPasswordHasher = $userPasswordHasher;
21 |
22 | return $this;
23 | }
24 |
25 | public function getRepository(): EntityRepository
26 | {
27 | return $this->em->getRepository(User::class);
28 | }
29 |
30 | public function encodePassword(User $user, string $plainPassword): void
31 | {
32 | $preparedPassword = trim($plainPassword);
33 |
34 | if (!$preparedPassword) {
35 | throw new EmptyUserPlainPasswordException('Empty user\'s password');
36 | }
37 |
38 | $hashPassword = $this->userPasswordHasher->hashPassword($user, $preparedPassword);
39 | $user->setPassword($hashPassword);
40 | }
41 |
42 | public function remove(object $entity): void
43 | {
44 | /** @var User $user */
45 | $user = $entity;
46 |
47 | $this->em->persist($user);
48 | $user->setIsDeleted(true);
49 | $this->em->flush();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Utils/Oauth2/Vk/VkUser.php:
--------------------------------------------------------------------------------
1 | response['user_id'];
16 | }
17 |
18 | public function getEmail(): ?string
19 | {
20 | return $this->response['email'] ?? null;
21 | }
22 |
23 | public function getFullName(): ?string
24 | {
25 | $fullname = '';
26 |
27 | if ($this->response['first_name']) {
28 | $fullname .= $this->response['first_name'].' ';
29 | }
30 |
31 | if ($this->response['last_name']) {
32 | $fullname .= $this->response['last_name'];
33 | }
34 |
35 | return $fullname;
36 | }
37 |
38 | public function toArray(): array
39 | {
40 | return $this->response;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/templates/admin/_embed/_card/_breadcrumbs.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ sectionTitle }}
4 |
/
5 |
6 | {% if entity and entity.id %}
7 | {{ entity.title|default('Edit') }}
8 | {% else %}
9 | Add new
10 | {% endif %}
11 |
12 |
13 |
14 | Add new
15 |
16 |
--------------------------------------------------------------------------------
/templates/admin/_embed/_card/_form_buttons.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ submitBtn|raw }}
7 |
8 | {% if entity and entity.id %}
9 |
10 | Delete row
11 |
12 | {% endif %}
13 |
--------------------------------------------------------------------------------
/templates/admin/_embed/_card/_header_action.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
{{ sectionTitle }}
3 |
4 | Add new
5 |
6 |
--------------------------------------------------------------------------------
/templates/admin/_embed/_filters/_header.html.twig:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/admin/_embed/_utils/_error_authenticator.html.twig:
--------------------------------------------------------------------------------
1 | {% if error %}
2 |
3 | {{ error.messageKey|trans(error.messageData, 'security') }}
4 |
5 | ×
6 |
7 |
8 | {% endif %}
--------------------------------------------------------------------------------
/templates/admin/_embed/_utils/_flash_message.html.twig:
--------------------------------------------------------------------------------
1 | {% for label, messages in app.flashes(['success','warning','info', 'danger']) %}
2 | {% for message in messages %}
3 |
4 | {{ message }}
5 |
6 | ×
7 |
8 |
9 | {% endfor %}
10 | {% endfor %}
11 |
--------------------------------------------------------------------------------
/templates/admin/_embed/_utils/_modal.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
{{ modalText }}
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/templates/admin/base.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}Admin Panel - RankedChoice{% endblock %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ encore_entry_link_tags('section-admin') }}
20 | {% block stylesheets %}{% endblock %}
21 |
22 |
23 | {% block layout_body %}{% endblock %}
24 |
25 | {{ encore_entry_script_tags('section-admin') }}
26 |
--------------------------------------------------------------------------------
/templates/admin/category/list.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'admin/layout/layout_main.html.twig' %}
2 |
3 | {% block title_detailed %}All Categories{% endblock %}
4 |
5 | {% block body %}
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 | Id
20 | Title
21 | Slug
22 |
23 |
24 |
25 |
26 | {% for category in categories %}
27 |
28 | {{ category.id }}
29 | {{ category.title }}
30 | {{ category.slug }}
31 |
32 | Edit
33 |
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 |
41 | {% endblock %}
42 |
--------------------------------------------------------------------------------
/templates/admin/layout/layout_empty_page.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'admin/base.html.twig' %}
2 |
3 | {% block layout_body %}
4 |
5 |
6 |
7 | {% block body %}{% endblock %}
8 |
9 |
10 | {% block javascripts %}{% endblock %}
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/templates/admin/layout/layout_main.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'admin/base.html.twig' %}
2 |
3 | {% block title %}
4 | Admin Panel - {% block title_detailed %} | RankedChoice{% endblock %}
5 | {% endblock %}
6 |
7 | {% block layout_body %}
8 |
9 |
10 |
11 | {{ include('admin/_embed/_main/_sidebar.html.twig') }}
12 |
13 |
14 |
15 |
16 | {{ include('admin/_embed/_main/_topbar.html.twig') }}
17 |
18 |
19 | {% include 'admin/_embed/_utils/_flash_message.html.twig' %}
20 |
21 | {% block body %}{% endblock %}
22 |
23 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | {% block javascripts %}{% endblock %}
42 |
43 | {% endblock %}
44 |
--------------------------------------------------------------------------------
/templates/admin/order/_embed/_table_actions_btn.html.twig:
--------------------------------------------------------------------------------
1 |
2 | Apply
3 |
4 | Reset filters
--------------------------------------------------------------------------------
/templates/admin/pages/dashboard.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'admin/layout/layout_main.html.twig' %}
2 |
3 | {% block title_detailed %}Dashboard{% endblock %}
4 |
5 | {% block body %}
6 |
7 |
8 |
9 |
Dashboard
10 |
11 |
12 |
13 |
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/templates/admin/product/_embed/_table_actions_btn.html.twig:
--------------------------------------------------------------------------------
1 |
2 | Apply
3 |
4 | Reset filters
--------------------------------------------------------------------------------
/templates/bundles/TwigBundle/Exception/error.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/base.html.twig' %}
2 |
3 | {% block body_id 'error' %}
4 |
5 | {% block body %}
6 |
22 | {% endblock %}
--------------------------------------------------------------------------------
/templates/front/_embed/_header.html.twig:
--------------------------------------------------------------------------------
1 | {% include 'front/_embed/_menu/_mobile_menu.html.twig' %}
2 |
3 |
--------------------------------------------------------------------------------
/templates/front/_embed/_menu/_login_via_social_network.html.twig:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/front/_embed/_menu/_menu_cart.html.twig:
--------------------------------------------------------------------------------
1 | {{ encore_entry_link_tags('appFrontMenuCart') }}
2 |
3 |
20 | {{ encore_entry_script_tags('appFrontMenuCart') }}
--------------------------------------------------------------------------------
/templates/front/_embed/_menu/_menu_nav_item.twig:
--------------------------------------------------------------------------------
1 | {% for category in nav_categories %}
2 |
7 | {% endfor %}
8 |
--------------------------------------------------------------------------------
/templates/front/_embed/_utils/_error_authenticator.html.twig:
--------------------------------------------------------------------------------
1 | {% if error %}
2 |
3 | {{ error.messageKey|trans(error.messageData, 'security') }}
4 |
5 | ×
6 |
7 |
8 | {% endif %}
--------------------------------------------------------------------------------
/templates/front/_embed/_utils/_flash_message.html.twig:
--------------------------------------------------------------------------------
1 | {% for label, messages in app.flashes(['success','warning','info', 'danger']) %}
2 | {% for message in messages %}
3 |
4 | {{ message }}
5 |
6 | ×
7 |
8 |
9 | {% endfor %}
10 | {% endfor %}
--------------------------------------------------------------------------------
/templates/front/_embed/_utils/_social_network_link_unlink_btn.html.twig:
--------------------------------------------------------------------------------
1 | {% if socialNameId %}
2 |
3 | {{ 'personal_account.social_group.link'|trans }}
4 |
5 |
6 | {{ 'personal_account.social_group.unlink'|trans }}
7 |
8 | {% else %}
9 | {% if 'github' != socialName %}
10 |
11 | {{ 'personal_account.social_group.link'|trans }}
12 |
13 | {% else %}
14 |
15 | {{ 'personal_account.social_group.link'|trans }}
16 |
17 | {% endif %}
18 |
19 | {{ 'personal_account.social_group.unlink'|trans }}
20 |
21 | {% endif %}
22 |
--------------------------------------------------------------------------------
/templates/front/email/base.html.twig:
--------------------------------------------------------------------------------
1 | {% apply inky_to_html|inline_css(source('@public' ~ asset('build/email.css'))) %}
2 |
3 |
12 |
13 | {% block content %}{% endblock %}
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 | {% endapply %}
--------------------------------------------------------------------------------
/templates/front/email/client/user_logged_in_via_social_network.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/email/base.html.twig' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
8 | Symfony shop - Your new password!
9 |
10 |
11 |
12 |
13 |
14 |
15 | Hello, {{ user.fullName }}!
16 |
17 | To login into your profile use this new password
18 | {{ plainPassword }}
19 |
20 |
21 |
22 |
23 |
24 |
25 | Please confirm your email address by clicking the following link:
26 | Confirm my Email .
27 | This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}.
28 |
29 |
30 |
31 |
32 |
33 | Visit our profile.
34 |
35 |
36 | {% endblock %}
37 |
--------------------------------------------------------------------------------
/templates/front/email/manager/created_order_from_cart.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/email/base.html.twig' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
8 | Client created order (ID: {{ order.id }})
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Order info:
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | #
26 | Title
27 | Quantity
28 | Price
29 |
30 |
31 |
32 | {% for orderProduct in order.orderProducts %}
33 |
34 | {{ loop.index }}
35 | {{ orderProduct.product.title }}
36 | {{ orderProduct.quantity }}
37 | {{ orderProduct.product.price }}
38 |
39 | {% endfor %}
40 |
41 |
42 |
43 |
44 | Total price: {{ order.totalPrice }}
45 |
46 |
47 |
48 | {% endblock %}
49 |
--------------------------------------------------------------------------------
/templates/front/email/security/confirmation_email.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/email/base.html.twig' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
8 | Hi! Please confirm your email!
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Please confirm your email address by clicking the following link:
17 | Confirm my Email .
18 | This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}.
19 |
20 |
21 |
22 |
23 | Visit our profile.
24 |
25 |
26 | {% endblock %}
--------------------------------------------------------------------------------
/templates/front/email/security/reset_password.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/email/base.html.twig' %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 |
8 | Your password reset request
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | To reset your password, please visit the following link
17 | {{ url('main_reset_password', {token: resetToken.token}) }}
18 | This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
19 |
20 |
21 | {% endblock %}
--------------------------------------------------------------------------------
/templates/front/reset_password/check_email.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/base.html.twig' %}
2 |
3 | {% block title %}Password Reset Email Sent{% endblock %}
4 |
5 | {% block body %}
6 |
7 |
8 |
9 |
10 |
11 |
Reset your password
12 |
13 | If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.
14 | This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
15 |
16 |
If you don't receive an email please check your spam folder or try again .
17 |
18 |
19 |
20 |
21 |
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/templates/front/reset_password/request.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/base.html.twig' %}
2 |
3 | {% block title %}Reset your password{% endblock %}
4 |
5 | {% block body %}
6 |
7 |
8 |
9 |
10 |
11 |
Reset your password
12 | {% for flashError in app.flashes('reset_password_error') %}
13 |
{{ flashError }}
14 | {% endfor %}
15 |
16 | {{ form_start(requestForm) }}
17 |
18 | {{ form_label(requestForm.email) }}
19 | {{ form_widget(requestForm.email) }}
20 |
21 | Enter your email address and we will send you a
22 | link to reset your password.
23 |
24 |
25 |
28 | {{ form_end(requestForm) }}
29 |
30 |
31 |
32 |
33 |
34 | {% endblock %}
35 |
--------------------------------------------------------------------------------
/templates/front/reset_password/reset.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'front/base.html.twig' %}
2 |
3 | {% block title %}Reset your password{% endblock %}
4 |
5 | {% block body %}
6 |
7 |
8 |
9 |
10 |
11 |
Reset your password
12 |
13 | {% for flashError in app.flashes('reset_password_error') %}
14 |
{{ flashError }}
15 | {% endfor %}
16 |
17 | {{ form_start(resetForm) }}
18 |
19 | {{ form_label(resetForm.plainPassword.first) }}
20 | {{ form_widget(resetForm.plainPassword.first) }}
21 | {{ form_errors(resetForm.plainPassword.first) }}
22 |
23 |
24 | {{ form_label(resetForm.plainPassword.second) }}
25 | {{ form_widget(resetForm.plainPassword.second) }}
26 | {{ form_errors(resetForm.plainPassword.second) }}
27 |
28 |
31 | {{ form_end(resetForm) }}
32 |
33 |
34 |
35 |
36 |
37 | {% endblock %}
38 |
--------------------------------------------------------------------------------
/templates/front/robots.txt.twig:
--------------------------------------------------------------------------------
1 | # For All bots
2 | User-agent: *
3 | Disallow: /
4 | Allow: /$
5 |
6 | # For Yandex bots
7 | User-agent: Yandex
8 | Disallow: /
9 | Allow: /$
10 |
11 | # For Google bots
12 | User-agent: GoogleBot
13 | Disallow: /
14 | Allow: /$
15 |
16 | Sitemap: {{ sitemap }}
17 | {# Для рабочего сайта #}
18 | {## For All bots#}
19 | {#User-agent: *#}
20 | {#Disallow: */admin/*#}
21 | {#Disallow: /admin/*#}
22 | {#Disallow: /bundles/*#}
23 | {#Disallow: /build/*#}
24 | {#Disallow: /upload/*#}
25 | {#Disallow: /admin/*?*#}
26 | {#Disallow: */admin/*?*#}
27 | {#Disallow: *_filter_form#}
28 | {#{% for activeCategory in activeCategories%}#}
29 | {#Allow: {{ activeCategory }}#}
30 | {#{% endfor %}#}
31 |
32 | {## For Yandex bots#}
33 | {#User-agent: Yandex#}
34 | {#Disallow: */admin/*#}
35 | {#Disallow: /admin/*#}
36 | {#Disallow: /bundles/*#}
37 | {#Disallow: /build/*#}
38 | {#Disallow: /upload/*#}
39 | {#Disallow: /admin/*?*#}
40 | {#Disallow: */admin/*?*#}
41 | {#Disallow: *_filter_form#}
42 | {#{% for activeCategory in activeCategories%}#}
43 | {#Allow: {{ activeCategory }}#}
44 | {#{% endfor %}#}
45 |
46 | {## For Google bots#}
47 | {#User-agent: GoogleBot#}
48 | {#Disallow: */admin/*#}
49 | {#Disallow: /admin/*#}
50 | {#Disallow: /bundles/*#}
51 | {#Disallow: /build/*#}
52 | {#Disallow: /upload/*#}
53 | {#Disallow: /admin/*?*#}
54 | {#Disallow: */admin/*?*#}
55 | {#Disallow: *_filter_form#}
56 | {#{% for activeCategory in activeCategories%}#}
57 | {#Allow: {{ activeCategory }}#}
58 | {#{% endfor %}#}
--------------------------------------------------------------------------------
/templates/front/sitemap.xml.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ data.loc }}
6 | {{ data.lastmod }}
7 | {{ data.changefreq }}
8 | {{ data.priority }}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/Functional/ApiPlatform/ResourceTestUtils.php:
--------------------------------------------------------------------------------
1 | 'application/ld+json',
14 | 'CONTENT_TYPE' => 'application/json',
15 | ];
16 |
17 | protected const REQUEST_HEADERS_PATCH = [
18 | 'HTTP_ACCEPT' => 'application/ld+json',
19 | 'CONTENT_TYPE' => 'application/merge-patch+json',
20 | ];
21 |
22 | protected function getResponseDecodeContent(AbstractBrowser $client)
23 | {
24 | return json_decode($client->getResponse()->getContent());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/TestUtils/Fixtures/UserFixtures.php:
--------------------------------------------------------------------------------
1 | bootEnv(dirname(__DIR__).'/.env');
14 | }
15 |
--------------------------------------------------------------------------------
/translations/LexikFormFilterBundle.en.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Is Published
7 | Опубликовано
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/translations/LexikFormFilterBundle.en.yml:
--------------------------------------------------------------------------------
1 | 'Is Published': Опубликовано
2 |
--------------------------------------------------------------------------------
/translations/LexikFormFilterBundle.ru.xlf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Is Published
7 | Is Published
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/translations/LexikFormFilterBundle.ru.yml:
--------------------------------------------------------------------------------
1 | 'Is Published': 'Is Published'
2 |
--------------------------------------------------------------------------------
/translations/validators.en.yml:
--------------------------------------------------------------------------------
1 | 'There is already an account with this email': 'There is already an account with this email'
2 | 'The password fields must match.': 'The password fields must match.'
3 | 'Please enter a title': 'Please enter a title'
4 | 'Please select user': 'Please select user'
5 | 'Please select status': 'Please select status'
6 | 'Please enter a price': 'Please enter a price'
7 | 'Please upload a valid image (*.jpg or *.png)': 'Please upload a valid image (*.jpg or *.png)'
8 | 'Please indicate a quantity': 'Please indicate a quantity'
9 | 'Please select a category': 'Please select a category'
10 | 'Price cannot be less than or equal to zero.': 'Price cannot be less than or equal to zero.'
11 |
--------------------------------------------------------------------------------