├── .env.sample ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── css │ ├── app.css │ ├── install.css │ └── settings.css └── js │ ├── app.js │ ├── install.js │ └── settings.js ├── bin └── console ├── composer.json ├── config ├── bootstrap.php ├── bundles.php ├── packages │ ├── assets.yaml │ ├── cache.yaml │ ├── dev │ │ ├── monolog.yaml │ │ └── routing.yaml │ ├── doctrine.yaml │ ├── doctrine_migrations.yaml │ ├── framework.yaml │ ├── prod │ │ ├── doctrine.yaml │ │ └── monolog.yaml │ ├── routing.yaml │ ├── test │ │ ├── framework.yaml │ │ ├── monolog.yaml │ │ └── routing.yaml │ ├── twig.yaml │ └── webpack_encore.yaml ├── routes.yaml ├── routes │ ├── annotations.yaml │ └── dev │ │ └── twig.yaml └── services.yaml ├── package.json ├── public └── index.php ├── src ├── Bitrix │ └── B24App.php ├── Controller │ ├── .gitignore │ ├── ActivityController.php │ └── DefaultController.php ├── Entity │ ├── .gitignore │ └── Portal.php ├── Kernel.php ├── Migrations │ └── .gitignore └── Repository │ ├── .gitignore │ └── PortalRepository.php ├── templates ├── base.html.twig ├── install.html.twig └── settings.html.twig └── webpack.config.js /.env.sample: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the later taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # 13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 14 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 15 | 16 | APP_DOMAIN=example.com 17 | 18 | ###> symfony/framework-bundle ### 19 | APP_ENV=dev 20 | APP_SECRET=fqwfqwfqwfwf 21 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 22 | #TRUSTED_HOSTS='^localhost|example\.com$' 23 | ###< symfony/framework-bundle ### 24 | 25 | ###> doctrine/doctrine-bundle ### 26 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 27 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 28 | # Configure your db driver and server_version in config/packages/doctrine.yaml 29 | DATABASE_URL=mysqli://login:password@host:3306/dbname 30 | ###< doctrine/doctrine-bundle ### 31 | 32 | 33 | ###> bitrix24 app ### 34 | B24_APPLICATION_SCOPE=user,bizproc 35 | B24_APPLICATION_ID=application_id 36 | B24_APPLICATION_SECRET=application_secret 37 | ###< bitrix24 ### -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | 3 | ###> symfony/framework-bundle ### 4 | /.env.local 5 | /.env.local.php 6 | /.env.*.local 7 | /public/bundles/ 8 | /var/ 9 | /vendor/ 10 | ###< symfony/framework-bundle ### 11 | 12 | ###> symfony/web-server-bundle ### 13 | /.web-server-pid 14 | ###< symfony/web-server-bundle ### 15 | 16 | ###> symfony/webpack-encore-bundle ### 17 | /node_modules/ 18 | /public/build/ 19 | npm-debug.log 20 | yarn-error.log 21 | ###< symfony/webpack-encore-bundle ### 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alexandr Shubin 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 10 | furnished 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Шаблон приложения Bitrix24 на Symfony 2 | 3 | О том что именно сделано и почему подробнее читайте тут 4 | https://verstaem.com/bitrix24/cloud-app-on-symfony/ 5 | 6 | ## Как запустить? 7 | 8 | 1\. Скачиваем шаблон в какую либо папку 9 | 10 | 2\. Открываем скачанную папку и выполняем в консоли 11 | ```bash 12 | $ composer install 13 | ``` 14 | 15 | 3\. Устанавливаем npm пакеты через yarn. Выполняем в консоли 16 | ```bash 17 | $ npm install -g yarn 18 | $ yarn install 19 | ``` 20 | 21 | 4\. Собираем фронт 22 | ```bash 23 | $ yarn encore prod 24 | ``` 25 | 26 | 5\. Переименовываем или копируем файл /.env.sample в /.env. 27 | 28 | В переименнованном файле указываем свои данные в переменных 29 | APP_DOMAIN, DATABASE_URL, B24_APPLICATION_SCOPE, B24_APPLICATION_ID, B24_APPLICATION_SECRET 30 | 31 | 6\. Настраиваем веб-сервер, чтобы корнем сайта была папка /public 32 | 33 | 7\. Добавляем локальное приложение на стороне Битрикс24 34 | * В качестве страницы приложения указываем https://ваш_домен/settings 35 | * В качестве страницы установки указываем htttps://ваш_домен/install 36 | 37 | ## URL приложения 38 | /install - страница установки 39 | 40 | /settings - страница настроек 41 | 42 | /test - страница добавления тестового действия бизнес процесса 43 | 44 | /activities/test - страница обработки действия бизнес процесса 45 | 46 | ## B24App 47 | 48 | В файле /src/Bitrix/B24App.php лежит класс содержащий код автоматического обновления accessToken 49 | -------------------------------------------------------------------------------- /assets/css/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: lightgray; 3 | } 4 | -------------------------------------------------------------------------------- /assets/css/install.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #FFF; 3 | } -------------------------------------------------------------------------------- /assets/css/settings.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #858585; 3 | } -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Welcome to your app's main JavaScript file! 3 | * 4 | * We recommend including the built version of this JavaScript file 5 | * (and its CSS file) in your base layout (base.html.twig). 6 | */ 7 | 8 | // any CSS you require will output into a single css file (app.css in this case) 9 | require('../css/app.css'); 10 | 11 | // Need jQuery? Install it with "yarn add jquery", then uncomment to require it. 12 | // const $ = require('jquery'); 13 | 14 | console.log('Hello Webpack Encore! Edit me in assets/js/app.js'); 15 | -------------------------------------------------------------------------------- /assets/js/install.js: -------------------------------------------------------------------------------- 1 | require('../css/install.css'); 2 | 3 | BX24.init(function () { 4 | var result = document.getElementById("result"); 5 | if(result.className === 'success') { 6 | BX24.installFinish(); 7 | } 8 | }); -------------------------------------------------------------------------------- /assets/js/settings.js: -------------------------------------------------------------------------------- 1 | require('../css/settings.css'); -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 19 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 20 | } 21 | 22 | if ($input->hasParameterOption('--no-debug', true)) { 23 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 24 | } 25 | 26 | require dirname(__DIR__).'/config/bootstrap.php'; 27 | 28 | if ($_SERVER['APP_DEBUG']) { 29 | umask(0000); 30 | 31 | if (class_exists(Debug::class)) { 32 | Debug::enable(); 33 | } 34 | } 35 | 36 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 37 | $application = new Application($kernel); 38 | $application->run($input); 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "license": "proprietary", 4 | "require": { 5 | "php": "^7.1.3", 6 | "ext-ctype": "*", 7 | "ext-curl": "*", 8 | "ext-iconv": "*", 9 | "ext-json": "*", 10 | "mesilov/bitrix24-php-sdk": "^0.6.0", 11 | "symfony/console": "4.2.*", 12 | "symfony/dotenv": "4.2.*", 13 | "symfony/flex": "^1.1", 14 | "symfony/framework-bundle": "4.2.*", 15 | "symfony/monolog-bundle": "^3.3", 16 | "symfony/orm-pack": "^1.0", 17 | "symfony/twig-bundle": "4.2.*", 18 | "symfony/webpack-encore-bundle": "^1.1", 19 | "symfony/yaml": "4.2.*" 20 | }, 21 | "config": { 22 | "preferred-install": { 23 | "*": "dist" 24 | }, 25 | "sort-packages": true 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "App\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "App\\Tests\\": "tests/" 35 | } 36 | }, 37 | "replace": { 38 | "paragonie/random_compat": "2.*", 39 | "symfony/polyfill-ctype": "*", 40 | "symfony/polyfill-iconv": "*", 41 | "symfony/polyfill-php71": "*", 42 | "symfony/polyfill-php70": "*", 43 | "symfony/polyfill-php56": "*" 44 | }, 45 | "scripts": { 46 | "auto-scripts": { 47 | "cache:clear": "symfony-cmd", 48 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 49 | }, 50 | "post-install-cmd": [ 51 | "@auto-scripts" 52 | ], 53 | "post-update-cmd": [ 54 | "@auto-scripts" 55 | ] 56 | }, 57 | "conflict": { 58 | "symfony/symfony": "*" 59 | }, 60 | "extra": { 61 | "symfony": { 62 | "allow-contrib": false, 63 | "require": "4.2.*" 64 | } 65 | }, 66 | "require-dev": { 67 | "roave/security-advisories": "dev-master", 68 | "symfony/maker-bundle": "^1.11", 69 | "symfony/web-server-bundle": "4.2.*" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /config/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2) 9 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { 10 | $_SERVER += $env; 11 | $_ENV += $env; 12 | } elseif (!class_exists(Dotenv::class)) { 13 | throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); 14 | } else { 15 | // load all the .env files 16 | (new Dotenv())->loadEnv(dirname(__DIR__).'/.env'); 17 | } 18 | 19 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 20 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 21 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 22 | -------------------------------------------------------------------------------- /config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], 6 | Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], 7 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 8 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 9 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 10 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 11 | Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], 12 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 13 | ]; 14 | -------------------------------------------------------------------------------- /config/packages/assets.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | assets: 3 | json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' 4 | -------------------------------------------------------------------------------- /config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Put the unique name of your app here: the prefix seed 4 | # is used to compute stable namespaces for cache keys. 5 | #prefix_seed: your_vendor_name/app_name 6 | 7 | # The app cache caches to the filesystem by default. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: ~ 20 | -------------------------------------------------------------------------------- /config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | # uncomment to get logging in your browser 9 | # you may have to allow bigger header sizes in your Web server configuration 10 | #firephp: 11 | # type: firephp 12 | # level: info 13 | #chromephp: 14 | # type: chromephp 15 | # level: info 16 | console: 17 | type: console 18 | process_psr_3_messages: false 19 | channels: ["!event", "!doctrine", "!console"] 20 | -------------------------------------------------------------------------------- /config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | # Adds a fallback DATABASE_URL if the env var is not set. 3 | # This allows you to run cache:warmup even if your 4 | # environment variables are not available yet. 5 | # You should not need to change this value. 6 | env(DATABASE_URL): '' 7 | 8 | doctrine: 9 | dbal: 10 | # configure these for your database server 11 | driver: 'pdo_mysql' 12 | server_version: '5.7' 13 | charset: utf8mb4 14 | default_table_options: 15 | charset: utf8mb4 16 | collate: utf8mb4_unicode_ci 17 | 18 | url: '%env(resolve:DATABASE_URL)%' 19 | orm: 20 | auto_generate_proxy_classes: true 21 | naming_strategy: doctrine.orm.naming_strategy.underscore 22 | auto_mapping: true 23 | mappings: 24 | App: 25 | is_bundle: false 26 | type: annotation 27 | dir: '%kernel.project_dir%/src/Entity' 28 | prefix: 'App\Entity' 29 | alias: App 30 | -------------------------------------------------------------------------------- /config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | dir_name: '%kernel.project_dir%/src/Migrations' 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | namespace: DoctrineMigrations 6 | -------------------------------------------------------------------------------- /config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #default_locale: en 4 | #csrf_protection: true 5 | #http_method_override: true 6 | 7 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 8 | # Remove or comment this section to explicitly disable session support. 9 | session: 10 | handler_id: ~ 11 | cookie_secure: auto 12 | cookie_samesite: lax 13 | 14 | #esi: true 15 | #fragments: true 16 | php_errors: 17 | log: true 18 | -------------------------------------------------------------------------------- /config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: service 6 | id: doctrine.system_cache_provider 7 | query_cache_driver: 8 | type: service 9 | id: doctrine.system_cache_provider 10 | result_cache_driver: 11 | type: service 12 | id: doctrine.result_cache_provider 13 | 14 | services: 15 | doctrine.result_cache_provider: 16 | class: Symfony\Component\Cache\DoctrineProvider 17 | public: false 18 | arguments: 19 | - '@doctrine.result_cache_pool' 20 | doctrine.system_cache_provider: 21 | class: Symfony\Component\Cache\DoctrineProvider 22 | public: false 23 | arguments: 24 | - '@doctrine.system_cache_pool' 25 | 26 | framework: 27 | cache: 28 | pools: 29 | doctrine.result_cache_pool: 30 | adapter: cache.app 31 | doctrine.system_cache_pool: 32 | adapter: cache.system 33 | -------------------------------------------------------------------------------- /config/packages/prod/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | excluded_404s: 8 | # regex: exclude all 404 errors from the logs 9 | - ^/ 10 | nested: 11 | type: stream 12 | path: "%kernel.logs_dir%/%kernel.environment%.log" 13 | level: debug 14 | console: 15 | type: console 16 | process_psr_3_messages: false 17 | channels: ["!event", "!doctrine"] 18 | deprecation: 19 | type: stream 20 | path: "%kernel.logs_dir%/%kernel.environment%.deprecations.log" 21 | deprecation_filter: 22 | type: filter 23 | handler: deprecation 24 | max_level: info 25 | channels: ["php"] 26 | -------------------------------------------------------------------------------- /config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | utf8: true 5 | -------------------------------------------------------------------------------- /config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | -------------------------------------------------------------------------------- /config/packages/test/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: '%kernel.project_dir%/templates' 3 | debug: '%kernel.debug%' 4 | strict_variables: '%kernel.debug%' 5 | -------------------------------------------------------------------------------- /config/packages/webpack_encore.yaml: -------------------------------------------------------------------------------- 1 | webpack_encore: 2 | # The path where Encore is building the assets. 3 | # This should match Encore.setOutputPath() in webpack.config.js. 4 | output_path: '%kernel.project_dir%/public/build' 5 | -------------------------------------------------------------------------------- /config/routes.yaml: -------------------------------------------------------------------------------- 1 | settings: 2 | path: /settings 3 | controller: App\Controller\DefaultController::settings 4 | 5 | install: 6 | path: /install 7 | controller: App\Controller\DefaultController::install 8 | 9 | test: 10 | path: /test 11 | controller: App\Controller\DefaultController::test 12 | 13 | activity_test: 14 | path: /activities/test 15 | controller: App\Controller\ActivityController::test -------------------------------------------------------------------------------- /config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | -------------------------------------------------------------------------------- /config/routes/dev/twig.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@TwigBundle/Resources/config/routing/errors.xml' 3 | prefix: /_error 4 | -------------------------------------------------------------------------------- /config/services.yaml: -------------------------------------------------------------------------------- 1 | # This file is the entry point to configure your own services. 2 | # Files in the packages/ subdirectory configure your dependencies. 3 | 4 | # Put parameters here that don't need to change on each machine where the app is deployed 5 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 6 | parameters: 7 | 8 | services: 9 | # default configuration for services in *this* file 10 | _defaults: 11 | autowire: true # Automatically injects dependencies in your services. 12 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 13 | 14 | # makes classes in src/ available to be used as services 15 | # this creates a service per class whose id is the fully-qualified class name 16 | App\: 17 | resource: '../src/*' 18 | exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' 19 | 20 | # controllers are imported separately to make sure services can be injected 21 | # as action arguments even if you don't extend any base controller class 22 | App\Controller\: 23 | resource: '../src/Controller' 24 | tags: ['controller.service_arguments'] 25 | 26 | App\Bitrix\B24App: 27 | calls: 28 | - [setEntityManager] 29 | 30 | # add more service definitions when explicit configuration is needed 31 | # please note that last definitions always *replace* previous ones 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@symfony/webpack-encore": "^0.22.0", 4 | "webpack-notifier": "^1.6.0" 5 | }, 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "scripts": { 9 | "dev-server": "encore dev-server", 10 | "dev": "encore dev", 11 | "watch": "encore dev --watch", 12 | "build": "encore production --progress" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /src/Bitrix/B24App.php: -------------------------------------------------------------------------------- 1 | setApplicationScope(explode(',', $_ENV['B24_APPLICATION_SCOPE'])); 34 | $this->setApplicationId($_ENV['B24_APPLICATION_ID']); 35 | $this->setApplicationSecret($_ENV['B24_APPLICATION_SECRET']); 36 | 37 | $this->setOnExpiredToken([$this, 'refreshAccessToken']); 38 | } 39 | 40 | /** 41 | * @inheritDoc 42 | */ 43 | public function call($methodName, array $additionalParameters = []) 44 | { 45 | if ($this->portal === null) { 46 | $this->fetchAccessToken(); 47 | } 48 | 49 | return parent::call($methodName, $additionalParameters); 50 | } 51 | 52 | /** 53 | * @throws Bitrix24Exception 54 | * @throws \Bitrix24\Exceptions\Bitrix24ApiException 55 | * @throws \Bitrix24\Exceptions\Bitrix24EmptyResponseException 56 | * @throws \Bitrix24\Exceptions\Bitrix24IoException 57 | * @throws \Bitrix24\Exceptions\Bitrix24MethodNotFoundException 58 | * @throws \Bitrix24\Exceptions\Bitrix24PaymentRequiredException 59 | * @throws \Bitrix24\Exceptions\Bitrix24PortalDeletedException 60 | * @throws \Bitrix24\Exceptions\Bitrix24PortalRenamedException 61 | * @throws \Bitrix24\Exceptions\Bitrix24TokenIsExpiredException 62 | * @throws \Bitrix24\Exceptions\Bitrix24TokenIsInvalidException 63 | * @throws \Bitrix24\Exceptions\Bitrix24WrongClientException 64 | * @throws \Exception 65 | */ 66 | public function fetchAccessToken(): void 67 | { 68 | $repository = $this->entityManager->getRepository(Portal::class); 69 | $this->portal = $repository->find(1); 70 | 71 | if ($this->portal === null) { 72 | throw new \RuntimeException('No access token found'); 73 | } 74 | 75 | $this->setDomain($this->portal->getDomain()); 76 | $this->setMemberId($this->portal->getMemberId()); 77 | $this->setAccessToken($this->portal->getAccessToken()); 78 | $this->setRefreshToken($this->portal->getRefreshToken()); 79 | 80 | if ($this->portal->getExpires() < new \DateTime('now')) { 81 | $this->refreshAccessToken(); 82 | } 83 | } 84 | 85 | /** 86 | * @throws Bitrix24Exception 87 | * @throws \Bitrix24\Exceptions\Bitrix24ApiException 88 | * @throws \Bitrix24\Exceptions\Bitrix24EmptyResponseException 89 | * @throws \Bitrix24\Exceptions\Bitrix24IoException 90 | * @throws \Bitrix24\Exceptions\Bitrix24MethodNotFoundException 91 | * @throws \Bitrix24\Exceptions\Bitrix24PaymentRequiredException 92 | * @throws \Bitrix24\Exceptions\Bitrix24PortalDeletedException 93 | * @throws \Bitrix24\Exceptions\Bitrix24PortalRenamedException 94 | * @throws \Bitrix24\Exceptions\Bitrix24TokenIsExpiredException 95 | * @throws \Bitrix24\Exceptions\Bitrix24TokenIsInvalidException 96 | * @throws \Bitrix24\Exceptions\Bitrix24WrongClientException 97 | * @throws \Exception 98 | */ 99 | protected function refreshAccessToken(): void 100 | { 101 | $this->setRedirectUri('https://' . $_ENV['APP_DOMAIN'] . '/install'); 102 | $result = $this->getNewAccessToken(); 103 | 104 | if ($result['member_id'] === $this->portal->getMemberId()) { 105 | $userData = [ 106 | 'access_token' => $result['access_token'], 107 | 'refresh_token' => $result['refresh_token'], 108 | ]; 109 | $this->persistPortalData($userData); 110 | 111 | //saving for current run 112 | $this->setAccessToken($this->portal->getAccessToken()); 113 | $this->setRefreshToken($this->portal->getRefreshToken()); 114 | } else { 115 | throw new Bitrix24Exception('Wrong member_id given'); 116 | } 117 | } 118 | 119 | /** 120 | * @param array $data 121 | * @throws \Exception 122 | */ 123 | public function addNewPortal(array $data): void 124 | { 125 | if (isset( 126 | $data['domain'], 127 | $data['access_token'], 128 | $data['refresh_token'], 129 | $data['member_id']) 130 | ) { 131 | $repository = $this->entityManager->getRepository(Portal::class); 132 | $this->portal = $repository->findOneByMemberId($data['member_id']); 133 | $this->persistPortalData($data); 134 | } else { 135 | throw new \RuntimeException('Invalid user data given'); 136 | } 137 | } 138 | 139 | /** 140 | * @param array $data 141 | * @throws \Exception 142 | */ 143 | protected function persistPortalData(array $data): void 144 | { 145 | if (isset($data['domain'])) { 146 | $this->portal->setDomain($data['domain']); 147 | } 148 | 149 | if (isset($data['access_token'])) { 150 | $this->portal->setAccessToken($data['access_token']); 151 | } 152 | 153 | if (isset($data['refresh_token'])) { 154 | $this->portal->setRefreshToken($data['refresh_token']); 155 | } 156 | 157 | if (isset($data['member_id'])) { 158 | $this->portal->setMemberId($data['member_id']); 159 | } 160 | 161 | //setting changed date 162 | $changed = new \DateTime('now'); 163 | $this->portal->setChanged($changed); 164 | 165 | //setting expires date 166 | $expires = clone $changed; 167 | $duration = 3600; 168 | if (isset($data['expires_in']) && (int)$data['expires_in'] < 1) { 169 | $duration = (int)$data['expires_in']; 170 | } 171 | $expires->add(new \DateInterval('PT' . $duration . 'S')); 172 | $this->portal->setExpires($expires); 173 | 174 | $this->entityManager->persist($this->portal); 175 | $this->entityManager->flush(); 176 | } 177 | 178 | /** 179 | * @param EntityManagerInterface $entityManager 180 | */ 181 | public function setEntityManager(EntityManagerInterface $entityManager): void 182 | { 183 | $this->entityManager = $entityManager; 184 | } 185 | } -------------------------------------------------------------------------------- /src/Controller/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alorian/bitrix24-symfony-boilerplate/d1aa4f835538bbfdc8db04fc419c8d7334a07c0f/src/Controller/.gitignore -------------------------------------------------------------------------------- /src/Controller/ActivityController.php: -------------------------------------------------------------------------------- 1 | get('properties')['document_id']; 15 | 16 | $requestData = [ 17 | 'event_token' => $request->get('event_token'), 18 | 'return_values' => [ 19 | 'message' => $message 20 | ] 21 | ]; 22 | 23 | $b24App->call('bizproc.event.send', $requestData); 24 | 25 | return new Response('Ok'); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Controller/DefaultController.php: -------------------------------------------------------------------------------- 1 | current(); 19 | 20 | $activityData = [ 21 | 'CODE' => 'helloWorld', // символьный код нашего действия 22 | 'HANDLER' => 'https://' . $_ENV['APP_DOMAIN'] . '/activities/test',// скрипт-обработчик действия 23 | 'AUTH_USER_ID' => $userResponse['result']['ID'], // ID пользователя, токен которого будет передан приложению. 24 | 'USE_SUBSCRIPTION' => '', 25 | 'NAME' => [ 26 | 'ru' => 'Hello World!' // название действия в редакторе БП 27 | ], 28 | 'DESCRIPTION' => [ 29 | 'ru' => 'Тестовое действие' // описание действия в редакторе БП 30 | ], 31 | 'PROPERTIES' => [// массив входных параметров 32 | 'document_id' => [ 33 | 'Name' => [ 34 | 'ru' => 'ID документа' 35 | ], 36 | 'Description' => [ 37 | 'ru' => 'ID текущего документа' 38 | ], 39 | 'Type' => 'int', 40 | 'Required' => 'Y', 41 | 'Multiple' => 'N', 42 | 'Default' => '', 43 | ] 44 | ], 45 | 'RETURN_PROPERTIES' => [// массив выходных параметров 46 | 'message' => [ 47 | 'Name' => [ 48 | 'ru' => 'Сообщение из приложения' 49 | ], 50 | 'Type' => 'text', 51 | 'Multiple' => 'N', 52 | 'Default' => null 53 | ], 54 | ] 55 | ]; 56 | $result = $b24App->call('bizproc.activity.add', $activityData); 57 | } catch (\Exception $exception) { 58 | $result['error'] = $exception->getMessage(); 59 | } 60 | 61 | return new Response('
' . print_r($result, true) . '
'); 62 | } 63 | 64 | public function settings(): Response 65 | { 66 | return $this->render('settings.html.twig'); 67 | } 68 | 69 | public function install(Request $request, B24App $B24App): Response 70 | { 71 | $errorsList = []; 72 | try { 73 | 74 | $data = [ 75 | 'domain' => $request->get('DOMAIN'), 76 | 'access_token' => $request->get('AUTH_ID'), 77 | 'refresh_token' => $request->get('REFRESH_ID'), 78 | 'member_id' => $request->get('member_id') 79 | ]; 80 | 81 | $B24App->addNewPortal($data); 82 | 83 | } catch (\Exception $exception) { 84 | $errorsList[] = $exception->getMessage(); 85 | } 86 | 87 | return $this->render('install.html.twig', ['errorsList' => $errorsList]); 88 | } 89 | } -------------------------------------------------------------------------------- /src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alorian/bitrix24-symfony-boilerplate/d1aa4f835538bbfdc8db04fc419c8d7334a07c0f/src/Entity/.gitignore -------------------------------------------------------------------------------- /src/Entity/Portal.php: -------------------------------------------------------------------------------- 1 | id; 52 | } 53 | 54 | public function getDomain(): ?string 55 | { 56 | return $this->domain; 57 | } 58 | 59 | public function setDomain(string $domain): self 60 | { 61 | $this->domain = $domain; 62 | 63 | return $this; 64 | } 65 | 66 | public function getAccessToken(): ?string 67 | { 68 | return $this->access_token; 69 | } 70 | 71 | public function setAccessToken(string $access_token): self 72 | { 73 | $this->access_token = $access_token; 74 | 75 | return $this; 76 | } 77 | 78 | public function getRefreshToken(): ?string 79 | { 80 | return $this->refresh_token; 81 | } 82 | 83 | public function setRefreshToken(string $refresh_token): self 84 | { 85 | $this->refresh_token = $refresh_token; 86 | 87 | return $this; 88 | } 89 | 90 | public function getMemberId(): ?string 91 | { 92 | return $this->member_id; 93 | } 94 | 95 | public function setMemberId(string $member_id): self 96 | { 97 | $this->member_id = $member_id; 98 | 99 | return $this; 100 | } 101 | 102 | public function getExpires(): ?\DateTimeInterface 103 | { 104 | return $this->expires; 105 | } 106 | 107 | public function setExpires(?\DateTimeInterface $expires): self 108 | { 109 | $this->expires = $expires; 110 | 111 | return $this; 112 | } 113 | 114 | public function getChanged(): ?\DateTimeInterface 115 | { 116 | return $this->changed; 117 | } 118 | 119 | public function setChanged(\DateTimeInterface $changed): self 120 | { 121 | $this->changed = $changed; 122 | 123 | return $this; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Kernel.php: -------------------------------------------------------------------------------- 1 | getProjectDir().'/config/bundles.php'; 21 | foreach ($contents as $class => $envs) { 22 | if ($envs[$this->environment] ?? $envs['all'] ?? false) { 23 | yield new $class(); 24 | } 25 | } 26 | } 27 | 28 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) 29 | { 30 | $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); 31 | $container->setParameter('container.dumper.inline_class_loader', true); 32 | $confDir = $this->getProjectDir().'/config'; 33 | 34 | $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); 35 | $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); 36 | $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); 37 | $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); 38 | } 39 | 40 | protected function configureRoutes(RouteCollectionBuilder $routes) 41 | { 42 | $confDir = $this->getProjectDir().'/config'; 43 | 44 | $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob'); 45 | $routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob'); 46 | $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Migrations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alorian/bitrix24-symfony-boilerplate/d1aa4f835538bbfdc8db04fc419c8d7334a07c0f/src/Migrations/.gitignore -------------------------------------------------------------------------------- /src/Repository/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alorian/bitrix24-symfony-boilerplate/d1aa4f835538bbfdc8db04fc419c8d7334a07c0f/src/Repository/.gitignore -------------------------------------------------------------------------------- /src/Repository/PortalRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('p') 25 | ->andWhere('p.member_id = :val') 26 | ->setParameter('val', $member_id) 27 | ->getQuery() 28 | ->getOneOrNullResult(); 29 | 30 | if($portal === null) { 31 | $portal = new Portal(); 32 | } 33 | 34 | return $portal; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | {% block body %}{% endblock %} 10 | {% block javascripts %}{% endblock %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /templates/install.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block stylesheets %} 4 | {{ parent() }} 5 | {{ encore_entry_link_tags('install') }} 6 | {% endblock %} 7 | {% block javascripts %} 8 | {{ parent() }} 9 | 10 | {{ encore_entry_script_tags('install') }} 11 | {% endblock %} 12 | 13 | {% block title %}Bitrix Settings{% endblock %} 14 | {% block body %} 15 |

Установка приложения

16 | 17 | {% if errorsList %} 18 |
19 | Ошибки при установке приложения: 20 | 25 |
26 | 27 | {% else %} 28 |
29 | Приложение успешно установлено 30 |
31 | {% endif %} 32 | 33 | {% endblock %} -------------------------------------------------------------------------------- /templates/settings.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'base.html.twig' %} 2 | 3 | {% block stylesheets %} 4 | {{ parent() }} 5 | {{ encore_entry_link_tags('settings') }} 6 | {% endblock %} 7 | {% block javascripts %} 8 | {{ parent() }} 9 | 10 | {{ encore_entry_script_tags('settings') }} 11 | {% endblock %} 12 | 13 | {% block title %}Bitrix Settings{% endblock %} 14 | {% block body %} 15 |

Настройки приложения

16 | 17 | Hello world! 18 | {% endblock %} -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var Encore = require('@symfony/webpack-encore'); 2 | 3 | Encore 4 | // directory where compiled assets will be stored 5 | .setOutputPath('public/build/') 6 | // public path used by the web server to access the output path 7 | .setPublicPath('/build') 8 | // only needed for CDN's or sub-directory deploy 9 | //.setManifestKeyPrefix('build/') 10 | 11 | /* 12 | * ENTRY CONFIG 13 | * 14 | * Add 1 entry for each "page" of your app 15 | * (including one that's included on every page - e.g. "app") 16 | * 17 | * Each entry will result in one JavaScript file (e.g. app.js) 18 | * and one CSS file (e.g. app.css) if you JavaScript imports CSS. 19 | */ 20 | .addEntry('app', './assets/js/app.js') 21 | .addEntry('settings', './assets/js/settings.js') 22 | .addEntry('install', './assets/js/install.js') 23 | //.addEntry('page1', './assets/js/page1.js') 24 | //.addEntry('page2', './assets/js/page2.js') 25 | 26 | // will require an extra script tag for runtime.js 27 | // but, you probably want this, unless you're building a single-page app 28 | .enableSingleRuntimeChunk() 29 | 30 | /* 31 | * FEATURE CONFIG 32 | * 33 | * Enable & configure other features below. For a full 34 | * list of features, see: 35 | * https://symfony.com/doc/current/frontend.html#adding-more-features 36 | */ 37 | .cleanupOutputBeforeBuild() 38 | .enableBuildNotifications() 39 | .enableSourceMaps(!Encore.isProduction()) 40 | // enables hashed filenames (e.g. app.abc123.css) 41 | .enableVersioning(Encore.isProduction()) 42 | 43 | // enables Sass/SCSS support 44 | //.enableSassLoader() 45 | 46 | // uncomment if you use TypeScript 47 | //.enableTypeScriptLoader() 48 | 49 | // uncomment if you're having problems with a jQuery plugin 50 | //.autoProvidejQuery() 51 | 52 | // uncomment if you use API Platform Admin (composer req api-admin) 53 | //.enableReactPreset() 54 | //.addEntry('admin', './assets/js/admin.js') 55 | ; 56 | 57 | module.exports = Encore.getWebpackConfig(); 58 | --------------------------------------------------------------------------------