├── .dockerignore ├── .editorconfig ├── .env ├── .env.test ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ ├── frankenphp.yml │ ├── lint.yml │ ├── mariadb.yml │ ├── mysql.yml │ ├── ossar-analysis.yml │ └── postgres.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── .prettierrc ├── .travis.yml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── assets ├── app.js ├── controllers │ └── .gitignore ├── js │ ├── admin │ │ ├── admin.js │ │ ├── menu-sorting.js │ │ └── settings.js │ ├── auth │ │ ├── _resend.js │ │ └── auth.js │ ├── bottom-bar.js │ ├── city.js │ ├── common │ │ └── _delete_button.js │ ├── ekko-lightbox.js │ ├── map.js │ ├── page.js │ ├── photo.js │ ├── select2.js │ ├── slugger.js │ └── user │ │ ├── _property.js │ │ ├── _sidebar.js │ │ ├── password │ │ ├── _update_password.js │ │ ├── _validate_password.js │ │ └── password.js │ │ ├── two_factor │ │ └── google_authenticator.js │ │ └── user.js ├── psd │ └── no-photo.psd └── styles │ ├── _variables.scss │ ├── admin.scss │ ├── app.scss │ ├── bottom-bar.scss │ ├── city.scss │ ├── detail.scss │ ├── ekko-lightbox.scss │ ├── photo.scss │ ├── select2.scss │ └── user │ └── security.scss ├── babel.config.js ├── bin ├── console └── phpunit ├── compose.override.yaml ├── compose.prod.yaml ├── compose.yaml ├── composer.json ├── composer.lock ├── config ├── bundles.php ├── packages │ ├── assets.yaml │ ├── cache.yaml │ ├── debug.yaml │ ├── dev │ │ └── easy_log_handler.yaml │ ├── doctrine.yaml │ ├── doctrine_migrations.yaml │ ├── framework.yaml │ ├── knp_paginator.yaml │ ├── lock.yaml │ ├── mailer.yaml │ ├── messenger.yaml │ ├── monolog.yaml │ ├── notifier.yaml │ ├── prod │ │ ├── deprecations.yaml │ │ └── webpack_encore.yaml │ ├── rate_limiter.yaml │ ├── routing.yaml │ ├── scheb_2fa.yaml │ ├── security.yaml │ ├── test │ │ ├── rate_limiter.yaml │ │ ├── validator.yaml │ │ └── webpack_encore.yaml │ ├── translation.yaml │ ├── twig.yaml │ ├── validator.yaml │ ├── web_profiler.yaml │ └── webpack_encore.yaml ├── preload.php ├── routes.yaml ├── routes │ ├── attributes.yaml │ ├── framework.yaml │ ├── scheb_2fa.yaml │ ├── security.yaml │ └── web_profiler.yaml ├── secrets │ └── prod │ │ └── .gitignore └── services.yaml ├── docs ├── docker.md └── images │ └── screenshot.png ├── frankenphp ├── Caddyfile ├── conf.d │ ├── 10-app.ini │ ├── 20-app.dev.ini │ └── 20-app.prod.ini ├── docker-entrypoint.sh └── worker.Caddyfile ├── migrations ├── Version20200328162849.php ├── Version20200705110619.php ├── Version20210104185420.php ├── Version20210106154655.php ├── Version20210106191712.php ├── Version20210106234742.php ├── Version20210107000119.php ├── Version20210215083338.php ├── Version20210215173522.php ├── Version20210815075052.php ├── Version20210817062054.php ├── Version20220320064544.php ├── Version20220325072850.php ├── Version20220824075712.php ├── Version20230304072206.php └── Version20230501134351.php ├── package.json ├── phpstan.dist.neon ├── phpunit.xml.dist ├── public ├── .htaccess ├── build │ ├── css │ │ ├── admin.832d2af4.css │ │ ├── app.79e704be.css │ │ ├── bottom-bar.38e93f4e.css │ │ ├── city.830c14fb.css │ │ ├── detail.3d2d5214.css │ │ ├── ekko-lightbox.74df6b0a.css │ │ ├── photo.a213acff.css │ │ ├── security.64e38d75.css │ │ └── select2.454a8588.css │ ├── entrypoints.json │ ├── fonts │ │ ├── fa-regular-400.491974d1.ttf │ │ ├── fa-regular-400.77206a6b.eot │ │ ├── fa-regular-400.7a333762.woff2 │ │ ├── fa-regular-400.bb58e57c.woff │ │ ├── fa-solid-900.1551f4f6.woff2 │ │ ├── fa-solid-900.9bbb245e.eot │ │ ├── fa-solid-900.be9ee23c.ttf │ │ └── fa-solid-900.eeccf4f6.woff │ ├── images │ │ ├── fa-regular-400.4689f52c.svg │ │ └── fa-solid-900.7a8b4f13.svg │ ├── js │ │ ├── admin.32201fb1.js │ │ ├── admin.32201fb1.js.LICENSE.txt │ │ ├── app.2ff2670c.js │ │ ├── app.2ff2670c.js.LICENSE.txt │ │ ├── auth.87b05c2f.js │ │ ├── bottom-bar.04c1ac09.js │ │ ├── city.b1325671.js │ │ ├── ekko-lightbox.11ef94c0.js │ │ ├── ekko-lightbox.11ef94c0.js.LICENSE.txt │ │ ├── google_authenticator.5fceab80.js │ │ ├── map.7d2feb0e.js │ │ ├── menu-sorting.287ae70d.js │ │ ├── page.3f48afaa.js │ │ ├── password.330d6897.js │ │ ├── photo.03a89e47.js │ │ ├── photo.03a89e47.js.LICENSE.txt │ │ ├── select2.98bad6eb.js │ │ ├── select2.98bad6eb.js.LICENSE.txt │ │ ├── settings.e36efd93.js │ │ ├── slugger.c09f068e.js │ │ └── user.3e9dfb9b.js │ ├── manifest.json │ └── runtime.8ab7f0c8.js ├── favicon.ico ├── images │ ├── bg-1.5.jpg │ ├── bg.jpg │ ├── icons │ │ ├── apple-touch-icon.png │ │ ├── chrome-touch-icon-192x192.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-256x256.png │ │ ├── icon-512x512.png │ │ └── icon.svg │ ├── languages │ │ ├── bg.png │ │ ├── en.png │ │ ├── hu.png │ │ ├── nl.png │ │ └── ru.png │ ├── logo-square.png │ ├── no-photo.png │ ├── residence-logo.png │ └── transparent.png ├── index.php ├── libs │ └── summernote │ │ └── 0.8.20 │ │ ├── font │ │ ├── summernote.eot │ │ ├── summernote.ttf │ │ ├── summernote.woff │ │ └── summernote.woff2 │ │ ├── summernote-bs4.css │ │ └── summernote-bs4.min.js ├── manifest.json ├── robots.txt └── uploads │ └── images │ ├── full │ └── demo │ │ ├── 1.jpeg │ │ ├── 10.jpeg │ │ ├── 11.jpeg │ │ ├── 12.jpeg │ │ ├── 13.jpeg │ │ ├── 2.jpeg │ │ ├── 3.jpeg │ │ ├── 4.jpeg │ │ ├── 5.jpeg │ │ ├── 6.jpeg │ │ ├── 7.jpeg │ │ ├── 8.jpeg │ │ └── 9.jpeg │ ├── large │ └── demo │ │ ├── 1.jpeg │ │ ├── 10.jpeg │ │ ├── 11.jpeg │ │ ├── 12.jpeg │ │ ├── 13.jpeg │ │ ├── 2.jpeg │ │ ├── 3.jpeg │ │ ├── 4.jpeg │ │ ├── 5.jpeg │ │ ├── 6.jpeg │ │ ├── 7.jpeg │ │ ├── 8.jpeg │ │ └── 9.jpeg │ ├── medium │ └── demo │ │ ├── 1.jpeg │ │ ├── 10.jpeg │ │ ├── 11.jpeg │ │ ├── 12.jpeg │ │ ├── 13.jpeg │ │ ├── 2.jpeg │ │ ├── 3.jpeg │ │ ├── 4.jpeg │ │ ├── 5.jpeg │ │ ├── 6.jpeg │ │ ├── 7.jpeg │ │ ├── 8.jpeg │ │ └── 9.jpeg │ └── small │ └── demo │ ├── 1.jpeg │ ├── 10.jpeg │ ├── 11.jpeg │ ├── 12.jpeg │ ├── 13.jpeg │ ├── 2.jpeg │ ├── 3.jpeg │ ├── 4.jpeg │ ├── 5.jpeg │ ├── 6.jpeg │ ├── 7.jpeg │ ├── 8.jpeg │ └── 9.jpeg ├── rector.php ├── src ├── Command │ ├── InstallCommand.php │ └── ListUsersCommand.php ├── Controller │ ├── AbstractPhotoController.php │ ├── Admin │ │ ├── CategoryController.php │ │ ├── CityController.php │ │ ├── CurrencyController.php │ │ ├── DashboardController.php │ │ ├── DealTypeController.php │ │ ├── DistrictController.php │ │ ├── FeatureController.php │ │ ├── MenuController.php │ │ ├── MetroController.php │ │ ├── NeighborhoodController.php │ │ ├── PageController.php │ │ ├── PhotoController.php │ │ ├── PropertyController.php │ │ ├── Settings │ │ │ ├── AbstractSettingsController.php │ │ │ ├── HeaderSettingsController.php │ │ │ ├── MainSettingsController.php │ │ │ └── MapSettingsController.php │ │ └── UserController.php │ ├── Ajax │ │ ├── Admin │ │ │ ├── MenuController.php │ │ │ ├── PhotoController.php │ │ │ └── SettingsController.php │ │ ├── AjaxController.php │ │ ├── Auth │ │ │ └── ResendVerificationController.php │ │ ├── CityController.php │ │ └── User │ │ │ ├── PhotoController.php │ │ │ ├── PropertyController.php │ │ │ └── Security │ │ │ ├── GoogleAuthenticatorController.php │ │ │ └── PasswordController.php │ ├── Auth │ │ ├── AuthController.php │ │ ├── LoginController.php │ │ ├── RegisterController.php │ │ ├── ResetPasswordController.php │ │ ├── TwoFactor │ │ │ └── EnterAuthCodeController.php │ │ └── VerificationController.php │ ├── BaseController.php │ ├── CityController.php │ ├── PageController.php │ ├── PropertyController.php │ ├── SitemapController.php │ ├── Traits │ │ └── MenuTrait.php │ └── User │ │ ├── PhotoController.php │ │ ├── ProfileController.php │ │ ├── PropertyController.php │ │ └── SecurityController.php ├── DataFixtures │ ├── AppFixtures.php │ ├── CategoryFixtures.php │ ├── CityFixtures.php │ ├── CurrencyFixtures.php │ ├── DealTypeFixtures.php │ ├── DistrictFixtures.php │ ├── FeatureFixtures.php │ ├── MenuFixtures.php │ ├── MetroFixtures.php │ ├── NeighborhoodFixtures.php │ ├── PageFixtures.php │ ├── PhotoFixtures.php │ ├── PropertyFixtures.php │ └── UserFixtures.php ├── Dto │ └── FeedbackDto.php ├── Entity │ ├── Category.php │ ├── City.php │ ├── Currency.php │ ├── DealType.php │ ├── District.php │ ├── Feature.php │ ├── Menu.php │ ├── Metro.php │ ├── Neighborhood.php │ ├── Page.php │ ├── Photo.php │ ├── Profile.php │ ├── Property.php │ ├── PropertyDescription.php │ ├── Settings.php │ ├── Traits │ │ ├── CityTrait.php │ │ ├── EntityIdTrait.php │ │ ├── EntityLocationTrait.php │ │ ├── EntityMetaTrait.php │ │ ├── EntityNameTrait.php │ │ ├── EntityTimestampableTrait.php │ │ ├── PropertyTrait.php │ │ └── TwoFactorTrait.php │ └── User.php ├── EventListener │ └── ExceptionListener.php ├── EventSubscriber │ └── ControllerSubscriber.php ├── Form │ ├── EventSubscriber │ │ ├── AddAgentFieldSubscriber.php │ │ ├── AddDistrictFieldSubscriber.php │ │ ├── AddMetroFieldSubscriber.php │ │ ├── AddNeighborhoodFieldSubscriber.php │ │ ├── UpdateDistrictFieldSubscriber.php │ │ ├── UpdateMetroFieldSubscriber.php │ │ └── UpdateNeighborhoodFieldSubscriber.php │ └── Type │ │ ├── CategoryType.php │ │ ├── CityType.php │ │ ├── CurrencyType.php │ │ ├── DealTypeType.php │ │ ├── DistrictType.php │ │ ├── FeatureType.php │ │ ├── FeedbackType.php │ │ ├── FilterSettingsType.php │ │ ├── LanguageType.php │ │ ├── LoginFormType.php │ │ ├── MainSettingsType.php │ │ ├── MapSettingsType.php │ │ ├── MenuType.php │ │ ├── MetroType.php │ │ ├── NeighborhoodType.php │ │ ├── PageType.php │ │ ├── PasswordType.php │ │ ├── ProfileType.php │ │ ├── PropertyDescriptionType.php │ │ ├── PropertyType.php │ │ ├── RegistrationFormType.php │ │ ├── UserEmailType.php │ │ └── UserType.php ├── Kernel.php ├── Mailer │ └── Mailer.php ├── Message │ ├── DeletePhotos.php │ ├── SendEmailConfirmationLink.php │ ├── SendFeedback.php │ └── SendResetPasswordLink.php ├── MessageHandler │ ├── DeletePhotosHandler.php │ ├── SendEmailConfirmationLinkHandler.php │ ├── SendFeedbackHandler.php │ └── SendResetPasswordLinkHandler.php ├── Middleware │ ├── ThrottleRequests.php │ └── VerifyCsrfToken.php ├── Repository │ ├── CategoryRepository.php │ ├── CityRepository.php │ ├── CurrencyRepository.php │ ├── DealTypeRepository.php │ ├── DistrictRepository.php │ ├── FeatureRepository.php │ ├── FilterRepository.php │ ├── MenuRepository.php │ ├── MetroRepository.php │ ├── NeighborhoodRepository.php │ ├── PageRepository.php │ ├── PhotoRepository.php │ ├── ProfileRepository.php │ ├── PropertyDescriptionRepository.php │ ├── PropertyRepository.php │ ├── ResettingRepository.php │ ├── SettingsRepository.php │ ├── SimilarRepository.php │ ├── UserPropertyRepository.php │ └── UserRepository.php ├── Security │ ├── RegistrationFormAuthenticator.php │ └── Voter │ │ ├── PropertyVoter.php │ │ └── UserVoter.php ├── Service │ ├── AbstractService.php │ ├── Admin │ │ ├── CategoryService.php │ │ ├── CityService.php │ │ ├── DashboardService.php │ │ ├── DealTypeService.php │ │ ├── PageService.php │ │ ├── PropertyService.php │ │ ├── SettingsService.php │ │ └── UserService.php │ ├── Auth │ │ ├── EmailVerifier.php │ │ └── ResettingService.php │ ├── Cache │ │ ├── ClearCache.php │ │ ├── GetCache.php │ │ └── UserDataCache.php │ ├── CityService.php │ ├── FileUploader.php │ ├── URLService.php │ └── User │ │ ├── GoogleAuthenticatorAdapter.php │ │ ├── GoogleAuthenticatorService.php │ │ ├── PasswordService.php │ │ └── PropertyService.php ├── Transformer │ ├── PropertyTransformer.php │ ├── RequestToArrayTransformer.php │ └── UserTransformer.php ├── Twig │ └── AppExtension.php ├── Utils │ ├── HtmlHelper.php │ ├── Slugger.php │ ├── SluggerInterface.php │ ├── TokenGenerator.php │ ├── TokenGeneratorInterface.php │ └── UserFormDataSelector.php └── Validator │ ├── ConfirmPassword.php │ ├── ConfirmPasswordValidator.php │ ├── PhotoRequirements.php │ ├── RegisteredUser.php │ └── RegisteredUserValidator.php ├── symfony.lock ├── templates ├── admin │ ├── category │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── city │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── currency │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── dashboard │ │ └── index.html.twig │ ├── deal_type │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── district │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── feature │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── layout │ │ ├── base.html.twig │ │ └── partials │ │ │ ├── _navbar.html.twig │ │ │ └── _sidebar.html.twig │ ├── menu │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── metro │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── neighborhood │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── page │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── photo │ │ ├── _delete_form.html.twig │ │ └── edit.html.twig │ ├── property │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig │ ├── settings │ │ ├── header_settings.html.twig │ │ ├── main_settings.html.twig │ │ ├── map_settings.html.twig │ │ └── partials │ │ │ ├── _filter_settings_form.html.twig │ │ │ ├── _header_settings_form.html.twig │ │ │ └── _nav_tabs.html.twig │ └── user │ │ ├── _action_form.html.twig │ │ ├── edit.html.twig │ │ ├── index.html.twig │ │ └── new.html.twig ├── auth │ ├── login.html.twig │ ├── passwords │ │ ├── password_change.html.twig │ │ └── password_reset.html.twig │ ├── register.html.twig │ └── two_factor │ │ └── two_factor_form.html.twig ├── bundles │ └── TwigBundle │ │ └── Exception │ │ ├── error404.html.twig │ │ └── error429.html.twig ├── common │ ├── _delete_button.html.twig │ └── _flash_messages.html.twig ├── emails │ ├── confirmation_email.html.twig │ └── reset.txt.twig ├── layout │ ├── base.html.twig │ └── partials │ │ ├── _locale_switcher.html.twig │ │ └── _navbar.html.twig ├── page │ ├── partials │ │ └── _contact_form.html.twig │ └── show.html.twig ├── property │ ├── index.html.twig │ ├── map.html.twig │ ├── partials │ │ ├── _contact_widget.html.twig │ │ ├── _overview.html.twig │ │ ├── _search_form.html.twig │ │ ├── _search_widget.html.twig │ │ ├── _similar_properties.html.twig │ │ └── _slider.html.twig │ └── show.html.twig ├── sitemap │ ├── cities.xml.twig │ ├── properties.xml.twig │ └── sitemap.xml.twig └── user │ ├── common │ ├── _floating_action_button.html.twig │ └── _sidebar.html.twig │ ├── photo │ ├── _delete_form.html.twig │ └── edit.html.twig │ ├── profile │ └── profile.html.twig │ ├── property │ ├── edit.html.twig │ ├── index.html.twig │ ├── new.html.twig │ └── partials │ │ └── _properties.html.twig │ └── security │ ├── _change_password.html.twig │ ├── _google_authenticator.html.twig │ └── security.html.twig ├── tests ├── E2E │ ├── Auth │ │ └── AuthTest.php │ ├── HomepageTest.php │ └── User │ │ ├── GoogleAuthenticatorTest.php │ │ └── PasswordChangeTest.php ├── Functional │ └── Controller │ │ ├── Admin │ │ ├── AbstractLocationControllerTest.php │ │ ├── CategoryControllerTest.php │ │ ├── CityControllerTest.php │ │ ├── CurrencyControllerTest.php │ │ ├── DashboardControllerTest.php │ │ ├── DealTypeControllerTest.php │ │ ├── DistrictControllerTest.php │ │ ├── FeatureControllerTest.php │ │ ├── MenuControllerTest.php │ │ ├── MetroControllerTest.php │ │ ├── NeighborhoodControllerTest.php │ │ ├── PageControllerTest.php │ │ ├── PropertyControllerTest.php │ │ ├── Settings │ │ │ ├── HeaderSettingsControllerTest.php │ │ │ ├── MainSettingsControllerTest.php │ │ │ └── MapSettingsControllerTest.php │ │ └── UserControllerTest.php │ │ ├── Ajax │ │ └── CityControllerTest.php │ │ ├── Auth │ │ ├── RegisterControllerTest.php │ │ ├── ResendVerificationControllerTest.php │ │ ├── ResettingControllerTest.php │ │ └── VerificationControllerTest.php │ │ ├── DefaultControllerTest.php │ │ ├── PageControllerTest.php │ │ ├── PropertyControllerTest.php │ │ ├── SitemapControllerTest.php │ │ └── User │ │ ├── PhotoControllerTest.php │ │ ├── ProfileControllerTest.php │ │ ├── PropertyControllerTest.php │ │ └── Security │ │ ├── GoogleAuthenticatorControllerTest.php │ │ └── PasswordControllerTest.php ├── Helper │ ├── PantherTestHelper.php │ └── WebTestHelper.php ├── Integration │ ├── Command │ │ └── ListUsersCommandTest.php │ └── Transformer │ │ ├── RequestToArrayTransformerTest.php │ │ └── UserTransformerTest.php ├── Unit │ └── Utils │ │ ├── HtmlHelperTest.php │ │ └── SluggerTest.php └── bootstrap.php ├── translations ├── messages.bg.xlf ├── messages.en.xlf ├── messages.hu.xlf ├── messages.nl.xlf ├── messages.ru.xlf ├── validators.en.xlf ├── validators.hu.xlf └── validators.ru.xlf ├── webpack.config.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/*.php~ 4 | **/*.dist.php 5 | **/*.dist 6 | **/*.cache 7 | **/._* 8 | **/.dockerignore 9 | **/.DS_Store 10 | **/.git/ 11 | **/.gitattributes 12 | **/.gitignore 13 | **/.gitmodules 14 | **/compose.*.yaml 15 | **/compose.*.yml 16 | **/compose.yaml 17 | **/compose.yml 18 | **/docker-compose.*.yaml 19 | **/docker-compose.*.yml 20 | **/docker-compose.yaml 21 | **/docker-compose.yml 22 | **/Dockerfile 23 | **/Thumbs.db 24 | .github/ 25 | docs/ 26 | public/bundles/ 27 | tests/ 28 | var/ 29 | vendor/ 30 | .editorconfig 31 | .env.*.local 32 | .env.local 33 | .env.local.php 34 | .env.test 35 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; top-most EditorConfig file 2 | root = true 3 | 4 | ; Unix-style newlines 5 | [*] 6 | charset = utf-8 7 | end_of_line = LF 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{php,html,twig}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.php] 16 | ij_php_comma_after_last_array_element = true 17 | ij_php_blank_lines_before_return_statement = 1 18 | ij_php_blank_lines_after_opening_tag = 1 19 | ij_php_space_after_type_cast = true 20 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_SECRET='$ecretf0rt3st' 4 | SYMFONY_DEPRECATIONS_HELPER=999999 5 | PANTHER_APP_ENV=test 6 | PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots 7 | DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.35&charset=utf8mb4" 8 | 9 | PANTHER_CHROME_ARGUMENTS="--disable-dev-shm-usage" 10 | PANTHER_NO_SANDBOX="1" 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@babel/eslint-parser", 3 | "env": { 4 | "node": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 2019, 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "comma-dangle": [ 12 | "error", 13 | { 14 | "arrays": "never", 15 | "objects": "never", 16 | "imports": "never", 17 | "exports": "never", 18 | "functions": "never" 19 | } 20 | ], 21 | "eqeqeq": ["error", "always"], 22 | "linebreak-style": ["off", "unix"], 23 | "newline-before-return": "error", 24 | "no-console": "error", 25 | "no-const-assign": "error", 26 | "no-extra-semi": "error", 27 | "no-lonely-if": "error", 28 | "no-unexpected-multiline": "error", 29 | "no-unused-vars": "error", 30 | "no-unreachable": "error", 31 | "no-var": "error", 32 | "object-curly-spacing": "off", 33 | "operator-assignment": "error", 34 | "prefer-destructuring": "error", 35 | "quotes": ["error", "single"], 36 | "semi": ["error", "always"] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.conf text eol=lf 4 | *.html text eol=lf 5 | *.ini text eol=lf 6 | *.js text eol=lf 7 | *.json text eol=lf 8 | *.md text eol=lf 9 | *.php text eol=lf 10 | *.sh text eol=lf 11 | *.yaml text eol=lf 12 | *.yml text eol=lf 13 | bin/console text eol=lf 14 | composer.lock text eol=lf merge=ours 15 | 16 | *.ico binary 17 | *.png binary 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /drivers/ 2 | /.idea/ 3 | /public/uploads/images/*/*.* 4 | 5 | ###> symfony/framework-bundle ### 6 | /.env.local 7 | /.env.local.php 8 | /.env.*.local 9 | /config/secrets/prod/prod.decrypt.private.php 10 | /public/bundles/ 11 | /var/ 12 | /vendor/ 13 | ###< symfony/framework-bundle ### 14 | 15 | ###> symfony/phpunit-bridge ### 16 | .phpunit 17 | .phpunit.result.cache 18 | /phpunit.xml 19 | ###< symfony/phpunit-bridge ### 20 | 21 | ###> symfony/webpack-encore-bundle ### 22 | /node_modules/ 23 | npm-debug.log 24 | yarn-error.log 25 | ###< symfony/webpack-encore-bundle ### 26 | 27 | ###> friendsofphp/php-cs-fixer ### 28 | /.php-cs-fixer.php 29 | ###< friendsofphp/php-cs-fixer ### 30 | 31 | ###> phpunit/phpunit ### 32 | /phpunit.xml 33 | .phpunit.result.cache 34 | ###< phpunit/phpunit ### 35 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 70, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 4, 6 | "trailingComma": "none" 7 | } 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | ResidenceCMS is an open source project. Contributions made by 5 | the community are welcome. Send us your ideas, code reviews, pull requests and 6 | feature requests to help us improve this project. All contributions must follow the [usual Symfony contribution requirements](https://symfony.com/doc/current/contributing/index.html). 7 | 8 | ## Any contributions you make will be under the MIT Software License 9 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2024 Valery Maslov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /assets/app.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | window.jQuery = $; 3 | window.$ = $; 4 | import 'popper.js'; 5 | import 'bootstrap'; 6 | import 'lazysizes'; 7 | import bootbox from 'bootbox'; 8 | window.bootbox = bootbox; 9 | 10 | $.ajaxSetup({ 11 | headers: { 12 | 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /assets/controllers/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/assets/controllers/.gitignore -------------------------------------------------------------------------------- /assets/js/admin/menu-sorting.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const changeButtons = () => { 4 | $('.js-down-one') 5 | .removeClass('js-down-one') 6 | .addClass('js-up-one') 7 | .html(' Up one'); 8 | 9 | $('.js-move:first') 10 | .removeClass('js-up-one') 11 | .addClass('js-down-one') 12 | .html(' Down one'); 13 | }; 14 | 15 | const sendRequest = () => { 16 | const item = $('.js-move'); 17 | 18 | if (item.length > 1) { 19 | let items = []; 20 | 21 | item.each(function () { 22 | items.push(parseInt($(this).attr('id'), 10)); 23 | }); 24 | 25 | $.ajax({ 26 | method: 'POST', 27 | url: '/en/admin/menu/sort', 28 | data: { items: items } 29 | }); 30 | } 31 | }; 32 | 33 | $(document).ready(function () { 34 | $('body').on('click', '.js-move', function () { 35 | let row = $(this).parents('tr:first'); 36 | 37 | if ($(this).is('.js-up-one')) { 38 | row.insertBefore(row.prev()); 39 | } else { 40 | row.insertAfter(row.next()); 41 | } 42 | 43 | changeButtons(); 44 | sendRequest(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /assets/js/admin/settings.js: -------------------------------------------------------------------------------- 1 | import Dropzone from 'dropzone'; 2 | 3 | Dropzone.autoDiscover = false; 4 | 5 | $(document).ready(function () { 6 | const configureDropzone = ($form) => { 7 | const token = $form.data('token'); 8 | if ($form.length) { 9 | $form.dropzone({ 10 | url: $form.attr('action'), 11 | acceptedFiles: 'image/*', 12 | sending: function (file, xhr, formData) { 13 | formData.append('csrf_token', token); 14 | }, 15 | queuecomplete: function () { 16 | setTimeout(function () { 17 | window.location.reload(); 18 | }, 200); 19 | } 20 | }); 21 | } 22 | }; 23 | 24 | configureDropzone($('form[action$="upload_logo_image"]')); 25 | configureDropzone($('form[action$="upload_header_image"]')); 26 | }); 27 | -------------------------------------------------------------------------------- /assets/js/auth/_resend.js: -------------------------------------------------------------------------------- 1 | (function ($, bootbox) { 2 | 'use strict'; 3 | 4 | const link = $('#resend'); 5 | 6 | // Show confirmation link 7 | if (link.length > 0) { 8 | $.ajax({ 9 | url: '/en/auth/should_link_be_visible', 10 | type: 'GET', 11 | success: function (response) { 12 | if (response.display === true) { 13 | link.show(); 14 | } 15 | } 16 | }); 17 | } 18 | 19 | const resend = () => { 20 | $.ajax({ 21 | url: link.data('path'), 22 | type: 'POST', 23 | success: function (response) { 24 | link.hide(); 25 | bootbox.alert(response.message); 26 | } 27 | }); 28 | }; 29 | 30 | link.click(resend); 31 | })($, bootbox); 32 | -------------------------------------------------------------------------------- /assets/js/auth/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import './_resend'; 4 | -------------------------------------------------------------------------------- /assets/js/bottom-bar.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 'use strict'; 3 | 4 | const $phone = $('#phone'); 5 | const $email = $('#email'); 6 | 7 | $('body').append(`
8 | ${$phone.attr('title')} 11 | ${$email.attr('title')} 14 |
`); 15 | })(window.jQuery); 16 | -------------------------------------------------------------------------------- /assets/js/common/_delete_button.js: -------------------------------------------------------------------------------- 1 | (function ($, bootbox) { 2 | 'use strict'; 3 | 4 | // Confirm deletion 5 | $('[data-type="delete"]').click(function (e) { 6 | e.preventDefault(); 7 | const $form = $(this).closest('form'); 8 | const message = $(this).data('message'); 9 | const confirmationText = $(this).data('confirmation-text'); 10 | const cancellationText = $(this).data('cancellation-text'); 11 | 12 | bootbox.confirm({ 13 | message, 14 | buttons: { 15 | cancel: { 16 | label: cancellationText, 17 | className: 'btn-light' 18 | }, 19 | confirm: { 20 | label: confirmationText, 21 | className: 'btn-danger' 22 | } 23 | }, 24 | callback: function (result) { 25 | if (result) { 26 | $form.submit(); 27 | } 28 | } 29 | }); 30 | }); 31 | })($, bootbox); 32 | -------------------------------------------------------------------------------- /assets/js/ekko-lightbox.js: -------------------------------------------------------------------------------- 1 | import 'ekko-lightbox/dist/ekko-lightbox'; 2 | 3 | $('body').on('click', '[data-toggle="lightbox"]', function (event) { 4 | event.preventDefault(); 5 | $(this).ekkoLightbox(); 6 | }); 7 | -------------------------------------------------------------------------------- /assets/js/map.js: -------------------------------------------------------------------------------- 1 | /** global: ymaps */ 2 | ymaps.ready(init); 3 | 4 | const $map = $('#map'); 5 | const latitude = $map.data('latitude'); 6 | const longitude = $map.data('longitude'); 7 | const hintContent = $map.attr('data-hintContent'); 8 | const balloonContent = $map.attr('data-balloonContent'); 9 | 10 | function init() { 11 | // Creating the map. 12 | let myMap = new ymaps.Map('map', { 13 | center: [latitude, longitude], 14 | zoom: 13 15 | }); 16 | 17 | let currentApartment = new ymaps.Placemark( 18 | [latitude, longitude], 19 | { 20 | hintContent: hintContent, 21 | balloonContent: balloonContent 22 | }, 23 | { 24 | preset: 'islands#blueHomeIcon' 25 | } 26 | ); 27 | 28 | myMap.geoObjects.add(currentApartment); 29 | } 30 | 31 | $map.css({ 32 | width: '100%', 33 | height: '280px', 34 | 'padding-top': '20px' 35 | }); 36 | -------------------------------------------------------------------------------- /assets/js/page.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | $(document).ready(function () { 4 | let $checkbox = $('#page_add_contact_form'); 5 | let $emailContainer = $('#email-address'); 6 | let $emailField = $('#page_contact_email_address'); 7 | 8 | if ($checkbox.attr('checked') === 'checked') { 9 | $emailContainer.slideDown(); 10 | $emailField.prop('required', true); 11 | } 12 | 13 | // Event Handler 14 | $checkbox.on('change', function () { 15 | updateDisplay(); 16 | }); 17 | 18 | // Action 19 | 20 | function updateDisplay() { 21 | let isChecked = $checkbox.is(':checked'); 22 | 23 | if (isChecked) { 24 | $emailContainer.slideDown(); 25 | $emailField.focus().prop('required', true); 26 | } else { 27 | $emailContainer.slideUp(); 28 | $emailField.prop('required', false); 29 | } 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /assets/js/select2.js: -------------------------------------------------------------------------------- 1 | import 'select2/dist/js/select2.min'; 2 | 3 | $(document).ready(function () { 4 | $('#property_features').select2(); 5 | }); 6 | -------------------------------------------------------------------------------- /assets/js/slugger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const slugify = require('slugify'); 4 | 5 | $(document).ready(function () { 6 | $('#name input').keyup(function () { 7 | $('#slug input').val( 8 | slugify($('#name input').val(), { 9 | lower: true, 10 | strict: true 11 | }) 12 | ); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /assets/js/user/_property.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 'use strict'; 3 | 4 | // Properties 5 | $('.btn-outline-secondary').click(function (e) { 6 | e.preventDefault(); 7 | $(this).addClass('disabled'); 8 | 9 | let url = $(this).attr('href'); 10 | let $div = $(this).parent().parent().parent(); 11 | 12 | $div.css({ opacity: '0.5' }); 13 | 14 | $.get(url).done(function () { 15 | $div.fadeOut(); 16 | changeCount(); 17 | }); 18 | }); 19 | 20 | // Change count 21 | function changeCount() { 22 | let $counter = $('.js-counter'); 23 | let counter = $counter.text(); 24 | counter = Number.parseInt(counter); 25 | counter -= 1; 26 | $counter.text(counter); 27 | } 28 | })($); 29 | -------------------------------------------------------------------------------- /assets/js/user/_sidebar.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 'use strict'; 3 | 4 | let currentUrl = window.location.href; 5 | 6 | if (currentUrl.indexOf('profile') !== -1) { 7 | $('.list-group-item-action:eq(2)').addClass('active'); 8 | } else if (currentUrl.indexOf('unpublished') !== -1) { 9 | $('.list-group-item-action:eq(1)').addClass('active'); 10 | } else if (currentUrl.indexOf('security') !== -1) { 11 | $('.list-group-item-action:eq(3)').addClass('active'); 12 | } else { 13 | $('.list-group-item-action:eq(0)').addClass('active'); 14 | } 15 | })(window.jQuery); 16 | -------------------------------------------------------------------------------- /assets/js/user/password/password.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import './_validate_password'; 4 | import './_update_password'; 5 | -------------------------------------------------------------------------------- /assets/js/user/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import './_sidebar'; 4 | import './_property'; 5 | import '../common/_delete_button'; 6 | -------------------------------------------------------------------------------- /assets/psd/no-photo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/assets/psd/no-photo.psd -------------------------------------------------------------------------------- /assets/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | // Body 3 | $body-bg: #f5f5fa; 4 | 5 | // Code 6 | $code-color: #212529 !default; 7 | $code-font-size: 12px !default; 8 | 9 | // Lead 10 | $lead-font-size: 2.5rem !default; 11 | 12 | // Jumbotron 13 | $jumbotron-bg: transparent !default; 14 | 15 | // Fonts 16 | $fa-font-path: "../fonts"; 17 | -------------------------------------------------------------------------------- /assets/styles/bottom-bar.scss: -------------------------------------------------------------------------------- 1 | .bottom-bar { 2 | display: none; 3 | } 4 | 5 | @media (max-width: 767px) { 6 | footer { 7 | margin-bottom: 70px; 8 | } 9 | .bottom-bar { 10 | display: block !important; 11 | position: fixed; 12 | bottom: 0; 13 | left: 0; 14 | width: 100%; 15 | z-index: 50; 16 | padding: 0; 17 | margin: 0; 18 | background-color: #fff; 19 | box-shadow: 0 0 5px rgb(0 0 0 / 30%); 20 | } 21 | .bottom-bar a { 22 | width: 50%; 23 | float: left; 24 | text-align: center; 25 | font-size: 15px; 26 | line-height: 3.2em; 27 | display: inline-block; 28 | z-index: 100; 29 | vertical-align: middle; 30 | touch-action: manipulation; 31 | cursor: pointer; 32 | -webkit-user-select: none; 33 | -moz-user-select: none; 34 | -ms-user-select: none; 35 | user-select: none; 36 | } 37 | a.btn-right { 38 | background-color: #007bff; 39 | } 40 | a.btn-right, a.btn-right:active, a.btn-right:focus, a.btn-right:hover { 41 | color: #fff; 42 | text-decoration: none; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /assets/styles/city.scss: -------------------------------------------------------------------------------- 1 | .location { 2 | font-size: 1.3rem; 3 | } 4 | 5 | .sub-location { 6 | font-size: 1rem; 7 | padding-left: 30px !important; 8 | } 9 | -------------------------------------------------------------------------------- /assets/styles/detail.scss: -------------------------------------------------------------------------------- 1 | #map { 2 | height: 0; 3 | } 4 | 5 | // Contact widget 6 | .icon-box { 7 | background-color: #f9f9f9; 8 | border-radius: 50%; 9 | height: 50px; 10 | text-align: center; 11 | transition: all linear .15s; 12 | width: 50px; 13 | float: left; 14 | } 15 | 16 | .contact-box { 17 | margin: 10px 30px; 18 | 19 | h4 { 20 | font-weight: 400; 21 | padding-top: 2px; 22 | } 23 | 24 | p { 25 | color: #959595; 26 | line-height: 1.8; 27 | 28 | a { 29 | color: #959595; 30 | 31 | &:hover { 32 | color: #f1572f; 33 | text-decoration: none; 34 | } 35 | } 36 | } 37 | 38 | .icon-box i { 39 | color: #f1572f; 40 | } 41 | } 42 | 43 | // Overview 44 | .overview { 45 | border: 1px solid #b5b5b5; 46 | margin: 0 30px 10px 0; 47 | padding: 20px; 48 | 49 | table { 50 | th { 51 | text-align: left; 52 | padding-right: 15px; 53 | font-weight: normal; 54 | } 55 | 56 | tr { 57 | vertical-align: text-top !important; 58 | } 59 | } 60 | } 61 | 62 | @media (min-width: 992px) { 63 | .overview { 64 | float: left; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /assets/styles/ekko-lightbox.scss: -------------------------------------------------------------------------------- 1 | @import "~ekko-lightbox/dist/ekko-lightbox"; 2 | -------------------------------------------------------------------------------- /assets/styles/select2.scss: -------------------------------------------------------------------------------- 1 | @import '~select2/dist/css/select2.css'; 2 | 3 | .select2-container--default .select2-selection--multiple { 4 | border: 1px solid #ced4da; 5 | } 6 | 7 | .select2-container--default.select2-container--focus .select2-selection--multiple { 8 | border-color: #8bbafe; 9 | outline: 0; 10 | box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25); 11 | } 12 | -------------------------------------------------------------------------------- /assets/styles/user/security.scss: -------------------------------------------------------------------------------- 1 | .security-buttons a i { 2 | background-color: #fff; 3 | padding: 20px; 4 | border-radius: 50%; 5 | color: #BDC3C7; 6 | } 7 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | modules: 'auto', 7 | targets: { node: 'current' } 8 | } 9 | ] 10 | ], 11 | plugins: [ 12 | [ 13 | '@babel/plugin-proposal-class-properties', 14 | { 15 | loose: true 16 | } 17 | ] 18 | ] 19 | }; 20 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | ['all' => true], 5 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 6 | Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], 7 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 8 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 9 | Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], 10 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 11 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 12 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 13 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 14 | Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true], 15 | Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], 16 | Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], 17 | SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle::class => ['all' => true], 18 | Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true], 19 | ]; 20 | -------------------------------------------------------------------------------- /config/packages/assets.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | assets: 3 | json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' 4 | -------------------------------------------------------------------------------- /config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Unique name of your app: used to compute stable namespaces for cache keys. 4 | #prefix_seed: your_vendor_name/app_name 5 | 6 | # The "app" cache stores to the filesystem by default. 7 | # The data in this cache should persist between deploys. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: null 20 | -------------------------------------------------------------------------------- /config/packages/debug.yaml: -------------------------------------------------------------------------------- 1 | when@dev: 2 | debug: 3 | # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. 4 | # See the "server:dump" command to start a new server. 5 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 6 | -------------------------------------------------------------------------------- /config/packages/dev/easy_log_handler.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | EasyCorp\EasyLog\EasyLogHandler: 3 | public: false 4 | arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] 5 | 6 | #// FIXME: How to add this configuration automatically without messing up with the monolog configuration? 7 | #monolog: 8 | # handlers: 9 | # buffered: 10 | # type: buffer 11 | # handler: easylog 12 | # channels: ['!event'] 13 | # level: debug 14 | # easylog: 15 | # type: service 16 | # id: EasyCorp\EasyLog\EasyLogHandler 17 | -------------------------------------------------------------------------------- /config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | migrations_paths: 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | 'DoctrineMigrations': '%kernel.project_dir%/migrations' 6 | enable_profiler: '%kernel.debug%' 7 | -------------------------------------------------------------------------------- /config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | # see https://symfony.com/doc/current/reference/configuration/framework.html 2 | framework: 3 | secret: '%env(APP_SECRET)%' 4 | csrf_protection: true 5 | 6 | # Note that the session will be started ONLY if you read or write from it. 7 | session: true 8 | 9 | #esi: true 10 | #fragments: true 11 | 12 | when@test: 13 | framework: 14 | test: true 15 | session: 16 | storage_factory_id: session.storage.factory.mock_file 17 | -------------------------------------------------------------------------------- /config/packages/knp_paginator.yaml: -------------------------------------------------------------------------------- 1 | knp_paginator: 2 | page_range: 3 3 | template: 4 | pagination: '@KnpPaginator/Pagination/twitter_bootstrap_v4_pagination.html.twig' 5 | -------------------------------------------------------------------------------- /config/packages/lock.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | lock: '%env(LOCK_DSN)%' 3 | -------------------------------------------------------------------------------- /config/packages/mailer.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | mailer: 3 | dsn: '%env(MAILER_DSN)%' 4 | 5 | when@test: 6 | framework: 7 | mailer: 8 | # this disables delivery of messages entirely 9 | dsn: 'null://null' 10 | -------------------------------------------------------------------------------- /config/packages/messenger.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | messenger: 3 | # reset_on_message: true 4 | # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. 5 | # failure_transport: failed 6 | 7 | transports: 8 | # https://symfony.com/doc/current/messenger.html#transport-configuration 9 | # async: '%env(MESSENGER_TRANSPORT_DSN)%' 10 | # failed: 'doctrine://default?queue_name=failed' 11 | # sync: 'sync://' 12 | 13 | routing: 14 | # Route your messages to the transports 15 | # 'App\Message\YourMessage': async 16 | -------------------------------------------------------------------------------- /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/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/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/rate_limiter.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | rate_limiter: 3 | auth: 4 | policy: 'fixed_window' 5 | limit: 6 6 | interval: '100 seconds' 7 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. 4 | # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands 5 | #default_uri: http://localhost 6 | 7 | when@prod: 8 | framework: 9 | router: 10 | strict_requirements: null 11 | -------------------------------------------------------------------------------- /config/packages/scheb_2fa.yaml: -------------------------------------------------------------------------------- 1 | scheb_two_factor: 2 | security_tokens: 3 | - Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken 4 | - Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken 5 | - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface 6 | 7 | google: 8 | enabled: true 9 | #server_name: server_name # Server name used in QR code 10 | issuer: issuer_placeholder # Issuer name used in QR code 11 | digits: 6 # Number of digits in authentication code 12 | leeway: 1 # Depends on the version of Spomky-Labs/otphp used: 13 | # Until v10: How many codes before/after the current one would be accepted 14 | # From v11: Acceptable time drift in seconds 15 | template: auth/two_factor/two_factor_form.html.twig # Template used to render the authentication form 16 | form_renderer: App\Controller\Auth\TwoFactor\EnterAuthCodeController 17 | -------------------------------------------------------------------------------- /config/packages/test/rate_limiter.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | rate_limiter: 3 | auth: 4 | policy: 'fixed_window' 5 | limit: 30 6 | interval: '100 seconds' 7 | cache_pool: 'cache.rate_limiter' 8 | -------------------------------------------------------------------------------- /config/packages/test/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | not_compromised_password: false 4 | -------------------------------------------------------------------------------- /config/packages/test/webpack_encore.yaml: -------------------------------------------------------------------------------- 1 | #webpack_encore: 2 | # strict_mode: false 3 | -------------------------------------------------------------------------------- /config/packages/translation.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | default_locale: '%env(LANGUAGE_CODE)%' 3 | translator: 4 | default_path: '%kernel.project_dir%/translations' 5 | fallbacks: 6 | - en 7 | providers: 8 | -------------------------------------------------------------------------------- /config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | file_name_pattern: '*.twig' 3 | globals: 4 | app_version: '%app_version%' 5 | locales: '%app_locales%' 6 | locale: '%locale%' 7 | 8 | when@test: 9 | twig: 10 | strict_variables: true 11 | -------------------------------------------------------------------------------- /config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | # Enables validator auto-mapping support. 4 | # For instance, basic validation constraints will be inferred from Doctrine's metadata. 5 | auto_mapping: 6 | App\Entity\: [] 7 | -------------------------------------------------------------------------------- /config/packages/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | when@dev: 2 | web_profiler: 3 | toolbar: true 4 | intercept_redirects: false 5 | 6 | framework: 7 | profiler: 8 | only_exceptions: false 9 | collect_serializer_data: true 10 | 11 | when@test: 12 | web_profiler: 13 | toolbar: false 14 | intercept_redirects: false 15 | 16 | framework: 17 | profiler: { collect: false } 18 | -------------------------------------------------------------------------------- /config/preload.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE property ADD created_at DATETIME DEFAULT CURRENT_TIMESTAMP, ADD updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, DROP published_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 property ADD published_at DATETIME NOT NULL, DROP created_at, DROP updated_at'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20210104185420.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE menu ADD locale VARCHAR(2) NOT NULL'); 24 | $this->addSql('ALTER TABLE page ADD locale VARCHAR(2) NOT NULL'); 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 page DROP locale'); 31 | $this->addSql('ALTER TABLE menu DROP locale'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /migrations/Version20210106154655.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE UNIQUE INDEX slug_locale_unique_key ON page (slug, locale)'); 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('DROP INDEX slug_locale_unique_key ON page'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20210106191712.php: -------------------------------------------------------------------------------- 1 | addSql("INSERT INTO settings (setting_name, setting_value) VALUES ('show_filter_by_features', '0')"); 23 | } 24 | 25 | public function down(Schema $schema) : void 26 | { 27 | $this->addSql("DELETE FROM settings WHERE setting_name='show_filter_by_features'"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /migrations/Version20210106234742.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE page CHANGE locale locale VARCHAR(2) DEFAULT \'en\' 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 page CHANGE locale locale VARCHAR(2) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20210107000119.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE UNIQUE INDEX url_locale_unique_key ON menu (url, locale)'); 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('DROP INDEX url_locale_unique_key ON menu'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20210215083338.php: -------------------------------------------------------------------------------- 1 | addSql("INSERT INTO settings (setting_name, setting_value) VALUES ('logo_image', '')"); 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("DELETE FROM settings WHERE setting_name='logo_image'"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20210215173522.php: -------------------------------------------------------------------------------- 1 | addSql("INSERT INTO settings (setting_name, setting_value) VALUES ('show_language_selector', '0')"); 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("DELETE FROM settings WHERE setting_name='show_language_selector'"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20210815075052.php: -------------------------------------------------------------------------------- 1 | addSql('CREATE TABLE profile (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, full_name VARCHAR(255) DEFAULT NULL, phone VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_8157AA0FA76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); 24 | $this->addSql('ALTER TABLE profile ADD CONSTRAINT FK_8157AA0FA76ED395 FOREIGN KEY (user_id) REFERENCES users (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('DROP TABLE profile'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /migrations/Version20220320064544.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE users ADD email_verified_at DATETIME DEFAULT NULL'); 24 | $this->addSql('UPDATE users SET email_verified_at=NOW()'); 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 users DROP email_verified_at'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /migrations/Version20220325072850.php: -------------------------------------------------------------------------------- 1 | addSql("INSERT INTO settings (setting_name, setting_value) VALUES ('anyone_can_register', '0')"); 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("DELETE FROM settings WHERE setting_name='anyone_can_register'"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20220824075712.php: -------------------------------------------------------------------------------- 1 | addSql("INSERT INTO settings (setting_name, setting_value) VALUES ('allow_html', '1')"); 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("DELETE FROM settings WHERE setting_name='allow_html'"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20230304072206.php: -------------------------------------------------------------------------------- 1 | addSql("INSERT INTO settings (setting_name, setting_value) VALUES ('show_bottom_bar', '1')"); 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("DELETE FROM settings WHERE setting_name='show_bottom_bar'"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrations/Version20230501134351.php: -------------------------------------------------------------------------------- 1 | addSql('ALTER TABLE users ADD google_authenticator_secret VARCHAR(255) 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 users DROP google_authenticator_secret'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /phpstan.dist.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 5 3 | paths: 4 | - bin/ 5 | - config/ 6 | - public/ 7 | - src/ 8 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Options -MultiViews 3 | 4 | RewriteEngine On 5 | #RewriteBase /path/to/app 6 | RewriteCond %{REQUEST_FILENAME} !-d 7 | RewriteCond %{REQUEST_FILENAME} !-f 8 | RewriteRule ^ index.php [QSA,L] 9 | 10 | -------------------------------------------------------------------------------- /public/build/css/bottom-bar.38e93f4e.css: -------------------------------------------------------------------------------- 1 | .bottom-bar{display:none}@media (max-width:767px){footer{margin-bottom:70px}.bottom-bar{background-color:#fff;bottom:0;box-shadow:0 0 5px rgba(0,0,0,.3);display:block!important;left:0;margin:0;padding:0;position:fixed;width:100%;z-index:50}.bottom-bar a{cursor:pointer;display:inline-block;float:left;font-size:15px;line-height:3.2em;text-align:center;touch-action:manipulation;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;width:50%;z-index:100}a.btn-right{background-color:#007bff}a.btn-right,a.btn-right:active,a.btn-right:focus,a.btn-right:hover{color:#fff;text-decoration:none}} -------------------------------------------------------------------------------- /public/build/css/city.830c14fb.css: -------------------------------------------------------------------------------- 1 | .location{font-size:1.3rem}.sub-location{font-size:1rem;padding-left:30px!important} -------------------------------------------------------------------------------- /public/build/css/detail.3d2d5214.css: -------------------------------------------------------------------------------- 1 | #map{height:0}.icon-box{background-color:#f9f9f9;border-radius:50%;float:left;height:50px;text-align:center;transition:all .15s linear;width:50px}.contact-box{margin:10px 30px}.contact-box h4{font-weight:400;padding-top:2px}.contact-box p{color:#959595;line-height:1.8}.contact-box p a{color:#959595}.contact-box p a:hover{color:#f1572f;text-decoration:none}.contact-box .icon-box i{color:#f1572f}.overview{border:1px solid #b5b5b5;margin:0 30px 10px 0;padding:20px}.overview table th{font-weight:400;padding-right:15px;text-align:left}.overview table tr{vertical-align:text-top!important}@media (min-width:992px){.overview{float:left}} -------------------------------------------------------------------------------- /public/build/css/security.64e38d75.css: -------------------------------------------------------------------------------- 1 | .security-buttons a i{background-color:#fff;border-radius:50%;color:#bdc3c7;padding:20px} -------------------------------------------------------------------------------- /public/build/fonts/fa-regular-400.491974d1.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-regular-400.491974d1.ttf -------------------------------------------------------------------------------- /public/build/fonts/fa-regular-400.77206a6b.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-regular-400.77206a6b.eot -------------------------------------------------------------------------------- /public/build/fonts/fa-regular-400.7a333762.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-regular-400.7a333762.woff2 -------------------------------------------------------------------------------- /public/build/fonts/fa-regular-400.bb58e57c.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-regular-400.bb58e57c.woff -------------------------------------------------------------------------------- /public/build/fonts/fa-solid-900.1551f4f6.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-solid-900.1551f4f6.woff2 -------------------------------------------------------------------------------- /public/build/fonts/fa-solid-900.9bbb245e.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-solid-900.9bbb245e.eot -------------------------------------------------------------------------------- /public/build/fonts/fa-solid-900.be9ee23c.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-solid-900.be9ee23c.ttf -------------------------------------------------------------------------------- /public/build/fonts/fa-solid-900.eeccf4f6.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/build/fonts/fa-solid-900.eeccf4f6.woff -------------------------------------------------------------------------------- /public/build/js/admin.32201fb1.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * JavaScript Cookie v2.2.1 3 | * https://github.com/js-cookie/js-cookie 4 | * 5 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 6 | * Released under the MIT license 7 | */ 8 | -------------------------------------------------------------------------------- /public/build/js/auth.87b05c2f.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunk=self.webpackChunk||[]).push([[795],{64:()=>{!function(s,e){"use strict";const t=s("#resend");t.length>0&&s.ajax({url:"/en/auth/should_link_be_visible",type:"GET",success:function(s){!0===s.display&&t.show()}});t.click((()=>{s.ajax({url:t.data("path"),type:"POST",success:function(s){t.hide(),e.alert(s.message)}})}))}($,bootbox)},298:(s,e,t)=>{"use strict";t(64)}},s=>{var e;e=298,s(s.s=e)}]); -------------------------------------------------------------------------------- /public/build/js/bottom-bar.04c1ac09.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunk=self.webpackChunk||[]).push([[576],{68:()=>{!function(t){"use strict";const a=t("#phone"),e=t("#email");t("body").append(`
\n ${a.attr("title")}\n ${e.attr("title")}\n
`)}(window.jQuery)}},t=>{var a;a=68,t(t.s=a)}]); -------------------------------------------------------------------------------- /public/build/js/city.b1325671.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunk=self.webpackChunk||[]).push([[768],{368:()=>{!function(t){"use strict";const o=t=>``,e=o=>{t.each(o,(function(o,e){t(e+" option").each((function(){""!==t(this).val()&&t(this).remove()}))}))};t("body").on("change","#property_city",(function(){e(["#property_district","#property_neighborhood","#property_metro_station"]);let n=t(this).val();const p="/en/city/"+n+".json";""!==n&&t.get(p).done((e=>{let{districts:n,neighborhoods:p,metro_stations:i}=e;n=n.map((t=>o(t))),p=p.map((t=>o(t))),i=i.map((t=>o(t))),t("#property_district").append(n),t("#property_neighborhood").append(p),t("#property_metro_station").append(i)}))}))}(window.jQuery)}},t=>{var o;o=368,t(t.s=o)}]); -------------------------------------------------------------------------------- /public/build/js/ekko-lightbox.11ef94c0.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Lightbox for Bootstrap by @ashleydw 3 | * https://github.com/ashleydw/lightbox 4 | * 5 | * License: https://github.com/ashleydw/lightbox/blob/master/LICENSE 6 | */ 7 | -------------------------------------------------------------------------------- /public/build/js/google_authenticator.5fceab80.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunk=self.webpackChunk||[]).push([[616],{5:()=>{!function(e){"use strict";const t=e("#generate_google_auth_secret"),a=e('[name="generatedSecret"]'),n=e('[name="authentication_code"]');e("#setUpAuthenticatorButton").click((function(){!0===t.data("generate-new-secret")&&e.ajax({method:"GET",url:"/en/user/google_authenticator_code"}).done((function(t){const{secret:n,qr_code:o}=t,c=new Image;c.src=o,a.val(n),e("#generatedQrCode").html(c),e("#generatedSecret").html(n)}))})),e("#enable2fa").click((function(){let o=n.val().trim();o?e.ajax({method:"PUT",url:t.attr("action"),data:{secret:a.val(),authentication_code:o}}).done((function(){location.reload()})).fail((function(t){var a;a=t.responseJSON.message,e("#twoFactorAuthErrorMessage").text(a).removeClass("d-none")})):n.addClass("is-invalid").focus()})),e("#disable2fa").click((function(){e.ajax({method:"DELETE",url:t.attr("action")}).done((function(){location.reload()})).fail((function(){location.reload()}))})),n.keyup((function(){e(this).removeClass("is-invalid")})),e("#setUpAuthenticator").on("hidden.bs.modal",(function(){n.val("").removeClass("is-invalid"),e("#twoFactorAuthErrorMessage").text("").addClass("d-none")}))}(window.jQuery)}},e=>{var t;t=5,e(e.s=t)}]); -------------------------------------------------------------------------------- /public/build/js/map.7d2feb0e.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunk=self.webpackChunk||[]).push([[997],{45:()=>{ymaps.ready((function(){let t=new ymaps.Map("map",{center:[a,e],zoom:13}),p=new ymaps.Placemark([a,e],{hintContent:n,balloonContent:o},{preset:"islands#blueHomeIcon"});t.geoObjects.add(p)}));const t=$("#map"),a=t.data("latitude"),e=t.data("longitude"),n=t.attr("data-hintContent"),o=t.attr("data-balloonContent");t.css({width:"100%",height:"280px","padding-top":"20px"})}},t=>{var a;a=45,t(t.s=a)}]); -------------------------------------------------------------------------------- /public/build/js/menu-sorting.287ae70d.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunk=self.webpackChunk||[]).push([[843],{521:()=>{const s=()=>{const s=$(".js-move");if(s.length>1){let e=[];s.each((function(){e.push(parseInt($(this).attr("id"),10))})),$.ajax({method:"POST",url:"/en/admin/menu/sort",data:{items:e}})}};$(document).ready((function(){$("body").on("click",".js-move",(function(){let e=$(this).parents("tr:first");$(this).is(".js-up-one")?e.insertBefore(e.prev()):e.insertAfter(e.next()),$(".js-down-one").removeClass("js-down-one").addClass("js-up-one").html(' Up one'),$(".js-move:first").removeClass("js-up-one").addClass("js-down-one").html(' Down one'),s()}))}))}},s=>{var e;e=521,s(s.s=e)}]); -------------------------------------------------------------------------------- /public/build/js/page.3f48afaa.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunk=self.webpackChunk||[]).push([[948],{436:()=>{$(document).ready((function(){let e=$("#page_add_contact_form"),c=$("#email-address"),d=$("#page_contact_email_address");"checked"===e.attr("checked")&&(c.slideDown(),d.prop("required",!0)),e.on("change",(function(){e.is(":checked")?(c.slideDown(),d.focus().prop("required",!0)):(c.slideUp(),d.prop("required",!1))}))}))}},e=>{var c;c=436,e(e.s=c)}]); -------------------------------------------------------------------------------- /public/build/js/select2.98bad6eb.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v3.7.1 3 | * https://jquery.com/ 4 | * 5 | * Copyright OpenJS Foundation and other contributors 6 | * Released under the MIT license 7 | * https://jquery.org/license 8 | * 9 | * Date: 2023-08-28T13:37Z 10 | */ 11 | 12 | /*! Select2 4.1.0-rc.0 | https://github.com/select2/select2/blob/master/LICENSE.md */ 13 | -------------------------------------------------------------------------------- /public/build/js/user.3e9dfb9b.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunk=self.webpackChunk||[]).push([[676],{506:()=>{!function(t,e){"use strict";t('[data-type="delete"]').click((function(a){a.preventDefault();const i=t(this).closest("form"),s=t(this).data("message"),n=t(this).data("confirmation-text"),c=t(this).data("cancellation-text");e.confirm({message:s,buttons:{cancel:{label:c,className:"btn-light"},confirm:{label:n,className:"btn-danger"}},callback:function(t){t&&i.submit()}})}))}($,bootbox)},559:()=>{!function(t){"use strict";t(".btn-outline-secondary").click((function(e){e.preventDefault(),t(this).addClass("disabled");let a=t(this).attr("href"),i=t(this).parent().parent().parent();i.css({opacity:"0.5"}),t.get(a).done((function(){i.fadeOut(),function(){let e=t(".js-counter"),a=e.text();a=Number.parseInt(a),a-=1,e.text(a)}()}))}))}($)},292:()=>{!function(t){"use strict";let e=window.location.href;-1!==e.indexOf("profile")?t(".list-group-item-action:eq(2)").addClass("active"):-1!==e.indexOf("unpublished")?t(".list-group-item-action:eq(1)").addClass("active"):-1!==e.indexOf("security")?t(".list-group-item-action:eq(3)").addClass("active"):t(".list-group-item-action:eq(0)").addClass("active")}(window.jQuery)},304:(t,e,a)=>{"use strict";a(292),a(559),a(506)}},t=>{var e;e=304,t(t.s=e)}]); -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/favicon.ico -------------------------------------------------------------------------------- /public/images/bg-1.5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/bg-1.5.jpg -------------------------------------------------------------------------------- /public/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/bg.jpg -------------------------------------------------------------------------------- /public/images/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/images/icons/chrome-touch-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/chrome-touch-icon-192x192.png -------------------------------------------------------------------------------- /public/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /public/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /public/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /public/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /public/images/languages/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/languages/bg.png -------------------------------------------------------------------------------- /public/images/languages/en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/languages/en.png -------------------------------------------------------------------------------- /public/images/languages/hu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/languages/hu.png -------------------------------------------------------------------------------- /public/images/languages/nl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/languages/nl.png -------------------------------------------------------------------------------- /public/images/languages/ru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/languages/ru.png -------------------------------------------------------------------------------- /public/images/logo-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/logo-square.png -------------------------------------------------------------------------------- /public/images/no-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/no-photo.png -------------------------------------------------------------------------------- /public/images/residence-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/residence-logo.png -------------------------------------------------------------------------------- /public/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coderberg/ResidenceCMS/72ae486fb774f94143d91af695da4bbf2236e5fe/public/images/transparent.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | withPaths([ 12 | __DIR__.'/src', 13 | __DIR__.'/tests', 14 | ]) 15 | ->withSets([ 16 | LevelSetList::UP_TO_PHP_82, 17 | SymfonySetList::SYMFONY_CODE_QUALITY, 18 | SetList::CODE_QUALITY, 19 | ]); 20 | -------------------------------------------------------------------------------- /src/Controller/Admin/Settings/AbstractSettingsController.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected array $settings; 17 | 18 | public function __construct( 19 | protected SettingsRepository $repository, 20 | protected SettingsService $service, 21 | ) { 22 | $this->settings = $this->repository->findAllAsArray(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Controller/Admin/Settings/MainSettingsController.php: -------------------------------------------------------------------------------- 1 | createForm(MainSettingsType::class, $this->settings); 18 | $form->handleRequest($request); 19 | if ($form->isSubmitted() && $form->isValid()) { 20 | $this->service->updateSettings($form->getNormData()); 21 | 22 | return $this->redirectToRoute('admin_settings'); 23 | } 24 | 25 | return $this->render('admin/settings/main_settings.html.twig', [ 26 | 'site' => $this->settings, 27 | 'form' => $form, 28 | ]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Controller/Admin/Settings/MapSettingsController.php: -------------------------------------------------------------------------------- 1 | createForm(MapSettingsType::class, $this->settings); 18 | $form->handleRequest($request); 19 | if ($form->isSubmitted() && $form->isValid()) { 20 | $this->service->updateSettings($form->getNormData()); 21 | 22 | return $this->redirectToRoute('admin_map_settings'); 23 | } 24 | 25 | return $this->render('admin/settings/map_settings.html.twig', [ 26 | 'site' => $this->settings, 27 | 'form' => $form, 28 | ]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Controller/Ajax/Admin/MenuController.php: -------------------------------------------------------------------------------- 1 | getPayload()->all('items'); 23 | $repository->reorderItems($items); 24 | 25 | return new JsonResponse(['status' => 'ok']); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Controller/Ajax/AjaxController.php: -------------------------------------------------------------------------------- 1 | update($request); 22 | 23 | return new JsonResponse([]); 24 | } catch (\Throwable $e) { 25 | return new JsonResponse([ 26 | 'message' => $e->getMessage(), 27 | ], Response::HTTP_UNPROCESSABLE_ENTITY); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Controller/Auth/AuthController.php: -------------------------------------------------------------------------------- 1 | 1], methods: ['GET'])] 17 | public function index( 18 | Request $request, 19 | #[MapEntity(mapping: ['slug' => 'slug'])] City $city, 20 | CityService $service): Response 21 | { 22 | $searchParams = $service->getSearchParams($request, $city); 23 | $properties = $service->getProperties($searchParams); 24 | $siteOptions = $service->decorateOptions($this->site($request), $city); 25 | 26 | return $this->render('property/index.html.twig', 27 | [ 28 | 'site' => $siteOptions, 29 | 'properties' => $properties, 30 | 'searchParams' => $searchParams, 31 | ] 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Controller/Traits/MenuTrait.php: -------------------------------------------------------------------------------- 1 | doctrine = $doctrine; 18 | } 19 | 20 | protected function menu(Request $request): array 21 | { 22 | return [ 23 | 'menu' => $this->doctrine->getRepository(Menu::class) 24 | ->findBy([ 25 | 'locale' => $request->getLocale(), 26 | ], ['sort_order' => 'ASC']), 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Controller/User/ProfileController.php: -------------------------------------------------------------------------------- 1 | getUser(); 22 | $profile = $user->getProfile(); 23 | $form = $this->createForm(ProfileType::class, $profile); 24 | $form->handleRequest($request); 25 | 26 | if ($form->isSubmitted() && $form->isValid()) { 27 | $entityManager->persist($profile); 28 | $entityManager->flush(); 29 | $this->addFlash('success', 'message.updated'); 30 | } 31 | 32 | return $this->render('user/profile/profile.html.twig', [ 33 | 'site' => $this->site($request), 34 | 'form' => $form, 35 | ]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Controller/User/SecurityController.php: -------------------------------------------------------------------------------- 1 | render('user/security/security.html.twig', [ 18 | 'site' => $this->site($request), 19 | ]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/DataFixtures/CategoryFixtures.php: -------------------------------------------------------------------------------- 1 | getCategoryData() as [$slug, $name]) { 16 | $category = new Category(); 17 | $category->setName($name); 18 | $category->setSlug($slug); 19 | $manager->persist($category); 20 | $this->addReference($name, $category); 21 | } 22 | $manager->flush(); 23 | } 24 | 25 | private function getCategoryData(): array 26 | { 27 | return [ 28 | // $categoryData = [$slug, $name]; 29 | ['apartment', 'Apartment'], 30 | ['duplex', 'Duplex'], 31 | ['penthouse', 'Penthouse'], 32 | ['villa', 'Villa'], 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DataFixtures/CityFixtures.php: -------------------------------------------------------------------------------- 1 | getCityData() as [$slug, $name, $title]) { 16 | $city = new City(); 17 | $city->setName($name); 18 | $city->setSlug($slug); 19 | $city->setTitle($title); 20 | $city->setMetaTitle($title); 21 | $manager->persist($city); 22 | $this->addReference($name, $city); 23 | } 24 | $manager->flush(); 25 | } 26 | 27 | private function getCityData(): array 28 | { 29 | return [ 30 | // $cityData = [$slug, $name, $title]; 31 | ['miami', 'Miami', 'Miami Luxury Real Estate'], 32 | ['palm-beach', 'Palm Beach', 'West Palm Beach, FL Apartments'], 33 | ['tampa', 'Tampa', 'Tampa Real Estate'], 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DataFixtures/DealTypeFixtures.php: -------------------------------------------------------------------------------- 1 | getDealTypeData() as [$slug, $name]) { 16 | $dealType = new DealType(); 17 | $dealType->setName($name); 18 | $dealType->setSlug($slug); 19 | $manager->persist($dealType); 20 | $this->addReference($name, $dealType); 21 | } 22 | $manager->flush(); 23 | } 24 | 25 | private function getDealTypeData(): array 26 | { 27 | return [ 28 | ['rent', 'Rent'], 29 | ['sale', 'Sale'], 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DataFixtures/DistrictFixtures.php: -------------------------------------------------------------------------------- 1 | getDistrictData() as [$city, $name, $slug]) { 18 | $district = new District(); 19 | $district->setCity($city); 20 | $district->setName($name); 21 | $district->setSlug($slug); 22 | $manager->persist($district); 23 | $this->addReference($name, $district); 24 | } 25 | $manager->flush(); 26 | } 27 | 28 | private function getDistrictData(): array 29 | { 30 | return [ 31 | [$this->getReference('Tampa', City::class), 'South Tampa', 'south-tampa'], 32 | ]; 33 | } 34 | 35 | public function getDependencies(): array 36 | { 37 | return [ 38 | CityFixtures::class, 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DataFixtures/FeatureFixtures.php: -------------------------------------------------------------------------------- 1 | getFeatureData() as [$name, $icon]) { 16 | $feature = new Feature(); 17 | $feature->setName($name); 18 | $feature->setIcon($icon); 19 | $manager->persist($feature); 20 | $this->addReference($name, $feature); 21 | } 22 | $manager->flush(); 23 | } 24 | 25 | private function getFeatureData(): array 26 | { 27 | return [ 28 | ['Air conditioning', null], 29 | ['Balcony', null], 30 | ['Elevator', null], 31 | ['Fire Alarm', null], 32 | ['First Floor Entry', null], 33 | ['High Impact Doors', null], 34 | ['Patio', null], 35 | ['Secure parking', ''], 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/DataFixtures/MenuFixtures.php: -------------------------------------------------------------------------------- 1 | getMenuData() as [$title, $url, $locale]) { 16 | $menu = new Menu(); 17 | $menu->setTitle($title); 18 | $menu->setUrl($url); 19 | $menu->setLocale($locale); 20 | $manager->persist($menu); 21 | $this->addReference($title, $menu); 22 | } 23 | $manager->flush(); 24 | } 25 | 26 | private function getMenuData(): array 27 | { 28 | return [ 29 | ['Homepage', '/', 'en'], 30 | ['About Us', '/info/about-us', 'en'], 31 | ['Contact', '/info/contact', 'en'], 32 | ['Начало', '/', 'bg'], 33 | ['За нас', '/info/about-us', 'bg'], 34 | ['Контакти', '/info/contact', 'bg'], 35 | ['Source Code', 'https://github.com/Coderberg/ResidenceCMS', 'en'], 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Entity/Category.php: -------------------------------------------------------------------------------- 1 | setting_name; 28 | } 29 | 30 | public function setSettingName(string $setting_name): self 31 | { 32 | $this->setting_name = $setting_name; 33 | 34 | return $this; 35 | } 36 | 37 | public function getSettingValue(): ?string 38 | { 39 | return $this->setting_value; 40 | } 41 | 42 | public function setSettingValue(?string $setting_value): self 43 | { 44 | $this->setting_value = $setting_value; 45 | 46 | return $this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Entity/Traits/CityTrait.php: -------------------------------------------------------------------------------- 1 | city; 19 | } 20 | 21 | public function setCity(?City $city): self 22 | { 23 | $this->city = $city; 24 | 25 | return $this; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Entity/Traits/EntityIdTrait.php: -------------------------------------------------------------------------------- 1 | id; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Entity/Traits/EntityMetaTrait.php: -------------------------------------------------------------------------------- 1 | meta_title; 21 | } 22 | 23 | public function setMetaTitle(?string $meta_title): self 24 | { 25 | $this->meta_title = $meta_title; 26 | 27 | return $this; 28 | } 29 | 30 | public function getMetaDescription(): ?string 31 | { 32 | return $this->meta_description; 33 | } 34 | 35 | public function setMetaDescription(?string $meta_description): self 36 | { 37 | $this->meta_description = $meta_description; 38 | 39 | return $this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Entity/Traits/EntityNameTrait.php: -------------------------------------------------------------------------------- 1 | name; 23 | } 24 | 25 | public function setName(string $name): self 26 | { 27 | $this->name = $name; 28 | 29 | return $this; 30 | } 31 | 32 | public function getSlug(): ?string 33 | { 34 | return $this->slug; 35 | } 36 | 37 | public function setSlug(string $slug): self 38 | { 39 | $this->slug = $slug; 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Entity/Traits/EntityTimestampableTrait.php: -------------------------------------------------------------------------------- 1 | 'CURRENT_TIMESTAMP'])] 13 | private ?\DateTimeInterface $createdAt = null; 14 | 15 | #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true, options: ['default' => 'CURRENT_TIMESTAMP'])] 16 | private ?\DateTimeInterface $updatedAt = null; 17 | 18 | public function getCreatedAt(): ?\DateTimeInterface 19 | { 20 | return $this->createdAt; 21 | } 22 | 23 | public function setCreatedAt(?\DateTimeInterface $createdAt): self 24 | { 25 | $this->createdAt = $createdAt; 26 | 27 | return $this; 28 | } 29 | 30 | public function getUpdatedAt(): ?\DateTimeInterface 31 | { 32 | return $this->updatedAt; 33 | } 34 | 35 | public function setUpdatedAt(?\DateTimeInterface $updatedAt): self 36 | { 37 | $this->updatedAt = $updatedAt; 38 | 39 | return $this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Entity/Traits/TwoFactorTrait.php: -------------------------------------------------------------------------------- 1 | googleAuthenticatorSecret; 18 | } 19 | 20 | public function getGoogleAuthenticatorUsername(): string 21 | { 22 | return $this->username; 23 | } 24 | 25 | public function getGoogleAuthenticatorSecret(): ?string 26 | { 27 | return $this->googleAuthenticatorSecret; 28 | } 29 | 30 | public function setGoogleAuthenticatorSecret(?string $googleAuthenticatorSecret): void 31 | { 32 | $this->googleAuthenticatorSecret = $googleAuthenticatorSecret; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/EventListener/ExceptionListener.php: -------------------------------------------------------------------------------- 1 | getThrowable(); 16 | if ($exception instanceof TokenNotFoundException) { 17 | $customResponse = new JsonResponse([ 18 | 'status' => 'fail', 'message' => $exception->getMessage(), 19 | ], 419); 20 | $event->setResponse($customResponse); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Form/Type/CategoryType.php: -------------------------------------------------------------------------------- 1 | add('name', null, [ 27 | 'attr' => [ 28 | 'autofocus' => true, 29 | ], 30 | 'label' => 'label.name', 31 | ]) 32 | ->add('slug', null, [ 33 | 'label' => 'label.slug', 34 | ]); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function configureOptions(OptionsResolver $resolver): void 41 | { 42 | $resolver->setDefaults([ 43 | 'data_class' => Category::class, 44 | ]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Form/Type/DistrictType.php: -------------------------------------------------------------------------------- 1 | add('city', EntityType::class, [ 23 | 'class' => City::class, 24 | 'choice_label' => 'name', 25 | 'label' => 'city', 26 | ]) 27 | ->add('name', null, [ 28 | 'label' => 'label.district_name', 29 | ]) 30 | ->add('slug', null, [ 31 | 'label' => 'label.slug', 32 | ]); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setDefaults([ 41 | 'data_class' => District::class, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Form/Type/FeatureType.php: -------------------------------------------------------------------------------- 1 | add('name', null, [ 21 | 'attr' => [ 22 | 'autofocus' => true, 23 | ], 24 | 'label' => 'label.feature', 25 | ]) 26 | ->add('icon', null, [ 27 | 'attr' => [ 28 | 'rows' => '1', 29 | 'placeholder' => 'placeholder.example_icon', 30 | ], 31 | 'label' => 'label.custom_icon', 32 | ]); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setDefaults([ 41 | 'data_class' => Feature::class, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Form/Type/LanguageType.php: -------------------------------------------------------------------------------- 1 | setDefaults([ 16 | 'attr' => [ 17 | 'class' => 'form-control', 18 | ], 19 | 'choice_loader' => null, 20 | 'choices' => [ 21 | 'Select language' => '', 22 | 'English' => 'en', 23 | 'Nederlands' => 'nl', 24 | 'Български' => 'bg', 25 | 'Русский' => 'ru', 26 | 'Magyar' => 'hu', 27 | ], 28 | 'label' => 'label.locale', 29 | ]); 30 | } 31 | 32 | public function getParent(): string 33 | { 34 | return LocaleType::class; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Form/Type/MetroType.php: -------------------------------------------------------------------------------- 1 | add('city', EntityType::class, [ 23 | 'class' => City::class, 24 | 'choice_label' => 'name', 25 | 'label' => 'city', 26 | ]) 27 | ->add('name', null, [ 28 | 'label' => 'label.metro_station_name', 29 | ]) 30 | ->add('slug', null, [ 31 | 'label' => 'label.slug', 32 | ]); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setDefaults([ 41 | 'data_class' => Metro::class, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Form/Type/NeighborhoodType.php: -------------------------------------------------------------------------------- 1 | add('city', EntityType::class, [ 23 | 'class' => City::class, 24 | 'choice_label' => 'name', 25 | 'label' => 'city', 26 | ]) 27 | ->add('name', null, [ 28 | 'label' => 'label.neighborhood_name', 29 | ]) 30 | ->add('slug', null, [ 31 | 'label' => 'label.slug', 32 | ]); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setDefaults([ 41 | 'data_class' => Neighborhood::class, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Form/Type/PasswordType.php: -------------------------------------------------------------------------------- 1 | add('password', null, [ 22 | 'attr' => [ 23 | 'autofocus' => true, 24 | ], 25 | 'label' => 'label.new_password', 26 | 'constraints' => [new Length(['min' => 5])], 27 | ]) 28 | ->add('password_confirmation', null, [ 29 | 'label' => 'label.confirm_password', 30 | 'constraints' => [new Length(['min' => 5]), new ConfirmPassword()], 31 | ]); 32 | } 33 | 34 | /** 35 | * {@inheritdoc} 36 | */ 37 | public function configureOptions(OptionsResolver $resolver): void 38 | { 39 | $resolver->setDefaults([]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Form/Type/UserEmailType.php: -------------------------------------------------------------------------------- 1 | add('email', EmailType::class, [ 23 | 'attr' => [ 24 | 'placeholder' => 'placeholder.enter_email', 25 | ], 26 | 'label' => 'label.email', 27 | 'constraints' => [new Length(['min' => 5]), new RegisteredUser()], 28 | ]); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function configureOptions(OptionsResolver $resolver): void 35 | { 36 | $resolver->setDefaults([]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | mailer->send($email); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Message/DeletePhotos.php: -------------------------------------------------------------------------------- 1 | property; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Message/SendEmailConfirmationLink.php: -------------------------------------------------------------------------------- 1 | user; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Message/SendFeedback.php: -------------------------------------------------------------------------------- 1 | feedback; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Message/SendResetPasswordLink.php: -------------------------------------------------------------------------------- 1 | user; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/MessageHandler/DeletePhotosHandler.php: -------------------------------------------------------------------------------- 1 | getProperty()->getPhotos(); 21 | 22 | foreach ($photos as $photo) { 23 | $this->fileUploader->remove($photo->getPhoto()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/MessageHandler/SendFeedbackHandler.php: -------------------------------------------------------------------------------- 1 | getFeedback(); 24 | 25 | $subject = $this->translator->trans('email.new_message'); 26 | 27 | $email = (new Email()) 28 | ->from(new Address($feedback->getFromEmail(), $feedback->getFromName())) 29 | ->to($feedback->getToEmail()) 30 | ->replyTo($feedback->getFromEmail()) 31 | ->subject($subject) 32 | ->text($feedback->getMessage()) 33 | ->html($feedback->getMessage()); 34 | 35 | $this->mailer->send($email); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Middleware/ThrottleRequests.php: -------------------------------------------------------------------------------- 1 | authLimiter->create( 20 | $request->getClientIp().$request->getPathInfo().$request->getMethod() 21 | ); 22 | 23 | if (!$limiter->consume(1)->isAccepted()) { 24 | throw new TooManyRequestsHttpException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | isCsrfTokenValid($this->getToken($request))) { 21 | throw new TokenNotFoundException('Sorry, your session has expired. Please refresh and try again.'); 22 | } 23 | } 24 | 25 | private function isCsrfTokenValid(?string $token): bool 26 | { 27 | return $this->tokenManager->isTokenValid(new CsrfToken('csrf_token', $token)); 28 | } 29 | 30 | private function getToken(Request $request) 31 | { 32 | return $request->headers->get('x-csrf-token') 33 | ?? $request->get('csrf_token'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Repository/CategoryRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('c') 33 | ->select('count(c.id)') 34 | ->getQuery() 35 | ->getSingleScalarResult(); 36 | 37 | return (int) $count; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Repository/CityRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('l') 33 | ->select('count(l.id)') 34 | ->getQuery() 35 | ->getSingleScalarResult(); 36 | 37 | return (int) $count; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Repository/CurrencyRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('o') 33 | ->select('count(o.id)') 34 | ->getQuery() 35 | ->getSingleScalarResult(); 36 | 37 | return (int) $count; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Repository/DistrictRepository.php: -------------------------------------------------------------------------------- 1 | setPassword($plainPassword); 21 | $user->setConfirmationToken(null); 22 | $user->setPasswordRequestedAt(null); 23 | $user = $this->transformer->transform($user); 24 | $this->save($user); 25 | } 26 | 27 | public function setToken(User $user, string $token): void 28 | { 29 | $user->setConfirmationToken($token); 30 | $user->setPasswordRequestedAt(new \DateTime()); 31 | $this->save($user); 32 | } 33 | 34 | private function save(User $user): void 35 | { 36 | $em = $this->getEntityManager(); 37 | $em->persist($user); 38 | $em->flush(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Repository/UserPropertyRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('p') 15 | ->where('p.author = :id'); 16 | 17 | if ('published' === $params['state']) { 18 | $qb->andWhere("p.state = 'published'"); 19 | } else { 20 | $qb->andWhere("p.state != 'published'"); 21 | } 22 | 23 | $qb->orderBy('p.id', 'DESC') 24 | ->setParameter('id', $params['user']); 25 | 26 | return $this->createPaginator($qb->getQuery(), $params['page']); 27 | } 28 | 29 | public function changeState(Property $property, string $state): bool 30 | { 31 | try { 32 | $property->setState($state); 33 | $em = $this->getEntityManager(); 34 | $em->persist($property); 35 | $em->flush(); 36 | 37 | return true; 38 | } catch (\Throwable) { 39 | return false; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Repository/UserRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('u') 33 | ->select('count(u.id)') 34 | ->getQuery() 35 | ->getSingleScalarResult(); 36 | 37 | return (int) $count; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Service/Admin/DashboardService.php: -------------------------------------------------------------------------------- 1 | getCount('properties_count', Property::class); 22 | } 23 | 24 | public function countCities(): int 25 | { 26 | return $this->getCount('cities_count', City::class); 27 | } 28 | 29 | public function countDealTypes(): int 30 | { 31 | return $this->getCount('deal_types_count', DealType::class); 32 | } 33 | 34 | public function countCategories(): int 35 | { 36 | return $this->getCount('categories_count', Category::class); 37 | } 38 | 39 | public function countPages(): int 40 | { 41 | return $this->getCount('pages_count', Page::class); 42 | } 43 | 44 | public function countUsers(): int 45 | { 46 | return $this->getCount('users_count', User::class); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Service/Auth/EmailVerifier.php: -------------------------------------------------------------------------------- 1 | verifyEmailHelper->validateEmailConfirmationFromRequest( 23 | request: $request, 24 | userId: (string) $user->getId(), 25 | userEmail: $user->getEmail() 26 | ); 27 | 28 | $user->setEmailVerifiedAt(new \DateTime('now')); 29 | 30 | $this->entityManager->persist($user); 31 | $this->entityManager->flush(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Service/Cache/ClearCache.php: -------------------------------------------------------------------------------- 1 | delete($key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Service/Cache/UserDataCache.php: -------------------------------------------------------------------------------- 1 | getKey($user); 19 | $cache = new FilesystemAdapter(); 20 | $sentAt = $cache->getItem($key); 21 | $sentAt->set(new \DateTime('now')); 22 | $sentAt->expiresAfter(3600); 23 | $cache->save($sentAt); 24 | } 25 | 26 | /** 27 | * @throws InvalidArgumentException 28 | */ 29 | public function getConfirmationSentAt(User $user): ?\DateTimeInterface 30 | { 31 | $key = $this->getKey($user); 32 | $cache = new FilesystemAdapter(); 33 | $sentAt = $cache->getItem($key); 34 | 35 | return $sentAt->get(); 36 | } 37 | 38 | private function getKey(User $user): string 39 | { 40 | return \sprintf('user.%s.email_confirmation_link.sent_at', $user->getId()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Service/User/GoogleAuthenticatorAdapter.php: -------------------------------------------------------------------------------- 1 | authenticator->checkCode($user, $code); 22 | } 23 | 24 | public function getQRContent(TwoFactorInterface $user): string 25 | { 26 | return str_replace( 27 | 'issuer_placeholder', 28 | ucfirst($this->requestStack->getCurrentRequest()->getHost()), 29 | $this->authenticator->getQRContent($user) 30 | ); 31 | } 32 | 33 | public function generateSecret(): string 34 | { 35 | return $this->authenticator->generateSecret(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Transformer/PropertyTransformer.php: -------------------------------------------------------------------------------- 1 | transformContent($property, HtmlHelper::text2Html(...)); 15 | } 16 | 17 | public function contentToPlainText(Property $property): Property 18 | { 19 | return $this->transformContent($property, HtmlHelper::html2Text(...)); 20 | } 21 | 22 | public function removeScriptsFromHtml(Property $property): Property 23 | { 24 | return $this->transformContent($property, HtmlHelper::removeScriptsFromHtml(...)); 25 | } 26 | 27 | private function transformContent(Property $property, callable $transformFunction): Property 28 | { 29 | $content = $property->getPropertyDescription()->getContent(); 30 | $transformedContent = \call_user_func($transformFunction, $content); 31 | $property->setPropertyDescription( 32 | $property->getPropertyDescription()->setContent($transformedContent) 33 | ); 34 | 35 | return $property; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Transformer/RequestToArrayTransformer.php: -------------------------------------------------------------------------------- 1 | $request->query->getInt('city'), 15 | 'deal_type' => $request->query->getInt('deal_type'), 16 | 'category' => $request->query->getInt('category'), 17 | 'bedrooms' => $request->query->getInt('bedrooms'), 18 | 'guests' => $request->query->getInt('guests'), 19 | 'feature' => $request->query->getInt('feature'), 20 | 'sort_by' => $request->query->get('sort_by', 'priority_number'), 21 | 'state' => $request->query->get('state', 'published'), 22 | 'page' => $request->query->getInt('page', 1), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Transformer/UserTransformer.php: -------------------------------------------------------------------------------- 1 | setRoles($user); 19 | 20 | return $this->setEncodedPassword($user); 21 | } 22 | 23 | private function setRoles(User $user): User 24 | { 25 | if (\in_array('ROLE_ADMIN', $user->getRoles(), true)) { 26 | $user->setRoles(['ROLE_ADMIN', 'ROLE_USER']); 27 | } else { 28 | $user->setRoles(['ROLE_USER']); 29 | } 30 | 31 | return $user; 32 | } 33 | 34 | private function setEncodedPassword(User $user): User 35 | { 36 | $password = $user->getPassword(); 37 | $user->setPassword($this->passwordHasher->hashPassword($user, (string) $password)); 38 | 39 | return $user; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Twig/AppExtension.php: -------------------------------------------------------------------------------- 1 | showPageNumber(...)), 21 | ]; 22 | } 23 | 24 | public function showPageNumber($number = 1): string 25 | { 26 | return ($number > 1) ? ' - '.$this->translator->trans('page').' '.$number : ''; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Utils/HtmlHelper.php: -------------------------------------------------------------------------------- 1 | #i', "\n", $html); 12 | 13 | return strip_tags((string) $text); 14 | } 15 | 16 | public static function text2Html(string $text): string 17 | { 18 | return preg_replace("/\r\n|\r|\n/", '
', $text); 19 | } 20 | 21 | public static function removeScriptsFromHtml(string $html): string 22 | { 23 | $sanitizedHtml = preg_replace('#(.*?)#is', '', $html); 24 | $sanitizedHtml = preg_replace('# on\w+="[^"]*"#i', '', (string) $sanitizedHtml); 25 | 26 | return preg_replace("# on\w+='[^']*'#i", '', (string) $sanitizedHtml); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Utils/Slugger.php: -------------------------------------------------------------------------------- 1 | slug($string)->lower(); 26 | 27 | return (string) $slug; 28 | } 29 | 30 | private static function ascii($value, $language = 'en'): string 31 | { 32 | return ASCII::to_ascii((string) $value, $language); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Utils/SluggerInterface.php: -------------------------------------------------------------------------------- 1 | get('email_verified')->getNormData(); 14 | } 15 | 16 | public function getEmailVerifiedAt(FormInterface $form): ?\DateTime 17 | { 18 | return $this->getEmailVerified($form) 19 | ? new \DateTime('now') 20 | : null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Validator/ConfirmPassword.php: -------------------------------------------------------------------------------- 1 | context->getRoot()->get('password')->getData(); 20 | 21 | if ($password !== $value) { 22 | $this->context->buildViolation($constraint->message) 23 | ->atPath('password_confirmation') 24 | ->addViolation(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Validator/PhotoRequirements.php: -------------------------------------------------------------------------------- 1 | 'Please select a file to upload', 18 | ]), 19 | new File([ 20 | 'maxSize' => '12M', 21 | 'mimeTypes' => [ 22 | 'image/*', 23 | ], 24 | ]), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Validator/RegisteredUser.php: -------------------------------------------------------------------------------- 1 | userRepository->findOneBy(['email' => $value]); 26 | 27 | if (!$existingUser instanceof User) { 28 | $this->context->buildViolation($constraint->message) 29 | ->addViolation(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /templates/admin/category/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/category/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_category' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_category' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_widget(form) }} 15 | 16 |
17 | 18 | 21 | 22 |
23 | 24 | {{ form_end(form) }} 25 | 26 |
27 |

28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/admin/city/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/city/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_city' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_city' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_widget(form) }} 15 | 16 |
17 | 18 | 21 | 22 |
23 | 24 | {{ form_end(form) }} 25 | 26 |
27 |

28 | 29 | {% endblock %} 30 | 31 | {% block javascripts %}{% endblock %} 32 | -------------------------------------------------------------------------------- /templates/admin/currency/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/currency/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_currency' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_currency' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_widget(form) }} 15 | 16 |
17 | 18 | 21 | 22 |
23 | 24 | {{ form_end(form) }} 25 | 26 |
27 |

28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/admin/deal_type/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/deal_type/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_deal_type' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_deal_type' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_widget(form) }} 15 | 16 |
17 | 18 | 21 | 22 |
23 | 24 | {{ form_end(form) }} 25 | 26 |
27 |

28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/admin/district/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/district/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_district' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 | 9 |
10 | {{ 'title.edit_district' | trans }} 11 |
12 | 13 |
14 | 15 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 16 | 17 | {{ form_start(form) }} 18 | {{ form_widget(form) }} 19 | 20 |
21 | 22 | 25 | 26 |
27 | 28 | {{ form_end(form) }} 29 | 30 |
31 |

32 | 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /templates/admin/feature/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/feature/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_feature' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_feature' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_widget(form) }} 15 | 16 |
17 | 18 | 21 | 22 |
23 | 24 | {{ form_end(form) }} 25 | 26 |
27 |

28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/admin/menu/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/metro/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/metro/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_metro_station' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_metro_station' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_widget(form) }} 15 | 16 |
17 | 18 | 21 | 22 |
23 | 24 | {{ form_end(form) }} 25 | 26 |
27 |

28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/admin/neighborhood/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/neighborhood/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_neighborhood' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 | 9 |
10 | {{ 'title.edit_neighborhood' | trans }} 11 |
12 | 13 |
14 | 15 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 16 | 17 | {{ form_start(form) }} 18 | {{ form_widget(form) }} 19 | 20 |
21 | 22 | 25 | 26 |
27 | 28 | {{ form_end(form) }} 29 | 30 |
31 |

32 | 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /templates/admin/page/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 |
7 | 8 | {{ 'action.edit' | trans }} 10 | 11 | {{ include('common/_delete_button.html.twig') }} 12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /templates/admin/photo/_delete_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | 13 | 14 |
15 | -------------------------------------------------------------------------------- /templates/admin/property/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 |
5 | 6 | {{ 'action.edit' | trans }} 8 | 9 | {{ 'action.edit_photos' | trans }} 11 | 12 | {{ include('common/_delete_button.html.twig') }} 13 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /templates/admin/settings/partials/_filter_settings_form.html.twig: -------------------------------------------------------------------------------- 1 | {{ form_start(form) }} 2 | 3 |
4 | 5 |
6 | {{ form_row(form.show_filter_by_city) }} 7 |
8 | 9 |
10 | {{ form_row(form.show_filter_by_deal_type) }} 11 |
12 | 13 |
14 | {{ form_row(form.show_filter_by_category) }} 15 |
16 | 17 |
18 | {{ form_row(form.show_filter_by_bedrooms) }} 19 |
20 | 21 |
22 | {{ form_row(form.show_filter_by_guests) }} 23 |
24 | 25 |
26 | {{ form_row(form.show_filter_by_features) }} 27 |
28 |
29 | 30 |
31 | 34 |
35 | 36 | {{ form_end(form) }} 37 | -------------------------------------------------------------------------------- /templates/admin/settings/partials/_nav_tabs.html.twig: -------------------------------------------------------------------------------- 1 | {% set currentPath = path(app.request.attributes.get('_route')) %} 2 | {% set mapClass = '' %} 3 | {% set headerClass = '' %} 4 | {% set mainClass = '' %} 5 | 6 | {% if '/map' in currentPath %} 7 | {% set mapClass = 'active' %} 8 | {% elseif '/header' in currentPath %} 9 | {% set headerClass = 'active' %} 10 | {% else %} 11 | {% set mainClass = 'active' %} 12 | {% endif %} 13 | 14 | 31 | -------------------------------------------------------------------------------- /templates/admin/user/_action_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | {{ 'action.edit' | trans }}     8 | 9 | {{ include('common/_delete_button.html.twig') }} 10 | 11 |
12 | -------------------------------------------------------------------------------- /templates/admin/user/edit.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'admin/layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.edit_user' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
{{ 'title.edit_user' | trans }}
9 |
10 | 11 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 12 | 13 | {{ form_start(form) }} 14 | {{ form_row(form.roles) }} 15 | {{ form_row(form.profile.full_name) }} 16 | {{ form_row(form.username) }} 17 | {{ form_row(form.profile.phone) }} 18 | {{ form_row(form.email) }} 19 | {{ form_row(form.email_verified) }} 20 | {{ form_row(form.password) }} 21 | 22 |
23 | 24 | 27 | 28 |
29 | 30 | {{ form_end(form) }} 31 | 32 |
33 |

34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /templates/auth/passwords/password_change.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.set_new_pass' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
9 |
10 |
{{ 'title.set_new_pass'|trans }}
11 | 12 |
13 | 14 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 15 | 16 | {{ form_start(form) }} 17 | {{ form_row(form.password) }} 18 | {{ form_row(form.password_confirmation) }} 19 | 20 |
21 |
22 | 25 |
26 |
27 | 28 | {{ form_end(form) }} 29 | 30 |
31 |
32 |
33 |
34 | 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /templates/auth/passwords/password_reset.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.reset_password' | trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 |
9 |
10 |
{{ 'title.reset_password' | trans }}
11 |
12 | 13 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 14 | {{ form_start(form) }} 15 | {{ form_widget(form) }} 16 | 17 |
18 | 21 |
22 | 23 | {{ form_end(form) }} 24 |
25 |
26 |
27 |
28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /templates/bundles/TwigBundle/Exception/error404.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "layout/base.html.twig" %} 2 | 3 | {% block title %}404 Not Found{% endblock %} 4 | 5 | {% block stylesheets %} 6 | 7 | 12 | 13 | {% endblock %} 14 | 15 | {% block body %} 16 | 17 |
18 |

404 

19 |

page not found

20 |

Go to Homepage

21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/bundles/TwigBundle/Exception/error429.html.twig: -------------------------------------------------------------------------------- 1 | {% extends "layout/base.html.twig" %} 2 | 3 | {% block title %}429 Too Many Requests{% endblock %} 4 | 5 | {% block stylesheets %} 6 | 7 | 12 | 13 | {% endblock %} 14 | 15 | {% block body %} 16 | 17 |
18 |

429 

19 |

Too Many Requests

20 |

Go to Homepage

21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/common/_delete_button.html.twig: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /templates/common/_flash_messages.html.twig: -------------------------------------------------------------------------------- 1 | {% for type, messages in app.flashes %} 2 | {% for message in messages %} 3 | {# Bootstrap alert, see https://getbootstrap.com/docs/4.4/components/alerts/ #} 4 | 11 | {% endfor %} 12 | {% endfor %} 13 | 14 | {% if app.user %} 15 | {% if not app.user.isVerified %} 16 | 24 | {% endif %} 25 | {% endif %} 26 | -------------------------------------------------------------------------------- /templates/emails/confirmation_email.html.twig: -------------------------------------------------------------------------------- 1 | {% set expiresAt = expiresAtMessageKey | trans(expiresAtMessageData, 'VerifyEmailBundle') %} 2 | 3 | {{ 4 | 'confirmation.email.message' | trans({ 5 | '%signedUrl%': signedUrl, 6 | '%expiresAt%': expiresAt 7 | }) | raw 8 | }} 9 | -------------------------------------------------------------------------------- /templates/emails/reset.txt.twig: -------------------------------------------------------------------------------- 1 | {{ 2 | 'resetting.email.message' | trans({ 3 | '%username%': username, 4 | '%confirmationUrl%': confirmationUrl 5 | }) 6 | }} 7 | -------------------------------------------------------------------------------- /templates/page/partials/_contact_form.html.twig: -------------------------------------------------------------------------------- 1 | {% form_theme form 'bootstrap_4_layout.html.twig' %} 2 | 3 | {{ form_start(form) }} 4 | 5 | {{ form_widget(form) }} 6 | 7 | 10 | 11 | {{ form_end(form) }} 12 | 13 |
14 | -------------------------------------------------------------------------------- /templates/sitemap/cities.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | {% for city in cities %} 7 | 8 | {{ url('city', {slug: city.slug}) }} 9 | daily 10 | 0.5 11 | 12 | {% endfor %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/sitemap/properties.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | {% for property in properties %} 7 | 8 | 9 | {{ url('property_show', { 10 | 'id': property.id, 11 | 'citySlug': property.city.slug, 12 | 'slug': property.slug 13 | }) }} 14 | 15 | {{ property.updatedAt|date('Y-m-d\\TH:i:s+00:00') }} 16 | 0.5 17 | 18 | {% endfor %} 19 | 20 | 21 | -------------------------------------------------------------------------------- /templates/sitemap/sitemap.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ url('cities_sitemap') }} 5 | daily 6 | 7 | 8 | {{ url('properties_sitemap') }} 9 | daily 10 | 11 | 12 | -------------------------------------------------------------------------------- /templates/user/common/_floating_action_button.html.twig: -------------------------------------------------------------------------------- 1 | {% if is_granted('ROLE_ADMIN') %} 2 | 3 | 4 | 5 | 6 | 7 | {% elseif app.user.isVerified %} 8 | 9 | 10 | 11 | 12 | 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /templates/user/common/_sidebar.html.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{ 'title.menu'|trans }}

5 |
6 |
7 | 8 | 25 |
26 | -------------------------------------------------------------------------------- /templates/user/photo/_delete_form.html.twig: -------------------------------------------------------------------------------- 1 |
3 | 4 | 5 | 6 | 13 | 14 |
15 | -------------------------------------------------------------------------------- /templates/user/property/index.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout/base.html.twig' %} 2 | 3 | {% block title %}{{ 'title.my_properties'|trans }}{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
8 | 9 |
10 | 11 |
12 |
13 |

{{ 'title.my_properties'|trans }} ({{ properties.getTotalItemCount }})

15 |
16 |
17 | 18 | 19 | {{ include('user/property/partials/_properties.html.twig') }} 20 |
21 | 22 | {{ include('user/common/_sidebar.html.twig') }} 23 | 24 |
25 | 26 | {{ include('user/common/_floating_action_button.html.twig') }} 27 | 28 | {{ knp_pagination_render(properties) }} 29 | 30 | {% endblock %} 31 | 32 | {% block javascripts %} 33 | 34 | {{ encore_entry_script_tags('js/user') }} 35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /tests/E2E/HomepageTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/'); 17 | $this->assertSelectorTextContains('h1', 'Popular Listing'); 18 | $this->assertPageTitleSame('Site Title'); 19 | $this->assertCount(6, $crawler->filter('.card-title')); 20 | 21 | // Page 2 22 | $crawler = $client->request('GET', '/en/?page=2'); 23 | $this->assertPageTitleSame('Site Title - Page 2'); 24 | $this->assertCount(1, $crawler->filter('.card-title')); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Functional/Controller/Admin/AbstractLocationControllerTest.php: -------------------------------------------------------------------------------- 1 | client = $this->authAsAdmin($this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Functional/Controller/Admin/DashboardControllerTest.php: -------------------------------------------------------------------------------- 1 | authAsAdmin($this); 18 | $client->request(Request::METHOD_GET, '/en/admin'); 19 | $this->assertResponseIsSuccessful(\sprintf('The %s public URL loads correctly.', '/admin')); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Functional/Controller/SitemapControllerTest.php: -------------------------------------------------------------------------------- 1 | request(Request::METHOD_GET, '/en/sitemap.xml'); 18 | $this->assertResponseHeaderSame('Content-Type', self::CONTENT_TYPE); 19 | } 20 | 21 | public function testCitiesSitemap(): void 22 | { 23 | $client = self::createClient(); 24 | $client->request(Request::METHOD_GET, '/en/sitemap/cities.xml'); 25 | $this->assertResponseHeaderSame('Content-Type', self::CONTENT_TYPE); 26 | } 27 | 28 | public function testPropertiesSitemap(): void 29 | { 30 | $client = self::createClient(); 31 | $client->request(Request::METHOD_GET, '/en/sitemap/properties.xml'); 32 | $this->assertResponseHeaderSame('Content-Type', self::CONTENT_TYPE); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Helper/PantherTestHelper.php: -------------------------------------------------------------------------------- 1 | request('GET', '/en/login'); 15 | $form = $crawler->selectButton('Sign in')->form([ 16 | 'login_form[username]' => $username, 17 | 'login_form[password]' => $password, 18 | ]); 19 | 20 | return $client->submit($form); 21 | } 22 | 23 | public function logout(PantherClient $client): void 24 | { 25 | $client->request('GET', '/en/logout'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Integration/Command/ListUsersCommandTest.php: -------------------------------------------------------------------------------- 1 | find('app:list-users'); 18 | $commandTester = new CommandTester($command); 19 | $commandTester->execute([]); 20 | 21 | // the output of the command in the console 22 | $output = $commandTester->getDisplay(); 23 | $this->assertStringContainsString('Rhonda Jordan', $output); 24 | $this->assertStringContainsString('John Smith', $output); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Integration/Transformer/RequestToArrayTransformerTest.php: -------------------------------------------------------------------------------- 1 | query->set('city', 3); 17 | $request->query->set('deal_type', 2); 18 | $request->query->set('category', 4); 19 | $request->query->set('bedrooms', 5); 20 | $request->query->set('page', 2); 21 | 22 | $transformer = new RequestToArrayTransformer(); 23 | 24 | $array = $transformer->transform($request); 25 | 26 | $this->assertSame($array['city'], 3); 27 | $this->assertSame($array['deal_type'], 2); 28 | $this->assertSame($array['category'], 4); 29 | $this->assertSame($array['bedrooms'], 5); 30 | $this->assertSame($array['page'], 2); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Unit/Utils/SluggerTest.php: -------------------------------------------------------------------------------- 1 | assertSame($slugIntl, Slugger::slugify($string)); 25 | } else { 26 | $this->assertSame($slug, Slugger::slugify($string)); 27 | } 28 | } 29 | 30 | private function getSlugs(): \Generator 31 | { 32 | yield ['Lorem Ipsum', 'lorem-ipsum', 'lorem-ipsum']; 33 | yield ['Lorem Ipsum!', 'lorem-ipsum', 'lorem-ipsum']; 34 | yield ['Русский текст', 'russkii-tekst', 'russkij-tekst']; 35 | yield [' Русский текст !!!', 'russkii-tekst', 'russkij-tekst']; 36 | yield ['Lorem Ipsum №69!', 'lorem-ipsum-69', 'lorem-ipsum-no69']; 37 | yield ['Magyar nyelvű szöveg', 'magyar-nyelvu-szoveg', 'magyar-nyelvu-szoveg']; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__).'/.env'); 11 | } 12 | 13 | if ($_SERVER['APP_DEBUG']) { 14 | umask(0o000); 15 | } 16 | 17 | if (isset($_ENV['BOOTSTRAP_CLEAR_CACHE_ENV'])) { 18 | // executes the "php bin/console cache:clear" command 19 | passthru(sprintf( 20 | 'APP_ENV=%s php "%s/../bin/console" cache:clear --no-warmup', 21 | $_ENV['BOOTSTRAP_CLEAR_CACHE_ENV'], 22 | __DIR__ 23 | )); 24 | } 25 | -------------------------------------------------------------------------------- /translations/validators.en.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | password.mismatch 7 | The entered passwords don't match. 8 | 9 | 10 | agree_terms 11 | You should agree to our terms. 12 | 13 | 14 | user_not_found 15 | We can't find a user with that e-mail address. 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /translations/validators.hu.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | password.mismatch 7 | A megadott jelszavak nem egyeznek meg. 8 | 9 | 10 | agree_terms 11 | El kell fogadnod az általános feltételeinket. 12 | 13 | 14 | user_not_found 15 | Nem találunk felhasználót ezzel az e-mail címmel. 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /translations/validators.ru.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | password.mismatch 7 | Введённые пароли не совпадают. 8 | 9 | 10 | agree_terms 11 | Вы должны согласиться с нашими условиями. 12 | 13 | 14 | user_not_found 15 | Не удаётся найти пользователя с таким адресом e-mail. 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------