├── .dockerignore ├── .github └── workflows │ └── docker.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── LICENSE.md ├── Makefile ├── README.md ├── bin ├── cron_worker.php └── task_worker.php ├── composer ├── composer.json ├── composer.lock ├── config └── vars.php ├── docker-compose.dev.yml ├── docker-compose.yml ├── docker ├── Dockerfile └── rootfs │ ├── bin │ └── docker-entrypoint.sh │ ├── docker-entrypoint-init.d │ └── 01-uname.sh │ ├── etc │ ├── nginx │ │ └── nginx.conf │ └── service │ │ ├── cron │ │ └── run │ │ ├── nginx │ │ └── run │ │ └── php │ │ └── run │ └── usr │ └── local │ └── etc │ └── php │ └── conf.d │ └── custom.ini ├── image.jpeg ├── orm ├── phinx ├── phinx.php ├── phpcs ├── phpunit ├── phpunit.xml ├── plugin └── installed.php ├── public ├── assets │ ├── css │ │ ├── atlantis.css │ │ └── bootstrap.min.css │ ├── img │ │ ├── icon │ │ │ ├── 100.png │ │ │ ├── 1024.png │ │ │ ├── 114.png │ │ │ ├── 120.png │ │ │ ├── 128.png │ │ │ ├── 144.png │ │ │ ├── 152.png │ │ │ ├── 16.png │ │ │ ├── 167.png │ │ │ ├── 180.png │ │ │ ├── 192.png │ │ │ ├── 20.png │ │ │ ├── 256.png │ │ │ ├── 29.png │ │ │ ├── 32.png │ │ │ ├── 40.png │ │ │ ├── 50.png │ │ │ ├── 512.png │ │ │ ├── 57.png │ │ │ ├── 58.png │ │ │ ├── 60.png │ │ │ ├── 64.png │ │ │ ├── 72.png │ │ │ ├── 76.png │ │ │ ├── 80.png │ │ │ └── 87.png │ │ ├── lviv.jpg │ │ ├── no_image.jpeg │ │ └── no_image.png │ └── js │ │ ├── core │ │ ├── bootstrap-tagsinput.min.js │ │ ├── bootstrap.min.js │ │ ├── chart.min.js │ │ ├── jquery.3.7.1.min.js │ │ ├── moment-timezone-with-data.js │ │ ├── moment-with-locales.min.js │ │ ├── moment.min.js │ │ ├── popper.min.js │ │ └── tooltip.min.js │ │ ├── cup │ │ └── script.js │ │ ├── plugin │ │ ├── bootstrap-notify │ │ │ └── bootstrap-notify.min.js │ │ ├── clusterize │ │ │ ├── clusterize.min.css │ │ │ └── clusterize.min.js │ │ ├── codemirror │ │ │ ├── addon │ │ │ │ ├── dialog │ │ │ │ │ ├── dialog.css │ │ │ │ │ └── dialog.js │ │ │ │ ├── edit │ │ │ │ │ └── matchbrackets.js │ │ │ │ ├── mode │ │ │ │ │ ├── loadmode.js │ │ │ │ │ ├── multiplex.js │ │ │ │ │ ├── multiplex_test.js │ │ │ │ │ ├── overlay.js │ │ │ │ │ └── simple.js │ │ │ │ ├── search │ │ │ │ │ ├── jump-to-line.js │ │ │ │ │ ├── search.js │ │ │ │ │ └── searchcursor.js │ │ │ │ └── selection │ │ │ │ │ ├── active-line.js │ │ │ │ │ ├── mark-selection.js │ │ │ │ │ └── selection-pointer.js │ │ │ ├── lib │ │ │ │ ├── codemirror.css │ │ │ │ └── codemirror.js │ │ │ └── mode │ │ │ │ ├── css │ │ │ │ ├── css.js │ │ │ │ ├── gss.html │ │ │ │ ├── gss_test.js │ │ │ │ ├── index.html │ │ │ │ ├── less.html │ │ │ │ ├── less_test.js │ │ │ │ ├── scss.html │ │ │ │ ├── scss_test.js │ │ │ │ └── test.js │ │ │ │ ├── htmlmixed │ │ │ │ ├── htmlmixed.js │ │ │ │ └── index.html │ │ │ │ ├── index.html │ │ │ │ ├── javascript │ │ │ │ ├── index.html │ │ │ │ ├── javascript.js │ │ │ │ ├── json-ld.html │ │ │ │ ├── test.js │ │ │ │ └── typescript.html │ │ │ │ ├── markdown │ │ │ │ ├── index.html │ │ │ │ ├── markdown.js │ │ │ │ └── test.js │ │ │ │ ├── meta.js │ │ │ │ ├── twig │ │ │ │ ├── index.html │ │ │ │ └── twig.js │ │ │ │ └── xml │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── xml.js │ │ ├── dropzone │ │ │ └── dropzone.min.js │ │ ├── froala │ │ │ ├── froala_editor.pkgd.css │ │ │ ├── froala_editor.pkgd.js │ │ │ └── froala_style.css │ │ ├── jquery-scrollbar │ │ │ └── jquery.scrollbar.min.js │ │ ├── jquery-ui-1.12.1.custom │ │ │ └── jquery-ui.min.js │ │ ├── jquery-ui-touch-punch │ │ │ └── jquery.ui.touch-punch.min.js │ │ ├── jquery.modal │ │ │ ├── jquery.modal.min.css │ │ │ └── jquery.modal.min.js │ │ ├── select2 │ │ │ └── select2.full.min.js │ │ └── sweetalert │ │ │ └── sweetalert2.all.min.js │ │ └── wse.js ├── index.php ├── resource │ └── .gitkeep └── uploads │ └── .gitkeep ├── scheme ├── migrations │ ├── 20240414153158.php │ ├── 20240702092036.php │ ├── 20240702092511.php │ ├── 20240727135000.php │ ├── 20240807113500.php │ ├── 20240807122500.php │ └── 20250226101251.php └── seeds │ └── ReferenceSeeder.php ├── src ├── Application │ ├── Actions │ │ ├── Api │ │ │ ├── ActionApi.php │ │ │ └── v1 │ │ │ │ ├── EntityAction.php │ │ │ │ ├── SearchAction.php │ │ │ │ ├── SignatureAction.php │ │ │ │ └── TelemetryAction.php │ │ ├── Auth │ │ │ ├── AuthAction.php │ │ │ ├── LoginAction.php │ │ │ ├── LogoutAction.php │ │ │ ├── RefreshTokenAction.php │ │ │ ├── RegisterAction.php │ │ │ └── RevokeTokenAction.php │ │ ├── Common │ │ │ ├── Catalog │ │ │ │ ├── CartAction.php │ │ │ │ ├── CartDoneAction.php │ │ │ │ ├── CatalogAction.php │ │ │ │ └── ListAction.php │ │ │ ├── File │ │ │ │ ├── FileAction.php │ │ │ │ ├── FileGetAction.php │ │ │ │ ├── FileUploadAction.php │ │ │ │ └── FileViewAction.php │ │ │ ├── ForbiddenPageAction.php │ │ │ ├── FormAction.php │ │ │ ├── GuestBookAction.php │ │ │ ├── MainPageAction.php │ │ │ ├── PageAction.php │ │ │ ├── Publication │ │ │ │ └── ListAction.php │ │ │ ├── SearchAction.php │ │ │ ├── User │ │ │ │ ├── UserAction.php │ │ │ │ ├── UserLoginAction.php │ │ │ │ ├── UserLogoutAction.php │ │ │ │ ├── UserProfileAction.php │ │ │ │ ├── UserRegisterAction.php │ │ │ │ ├── UserRevokeTokenAction.php │ │ │ │ └── UserSubscribeAction.php │ │ │ └── XMLFileAction.php │ │ └── Cup │ │ │ ├── Catalog │ │ │ ├── Attribute │ │ │ │ ├── AttributeCreateAction.php │ │ │ │ ├── AttributeDeleteAction.php │ │ │ │ ├── AttributeListAction.php │ │ │ │ └── AttributeUpdateAction.php │ │ │ ├── CatalogAction.php │ │ │ ├── Category │ │ │ │ ├── CategoryCreateAction.php │ │ │ │ ├── CategoryDeleteAction.php │ │ │ │ ├── CategoryListAction.php │ │ │ │ └── CategoryUpdateAction.php │ │ │ ├── CategoryStatisticAction.php │ │ │ ├── Order │ │ │ │ ├── OrderCreateAction.php │ │ │ │ ├── OrderDeleteAction.php │ │ │ │ ├── OrderDispatchAction.php │ │ │ │ ├── OrderDocumentAction.php │ │ │ │ ├── OrderExportAction.php │ │ │ │ ├── OrderListAction.php │ │ │ │ └── OrderUpdateAction.php │ │ │ └── Product │ │ │ │ ├── ProductCreateAction.php │ │ │ │ ├── ProductDeleteAction.php │ │ │ │ ├── ProductExportAction.php │ │ │ │ ├── ProductImportAction.php │ │ │ │ ├── ProductListAction.php │ │ │ │ └── ProductUpdateAction.php │ │ │ ├── EditorPageAction.php │ │ │ ├── File │ │ │ ├── FileAction.php │ │ │ ├── FileDeleteAction.php │ │ │ ├── FileListAction.php │ │ │ └── Image │ │ │ │ ├── DeleteAction.php │ │ │ │ └── GetAction.php │ │ │ ├── ForbiddenPageAction.php │ │ │ ├── Form │ │ │ ├── Data │ │ │ │ ├── DataDeleteAction.php │ │ │ │ ├── DataListAction.php │ │ │ │ ├── DataPreviewAction.php │ │ │ │ └── DataViewAction.php │ │ │ ├── FormAction.php │ │ │ ├── FormCreateAction.php │ │ │ ├── FormDeleteAction.php │ │ │ ├── FormListAction.php │ │ │ └── FormUpdateAction.php │ │ │ ├── GuestBook │ │ │ ├── GuestBookAction.php │ │ │ ├── GuestBookDeleteAction.php │ │ │ ├── GuestBookListAction.php │ │ │ └── GuestBookUpdateAction.php │ │ │ ├── LogPageAction.php │ │ │ ├── LoginPageAction.php │ │ │ ├── MainPageAction.php │ │ │ ├── Page │ │ │ ├── PageAction.php │ │ │ ├── PageCreateAction.php │ │ │ ├── PageDeleteAction.php │ │ │ ├── PageListAction.php │ │ │ └── PageUpdateAction.php │ │ │ ├── ParametersPageAction.php │ │ │ ├── Publication │ │ │ ├── Category │ │ │ │ ├── CategoryCreateAction.php │ │ │ │ ├── CategoryDeleteAction.php │ │ │ │ ├── CategoryListAction.php │ │ │ │ └── CategoryUpdateAction.php │ │ │ ├── PublicationAction.php │ │ │ ├── PublicationCreateAction.php │ │ │ ├── PublicationDeleteAction.php │ │ │ ├── PublicationListAction.php │ │ │ ├── PublicationPreviewAction.php │ │ │ └── PublicationUpdateAction.php │ │ │ ├── Reference │ │ │ ├── ReferenceAction.php │ │ │ ├── ReferenceCreateAction.php │ │ │ ├── ReferenceDeleteAction.php │ │ │ ├── ReferenceListAction.php │ │ │ └── ReferenceUpdateAction.php │ │ │ ├── RefreshAction.php │ │ │ ├── SystemPageAction.php │ │ │ ├── TaskRunAction.php │ │ │ └── User │ │ │ ├── Group │ │ │ ├── CreateAction.php │ │ │ ├── DeleteAction.php │ │ │ ├── ListAction.php │ │ │ └── UpdateAction.php │ │ │ ├── NewsLetter │ │ │ └── CreateAction.php │ │ │ ├── Subscriber │ │ │ ├── CreateAction.php │ │ │ ├── DeleteAction.php │ │ │ └── ListAction.php │ │ │ ├── UserAction.php │ │ │ ├── UserCreateAction.php │ │ │ ├── UserDeleteAction.php │ │ │ ├── UserListAction.php │ │ │ ├── UserUpdateAction.php │ │ │ └── UserViewAction.php │ ├── Auth.php │ ├── Auth │ │ ├── AbstractAuthProvider.php │ │ ├── BasicAuthProvider.php │ │ └── OpenAuthProvider.php │ ├── Mail.php │ ├── Mail │ │ ├── MailProviderInterface.php │ │ ├── SMTPProvider.php │ │ └── SPProvider.php │ ├── Middlewares │ │ ├── AccessCheckerMiddleware.php │ │ ├── AuthorizationAPIMiddleware.php │ │ ├── AuthorizationMiddleware.php │ │ ├── CORSMiddleware.php │ │ ├── IsRouteEnabledAPIMiddleware.php │ │ ├── IsRouteEnabledMiddleware.php │ │ ├── IsSiteEnabledMiddleware.php │ │ ├── LocaleMiddleware.php │ │ ├── NonWWWMiddleware.php │ │ └── PluginMiddleware.php │ ├── PubSub.php │ ├── Twig │ │ ├── LocaleNode.php │ │ └── LocaleParser.php │ ├── TwigExtension.php │ └── i18n.php ├── Domain │ ├── AbstractAction.php │ ├── AbstractException.php │ ├── AbstractExtension.php │ ├── AbstractHttpException.php │ ├── AbstractMiddleware.php │ ├── AbstractNotFoundException.php │ ├── AbstractPlugin.php │ ├── AbstractSchedule.php │ ├── AbstractService.php │ ├── AbstractTask.php │ ├── Casts │ │ ├── AddressUrl.php │ │ ├── Boolean.php │ │ ├── Catalog │ │ │ ├── Attribute │ │ │ │ └── Type.php │ │ │ ├── Order │ │ │ │ └── Delivery.php │ │ │ ├── Product │ │ │ │ ├── Dimension.php │ │ │ │ ├── Tags.php │ │ │ │ └── Type.php │ │ │ └── Status.php │ │ ├── Decimal.php │ │ ├── Email.php │ │ ├── Enum.php │ │ ├── GuestBook │ │ │ └── Status.php │ │ ├── Json.php │ │ ├── Meta.php │ │ ├── Page │ │ │ └── Type.php │ │ ├── Phone.php │ │ ├── Reference │ │ │ └── Type.php │ │ ├── Sort.php │ │ ├── Task │ │ │ └── Status.php │ │ ├── User │ │ │ ├── Company.php │ │ │ ├── Legal.php │ │ │ ├── Messenger.php │ │ │ ├── Password.php │ │ │ └── Status.php │ │ └── Uuid.php │ ├── Exceptions │ │ ├── FileNotFoundException.php │ │ ├── HttpBadRequestException.php │ │ ├── HttpForbiddenException.php │ │ ├── HttpMethodNotAllowedException.php │ │ ├── HttpNotFoundException.php │ │ ├── HttpNotImplementedException.php │ │ ├── HttpRedirectException.php │ │ ├── JWTExpiredException.php │ │ ├── NullPointException.php │ │ ├── WrongAuthProviderException.php │ │ ├── WrongEmailValueException.php │ │ ├── WrongIpValueException.php │ │ └── WrongPhoneValueException.php │ ├── Models │ │ ├── CatalogAttribute.php │ │ ├── CatalogCategory.php │ │ ├── CatalogOrder.php │ │ ├── CatalogProduct.php │ │ ├── File.php │ │ ├── Form.php │ │ ├── FormData.php │ │ ├── GuestBook.php │ │ ├── Page.php │ │ ├── Parameter.php │ │ ├── Publication.php │ │ ├── PublicationCategory.php │ │ ├── Reference.php │ │ ├── Task.php │ │ ├── User.php │ │ ├── UserGroup.php │ │ ├── UserIntegration.php │ │ ├── UserSubscriber.php │ │ └── UserToken.php │ ├── Plugin │ │ ├── AbstractDeliveryPlugin.php │ │ ├── AbstractLanguagePlugin.php │ │ ├── AbstractLegacyPlugin.php │ │ ├── AbstractMailPlugin.php │ │ ├── AbstractOAuthPlugin.php │ │ └── AbstractPaymentPlugin.php │ ├── References │ │ ├── Catalog.php │ │ ├── Date.php │ │ ├── Documents.php │ │ ├── Publication.php │ │ └── User.php │ ├── Schedules │ │ └── Test.php │ ├── Service │ │ ├── Catalog │ │ │ ├── AttributeService.php │ │ │ ├── CategoryService.php │ │ │ ├── Exception │ │ │ │ ├── AddressAlreadyExistsException.php │ │ │ │ ├── AttributeNotFoundException.php │ │ │ │ ├── CategoryNotFoundException.php │ │ │ │ ├── MissingCategoryValueException.php │ │ │ │ ├── MissingTitleValueException.php │ │ │ │ ├── OrderNotFoundException.php │ │ │ │ ├── OrderShippingLimitException.php │ │ │ │ ├── ProductNotFoundException.php │ │ │ │ ├── RelationNotFoundException.php │ │ │ │ ├── TitleAlreadyExistsException.php │ │ │ │ ├── WrongEmailValueException.php │ │ │ │ ├── WrongPhoneValueException.php │ │ │ │ └── WrongTitleValueException.php │ │ │ ├── OrderService.php │ │ │ └── ProductService.php │ │ ├── File │ │ │ ├── Exception │ │ │ │ ├── FileAlreadyExistsException.php │ │ │ │ └── FileNotFoundException.php │ │ │ └── FileService.php │ │ ├── Form │ │ │ ├── DataService.php │ │ │ ├── Exception │ │ │ │ ├── AddressAlreadyExistsException.php │ │ │ │ ├── FormDataNotFoundException.php │ │ │ │ ├── FormNotFoundException.php │ │ │ │ ├── MissingMessageValueException.php │ │ │ │ ├── MissingTitleValueException.php │ │ │ │ ├── TitleAlreadyExistsException.php │ │ │ │ └── WrongTitleValueException.php │ │ │ └── FormService.php │ │ ├── GuestBook │ │ │ ├── Exception │ │ │ │ ├── EntryNotFoundException.php │ │ │ │ ├── MissingEmailValueException.php │ │ │ │ ├── MissingMessageValueException.php │ │ │ │ ├── MissingNameValueException.php │ │ │ │ ├── WrongEmailValueException.php │ │ │ │ └── WrongNameValueException.php │ │ │ └── GuestBookService.php │ │ ├── Page │ │ │ ├── Exception │ │ │ │ ├── AddressAlreadyExistsException.php │ │ │ │ ├── MissingTitleValueException.php │ │ │ │ ├── PageNotFoundException.php │ │ │ │ ├── TitleAlreadyExistsException.php │ │ │ │ └── WrongTitleValueException.php │ │ │ └── PageService.php │ │ ├── Parameter │ │ │ ├── Exception │ │ │ │ ├── ParameterAlreadyExistsException.php │ │ │ │ └── ParameterNotFoundException.php │ │ │ └── ParameterService.php │ │ ├── Publication │ │ │ ├── CategoryService.php │ │ │ ├── Exception │ │ │ │ ├── AddressAlreadyExistsException.php │ │ │ │ ├── CategoryNotFoundException.php │ │ │ │ ├── MissingCategoryValueException.php │ │ │ │ ├── MissingTitleValueException.php │ │ │ │ ├── PublicationNotFoundException.php │ │ │ │ ├── TitleAlreadyExistsException.php │ │ │ │ └── WrongTitleValueException.php │ │ │ └── PublicationService.php │ │ ├── Reference │ │ │ ├── Exception │ │ │ │ ├── MissingTitleValueException.php │ │ │ │ ├── MissingTypeValueException.php │ │ │ │ ├── ReferenceNotFoundException.php │ │ │ │ ├── TitleAlreadyExistsException.php │ │ │ │ └── WrongTitleValueException.php │ │ │ └── ReferenceService.php │ │ ├── Task │ │ │ ├── Exception │ │ │ │ ├── MissingActionValueException.php │ │ │ │ ├── MissingTitleValueException.php │ │ │ │ ├── TaskNotFoundException.php │ │ │ │ └── WrongTitleValueException.php │ │ │ └── TaskService.php │ │ └── User │ │ │ ├── Exception │ │ │ ├── EmailAlreadyExistsException.php │ │ │ ├── EmailBannedException.php │ │ │ ├── IntegrationNotFoundException.php │ │ │ ├── MissingTitleValueException.php │ │ │ ├── MissingUniqueValueException.php │ │ │ ├── PhoneAlreadyExistsException.php │ │ │ ├── TitleAlreadyExistsException.php │ │ │ ├── TokenNotFoundException.php │ │ │ ├── UserGroupNotFoundException.php │ │ │ ├── UserNotFoundException.php │ │ │ ├── UsernameAlreadyExistsException.php │ │ │ ├── WrongCodeException.php │ │ │ ├── WrongEmailValueException.php │ │ │ ├── WrongIpValueException.php │ │ │ ├── WrongPasswordException.php │ │ │ ├── WrongPhoneValueException.php │ │ │ ├── WrongTitleValueException.php │ │ │ └── WrongUsernameValueException.php │ │ │ ├── GroupService.php │ │ │ ├── SubscriberService.php │ │ │ ├── TokenService.php │ │ │ └── UserService.php │ ├── Tasks │ │ ├── Catalog │ │ │ └── ProductImportTask.php │ │ ├── ConvertImageTask.php │ │ ├── ReConvertImageTask.php │ │ ├── SearchIndexTask.php │ │ ├── SendJSONTask.php │ │ ├── SendMailTask.php │ │ └── SendNewsLetterMailTask.php │ └── Traits │ │ ├── HasFiles.php │ │ ├── HasParameters.php │ │ ├── HasRenderer.php │ │ ├── HasStorage.php │ │ └── UseSecurity.php ├── Locale │ └── en-US.php ├── Template │ ├── cup │ │ ├── auth │ │ │ ├── forbidden.twig │ │ │ └── login.twig │ │ ├── catalog │ │ │ ├── attribute │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── category │ │ │ │ ├── form.twig │ │ │ │ ├── index-item.twig │ │ │ │ └── index.twig │ │ │ ├── order │ │ │ │ ├── dispatch.twig │ │ │ │ ├── document-view.twig │ │ │ │ ├── form-modal-product.twig │ │ │ │ ├── form-modal-user.twig │ │ │ │ ├── form-product-item.twig │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── product │ │ │ │ ├── form-modal-related.twig │ │ │ │ ├── form.twig │ │ │ │ ├── index-modal-import.twig │ │ │ │ └── index.twig │ │ │ └── statistic.twig │ │ ├── donate.twig │ │ ├── editor.twig │ │ ├── editor │ │ │ ├── index-aside-file.twig │ │ │ ├── index-aside.twig │ │ │ └── index.twig │ │ ├── field.twig │ │ ├── file │ │ │ └── index.twig │ │ ├── form-file.twig │ │ ├── form-header.twig │ │ ├── form-image.twig │ │ ├── form-meta.twig │ │ ├── form-save.twig │ │ ├── form-tags.twig │ │ ├── form.twig │ │ ├── form │ │ │ ├── form.twig │ │ │ ├── index.twig │ │ │ └── view │ │ │ │ ├── detail.twig │ │ │ │ └── list.twig │ │ ├── guestbook │ │ │ ├── form.twig │ │ │ └── index.twig │ │ ├── layout.twig │ │ ├── logs.twig │ │ ├── navigation.twig │ │ ├── page │ │ │ ├── form.twig │ │ │ └── index.twig │ │ ├── parameters │ │ │ ├── field.twig │ │ │ └── index.twig │ │ ├── publication │ │ │ ├── category │ │ │ │ ├── form.twig │ │ │ │ ├── index-item.twig │ │ │ │ └── index.twig │ │ │ ├── form.twig │ │ │ ├── index.twig │ │ │ └── preview.twig │ │ ├── reference │ │ │ ├── address-format │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── countries │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── currencies │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── deliveries │ │ │ │ ├── form-item.twig │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── documents │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── length-classes │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── manufacturer │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── order-status │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── payments │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── social-networks │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── stock-status │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── store-locations │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ ├── tax-rates │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ │ └── weight-classes │ │ │ │ ├── form.twig │ │ │ │ └── index.twig │ │ ├── sidebar.twig │ │ ├── system │ │ │ ├── index-step-1.twig │ │ │ ├── index-step-2.twig │ │ │ ├── index-step-3.twig │ │ │ └── index.twig │ │ ├── toolbar.twig │ │ └── user │ │ │ ├── form.twig │ │ │ ├── group │ │ │ ├── form.twig │ │ │ └── index.twig │ │ │ ├── index.twig │ │ │ ├── newsletter │ │ │ └── form.twig │ │ │ ├── subscriber │ │ │ └── index.twig │ │ │ └── view.twig │ ├── errors │ │ ├── p400.twig │ │ ├── p403.twig │ │ ├── p404.twig │ │ ├── p405.twig │ │ ├── p500.twig │ │ └── p503.twig │ ├── layout.twig │ └── mixin │ │ ├── adbd.twig │ │ ├── catalog.twig │ │ ├── cookies.twig │ │ ├── datatable.twig │ │ ├── form.twig │ │ ├── gallery.twig │ │ ├── img-script.twig │ │ ├── img.twig │ │ ├── picture.twig │ │ ├── quiz.twig │ │ └── recaptcha.twig ├── bootstrap.php ├── dependencies.php ├── helpers.php ├── middleware.php ├── routes.php ├── services.php └── settings.php ├── tests ├── API │ ├── CatalogCategoryAPITest.php │ ├── CatalogProductAPITest.php │ └── CommonAPITest.php ├── Domain │ └── Service │ │ ├── Catalog │ │ ├── AttributeServiceTest.php │ │ ├── AttributeTest.php │ │ ├── CategoryServiceTest.php │ │ ├── OrderServiceTest.php │ │ └── ProductServiceTest.php │ │ ├── File │ │ └── FileServiceTest.php │ │ ├── Form │ │ ├── FormDataServiceTest.php │ │ └── FormServiceTest.php │ │ ├── GuestBook │ │ └── GuestBookServiceTest.php │ │ ├── Page │ │ └── PageServiceTest.php │ │ ├── Parameter │ │ └── ParameterServiceTest.php │ │ ├── Publication │ │ ├── CategoryServiceTest.php │ │ └── PublicationServiceTest.php │ │ ├── Reference │ │ └── ReferenceServiceTest.php │ │ ├── Task │ │ └── TaskServiceTest.php │ │ └── User │ │ ├── GroupServiceTest.php │ │ ├── SubscriberServiceTest.php │ │ ├── TokenServiceTest.php │ │ └── UserServiceTest.php └── TestCase.php ├── theme └── default │ ├── main.twig │ ├── p400.twig │ ├── p404.twig │ ├── p405.twig │ └── p500.twig └── var ├── cache └── .gitkeep ├── log └── .gitkeep ├── upload └── .gitkeep └── xml └── .gitkeep /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .git 3 | .composer 4 | hooks 5 | vendor 6 | .gitignore 7 | composer 8 | docker-compose.yml 9 | LICENSE.md 10 | README.md 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | .DS_Store 4 | 5 | /theme/* 6 | !/theme/default 7 | !/theme/older 8 | 9 | /plugin/* 10 | !/plugin/installed.php 11 | /vendor/* 12 | !/vendor/.gitkeep 13 | /var/*.sqlite 14 | /var/*.sqlite-journal 15 | /var/*.key 16 | /var/cache/* 17 | !/var/cache/.gitkeep 18 | /var/log/* 19 | !/var/log/.gitkeep 20 | /var/upload/* 21 | !/var/upload/.gitkeep 22 | /var/xml/*.xml 23 | !/var/xml/.gitkeep 24 | /var/.lock 25 | /var/worker.pid 26 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011-2024 Aleksey Ilyin (getwebspace.org) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | up: 2 | @docker-compose -f docker-compose.dev.yml up -d || : 3 | @docker-compose -f docker-compose.dev.yml exec platform composer install || : 4 | @chmod -R 0777 plugin || : 5 | @chmod -R 0777 public/resource || : 6 | @chmod -R 0777 theme || : 7 | @chmod -R 0777 var || : 8 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/phinx migrate 9 | 10 | down: 11 | @docker-compose -f docker-compose.dev.yml down 12 | 13 | run-test: 14 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/phpunit --color=always --configuration phpunit.xml 15 | 16 | run-lint: 17 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php 18 | 19 | migrate-up: 20 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/phinx migrate 21 | 22 | migrate-down: 23 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/phinx rollback 24 | 25 | migrate-create: 26 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/phinx create 27 | 28 | migrate-status: 29 | @docker-compose -f docker-compose.dev.yml exec platform ./vendor/bin/phinx status 30 | -------------------------------------------------------------------------------- /bin/cron_worker.php: -------------------------------------------------------------------------------- 1 | getContainer(); 19 | 20 | /** @var \Monolog\Logger $logger */ 21 | $logger = $container->get(\Psr\Log\LoggerInterface::class); 22 | 23 | // simple scheduler 24 | $scheduler = $container->get('scheduler'); 25 | 26 | // add jobs 27 | // $scheduler->register(\App\Domain\Schedules\Test::class, '*/5 * * * *'); 28 | 29 | // check jobs 30 | foreach ($scheduler->get() as $scheduled) { 31 | $schedule = $scheduled['schedule']; 32 | 33 | /** @var \App\Domain\AbstractSchedule $job */ 34 | $job = $scheduled['job']; 35 | 36 | if ($job->isShouldRun($schedule)) { 37 | $job->run(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /composer: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run --rm --interactive --tty --dns 1.1.1.1 --volume $PWD:/app composer:2 --ignore-platform-reqs "$@" 4 | -------------------------------------------------------------------------------- /config/vars.php: -------------------------------------------------------------------------------- 1 | &2 "*** Running: $script" 29 | $script 30 | retval=$? 31 | if [ $retval != 0 ]; then 32 | echo >&2 "*** Failed with return value: $?" 33 | exit $retval 34 | fi 35 | done 36 | echo "Finished startup scripts in /docker-entrypoint-init.d" 37 | 38 | echo "Starting runit..." 39 | exec runsvdir -P /etc/service & 40 | 41 | RUNSVDIR=$! 42 | echo "Started runsvdir, PID is $RUNSVDIR" 43 | echo "wait for processes to start...." 44 | 45 | sleep 5 46 | for _srv in $(ls -1 /etc/service); do 47 | sv status $_srv 48 | done 49 | 50 | # catch shutdown signals 51 | trap shutdown SIGTERM SIGHUP SIGQUIT SIGINT 52 | wait $RUNSVDIR 53 | 54 | shutdown 55 | -------------------------------------------------------------------------------- /docker/rootfs/docker-entrypoint-init.d/01-uname.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test file to check init scripts 3 | uname -a 4 | -------------------------------------------------------------------------------- /docker/rootfs/etc/service/cron/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # pipe stderr to stdout and run cron 4 | exec 2>&1 5 | exec crond -f 6 | -------------------------------------------------------------------------------- /docker/rootfs/etc/service/nginx/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # pipe stderr to stdout and run nginx omiting ENV vars to avoid security leaks 4 | exec 2>&1 5 | exec env - PATH=$PATH nginx -g 'daemon off;' 6 | -------------------------------------------------------------------------------- /docker/rootfs/etc/service/php/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # pipe stderr to stdout and run php-fpm 4 | exec 2>&1 5 | exec php-fpm -F 6 | -------------------------------------------------------------------------------- /docker/rootfs/usr/local/etc/php/conf.d/custom.ini: -------------------------------------------------------------------------------- 1 | short_open_tag= On 2 | memory_limit= 128M 3 | post_max_size= 64M 4 | upload_max_filesize= 32M 5 | 6 | [Date] 7 | date.timezone="UTC" 8 | 9 | [opcache] 10 | opcache.enable=1 11 | opcache.enable_cli=1 12 | opcache.jit_buffer_size=100M 13 | opcache.jit=1255 14 | -------------------------------------------------------------------------------- /image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/image.jpeg -------------------------------------------------------------------------------- /orm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker-compose exec platform vendor/bin/doctrine "$@" 4 | -------------------------------------------------------------------------------- /phinx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker-compose exec -e PHP_CS_FIXER_IGNORE_ENV=1 platform vendor/bin/phinx "$@" 4 | -------------------------------------------------------------------------------- /phinx.php: -------------------------------------------------------------------------------- 1 | [ 6 | 'migrations' => 'scheme/migrations', 7 | 'seeds' => 'scheme/seeds', 8 | ], 9 | 'environments' => [ 10 | 'default_migration_table' => 'phinx_migrations', 11 | 'default_environment' => ($_ENV['TEST'] ?? false) ? 'dev' : 'prod', 12 | 'dev' => [ 13 | 'dsn' => 'sqlite://./var/database-test', 14 | 'suffix' => '.sqlite', 15 | ], 16 | 'prod' => [ 17 | 'dsn' => !empty($_ENV['DATABASE']) ? $_ENV['DATABASE'] : 'sqlite://./var/database', 18 | 'suffix' => '.sqlite', 19 | ], 20 | ], 21 | 'version_order' => 'creation', 22 | ]; 23 | -------------------------------------------------------------------------------- /phpcs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker-compose exec -e PHP_CS_FIXER_IGNORE_ENV=1 platform vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php -v "$@" 4 | -------------------------------------------------------------------------------- /phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker-compose exec platform vendor/bin/phpunit --color=always --configuration phpunit.xml "$@" 4 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/ 6 | 7 | 8 | 9 | 10 | ./src 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /plugin/installed.php: -------------------------------------------------------------------------------- 1 | get('plugin'); 8 | 9 | // Example 10 | // $plugins->register(\Plugin\Example\ExamplePlugin::class); 11 | // $plugins->register(new \Plugin\Example\ExamplePlugin($container)); 12 | -------------------------------------------------------------------------------- /public/assets/img/icon/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/100.png -------------------------------------------------------------------------------- /public/assets/img/icon/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/1024.png -------------------------------------------------------------------------------- /public/assets/img/icon/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/114.png -------------------------------------------------------------------------------- /public/assets/img/icon/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/120.png -------------------------------------------------------------------------------- /public/assets/img/icon/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/128.png -------------------------------------------------------------------------------- /public/assets/img/icon/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/144.png -------------------------------------------------------------------------------- /public/assets/img/icon/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/152.png -------------------------------------------------------------------------------- /public/assets/img/icon/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/16.png -------------------------------------------------------------------------------- /public/assets/img/icon/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/167.png -------------------------------------------------------------------------------- /public/assets/img/icon/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/180.png -------------------------------------------------------------------------------- /public/assets/img/icon/192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/192.png -------------------------------------------------------------------------------- /public/assets/img/icon/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/20.png -------------------------------------------------------------------------------- /public/assets/img/icon/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/256.png -------------------------------------------------------------------------------- /public/assets/img/icon/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/29.png -------------------------------------------------------------------------------- /public/assets/img/icon/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/32.png -------------------------------------------------------------------------------- /public/assets/img/icon/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/40.png -------------------------------------------------------------------------------- /public/assets/img/icon/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/50.png -------------------------------------------------------------------------------- /public/assets/img/icon/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/512.png -------------------------------------------------------------------------------- /public/assets/img/icon/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/57.png -------------------------------------------------------------------------------- /public/assets/img/icon/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/58.png -------------------------------------------------------------------------------- /public/assets/img/icon/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/60.png -------------------------------------------------------------------------------- /public/assets/img/icon/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/64.png -------------------------------------------------------------------------------- /public/assets/img/icon/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/72.png -------------------------------------------------------------------------------- /public/assets/img/icon/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/76.png -------------------------------------------------------------------------------- /public/assets/img/icon/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/80.png -------------------------------------------------------------------------------- /public/assets/img/icon/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/icon/87.png -------------------------------------------------------------------------------- /public/assets/img/lviv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/lviv.jpg -------------------------------------------------------------------------------- /public/assets/img/no_image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/no_image.jpeg -------------------------------------------------------------------------------- /public/assets/img/no_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/assets/img/no_image.png -------------------------------------------------------------------------------- /public/assets/js/plugin/clusterize/clusterize.min.css: -------------------------------------------------------------------------------- 1 | .clusterize-scroll{max-height:50vh;overflow:auto;margin-right:-15px;margin-left:-15px}.clusterize-scroll::-webkit-scrollbar{width:6px;height:6px}.clusterize-scroll::-webkit-scrollbar-track{border-radius:10px;background:rgba(0,0,0,0.1)}.clusterize-scroll::-webkit-scrollbar-thumb{border-radius:10px;background:rgba(0,0,0,0.2)}.clusterize-scroll::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,0.4)}.clusterize-scroll::-webkit-scrollbar-thumb:active{background:rgba(0,0,0,0.9)}.clusterize-extra-row{margin-top:0!important;margin-bottom:0!important}.clusterize-extra-row.clusterize-keep-parity{display:none}.clusterize-header{font-weight:700;align-items:center;padding-right:6px}.clusterize-content{outline:0;counter-reset:clusterize-counter}.clusterize-content .row{margin-right:0;margin-left:0;align-items:center}.clusterize-content .row:hover{background-color:rgba(0,0,0,.075)!important}.clusterize-content .row:nth-of-type(odd){background:rgba(0,0,0,.025)}.clusterize-content [class^=col],.clusterize-header [class^=col]{padding-top:.3rem;padding-bottom:.3rem}.clusterize-no-data [class^=col]{text-align:center} 2 | -------------------------------------------------------------------------------- /public/assets/js/plugin/codemirror/addon/dialog/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: absolute; 3 | left: 0; right: 0; 4 | background: inherit; 5 | z-index: 15; 6 | padding: .1em .8em; 7 | overflow: hidden; 8 | color: inherit; 9 | } 10 | 11 | .CodeMirror-dialog-top { 12 | border-bottom: 1px solid #eee; 13 | top: 0; 14 | } 15 | 16 | .CodeMirror-dialog-bottom { 17 | border-top: 1px solid #eee; 18 | bottom: 0; 19 | } 20 | 21 | .CodeMirror-dialog input { 22 | border: none; 23 | outline: none; 24 | background: transparent; 25 | width: 20em; 26 | color: inherit; 27 | font-family: monospace; 28 | } 29 | 30 | .CodeMirror-dialog button { 31 | font-size: 70%; 32 | } 33 | -------------------------------------------------------------------------------- /public/assets/js/plugin/codemirror/addon/mode/multiplex_test.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function() { 5 | CodeMirror.defineMode("markdown_with_stex", function(){ 6 | var inner = CodeMirror.getMode({}, "stex"); 7 | var outer = CodeMirror.getMode({}, "markdown"); 8 | 9 | var innerOptions = { 10 | open: '$', 11 | close: '$', 12 | mode: inner, 13 | delimStyle: 'delim', 14 | innerStyle: 'inner' 15 | }; 16 | 17 | return CodeMirror.multiplexingMode(outer, innerOptions); 18 | }); 19 | 20 | var mode = CodeMirror.getMode({}, "markdown_with_stex"); 21 | 22 | function MT(name) { 23 | test.mode( 24 | name, 25 | mode, 26 | Array.prototype.slice.call(arguments, 1), 27 | 'multiplexing'); 28 | } 29 | 30 | MT( 31 | "stexInsideMarkdown", 32 | "[strong **Equation:**] [delim&delim-open $][inner&tag \\pi][delim&delim-close $]"); 33 | })(); 34 | -------------------------------------------------------------------------------- /public/assets/js/plugin/codemirror/mode/css/gss_test.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function() { 5 | "use strict"; 6 | 7 | var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-gss"); 8 | function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "gss"); } 9 | 10 | MT("atComponent", 11 | "[def @component] {", 12 | "[tag foo] {", 13 | " [property color]: [keyword black];", 14 | "}", 15 | "}"); 16 | 17 | })(); 18 | -------------------------------------------------------------------------------- /public/assets/js/plugin/codemirror/mode/twig/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeMirror: Twig mode 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 |
26 |

Twig mode

27 |
40 | 45 |
46 | -------------------------------------------------------------------------------- /public/assets/js/plugin/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Touch Punch 0.2.3 3 | * 4 | * Copyright 2011–2014, Dave Furfero 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * 7 | * Depends: 8 | * jquery.ui.widget.js 9 | * jquery.ui.mouse.js 10 | */ 11 | "use strict";!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | get('settings'); 35 | $displayErrorDetails = $settings['displayErrorDetails']; 36 | $logError = $settings['logError']; 37 | $logErrorDetails = $settings['logErrorDetails']; 38 | $logger = $container->get(\Psr\Log\LoggerInterface::class); 39 | 40 | $app->add(\Slim\Views\TwigMiddleware::createFromContainer($app)); 41 | $app->addRoutingMiddleware(); 42 | $app->addErrorMiddleware($displayErrorDetails, $logError, $logErrorDetails, $logger); 43 | $app->run(); 44 | 45 | // And nothing more :) 46 | -------------------------------------------------------------------------------- /public/resource/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/resource/.gitkeep -------------------------------------------------------------------------------- /public/uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/public/uploads/.gitkeep -------------------------------------------------------------------------------- /scheme/migrations/20240702092036.php: -------------------------------------------------------------------------------- 1 | table('user'); 13 | $table 14 | ->addColumn('loyalty', 'text', ['default' => '[]', 'after' => 'source']) 15 | ->update(); 16 | } 17 | 18 | public function down(): void 19 | { 20 | // revert user loyalty field 21 | $table = $this->table('user'); 22 | $table 23 | ->removeColumn('loyalty') 24 | ->update(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scheme/migrations/20240702092511.php: -------------------------------------------------------------------------------- 1 | [] 12 | $table = $this->table('user_group'); 13 | $table 14 | ->changeColumn('access', 'text', ['default' => '[]']) 15 | ->update(); 16 | } 17 | 18 | public function down(): void 19 | { 20 | // revert user_group default value [] => {} 21 | $table = $this->table('user_group'); 22 | $table 23 | ->changeColumn('access', 'text', ['default' => '{}']) 24 | ->update(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scheme/migrations/20240727135000.php: -------------------------------------------------------------------------------- 1 | table('file'); 13 | $table 14 | ->removeColumn('private') 15 | ->update(); 16 | } 17 | 18 | public function down(): void 19 | { 20 | // revert file private field 21 | $table = $this->table('file'); 22 | $table 23 | ->addColumn('private', 'boolean', ['default' => 0]) 24 | ->update(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scheme/migrations/20240807113500.php: -------------------------------------------------------------------------------- 1 | table('reference'); 13 | $table->removeIndex('title')->save(); 14 | $table->removeIndex('type')->save(); 15 | $table->addIndex(['type', 'title'], ['unique' => true])->save(); 16 | } 17 | 18 | public function down(): void 19 | { 20 | // remove new index 21 | $table = $this->table('reference'); 22 | $table 23 | ->removeIndex(['type', 'title'], ['unique' => true]) 24 | ->save(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scheme/migrations/20240807122500.php: -------------------------------------------------------------------------------- 1 | table('user'); 13 | $table->removeIndex('username')->save(); 14 | $table->removeIndex('email')->save(); 15 | $table->removeIndex('phone')->save(); 16 | $table->addIndex(['username', 'email', 'phone'], ['unique' => true])->save(); 17 | } 18 | 19 | public function down(): void 20 | { 21 | // remove new index 22 | $table = $this->table('user'); 23 | $table 24 | ->removeIndex(['username', 'email', 'phone']) 25 | ->save(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /scheme/migrations/20250226101251.php: -------------------------------------------------------------------------------- 1 | table('params'); 15 | $table->changeColumn('value', 'text', ['null' => false, 'default' => '']) 16 | ->update(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Application/Actions/Api/ActionApi.php: -------------------------------------------------------------------------------- 1 | respondWithText($this->getPublicKey()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Application/Actions/Auth/AuthAction.php: -------------------------------------------------------------------------------- 1 | auth = $container->get(Auth::class); 18 | } 19 | 20 | protected function isRequestJson(): bool 21 | { 22 | $headerAccept = $this->request->getHeaderLine('accept'); 23 | 24 | return str_contains($headerAccept, 'application/json') || $headerAccept === '*/*'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Application/Actions/Auth/LogoutAction.php: -------------------------------------------------------------------------------- 1 | getParam('redirect', '/'); 10 | $refresh_token = $this->getParam('token', $this->getCookie('refresh_token', null)); 11 | 12 | if ($refresh_token) { 13 | $this->auth->logout( 14 | $this->getParam('provider', $_SESSION['auth_provider'] ?? 'BasicAuthProvider'), 15 | $refresh_token, 16 | ); 17 | 18 | @setcookie('access_token', '', time(), '/'); 19 | @setcookie('refresh_token', '', time(), '/auth'); 20 | } 21 | 22 | switch ($this->isRequestJson()) { 23 | case true: 24 | return $this->respondWithJson(); 25 | 26 | case false: 27 | default: 28 | return $this->response->withAddedHeader('Location', $redirect)->withStatus(307); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Application/Actions/Auth/RevokeTokenAction.php: -------------------------------------------------------------------------------- 1 | getParam('redirect', '/'); 12 | $refresh_token = $this->getParam('token', $this->getCookie('refresh_token')); 13 | $uuid = $this->getParam('uuid'); 14 | 15 | if ($refresh_token) { 16 | $this->auth->revoke( 17 | $this->getParam('provider', $_SESSION['auth_provider'] ?? 'BasicAuthProvider'), 18 | $refresh_token, 19 | $uuid 20 | ); 21 | 22 | /** @var User $user */ 23 | if (($user = $this->request->getAttribute('user', false)) !== false && $user->tokens->isEmpty()) { 24 | $redirect = '/auth/logout'; 25 | } 26 | } 27 | 28 | switch ($this->isRequestJson()) { 29 | case true: 30 | return $this->respondWithJson(); 31 | 32 | case false: 33 | default: 34 | return $this->response->withAddedHeader('Location', $redirect)->withStatus(307); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/Catalog/CartDoneAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('order') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('order'))) { 15 | try { 16 | $order = $this->catalogOrderService->read(['uuid' => $this->resolveArg('order')]); 17 | 18 | if ($order) { 19 | return $this 20 | ->respond($this->parameter('catalog_cart_complete_template', 'catalog.cart.complete.twig'), [ 21 | 'order' => $order, 22 | ]) 23 | ->withAddedHeader('X-Robots-Tag', 'noindex, nofollow'); 24 | } 25 | } catch (OrderNotFoundException $e) { 26 | return $this->respond('p404.twig')->withStatus(404); 27 | } 28 | } 29 | 30 | return $this->respondWithRedirect('/cart'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/Catalog/CatalogAction.php: -------------------------------------------------------------------------------- 1 | userService = $container->get(UserService::class); 30 | $this->catalogCategoryService = $container->get(CatalogCategoryService::class); 31 | $this->catalogProductService = $container->get(CatalogProductService::class); 32 | $this->catalogOrderService = $container->get(CatalogOrderService::class); 33 | $this->referenceService = $container->get(ReferenceService::class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/File/FileAction.php: -------------------------------------------------------------------------------- 1 | fileService = $container->get(FileService::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/File/FileGetAction.php: -------------------------------------------------------------------------------- 1 | fileService->read([ 11 | 'salt' => $this->resolveArg('salt'), 12 | 'hash' => $this->resolveArg('hash'), 13 | ]); 14 | 15 | return $this->response 16 | ->withHeader('Content-Type', 'application/download') 17 | ->withHeader('Content-Description', 'File Transfer') 18 | ->withHeader('Content-Transfer-Encoding', 'binary') 19 | ->withHeader('Content-Disposition', 'attachment; filename="' . $file->filename() . '"') 20 | ->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') 21 | ->withHeader('Pragma', 'private') 22 | ->withHeader('Expires', '0') 23 | ->withBody(new \Slim\Psr7\Stream($file->resource())); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/File/FileUploadAction.php: -------------------------------------------------------------------------------- 1 | getUploadedFiles(array_key_first($_FILES)); 10 | $path_only = $this->getParam('path_only', false); 11 | 12 | if ($models && $path_only) { 13 | $file = array_shift($models)[0] ?? false; 14 | 15 | if ($file) { 16 | /** @var \App\Domain\Models\File $file */ 17 | return $this->respondWithJson(['link' => $file->public_path()]); 18 | } 19 | } 20 | 21 | return $this->respondWithJson($models); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/File/FileViewAction.php: -------------------------------------------------------------------------------- 1 | fileService->read([ 11 | 'salt' => $this->resolveArg('salt'), 12 | 'hash' => $this->resolveArg('hash'), 13 | ]); 14 | 15 | return $this->response 16 | ->withHeader('Content-Type', $file->type) 17 | ->withHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') 18 | ->withHeader('Pragma', 'private') 19 | ->withHeader('Expires', '0') 20 | ->withBody(new \Slim\Psr7\Stream($file->resource())); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/ForbiddenPageAction.php: -------------------------------------------------------------------------------- 1 | respond('p403.twig')->withStatus(403); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/MainPageAction.php: -------------------------------------------------------------------------------- 1 | respond($this->parameter('common_template', 'main.twig')); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/PageAction.php: -------------------------------------------------------------------------------- 1 | container->get(PageService::class); 15 | 16 | try { 17 | $page = $pageService->read(['address' => ltrim($this->resolveArg('args'), '/')]); 18 | 19 | return $this->respond($page->template, [ 20 | 'page' => $page, 21 | ]); 22 | } catch (HttpBadRequestException $e) { 23 | return $this->respond('p400.twig')->withStatus(400); 24 | } catch (PageNotFoundException $e) { 25 | return $this->respond('p404.twig')->withStatus(404); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/User/UserAction.php: -------------------------------------------------------------------------------- 1 | auth = $container->get(Auth::class); 24 | $this->userService = $container->get(UserService::class); 25 | $this->userSubscriberService = $container->get(UserSubscriberService::class); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/User/UserLogoutAction.php: -------------------------------------------------------------------------------- 1 | respondWithRedirect('/auth/logout'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/User/UserRevokeTokenAction.php: -------------------------------------------------------------------------------- 1 | $this->getParam('redirect', '/user/profile'), 11 | 'uuid' => $this->getParam('uuid'), 12 | ]; 13 | 14 | return $this->respondWithRedirect('/auth/revoke?' . urldecode(http_build_query($params))); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Application/Actions/Common/XMLFileAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('name'); 12 | $path = VAR_DIR . '/xml/' . $name . '.xml'; 13 | 14 | if (file_exists($path)) { 15 | $this->response->getBody()->write(file_get_contents(VAR_DIR . '/xml/' . $name . '.xml')); 16 | 17 | return $this->response->withAddedHeader('Content-type', 'text/xml; charset=utf-8'); 18 | } 19 | 20 | return $this->respond('p404.twig')->withStatus(404); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Attribute/AttributeDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('attribute') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('attribute'))) { 13 | try { 14 | $attribute = $this->catalogAttributeService->read([ 15 | 'uuid' => $this->resolveArg('attribute'), 16 | ]); 17 | 18 | if ($attribute) { 19 | $this->catalogAttributeService->delete($attribute); 20 | 21 | $this->container->get(\App\Application\PubSub::class)->publish('cup:catalog:attribute:delete', $attribute); 22 | } 23 | } catch (AttributeNotFoundException $e) { 24 | // nothing 25 | } 26 | } 27 | 28 | return $this->respondWithRedirect('/cup/catalog/attribute'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Attribute/AttributeListAction.php: -------------------------------------------------------------------------------- 1 | catalogAttributeService->read([ 12 | 'order' => [ 13 | 'group' => 'asc', 14 | 'title' => 'asc', 15 | ], 16 | ]); 17 | 18 | return $this->respondWithTemplate('cup/catalog/attribute/index.twig', [ 19 | 'attributes' => $list, 20 | ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Category/CategoryListAction.php: -------------------------------------------------------------------------------- 1 | catalogCategoryService->read([ 12 | 'status' => \App\Domain\Casts\Catalog\Status::WORK, 13 | 'order' => [ 14 | 'order' => 'ASC', 15 | 'title' => 'ASC', 16 | ], 17 | ]); 18 | 19 | return $this->respondWithTemplate('cup/catalog/category/index.twig', [ 20 | 'categories' => $categories, 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Order/OrderDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('order') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('order'))) { 13 | try { 14 | $order = $this->catalogOrderService->read([ 15 | 'uuid' => $this->resolveArg('order'), 16 | ]); 17 | 18 | if ($order) { 19 | $this->catalogOrderService->delete($order); 20 | 21 | $this->container->get(\App\Application\PubSub::class)->publish('cup:catalog:order:delete', $order); 22 | } 23 | } catch (OrderNotFoundException $e) { 24 | // nothing 25 | } 26 | } 27 | 28 | return $this->respondWithRedirect('/cup/catalog/order'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Order/OrderDispatchAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('order') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('order'))) { 12 | $order = $this->catalogOrderService->read(['uuid' => $this->resolveArg('order')]); 13 | 14 | if ($order) { 15 | return $this->respondWithTemplate('cup/catalog/order/dispatch.twig', [ 16 | 'order' => $order, 17 | 'template' => $this->parameter('catalog_dispatch', ''), 18 | ]); 19 | } 20 | } 21 | 22 | return $this->respondWithRedirect('/cup/catalog/order'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Order/OrderDocumentAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('order') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('order')) 13 | && $this->resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid')) 14 | ) { 15 | $order = $this->catalogOrderService->read(['uuid' => $this->resolveArg('order')]); 16 | 17 | if ($order) { 18 | $document = $this->referenceService->read(['uuid' => $this->resolveArg('uuid')]); 19 | 20 | return $this->respondWithTemplate('cup/catalog/order/document-view.twig', [ 21 | 'order' => $order, 22 | 'document' => $document, 23 | 'template' => $document->value['template'] ?? '', 24 | ]); 25 | } 26 | } 27 | 28 | return $this->respondWithRedirect('/cup/catalog/order'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Product/ProductDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('product') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('product'))) { 15 | try { 16 | $product = $this->catalogProductService->read([ 17 | 'uuid' => $this->resolveArg('product'), 18 | 'status' => \App\Domain\Casts\Catalog\Status::WORK, 19 | ]); 20 | 21 | if ($product) { 22 | $this->catalogProductService->update($product, [ 23 | 'status' => \App\Domain\Casts\Catalog\Status::DELETE, 24 | ]); 25 | 26 | $this->container->get(\App\Application\PubSub::class)->publish('cup:catalog:product:delete', $product); 27 | } 28 | 29 | return $this->respondWithRedirect('/cup/catalog/product' . ($product ? '/' . $product->category->uuid : '')); 30 | } catch (ProductNotFoundException $e) { 31 | // nothing 32 | } 33 | } 34 | 35 | return $this->respondWithRedirect('/cup/catalog/product'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Catalog/Product/ProductImportAction.php: -------------------------------------------------------------------------------- 1 | isPost()) { 12 | // Fields 13 | $fields = array_map('trim', explode(PHP_EOL, $this->parameter('catalog_import_columns', ''))); 14 | 15 | if ($fields) { 16 | /** @var \App\Domain\Models\File $file */ 17 | $file = array_first($this->getUploadedFiles('excel', 0)); 18 | 19 | if ($file) { 20 | // add import task 21 | $task = new \App\Domain\Tasks\Catalog\ProductImportTask($this->container); 22 | $task->execute([ 23 | 'fields' => $fields, // todo check is later 24 | 'file' => $file->uuid, 25 | ]); 26 | 27 | // run worker 28 | \App\Domain\AbstractTask::worker($task); 29 | } 30 | } 31 | } 32 | 33 | return $this->response->withAddedHeader('Location', $_SERVER['HTTP_REFERER'] ?? '/cup/catalog/product')->withStatus(301); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/File/FileAction.php: -------------------------------------------------------------------------------- 1 | fileService = $container->get(FileService::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/File/FileDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $file = $this->fileService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | 17 | if ($file) { 18 | $this->fileService->delete($file); 19 | 20 | $this->container->get(\App\Application\PubSub::class)->publish('cup:file:delete', $file); 21 | } 22 | } catch (FileNotFoundException $e) { 23 | // nothing 24 | } 25 | } 26 | 27 | return $this->respondWithRedirect('/cup/file'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/File/FileListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/file/index.twig', [ 10 | 'list' => $this->fileService->read(['order' => ['date' => 'desc']]), 11 | ]); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/File/Image/DeleteAction.php: -------------------------------------------------------------------------------- 1 | getParam('src', false); 13 | 14 | if ($src !== false) { 15 | $info = pathinfo($src); 16 | 17 | try { 18 | $file = $this->fileService->read([ 19 | 'name' => str_escape($info['filename']), 20 | 'ext' => str_escape($info['extension']), 21 | ]); 22 | 23 | if ($file) { 24 | $this->fileService->delete($file); 25 | 26 | $this->container->get(\App\Application\PubSub::class)->publish('cup:file:delete', $file); 27 | } 28 | 29 | return $this->respondWithJson(['status' => 'ok']); 30 | } catch (FileNotFoundException $e) { 31 | // nothing 32 | } 33 | } 34 | 35 | return $this->respondWithJson(['status' => 'not found']); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/File/Image/GetAction.php: -------------------------------------------------------------------------------- 1 | fileService->read() as $file) { 14 | /** @var \App\Domain\Models\File $file */ 15 | if (str_starts_with($file->type, 'image/')) { 16 | $result[] = [ 17 | 'url' => $file->public_path(), 18 | 'thumb' => $file->public_path('small'), 19 | ]; 20 | } 21 | } 22 | 23 | return $this->respondWithJson($result); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/ForbiddenPageAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/auth/forbidden.twig')->withStatus(403); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Form/Data/DataDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid')) 14 | && $this->resolveArg('data') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('data')) 15 | ) { 16 | try { 17 | $data = $this->formDataService->read([ 18 | 'uuid' => $this->resolveArg('data'), 19 | ]); 20 | 21 | if ($data) { 22 | $this->formDataService->delete($data); 23 | 24 | $this->container->get(\App\Application\PubSub::class)->publish('cup:form:data:delete', $data); 25 | } 26 | } catch (FormDataNotFoundException $e) { 27 | // nothing 28 | } 29 | } 30 | 31 | return $this->respondWithRedirect('/cup/form/' . $this->resolveArg('uuid') . '/view'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Form/Data/DataListAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | $form = $this->formService->read(['uuid' => $this->resolveArg('uuid')]); 13 | 14 | if ($form) { 15 | $list = $this->formDataService->read([ 16 | 'form_uuid' => $form->uuid, 17 | 'order' => [ 18 | 'date' => 'desc', 19 | ], 20 | ]); 21 | 22 | return $this->respondWithTemplate('cup/form/view/list.twig', [ 23 | 'form' => $form, 24 | 'list' => $list, 25 | ]); 26 | } 27 | } 28 | 29 | return $this->respondWithRedirect('/cup/form'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Form/Data/DataViewAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid')) 13 | && $this->resolveArg('data') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('data')) 14 | ) { 15 | $data = $this->formDataService->read([ 16 | 'uuid' => $this->resolveArg('data'), 17 | ]); 18 | 19 | if ($data) { 20 | return $this->respondWithTemplate('cup/form/view/detail.twig', [ 21 | 'item' => $data, 22 | ]); 23 | } 24 | } 25 | 26 | return $this->respondWithRedirect('/cup/form'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Form/FormAction.php: -------------------------------------------------------------------------------- 1 | formService = $container->get(FormService::class); 21 | $this->formDataService = $container->get(FormDataService::class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Form/FormDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $form = $this->formService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | 17 | if ($form) { 18 | foreach ($this->formDataService->read(['form_uuid' => $this->resolveArg('uuid')]) as $item) { 19 | $this->formDataService->delete($item); 20 | } 21 | 22 | $this->formService->delete($form); 23 | 24 | $this->container->get(\App\Application\PubSub::class)->publish('cup:form:delete', $form); 25 | } 26 | } catch (FormNotFoundException $e) { 27 | // nothing 28 | } 29 | } 30 | 31 | return $this->respondWithRedirect('/cup/form'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Form/FormListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/form/index.twig', [ 10 | 'list' => $this->formService->read(['order' => ['title' => 'asc']]), 11 | ]); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/GuestBook/GuestBookAction.php: -------------------------------------------------------------------------------- 1 | guestBookService = $container->get(GuestBookService::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/GuestBook/GuestBookDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $entry = $this->guestBookService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | 17 | if ($entry) { 18 | $this->guestBookService->delete($entry); 19 | 20 | $this->container->get(\App\Application\PubSub::class)->publish('cup:guestbook:delete', $entry); 21 | } 22 | } catch (EntryNotFoundException $e) { 23 | // nothing 24 | } 25 | } 26 | 27 | return $this->respondWithRedirect('/cup/guestbook'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/GuestBook/GuestBookListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/guestbook/index.twig', [ 10 | 'list' => $this->guestBookService->read(['order' => ['date' => 'desc']]), 11 | ]); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/LogPageAction.php: -------------------------------------------------------------------------------- 1 | getFileContents($path); 16 | } 17 | } 18 | 19 | return $this->respondWithTemplate('cup/logs.twig', [ 20 | 'files' => array_reverse($files), 21 | ]); 22 | } 23 | 24 | private function getFileContents($path, $lines = 1000): string 25 | { 26 | $file = file($path); 27 | 28 | return implode(PHP_EOL, array_map('trim', array_slice($file, 0 - $lines))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Page/PageAction.php: -------------------------------------------------------------------------------- 1 | pageService = $container->get(PageService::class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Page/PageDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $page = $this->pageService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | 17 | if ($page) { 18 | $this->pageService->delete($page); 19 | 20 | $this->container->get(\App\Application\PubSub::class)->publish('cup:page:delete', $page); 21 | } 22 | } catch (PageNotFoundException $e) { 23 | // nothing 24 | } 25 | } 26 | 27 | return $this->respondWithRedirect('/cup/page'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Page/PageListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/page/index.twig', [ 10 | 'list' => $this->pageService->read(['order' => ['title' => 'asc']]), 11 | ]); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Publication/Category/CategoryListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/publication/category/index.twig', [ 12 | 'categories' => $this->publicationCategoryService->read(['order' => ['title' => 'asc']]), 13 | ]); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Publication/PublicationAction.php: -------------------------------------------------------------------------------- 1 | publicationCategoryService = $container->get(PublicationCategoryService::class); 21 | $this->publicationService = $container->get(PublicationService::class); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Publication/PublicationDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $publication = $this->publicationService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | 17 | if ($publication) { 18 | $this->publicationService->delete($publication); 19 | 20 | $this->container->get(\App\Application\PubSub::class)->publish('cup:publication:delete', $publication); 21 | } 22 | } catch (PublicationNotFoundException $e) { 23 | // nothing 24 | } 25 | } 26 | 27 | return $this->response->withAddedHeader('Location', '/cup/publication')->withStatus(301); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Publication/PublicationListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/publication/index.twig', [ 10 | 'categories' => $this->publicationCategoryService->read(), 11 | 'publications' => $this->publicationService->read(['order' => ['date' => 'desc']]), 12 | ]); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Publication/PublicationPreviewAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/publication/preview.twig'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Reference/ReferenceDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $reference = $this->referenceService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | $this->referenceService->delete($reference); 17 | 18 | $this->container->get(\App\Application\PubSub::class)->publish('cup:reference:delete', $reference); 19 | } catch (ReferenceNotFoundException $e) { 20 | // nothing 21 | } 22 | } 23 | 24 | return $this->respondWithRedirect($_SERVER['HTTP_REFERER']); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/Reference/ReferenceListAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('entity'); 10 | 11 | return $this->respondWithTemplate("cup/reference/{$entity}/index.twig", [ 12 | 'list' => $this->referenceService->read([ 13 | 'type' => $this->resolveReferenceType($entity), 14 | ]), 15 | ]); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/TaskRunAction.php: -------------------------------------------------------------------------------- 1 | request->getHeaderLine('Referer') ?? '/cup'; 12 | 13 | if ($this->isPost()) { 14 | if (($name = $this->getParam('task', null)) !== null && class_exists($name)) { 15 | /** @var \App\Domain\AbstractTask $task */ 16 | $task = new $name($this->container); 17 | $task->execute($this->getParam('params', [])); 18 | 19 | // run worker 20 | \App\Domain\AbstractTask::worker($task); 21 | 22 | $this->container->get(\App\Application\PubSub::class)->publish('cup:task:run', $task); 23 | 24 | $this->response = $this->response->withAddedHeader('Location', $redirect)->withStatus(301); 25 | } 26 | } 27 | 28 | return $this->respondWithJson(['run' => time()]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/Group/DeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 13 | try { 14 | $userGroup = $this->userGroupService->read([ 15 | 'uuid' => $this->resolveArg('uuid'), 16 | ]); 17 | 18 | if ($userGroup) { 19 | $this->userGroupService->delete($userGroup); 20 | 21 | $this->container->get(\App\Application\PubSub::class)->publish('cup:user:group:delete', $userGroup); 22 | } 23 | } catch (UserGroupNotFoundException $e) { 24 | // nothing 25 | } 26 | } 27 | 28 | return $this->respondWithRedirect('/cup/user/group'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/Group/ListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/user/group/index.twig', [ 12 | 'groups' => $this->userGroupService->read(['order' => ['title' => 'asc']]), 13 | 'users' => $this->userService->read(['status' => \App\Domain\Casts\User\Status::WORK]), 14 | ]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/NewsLetter/CreateAction.php: -------------------------------------------------------------------------------- 1 | isPost()) { 12 | $task = new \App\Domain\Tasks\SendNewsLetterMailTask($this->container); 13 | $task->execute([ 14 | 'subject' => $this->getParam('subject'), 15 | 'body' => $this->getParam('body'), 16 | 'type' => $this->getParam('type'), 17 | ]); 18 | 19 | // run worker 20 | \App\Domain\AbstractTask::worker($task); 21 | 22 | return $this->respondWithRedirect('/cup/user/newsletter'); 23 | } 24 | 25 | return $this->respondWithTemplate('cup/user/newsletter/form.twig'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/Subscriber/CreateAction.php: -------------------------------------------------------------------------------- 1 | isPost()) { 14 | try { 15 | $this->userSubscriberService->create([ 16 | 'email' => $this->getParam('email'), 17 | ]); 18 | } catch (EmailAlreadyExistsException|WrongEmailValueException $e) { 19 | $this->addError('email', $e->getMessage()); 20 | } 21 | } 22 | 23 | return $this->respondWithRedirect('/cup/user/subscriber'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/Subscriber/DeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 13 | try { 14 | $userSubscriber = $this->userSubscriberService->read(['uuid' => $this->resolveArg('uuid')]); 15 | 16 | if ($userSubscriber) { 17 | $this->userSubscriberService->delete($userSubscriber); 18 | } 19 | } catch (UserNotFoundException $e) { 20 | // nothing 21 | } 22 | } 23 | 24 | return $this->respondWithRedirect('/cup/user/subscriber'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/Subscriber/ListAction.php: -------------------------------------------------------------------------------- 1 | respondWithTemplate('cup/user/subscriber/index.twig', [ 12 | 'list' => $this->userSubscriberService->read(['order' => ['date' => 'desc']]), 13 | ]); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/UserAction.php: -------------------------------------------------------------------------------- 1 | userService = $container->get(UserService::class); 24 | $this->userGroupService = $container->get(UserGroupService::class); 25 | $this->userSubscriberService = $container->get(UserSubscriberService::class); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/UserDeleteAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid') && \Ramsey\Uuid\Uuid::isValid($this->resolveArg('uuid'))) { 12 | try { 13 | $user = $this->userService->read([ 14 | 'uuid' => $this->resolveArg('uuid'), 15 | ]); 16 | 17 | if ($user) { 18 | $this->userService->delete($user); 19 | 20 | $this->container->get(\App\Application\PubSub::class)->publish('cup:user:delete', $user); 21 | } 22 | } catch (UserNotFoundException $e) { 23 | // nothing 24 | } 25 | } 26 | 27 | return $this->response->withAddedHeader('Location', '/cup/user')->withStatus(301); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Application/Actions/Cup/User/UserViewAction.php: -------------------------------------------------------------------------------- 1 | resolveArg('uuid')) { 10 | $user = $this->userService->read(['uuid' => $this->resolveArg('uuid')]); 11 | 12 | if ($user) { 13 | return $this->respondWithTemplate('cup/user/view.twig', [ 14 | 'item' => $user, 15 | ]); 16 | } 17 | } 18 | 19 | return $this->respondWithRedirect('/cup/user'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Application/Mail.php: -------------------------------------------------------------------------------- 1 | '', 15 | 'mail_from_name' => '', 16 | 'subject' => 'WebSpaceEngine | Default subject', 17 | 'to' => '', // string|array(address=>name) 18 | 'cc' => '', // string|array(address=>name) 19 | 'bcc' => '', // string|array(address=>name) 20 | 'body' => '', 21 | 'isHtml' => false, 22 | 'attachments' => [], 23 | 24 | // sendpulse section 25 | 'sendpulse_is_enabled' => 'off', 26 | 'sendpulse_id' => '', 27 | 'sendpulse_secret' => '', 28 | 29 | // smtp section 30 | 'smtp_login' => '', 31 | 'smtp_pass' => '', 32 | 'smtp_secure' => '', 33 | 'smtp_host' => '', 34 | 'smtp_port' => '', 35 | 'smtp_timeout' => 30, 36 | 'smtp_options' => [], 37 | ]; 38 | $data = array_merge($default, $data); 39 | 40 | return match (true) { 41 | $data['sendpulse_is_enabled'] && $data['sendpulse_id'] && $data['sendpulse_secret'] => SPProvider::send($data), 42 | default => SMTPProvider::send($data), 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Application/Mail/MailProviderInterface.php: -------------------------------------------------------------------------------- 1 | handle($request); 17 | 18 | if (($value = $this->parameter('entity_cors_origin', false)) !== false) { 19 | $origin = $request->getHeaderLine('Origin'); 20 | 21 | if ($origin && in_array($origin, explode(PHP_EOL, $value), true) || $value === '*') { 22 | $response = $response->withHeader('Access-Control-Allow-Origin', $origin); 23 | } 24 | } 25 | if (($value = $this->parameter('entity_cors_headers', false)) !== false) { 26 | $response = $response->withHeader('Access-Control-Allow-Headers', $value); 27 | } 28 | if (($value = $this->parameter('entity_cors_methods', false)) !== false) { 29 | $response = $response->withHeader('Access-Control-Allow-Methods', mb_strtoupper($value)); 30 | } 31 | 32 | return $response; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Application/Middlewares/IsRouteEnabledAPIMiddleware.php: -------------------------------------------------------------------------------- 1 | getRoute(); 20 | $routeName = explode(':', $route->getName())[2] ?? ''; 21 | 22 | if ($routeName && $this->parameter($routeName . '_is_enabled', 'yes') !== 'no') { 23 | return $handler->handle($request); 24 | } 25 | 26 | $response = new Response(); 27 | $response->getBody()->write('Access denied'); 28 | 29 | return $response 30 | ->withHeader('Content-Type', 'text/plain; charset=utf-8') 31 | ->withStatus(423); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Application/Middlewares/IsRouteEnabledMiddleware.php: -------------------------------------------------------------------------------- 1 | getRoute(); 20 | $routeName = explode(':', $route->getName())[1] ?? ''; 21 | 22 | if ($routeName && $this->parameter($routeName . '_is_enabled', 'yes') !== 'no') { 23 | return $handler->handle($request); 24 | } 25 | 26 | return (new Response()) 27 | ->withHeader('Location', str_starts_with($route->getPattern(), '/cup') ? '/cup/forbidden' : '/forbidden') 28 | ->withStatus(307); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Middlewares/IsSiteEnabledMiddleware.php: -------------------------------------------------------------------------------- 1 | parameter('common_site_enabled', 'yes') !== 'yes') { 18 | $renderer = $this->container->get('view'); 19 | 20 | if (($path = realpath(THEME_DIR . '/' . $this->parameter('common_theme', 'default'))) !== false) { 21 | $renderer->getLoader()->addPath($path); 22 | } 23 | 24 | // add default errors pages 25 | $renderer->getLoader()->addPath(VIEW_ERROR_DIR); 26 | 27 | $response = (new Response())->withStatus(503); 28 | $response->getBody()->write($renderer->fetch('p503.twig')); 29 | 30 | return $response; 31 | } 32 | 33 | return $handler->handle($request); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Application/Middlewares/LocaleMiddleware.php: -------------------------------------------------------------------------------- 1 | parameter('common_language', 'en-US'); 19 | $user_locale = $request->getCookieParams()['language'] ?? null; 20 | $query_locale = $request->getQueryParams()['lang'] ?? null; 21 | 22 | // change lang by cookie 23 | if ($query_locale !== null) { 24 | $user_locale = $query_locale; 25 | 26 | @setcookie('language', $query_locale, time() + \App\Domain\References\Date::YEAR, '/'); 27 | } 28 | 29 | // change lang by user settings 30 | if (!$user_locale && ($user = $request->getAttribute('user', false)) !== false) { 31 | /** @var User $user */ 32 | if ($code = $user->language) { 33 | $user_locale = $code; 34 | } 35 | } 36 | 37 | i18n::init([ 38 | 'locale' => i18n::getLanguageFromHeader($request->getHeaderLine('Accept-Language'), $default_locale), 39 | 'default' => $default_locale, 40 | 'force' => $user_locale, 41 | ]); 42 | 43 | return $handler->handle($request); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Application/Middlewares/NonWWWMiddleware.php: -------------------------------------------------------------------------------- 1 | parameter('common_non_www', 'no') === 'yes') { 18 | $scheme = $request->getUri()->getScheme(); 19 | $host = $request->getUri()->getHost(); 20 | 21 | if (str_starts_with($host, 'www')) { 22 | return (new Response()) 23 | ->withHeader('Location', $scheme . '://' . str_replace('www.', '', $host)) 24 | ->withStatus(308); 25 | } 26 | } 27 | 28 | return $handler->handle($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Domain/AbstractException.php: -------------------------------------------------------------------------------- 1 | message = $message; 15 | } 16 | 17 | parent::__construct($this->message, $this->code, $previous); 18 | } 19 | 20 | public function getTitle(): string 21 | { 22 | return $this->title ?: (new \ReflectionClass($this))->getShortName(); 23 | } 24 | 25 | public function setTitle(string $title): self 26 | { 27 | $this->title = $title; 28 | 29 | return $this; 30 | } 31 | 32 | public function getDescription(): string 33 | { 34 | return $this->description ?: $this->getMessage(); 35 | } 36 | 37 | public function setDescription(string $description): self 38 | { 39 | $this->description = $description; 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Domain/AbstractExtension.php: -------------------------------------------------------------------------------- 1 | container = $container; 27 | $this->db = $container->get(DataBase::class); 28 | $this->arrayCache = $container->get(ArrayCache::class); 29 | $this->fileCache = $container->get(FileCache::class); 30 | } 31 | 32 | public function getTokenParsers() 33 | { 34 | return []; 35 | } 36 | 37 | public function getNodeVisitors() 38 | { 39 | return []; 40 | } 41 | 42 | public function getFilters() 43 | { 44 | return []; 45 | } 46 | 47 | public function getTests() 48 | { 49 | return []; 50 | } 51 | 52 | public function getFunctions() 53 | { 54 | return []; 55 | } 56 | 57 | public function getOperators() 58 | { 59 | return []; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Domain/AbstractHttpException.php: -------------------------------------------------------------------------------- 1 | container = $container; 28 | $this->db = $container->get(DataBase::class); 29 | $this->arrayCache = $container->get(ArrayCache::class); 30 | $this->fileCache = $container->get(FileCache::class); 31 | } 32 | 33 | abstract public function __invoke(Request $request, RequestHandlerInterface $handler): \Slim\Psr7\Response; 34 | 35 | protected function getRequestRemoteIP(Request $request): string 36 | { 37 | return 38 | $request->getServerParams()['HTTP_X_REAL_IP'] ?? 39 | $request->getServerParams()['HTTP_X_FORWARDED_FOR'] ?? 40 | $request->getServerParams()['REMOTE_ADDR'] ?? 41 | ''; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Domain/AbstractNotFoundException.php: -------------------------------------------------------------------------------- 1 | [], 25 | 'limit' => null, 26 | 'offset' => null, 27 | ]; 28 | 29 | public function __construct(ContainerInterface $container) 30 | { 31 | $this->container = $container; 32 | $this->db = $container->get(DataBase::class); 33 | $this->arrayCache = $container->get(ArrayCache::class); 34 | $this->fileCache = $container->get(FileCache::class); 35 | } 36 | 37 | abstract public function create(array $data = []); 38 | 39 | abstract public function read(array $data = []); 40 | 41 | abstract public function update($entity, array $data = []); 42 | 43 | abstract public function delete($entity); 44 | } 45 | -------------------------------------------------------------------------------- /src/Domain/Casts/AddressUrl.php: -------------------------------------------------------------------------------- 1 | title ?? ''; 19 | } 20 | 21 | $value = mb_strtolower($value); 22 | $value = i18n::getTranslatedText($value); 23 | $value = trim($value); 24 | $value = preg_replace(['/[^\w\s\/-]/', '/\s+/'], ['', '-'], $value); 25 | 26 | return implode('/', array_unique(explode('/', $value))); // for fix duplicate parts 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Domain/Casts/Boolean.php: -------------------------------------------------------------------------------- 1 | '', 11 | 'address' => '', 12 | ]; 13 | 14 | public function get($model, string $key, mixed $value, array $attributes): array 15 | { 16 | $value = json_decode($value, true); 17 | 18 | return array_merge($this->default, $value); 19 | } 20 | 21 | public function set($model, string $key, mixed $value, array $attributes): string 22 | { 23 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Domain/Casts/Catalog/Product/Dimension.php: -------------------------------------------------------------------------------- 1 | 0.0, 11 | 'width' => 0.0, 12 | 'height' => 0.0, 13 | 'weight' => 0.0, 14 | 'length_class' => '', 15 | 'weight_class' => '', 16 | ]; 17 | 18 | public function get($model, string $key, mixed $value, array $attributes): array 19 | { 20 | $value = json_decode($value, true); 21 | 22 | return array_merge($this->default, $value); 23 | } 24 | 25 | public function set($model, string $key, mixed $value, array $attributes): string 26 | { 27 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Domain/Casts/Catalog/Product/Tags.php: -------------------------------------------------------------------------------- 1 | '', 11 | 'description' => '', 12 | 'keywords' => '', 13 | ]; 14 | 15 | public function get($model, string $key, mixed $value, array $attributes): array 16 | { 17 | $value = json_decode($value, true); 18 | 19 | return array_merge($this->default, $value); 20 | } 21 | 22 | public function set($model, string $key, mixed $value, array $attributes): string 23 | { 24 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Domain/Casts/Page/Type.php: -------------------------------------------------------------------------------- 1 | '', 11 | 'direction' => '', 12 | ]; 13 | 14 | public function get($model, string $key, mixed $value, array $attributes): array 15 | { 16 | $value = json_decode($value, true); 17 | 18 | return array_merge($this->default, $value); 19 | } 20 | 21 | public function set($model, string $key, mixed $value, array $attributes): string 22 | { 23 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Domain/Casts/Task/Status.php: -------------------------------------------------------------------------------- 1 | '', 11 | 'position' => '', 12 | ]; 13 | 14 | public function get($model, string $key, mixed $value, array $attributes): array 15 | { 16 | $value = json_decode($value, true); 17 | 18 | return array_merge($this->default, $value); 19 | } 20 | 21 | public function set($model, string $key, mixed $value, array $attributes): string 22 | { 23 | if ($value) { 24 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 25 | } 26 | 27 | return '{}'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Domain/Casts/User/Legal.php: -------------------------------------------------------------------------------- 1 | '', 11 | 'number' => '', 12 | ]; 13 | 14 | public function get($model, string $key, mixed $value, array $attributes): array 15 | { 16 | $value = json_decode($value, true); 17 | 18 | return array_merge($this->default, $value); 19 | } 20 | 21 | public function set($model, string $key, mixed $value, array $attributes): string 22 | { 23 | if ($value) { 24 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 25 | } 26 | 27 | return '{}'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Domain/Casts/User/Messenger.php: -------------------------------------------------------------------------------- 1 | '', 11 | 'telegram' => '', 12 | 'whatsapp' => '', 13 | 'viber' => '', 14 | 'facebook' => '', 15 | 'instagram' => '', 16 | 'signal' => '', 17 | ]; 18 | 19 | public function get($model, string $key, mixed $value, array $attributes): array 20 | { 21 | $value = json_decode($value, true); 22 | 23 | return array_merge($this->default, $value); 24 | } 25 | 26 | public function set($model, string $key, mixed $value, array $attributes): string 27 | { 28 | if ($value) { 29 | return json_encode(array_merge($this->default, $value), JSON_UNESCAPED_UNICODE); 30 | } 31 | 32 | return '{}'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Domain/Casts/User/Password.php: -------------------------------------------------------------------------------- 1 | password; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Domain/Casts/User/Status.php: -------------------------------------------------------------------------------- 1 | url = $url; 22 | 23 | parent::__construct($this->message, $previous); 24 | } 25 | 26 | public function getUrl(): string 27 | { 28 | return $this->url; 29 | } 30 | 31 | public function setUrl(string $url): self 32 | { 33 | $this->url = $url; 34 | 35 | return $this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Domain/Exceptions/JWTExpiredException.php: -------------------------------------------------------------------------------- 1 | Uuid::class, 43 | 'data' => Json::class, 44 | 'message' => 'string', 45 | 'date' => 'datetime', 46 | ]; 47 | 48 | protected $attributes = [ 49 | 'form_uuid' => '', 50 | 'data' => '{}', 51 | 'message' => '', 52 | 'date' => 'now', 53 | ]; 54 | 55 | public function form(): HasOne 56 | { 57 | return $this->hasOne(Form::class, 'uuid', 'form_uuid'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Domain/Models/GuestBook.php: -------------------------------------------------------------------------------- 1 | 'string', 43 | 'email' => Email::class, 44 | 'message' => 'string', 45 | 'response' => 'string', 46 | 'status' => GuestBookStatus::class, 47 | 'date' => 'datetime', 48 | ]; 49 | 50 | protected $attributes = [ 51 | 'name' => '', 52 | 'email' => '', 53 | 'message' => '', 54 | 'response' => '', 55 | 'status' => \App\Domain\Casts\GuestBook\Status::MODERATE, 56 | 'date' => 'now', 57 | ]; 58 | } 59 | -------------------------------------------------------------------------------- /src/Domain/Models/Parameter.php: -------------------------------------------------------------------------------- 1 | 'string', 31 | 'value' => 'string', 32 | ]; 33 | 34 | protected $attributes = [ 35 | 'name' => '', 36 | 'value' => '', 37 | ]; 38 | } 39 | -------------------------------------------------------------------------------- /src/Domain/Models/Reference.php: -------------------------------------------------------------------------------- 1 | ReferenceType::class, 42 | 'title' => 'string', 43 | 'value' => Json::class, 44 | 'order' => 'int', 45 | 'status' => Boolean::class, 46 | ]; 47 | 48 | protected $attributes = [ 49 | 'type' => '', 50 | 'title' => '', 51 | 'value' => '{}', 52 | 'order' => 1, 53 | 'status' => true, 54 | ]; 55 | 56 | public function value(?string $key = null, mixed $default = null): mixed 57 | { 58 | if ($key) { 59 | return $this->value[$key] ?? $default; 60 | } 61 | 62 | return $this->value; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Domain/Models/Task.php: -------------------------------------------------------------------------------- 1 | 'string', 46 | 'action' => 'string', 47 | 'progress' => Decimal::class, 48 | 'status' => TaskStatus::class, 49 | 'params' => Json::class, 50 | 'output' => 'string', 51 | 'date' => 'datetime', 52 | ]; 53 | 54 | protected $attributes = [ 55 | 'title' => '', 56 | 'action' => '', 57 | 'progress' => .00, 58 | 'status' => \App\Domain\Casts\Task\Status::QUEUE, 59 | 'params' => '{}', 60 | 'output' => '', 61 | 'date' => 'now', 62 | ]; 63 | } 64 | -------------------------------------------------------------------------------- /src/Domain/Models/UserGroup.php: -------------------------------------------------------------------------------- 1 | $users 18 | */ 19 | class UserGroup extends Model 20 | { 21 | use HasUuids; 22 | use HasFiles; 23 | 24 | protected $table = 'user_group'; 25 | 26 | protected $primaryKey = 'uuid'; 27 | 28 | public const CREATED_AT = null; 29 | public const UPDATED_AT = null; 30 | 31 | protected $fillable = [ 32 | 'title', 33 | 'description', 34 | 'access', 35 | ]; 36 | 37 | protected $guarded = []; 38 | 39 | protected $casts = [ 40 | 'title' => 'string', 41 | 'description' => 'string', 42 | 'access' => Json::class, 43 | ]; 44 | 45 | protected $attributes = [ 46 | 'title' => '', 47 | 'description' => '', 48 | 'access' => '[]', 49 | ]; 50 | 51 | protected $hidden = [ 52 | 'access', 53 | ]; 54 | 55 | public function users(): HasMany 56 | { 57 | return $this->hasMany(User::class, 'group_uuid', 'uuid'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Domain/Models/UserIntegration.php: -------------------------------------------------------------------------------- 1 | Uuid::class, 42 | 'provider' => 'string', 43 | 'unique' => 'string', 44 | 'date' => 'datetime', 45 | ]; 46 | 47 | public function user(): BelongsTo 48 | { 49 | return $this->belongsTo(User::class, 'user_uuid', 'uuid'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Domain/Models/UserSubscriber.php: -------------------------------------------------------------------------------- 1 | 'string', 33 | 'date' => 'datetime', 34 | ]; 35 | 36 | protected $attributes = [ 37 | 'email' => '', 38 | 'date' => 'now', 39 | ]; 40 | } 41 | -------------------------------------------------------------------------------- /src/Domain/Models/UserToken.php: -------------------------------------------------------------------------------- 1 | Uuid::class, 44 | 'unique' => 'string', 45 | 'comment' => 'string', 46 | 'ip' => 'string', 47 | 'agent' => 'string', 48 | 'date' => 'datetime', 49 | ]; 50 | 51 | protected $attributes = [ 52 | 'user_uuid' => '', 53 | 'unique' => '', 54 | 'comment' => '', 55 | 'ip' => '', 56 | 'agent' => '', 57 | 'date' => 'now', 58 | ]; 59 | 60 | public function user(): BelongsTo 61 | { 62 | return $this->belongsTo(User::class, 'user_uuid', 'uuid'); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Domain/Plugin/AbstractDeliveryPlugin.php: -------------------------------------------------------------------------------- 1 | routes = true; 18 | $this->handledRoutes = array_merge($this->handledRoutes, $name); 19 | } 20 | 21 | public function getHandledRoute(): array 22 | { 23 | return $this->handledRoutes; 24 | } 25 | 26 | /** 27 | * The function will be executed BEFORE processing the selected route 28 | */ 29 | abstract public function before(Request $request, string $routeName): void; 30 | 31 | /** 32 | * The function will be executed AFTER processing the selected route 33 | */ 34 | abstract public function after(Request $request, Response $response, string $routeName): Response; 35 | } 36 | -------------------------------------------------------------------------------- /src/Domain/Plugin/AbstractMailPlugin.php: -------------------------------------------------------------------------------- 1 | logger->info(date('Y-m-d H:i:s') . " - Running LogJob\n"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Domain/Service/Catalog/Exception/AddressAlreadyExistsException.php: -------------------------------------------------------------------------------- 1 | [], 16 | ]; 17 | $params = array_merge($default, $params); 18 | 19 | return parent::execute($params); 20 | } 21 | 22 | /** 23 | * @throws \Psr\Container\ContainerExceptionInterface 24 | * @throws \Psr\Container\NotFoundExceptionInterface 25 | * @throws \App\Domain\Service\Task\Exception\TaskNotFoundException 26 | */ 27 | protected function action(array $args = []): void 28 | { 29 | if ($this->parameter('image_enable', 'no') === 'no') { 30 | $this->setStatusCancel(); 31 | 32 | return; 33 | } 34 | 35 | $fileService = $this->container->get(FileService::class); 36 | 37 | // add task convert 38 | $task = new \App\Domain\Tasks\ConvertImageTask($this->container); 39 | $task->execute([ 40 | 'uuid' => $fileService 41 | ->read() 42 | ->filter(fn (\App\Domain\Models\File $file) => str_starts_with($file->type, 'image/')) 43 | ->pluck('uuid'), 44 | ]); 45 | 46 | // run worker 47 | \App\Domain\AbstractTask::worker($task); 48 | 49 | $this->setStatusDone(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Domain/Tasks/SendJSONTask.php: -------------------------------------------------------------------------------- 1 | '', 15 | 'data' => [], 16 | 'files' => [], 17 | ]; 18 | $params = array_merge($default, $params); 19 | 20 | return parent::execute($params); 21 | } 22 | 23 | protected function action(array $args = []): void 24 | { 25 | $data = (array) $args['data']; 26 | 27 | if ($args['files']) { 28 | $data['files'] = $args['files']; 29 | } 30 | 31 | $result = file_get_contents($args['url'], false, stream_context_create([ 32 | 'http' => [ 33 | 'method' => 'POST', 34 | 'content' => json_encode($data), 35 | 'header' => 'Content-Type: application/json;' . PHP_EOL . 'Accept: application/json' . PHP_EOL, 36 | 'timeout' => 30, 37 | ], 38 | ])); 39 | 40 | if ($result !== false) { 41 | $this->container->get(\App\Application\PubSub::class)->publish('task:json:send'); 42 | 43 | $this->setStatusDone($result); 44 | } else { 45 | $this->setStatusFail(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Domain/Traits/HasFiles.php: -------------------------------------------------------------------------------- 1 | $files 11 | * @property Collection $documents 12 | * @property Collection $images 13 | * @property Collection $audios 14 | * @property Collection $videos 15 | */ 16 | trait HasFiles 17 | { 18 | public function files(): MorphToMany 19 | { 20 | return $this 21 | ->morphToMany( 22 | File::class, 23 | 'object', 24 | 'file_related', 25 | 'entity_uuid', 26 | 'file_uuid', 27 | ) 28 | ->withPivot('comment', 'order') 29 | ->orderBy('file_related.order'); 30 | } 31 | 32 | public function documents(): MorphToMany 33 | { 34 | return $this 35 | ->files() 36 | ->where('type', 'like', 'application/%') 37 | ->orWhere('type', 'like', 'text/%'); 38 | } 39 | 40 | public function images(): MorphToMany 41 | { 42 | return $this 43 | ->files() 44 | ->where('type', 'like', 'image/%'); 45 | } 46 | 47 | public function audios(): MorphToMany 48 | { 49 | return $this 50 | ->files() 51 | ->where('type', 'like', 'audio/%'); 52 | } 53 | 54 | public function videos(): MorphToMany 55 | { 56 | return $this 57 | ->files() 58 | ->where('type', 'like', 'video/%'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Domain/Traits/HasStorage.php: -------------------------------------------------------------------------------- 1 | 5 |
6 |
7 |

{{ 'Access is denied'|locale }}

8 |
9 |
10 | 11 | {% endblock %} 12 | 13 | {% block content %} 14 |
15 |
16 |
17 |
18 | {{ 'You currently do not have access to the requested page!'|locale }} 19 |

20 | {{ 'If you are sure that the address is typed correctly:'|locale }}
21 | — {{ 'check permissions'|locale }};
22 | — {{ 'contact the administrator'|locale }}. 23 |

24 |
25 |
26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /src/Template/cup/catalog/order/dispatch.twig: -------------------------------------------------------------------------------- 1 | {% extends 'cup/layout.twig' %} 2 | 3 | {% block title %}{{ 'Dispatch Note'|locale }} #{{ order.external_id ?: order.serial }}{% endblock %} 4 | 5 | {% block breadcrumb %} 6 |
7 |
8 |
9 |

{{ 'Dispatch Note'|locale }}

10 |
11 | 19 |
20 |
21 | {% endblock %} 22 | 23 | {% block content %} 24 |
25 | {{ include(template_from_string(template)) }} 26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /src/Template/cup/catalog/order/document-view.twig: -------------------------------------------------------------------------------- 1 | {% extends 'cup/layout.twig' %} 2 | 3 | {% block title %}{{ document.title|locale }} #{{ order.external_id ?: order.serial }}{% endblock %} 4 | 5 | {% block breadcrumb %} 6 |
7 |
8 |
9 |

{{ document.title|locale }}

10 |
11 | 19 |
20 |
21 | {% endblock %} 22 | 23 | {% block content %} 24 |
25 | {{ include(template_from_string(template)) }} 26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /src/Template/cup/catalog/order/form-modal-product.twig: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/Template/cup/catalog/product/form-modal-related.twig: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/Template/cup/catalog/product/index-modal-import.twig: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /src/Template/cup/donate.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 40 | -------------------------------------------------------------------------------- /src/Template/cup/form-header.twig: -------------------------------------------------------------------------------- 1 | {# 2 | item - entity 3 | create - header 1 4 | update - header 2 5 | #} 6 |
7 | {% if item is null %} 8 |

{{ create|locale }}

9 | {% else %} 10 |

11 | {{ update|locale }} 12 | 13 |

14 | {% endif %} 15 |
16 | -------------------------------------------------------------------------------- /src/Template/cup/form-meta.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 | {% include 'cup/form.twig' with { 4 | 'label': 'Meta tag Title'|locale, 5 | 'type': 'text', 6 | 'name': 'meta[title]', 7 | 'args': { 8 | 'value': entity.meta.title, 9 | } 10 | } %} 11 |
12 |
13 | {% include 'cup/form.twig' with { 14 | 'label': 'Meta tag Description'|locale, 15 | 'type': 'text', 16 | 'name': 'meta[description]', 17 | 'args': { 18 | 'value': entity.meta.description, 19 | } 20 | } %} 21 |
22 |
23 | {% include 'cup/form.twig' with { 24 | 'label': 'Meta tag Keywords'|locale, 25 | 'type': 'text', 26 | 'name': 'meta[keywords]', 27 | 'args': { 28 | 'value': entity.meta.keywords, 29 | } 30 | } %} 31 |
32 |
33 | -------------------------------------------------------------------------------- /src/Template/cup/form-save.twig: -------------------------------------------------------------------------------- 1 |
2 | 5 | 8 |
9 | -------------------------------------------------------------------------------- /src/Template/cup/form-tags.twig: -------------------------------------------------------------------------------- 1 | {% block tags %} 2 | {% set inputId = random() %} 3 | 4 | {% if args is null %} 5 | {% set args = {} %} 6 | {% endif %} 7 | 8 |
9 |
10 |
11 | 14 | 15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /src/Template/cup/reference/deliveries/form-item.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 | {% include 'cup/form.twig' with { 4 | 'label': 'Service'|locale, 5 | 'type': 'select', 6 | 'name': 'value[' ~ (loop.index0 ?? 0) ~ '][uuid]', 7 | 'args': { 8 | 'required': true, 9 | 'option': products, 10 | 'selected': pair.uuid, 11 | } 12 | } %} 13 |
14 |
15 | {% include 'cup/form.twig' with { 16 | 'label': 'Condition'|locale, 17 | 'type': 'number', 18 | 'name': 'value[' ~ (loop.index0 ?? 0) ~ '][condition]', 19 | 'prefix': 'total >='|locale, 20 | 'postfix': '', 21 | 'postfix_btn': '', 22 | 'args': { 23 | 'required': true, 24 | 'value': pair.condition ?? 0, 25 | 'placeholder': 0, 26 | 'step': 'any', 27 | 'min': 0, 28 | } 29 | } %} 30 |
31 |
32 | -------------------------------------------------------------------------------- /src/Template/cup/system/index.twig: -------------------------------------------------------------------------------- 1 | {% extends 'cup/layout.twig' %} 2 | 3 | {% block body %} 4 | 5 |
6 |
7 |

8 | {{ (user == null ? 'Install' : 'Update')|locale }} 9 | {{ 'platform WebSpace Engine'|locale }} 10 |

11 |
12 | 13 | {% include 'cup/system/index-step-' ~ step ~ '.twig' %} 14 | 15 | 21 |
22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /src/Template/errors/p400.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% block body %} 4 |

Bad request

5 | {{ exception.getMessage() }} 6 |
{{ exception.getTraceAsString() }}
7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /src/Template/errors/p403.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% block body %} 4 | Forbidden 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /src/Template/errors/p404.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% block body %} 4 | Not found 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /src/Template/errors/p405.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% block body %} 4 | Not allowed 5 |
{{ methods.implode(' ') }}
6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /src/Template/errors/p500.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% block body %} 4 | {{ exception.getMessage() }} 5 |
{{ exception.getTraceAsString() }}
6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /src/Template/errors/p503.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% block body %} 4 |

The site is temporarily disabled!

5 | Please try to visit this site later! 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /src/Template/layout.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% block head '' %} 15 | {% block style '' %} 16 | 17 | {% block title %}{{ title ? title ~ ' | ' : '' }}{{ parameter('common_title', '') }}{% endblock %} 18 | 19 | 20 | 21 | {% block body %} 22 | 23 | {% block bodyinner '' %} 24 | {% block script '' %} 25 | 26 | {% endblock %} 27 | 28 | -------------------------------------------------------------------------------- /src/Template/mixin/img.twig: -------------------------------------------------------------------------------- 1 | {# 2 | placeholder - image link for placeholder 3 | src - image link 4 | id - attr id 5 | class - attr class 6 | alt - attr alt 7 | title - attr title 8 | style - attr style 9 | args - other args 10 | #} 11 | {% htmlcompress %} 12 | 23 | {% endhtmlcompress %} 24 | -------------------------------------------------------------------------------- /src/bootstrap.php: -------------------------------------------------------------------------------- 1 | build(); 24 | 25 | // include plugins 26 | require PLUGIN_DIR . '/installed.php'; 27 | 28 | // instantiate the app 29 | $app = $container->get(\Slim\App::class); 30 | -------------------------------------------------------------------------------- /src/middleware.php: -------------------------------------------------------------------------------- 1 | add(\App\Application\Middlewares\PluginMiddleware::class); 11 | 12 | // redirect to non-www domain 13 | $app->add(\App\Application\Middlewares\NonWWWMiddleware::class); 14 | 15 | // redirect to address without slash in end 16 | $app->add(function (Request $request, RequestHandlerInterface $handler) { 17 | $path = $request->getUri()->getPath(); 18 | 19 | if ($path !== '/' && str_ends_with($path, '/')) { 20 | $query = $request->getUri()->getQuery(); 21 | 22 | return (new Response()) 23 | ->withAddedHeader('Location', rtrim($path, '/') . ($query ? '?' . $query : '')) 24 | ->withStatus(301); 25 | } 26 | 27 | return $handler->handle($request); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /theme/default/main.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | 3 | {% set title = 'WebSpace Engine' %} 4 | 5 | {% block head %} 6 | 7 | 8 | 9 | 13 | {% endblock %} 14 | 15 | {% block body %} 16 | 17 | {% block bodyinner %} 18 | {% block page %} 19 | Default page 20 | {% endblock %} 21 | {% endblock %} 22 | 23 | {% endblock %} 24 | 25 | -------------------------------------------------------------------------------- /theme/default/p400.twig: -------------------------------------------------------------------------------- 1 |

Bad request

2 | {{ exception.getMessage() }} 3 |
{{ exception.getTraceAsString() }}
4 | -------------------------------------------------------------------------------- /theme/default/p404.twig: -------------------------------------------------------------------------------- 1 | Not found 2 | -------------------------------------------------------------------------------- /theme/default/p405.twig: -------------------------------------------------------------------------------- 1 | Not allowed 2 |
{{ methods.implode(' ') }}
3 | 4 | -------------------------------------------------------------------------------- /theme/default/p500.twig: -------------------------------------------------------------------------------- 1 | {{ exception.getMessage() }} 2 |
{{ exception.getTraceAsString() }}
3 | -------------------------------------------------------------------------------- /var/cache/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/var/cache/.gitkeep -------------------------------------------------------------------------------- /var/log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/var/log/.gitkeep -------------------------------------------------------------------------------- /var/upload/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/var/upload/.gitkeep -------------------------------------------------------------------------------- /var/xml/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getwebspace/platform/f5b666574e03675f18df7b48de619a85f200af51/var/xml/.gitkeep --------------------------------------------------------------------------------