Initial commit
This commit is contained in:
47
.gitignore
vendored
Normal file
47
.gitignore
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
.vscode
|
||||||
|
# yii console commands
|
||||||
|
/yii
|
||||||
|
/yii_test
|
||||||
|
/yii_test.bat
|
||||||
|
mysql-data
|
||||||
|
|
||||||
|
# phpstorm project files
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# netbeans project files
|
||||||
|
nbproject
|
||||||
|
|
||||||
|
# zend studio for eclipse project files
|
||||||
|
.buildpath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
|
||||||
|
# windows thumbnail cache
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# composer vendor dir
|
||||||
|
/vendor
|
||||||
|
|
||||||
|
# composer itself is not needed
|
||||||
|
composer.phar
|
||||||
|
|
||||||
|
|
||||||
|
# Mac DS_Store Files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# phpunit itself is not needed
|
||||||
|
phpunit.phar
|
||||||
|
# local phpunit config
|
||||||
|
/phpunit.xml
|
||||||
|
|
||||||
|
# vagrant runtime
|
||||||
|
/.vagrant
|
||||||
|
|
||||||
|
# ignore generated files
|
||||||
|
/frontend/web/index.php
|
||||||
|
/frontend/web/index-test.php
|
||||||
|
/frontend/web/robots.txt
|
||||||
|
/backend/web/index.php
|
||||||
|
/backend/web/index-test.php
|
||||||
|
/backend/web/robots.txt
|
||||||
|
/frontend/web/uploads/
|
||||||
17
CHANGELOG.md
Normal file
17
CHANGELOG.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
* Initial release
|
||||||
|
|
||||||
|
## [0.1.0] - 2025-02-10
|
||||||
|
|
||||||
|
* First release
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/cgsmith/calorie/compare/0.0.1...HEAD
|
||||||
|
[0.1.0]: https://github.com/cgsmith/calorie/releases/tag/0.1.0
|
||||||
110
README.md
Normal file
110
README.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# Food Tracker Application - Yii2 MVP
|
||||||
|
|
||||||
|
This document outlines the development plan for a minimal viable product (MVP) of a food tracking application built
|
||||||
|
using the Yii2 framework.
|
||||||
|
|
||||||
|
Read about it in the [MVP](mvp.md)
|
||||||
|
|
||||||
|
## Application Overview
|
||||||
|
|
||||||
|
The application allows users to log meals, including uploading images for analysis, and view daily nutritional
|
||||||
|
summaries. This MVP focuses on core functionality, prioritizing ease of development and rapid iteration. Social login is
|
||||||
|
not included in this version.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> If you have any problems with these steps please [create an issue](../../issues/new)
|
||||||
|
|
||||||
|
You will need a few items locally before setting up. Everything runs in `docker` except for the first few setup items.
|
||||||
|
On your host download or make sure you have:
|
||||||
|
|
||||||
|
* [PHP 8.3+](https://www.php.net)
|
||||||
|
* [composer](https://getcomposer.org/)
|
||||||
|
* [docker](https://docs.docker.com/desktop/)
|
||||||
|
|
||||||
|
After having the necessary software then you can perform the following steps to setup your test instance:
|
||||||
|
|
||||||
|
1. `git clone git@github.com:cgsmith/calorie.git`
|
||||||
|
2. `cd calorie`
|
||||||
|
3. `composer install`
|
||||||
|
4. `docker compose up -d`
|
||||||
|
5. `php init --env=Development`
|
||||||
|
|
||||||
|
You're application should be running at http://localhost:20080!
|
||||||
|
|
||||||
|
### Database initialization
|
||||||
|
|
||||||
|
1. `docker exec -it calorie-frontend-1 bash`
|
||||||
|
2. `yii migrate`
|
||||||
|
3. `yii fixture/load "*"` (creates chris@fivedevs.com with password of `password`)
|
||||||
|
|
||||||
|
🎉 You should be able to login!
|
||||||
|
|
||||||
|
### Setting up Xdebug Locally
|
||||||
|
|
||||||
|
Xdebug is installed and configured on the docker container.
|
||||||
|
In [PhpStorm](https://www.jetbrains.com/help/phpstorm/configuring-xdebug.html#integrationWithProduct) you will need
|
||||||
|
to still configure it.
|
||||||
|
|
||||||
|
#### PhpStorm Setup
|
||||||
|
|
||||||
|
1. `Ctrl + Alt + S` to open settings
|
||||||
|
2. Goto `PHP > Servers`
|
||||||
|
3. Add a new server called 'Calorie'
|
||||||
|
1. Host: `localhost`
|
||||||
|
2. Port: `20080`
|
||||||
|
3. Check `Use path mappings`
|
||||||
|
4. Map the repo to the app folder: `~/calorie -> /app`
|
||||||
|
4. Under `PHP > Debug` in the settings screen add the following ports to listen on: `9005`
|
||||||
|
|
||||||
|
You can add the port by adding a comma to separate.
|
||||||
|
|
||||||
|
#### VSCode setup
|
||||||
|
|
||||||
|
1. Open extensions `Ctrl + Shift + X`
|
||||||
|
2. Download PHP Debug extension (publisher is xdebug.org)
|
||||||
|
3. Goto `Run > Add Configuration` menu
|
||||||
|
4. Select PHP
|
||||||
|
5. Change the port setting to `9005`
|
||||||
|
|
||||||
|
Your VSCode IDE is now ready to start receiving Xdebug signals! For more documentation on setup please
|
||||||
|
see [Xdebug extension documentation](https://github.com/xdebug/vscode-php-debug)
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Tests should run within the docker container
|
||||||
|
> Run with `docker exec -e XDEBUG_MODE=off calorie-frontend-1 ./vendor/bin/codecept run` from your
|
||||||
|
> host.
|
||||||
|
|
||||||
|
For running tests the project uses [Codeception](https://codeception.com). To run these tests just run `composer test`.
|
||||||
|
You can also run this by running `./vendor/bin/codecept run` which will take the entire `codeception.yml` and run the
|
||||||
|
tests.
|
||||||
|
|
||||||
|
These will also run automatically on deployment.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Follow Semantic Versioning and update the CHANGELOG when making a release! Sentry manages releases with the SHA
|
||||||
|
> from git - while we manage the release with version numbers in a sane way.
|
||||||
|
|
||||||
|
[Deployer](https://deployer.org) is used for the atomic deployments. An atomic deployment simply changes the symlink for
|
||||||
|
the webserver and then restarts the webserver after running any database migrations. This process, like all processes,
|
||||||
|
can always be improved upon. An atomic deployment allows a server administrator to symlink to a prior version of working
|
||||||
|
code as long as they navigate to the correct git SHA and change the symlink.
|
||||||
|
|
||||||
|
Deployer can be run from the command line with a command like below:
|
||||||
|
|
||||||
|
** Deploy to testing **
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./vendor/bin/dep deploy test.calorie
|
||||||
|
```
|
||||||
|
|
||||||
|
** Deploy to production **
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./vendor/bin/dep deploy calorie --tag=1.0.0 # change your tag here
|
||||||
|
```
|
||||||
0
api/Dockerfile
Normal file
0
api/Dockerfile
Normal file
1
api/assets/AppAsset.php
Normal file
1
api/assets/AppAsset.php
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?php
|
||||||
15
api/codeception.yml
Normal file
15
api/codeception.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace: backend\tests
|
||||||
|
actor_suffix: Tester
|
||||||
|
paths:
|
||||||
|
tests: tests
|
||||||
|
output: tests/_output
|
||||||
|
data: tests/_data
|
||||||
|
support: tests/_support
|
||||||
|
bootstrap: _bootstrap.php
|
||||||
|
settings:
|
||||||
|
colors: true
|
||||||
|
memory_limit: 1024M
|
||||||
|
modules:
|
||||||
|
config:
|
||||||
|
Yii2:
|
||||||
|
configFile: 'config/codeception-local.php'
|
||||||
0
api/config/.gitignore
vendored
Normal file
0
api/config/.gitignore
vendored
Normal file
0
api/config/bootstrap.php
Normal file
0
api/config/bootstrap.php
Normal file
0
api/config/codeception-local.php
Normal file
0
api/config/codeception-local.php
Normal file
25
api/config/main-local.php
Normal file
25
api/config/main-local.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$config = [
|
||||||
|
'components' => [
|
||||||
|
'request' => [
|
||||||
|
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
|
||||||
|
'cookieValidationKey' => 'fKCQhZtDFPI3Xt13xTqMdvn16F_Yd1Sl',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!YII_ENV_TEST) {
|
||||||
|
// configuration adjustments for 'dev' environment
|
||||||
|
$config['bootstrap'][] = 'debug';
|
||||||
|
$config['modules']['debug'] = [
|
||||||
|
'class' => \yii\debug\Module::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
$config['bootstrap'][] = 'gii';
|
||||||
|
$config['modules']['gii'] = [
|
||||||
|
'class' => \yii\gii\Module::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
||||||
50
api/config/main.php
Normal file
50
api/config/main.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
$params = array_merge(
|
||||||
|
require __DIR__ . '/../../common/config/params.php',
|
||||||
|
require __DIR__ . '/../../common/config/params-local.php',
|
||||||
|
require __DIR__ . '/params.php',
|
||||||
|
require __DIR__ . '/params-local.php'
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => 'app-backend',
|
||||||
|
'basePath' => dirname(__DIR__),
|
||||||
|
'controllerNamespace' => 'backend\controllers',
|
||||||
|
'bootstrap' => ['log'],
|
||||||
|
'modules' => [],
|
||||||
|
'components' => [
|
||||||
|
'request' => [
|
||||||
|
'csrfParam' => '_csrf-backend',
|
||||||
|
],
|
||||||
|
'user' => [
|
||||||
|
'identityClass' => 'common\models\User',
|
||||||
|
'enableAutoLogin' => true,
|
||||||
|
'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
|
||||||
|
],
|
||||||
|
'session' => [
|
||||||
|
// this is the name of the session cookie used for login on the backend
|
||||||
|
'name' => 'advanced-backend',
|
||||||
|
],
|
||||||
|
'log' => [
|
||||||
|
'traceLevel' => YII_DEBUG ? 3 : 0,
|
||||||
|
'targets' => [
|
||||||
|
[
|
||||||
|
'class' => \yii\log\FileTarget::class,
|
||||||
|
'levels' => ['error', 'warning'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'errorHandler' => [
|
||||||
|
'errorAction' => 'site/error',
|
||||||
|
],
|
||||||
|
/*
|
||||||
|
'urlManager' => [
|
||||||
|
'enablePrettyUrl' => true,
|
||||||
|
'showScriptName' => false,
|
||||||
|
'rules' => [
|
||||||
|
],
|
||||||
|
],
|
||||||
|
*/
|
||||||
|
],
|
||||||
|
'params' => $params,
|
||||||
|
];
|
||||||
0
api/config/params-local.php
Normal file
0
api/config/params-local.php
Normal file
0
api/config/params.php
Normal file
0
api/config/params.php
Normal file
0
api/config/test-local.php
Normal file
0
api/config/test-local.php
Normal file
0
api/config/test.php
Normal file
0
api/config/test.php
Normal file
106
api/controllers/SiteController.php
Normal file
106
api/controllers/SiteController.php
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace backend\controllers;
|
||||||
|
|
||||||
|
use common\models\LoginForm;
|
||||||
|
use Yii;
|
||||||
|
use yii\filters\VerbFilter;
|
||||||
|
use yii\filters\AccessControl;
|
||||||
|
use yii\web\Controller;
|
||||||
|
use yii\web\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Site controller
|
||||||
|
*/
|
||||||
|
class SiteController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function behaviors()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'access' => [
|
||||||
|
'class' => AccessControl::class,
|
||||||
|
'rules' => [
|
||||||
|
[
|
||||||
|
'actions' => ['login', 'error'],
|
||||||
|
'allow' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'actions' => ['logout', 'index'],
|
||||||
|
'allow' => true,
|
||||||
|
'roles' => ['@'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'verbs' => [
|
||||||
|
'class' => VerbFilter::class,
|
||||||
|
'actions' => [
|
||||||
|
'logout' => ['post'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'error' => [
|
||||||
|
'class' => \yii\web\ErrorAction::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays homepage.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function actionIndex()
|
||||||
|
{
|
||||||
|
|
||||||
|
return $this->render('index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login action.
|
||||||
|
*
|
||||||
|
* @return string|Response
|
||||||
|
*/
|
||||||
|
public function actionLogin()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!Yii::$app->user->isGuest) {
|
||||||
|
return $this->goHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->layout = 'blank';
|
||||||
|
|
||||||
|
$model = new LoginForm();
|
||||||
|
if ($model->load(Yii::$app->request->post()) && $model->login()) {
|
||||||
|
return $this->goBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
$model->password = '';
|
||||||
|
|
||||||
|
return $this->render('login', [
|
||||||
|
'model' => $model,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout action.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function actionLogout()
|
||||||
|
{
|
||||||
|
Yii::$app->user->logout();
|
||||||
|
|
||||||
|
return $this->goHome();
|
||||||
|
}
|
||||||
|
}
|
||||||
0
api/models/.gitkeep
Normal file
0
api/models/.gitkeep
Normal file
0
api/runtime/.gitignore
vendored
Normal file
0
api/runtime/.gitignore
vendored
Normal file
0
api/tests/_bootstrap.php
Normal file
0
api/tests/_bootstrap.php
Normal file
0
api/tests/_data/.gitignore
vendored
Normal file
0
api/tests/_data/.gitignore
vendored
Normal file
13
api/tests/_data/login_data.php
Normal file
13
api/tests/_data/login_data.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'username' => 'erau',
|
||||||
|
'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',
|
||||||
|
// password_0
|
||||||
|
'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne',
|
||||||
|
'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490',
|
||||||
|
'created_at' => '1392559490',
|
||||||
|
'updated_at' => '1392559490',
|
||||||
|
'email' => 'sfriesen@jenkins.info',
|
||||||
|
],
|
||||||
|
];
|
||||||
0
api/tests/_output/.gitignore
vendored
Normal file
0
api/tests/_output/.gitignore
vendored
Normal file
0
api/tests/_support/.gitignore
vendored
Normal file
0
api/tests/_support/.gitignore
vendored
Normal file
0
api/tests/_support/FunctionalTester.php
Normal file
0
api/tests/_support/FunctionalTester.php
Normal file
0
api/tests/_support/UnitTester.php
Normal file
0
api/tests/_support/UnitTester.php
Normal file
5
api/tests/functional.suite.yml
Normal file
5
api/tests/functional.suite.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
suite_namespace: backend\tests\functional
|
||||||
|
actor: FunctionalTester
|
||||||
|
modules:
|
||||||
|
enabled:
|
||||||
|
- Yii2
|
||||||
44
api/tests/functional/LoginCest.php
Normal file
44
api/tests/functional/LoginCest.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace backend\tests\functional;
|
||||||
|
|
||||||
|
use backend\tests\FunctionalTester;
|
||||||
|
use common\fixtures\UserFixture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LoginCest
|
||||||
|
*/
|
||||||
|
class LoginCest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Load fixtures before db transaction begin
|
||||||
|
* Called in _before()
|
||||||
|
* @see \Codeception\Module\Yii2::_before()
|
||||||
|
* @see \Codeception\Module\Yii2::loadFixtures()
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function _fixtures()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'user' => [
|
||||||
|
'class' => UserFixture::class,
|
||||||
|
'dataFile' => codecept_data_dir() . 'login_data.php'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param FunctionalTester $I
|
||||||
|
*/
|
||||||
|
public function loginUser(FunctionalTester $I)
|
||||||
|
{
|
||||||
|
$I->amOnRoute('/site/login');
|
||||||
|
$I->fillField('Username', 'erau');
|
||||||
|
$I->fillField('Password', 'password_0');
|
||||||
|
$I->click('login-button');
|
||||||
|
|
||||||
|
$I->see('Logout (erau)', 'form button[type=submit]');
|
||||||
|
$I->dontSeeLink('Login');
|
||||||
|
$I->dontSeeLink('Signup');
|
||||||
|
}
|
||||||
|
}
|
||||||
0
api/tests/functional/_bootstrap.php
Normal file
0
api/tests/functional/_bootstrap.php
Normal file
2
api/tests/unit.suite.yml
Normal file
2
api/tests/unit.suite.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
suite_namespace: backend\tests\unit
|
||||||
|
actor: UnitTester
|
||||||
0
api/tests/unit/_bootstrap.php
Normal file
0
api/tests/unit/_bootstrap.php
Normal file
0
api/views/layouts/blank.php
Normal file
0
api/views/layouts/blank.php
Normal file
0
api/views/site/error.php
Normal file
0
api/views/site/error.php
Normal file
53
api/views/site/index.php
Normal file
53
api/views/site/index.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/** @var yii\web\View $this */
|
||||||
|
|
||||||
|
$this->title = 'My Yii Application';
|
||||||
|
?>
|
||||||
|
<div class="site-index">
|
||||||
|
|
||||||
|
<div class="jumbotron text-center bg-transparent">
|
||||||
|
<h1 class="display-4">Welcome!</h1>
|
||||||
|
|
||||||
|
<p class="lead">We are excited for you to get started.</p>
|
||||||
|
|
||||||
|
<p><a class="btn btn-lg btn-success" href="http://www.yiiframework.com">Get started with Yii</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body-content">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<h2>Heading</h2>
|
||||||
|
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
||||||
|
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||||
|
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||||
|
fugiat nulla pariatur.</p>
|
||||||
|
|
||||||
|
<p><a class="btn btn-outline-secondary" href="http://www.yiiframework.com/doc/">Yii Documentation »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<h2>Heading</h2>
|
||||||
|
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
||||||
|
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||||
|
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||||
|
fugiat nulla pariatur.</p>
|
||||||
|
|
||||||
|
<p><a class="btn btn-outline-secondary" href="http://www.yiiframework.com/forum/">Yii Forum »</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<h2>Heading</h2>
|
||||||
|
|
||||||
|
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
|
||||||
|
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||||
|
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||||
|
fugiat nulla pariatur.</p>
|
||||||
|
|
||||||
|
<p><a class="btn btn-outline-secondary" href="http://www.yiiframework.com/extensions/">Yii Extensions »</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
0
api/web/assets/.gitignore
vendored
Normal file
0
api/web/assets/.gitignore
vendored
Normal file
0
api/web/css/site.css
Normal file
0
api/web/css/site.css
Normal file
0
api/web/favicon.ico
Normal file
0
api/web/favicon.ico
Normal file
0
api/web/index-test.php
Normal file
0
api/web/index-test.php
Normal file
0
api/web/index.php
Normal file
0
api/web/index.php
Normal file
0
api/web/robots.txt
Normal file
0
api/web/robots.txt
Normal file
8
codeception.yml
Normal file
8
codeception.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# global codeception file to run tests from all apps
|
||||||
|
include:
|
||||||
|
- common
|
||||||
|
- frontend
|
||||||
|
paths:
|
||||||
|
output: console/runtime/output
|
||||||
|
settings:
|
||||||
|
colors: true
|
||||||
15
common/codeception.yml
Normal file
15
common/codeception.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace: common\tests
|
||||||
|
actor_suffix: Tester
|
||||||
|
paths:
|
||||||
|
tests: tests
|
||||||
|
output: tests/_output
|
||||||
|
data: tests/_data
|
||||||
|
support: tests/_support
|
||||||
|
bootstrap: _bootstrap.php
|
||||||
|
settings:
|
||||||
|
colors: true
|
||||||
|
memory_limit: 1024M
|
||||||
|
modules:
|
||||||
|
config:
|
||||||
|
Yii2:
|
||||||
|
configFile: 'config/codeception-local.php'
|
||||||
19
common/components/PostmarkComponent.php
Normal file
19
common/components/PostmarkComponent.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\components;
|
||||||
|
|
||||||
|
use Postmark\PostmarkClient;
|
||||||
|
use yii\base\Component;
|
||||||
|
|
||||||
|
class PostmarkComponent extends Component
|
||||||
|
{
|
||||||
|
/** @var PostmarkClient */
|
||||||
|
public PostmarkClient $client;
|
||||||
|
public mixed $serverToken;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
$this->client = new PostmarkClient($this->serverToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
469
common/components/SonarApiComponent.php
Normal file
469
common/components/SonarApiComponent.php
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\components;
|
||||||
|
|
||||||
|
use common\jobs\EmailJob;
|
||||||
|
use common\models\Account;
|
||||||
|
use common\models\InvoiceItem;
|
||||||
|
use common\models\SalesAgent;
|
||||||
|
use common\models\Service;
|
||||||
|
use Yii;
|
||||||
|
use yii\helpers\Url;
|
||||||
|
use yii\httpclient\Client;
|
||||||
|
use yii\httpclient\Request;
|
||||||
|
use yii\httpclient\RequestEvent;
|
||||||
|
|
||||||
|
class SonarApiComponent extends \yii\base\Component
|
||||||
|
{
|
||||||
|
public Client $client;
|
||||||
|
public string $baseUrl;
|
||||||
|
public string $bearerToken;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
$this->client = new Client([
|
||||||
|
'baseUrl' => $this->baseUrl,
|
||||||
|
'requestConfig' => [
|
||||||
|
'format' => Client::FORMAT_JSON
|
||||||
|
],
|
||||||
|
'responseConfig' => [
|
||||||
|
'format' => Client::FORMAT_JSON
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Setup event for auth before each send
|
||||||
|
$this->client->on(Request::EVENT_BEFORE_SEND, function (RequestEvent $event) {
|
||||||
|
$event->request->addHeaders(['Authorization' => 'Bearer ' . $this->bearerToken]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccounts(int $page = 1, int $limit = 100)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'form_params' => [
|
||||||
|
'query' => 'query accountsWithSalesAgents(
|
||||||
|
$paginator: Paginator,
|
||||||
|
$search: [Search],
|
||||||
|
$sorter: [Sorter]
|
||||||
|
) {
|
||||||
|
accounts(
|
||||||
|
paginator: $paginator
|
||||||
|
search: $search
|
||||||
|
sorter: $sorter
|
||||||
|
reverse_relation_filters: {
|
||||||
|
relation: "custom_field_data",
|
||||||
|
search: {
|
||||||
|
integer_fields: {
|
||||||
|
attribute: "custom_field_id",
|
||||||
|
search_value: 12,
|
||||||
|
operator: EQ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
general_search_mode: ROOT_PLUS_RELATIONS
|
||||||
|
account_status_id: 1
|
||||||
|
) {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
account_status {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
account_services {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
name_override
|
||||||
|
price_override
|
||||||
|
price_override_reason
|
||||||
|
service {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
amount
|
||||||
|
enabled
|
||||||
|
application
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_info {
|
||||||
|
page
|
||||||
|
records_per_page
|
||||||
|
total_count
|
||||||
|
total_pages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
custom_field_data(custom_field_id:12) {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
custom_field_id
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_info {
|
||||||
|
page
|
||||||
|
records_per_page
|
||||||
|
total_count
|
||||||
|
total_pages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
'variables' => [
|
||||||
|
'paginator' => [
|
||||||
|
'page' => $page,
|
||||||
|
'records_per_page' => $limit
|
||||||
|
],
|
||||||
|
'search' => [],
|
||||||
|
'sorter' => [
|
||||||
|
[
|
||||||
|
'attribute' => 'updated_at',
|
||||||
|
'direction' => 'ASC',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->client->createRequest()
|
||||||
|
->setMethod('POST')
|
||||||
|
->setData($data)
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$account = json_decode($response->getContent(), true);
|
||||||
|
|
||||||
|
return $account['form_params']['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInvoices(string $startDate, string $endDate)
|
||||||
|
{
|
||||||
|
$page = 1;
|
||||||
|
$limit = 100;
|
||||||
|
$invoices = [];
|
||||||
|
do {
|
||||||
|
$data = [
|
||||||
|
'form_params' => [
|
||||||
|
'query' => 'query accountInvoice($paginator: Paginator, $search: [Search], $sorter: [Sorter]) {
|
||||||
|
invoices(
|
||||||
|
paginator: $paginator
|
||||||
|
search: $search
|
||||||
|
sorter: $sorter
|
||||||
|
general_search_mode: ROOT_PLUS_RELATIONS
|
||||||
|
) {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
account_id
|
||||||
|
total_debits
|
||||||
|
void
|
||||||
|
remaining_due
|
||||||
|
date
|
||||||
|
due_date
|
||||||
|
end_date
|
||||||
|
delinquent
|
||||||
|
debits {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
service_id
|
||||||
|
service_name
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
credits {
|
||||||
|
entities {
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_info {
|
||||||
|
page
|
||||||
|
records_per_page
|
||||||
|
total_count
|
||||||
|
total_pages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
'variables' => [
|
||||||
|
'paginator' => [
|
||||||
|
'page' => $page,
|
||||||
|
'records_per_page' => $limit
|
||||||
|
],
|
||||||
|
'search' => [
|
||||||
|
[
|
||||||
|
'date_fields' => [
|
||||||
|
['attribute' => 'date', 'search_value' => $startDate, 'operator' => 'GTE'],
|
||||||
|
['attribute' => 'date', 'search_value' => $endDate, 'operator' => 'LTE'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
],
|
||||||
|
'sorter' => [
|
||||||
|
[
|
||||||
|
'attribute' => 'updated_at',
|
||||||
|
'direction' => 'ASC',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->client->createRequest()
|
||||||
|
->setMethod('POST')
|
||||||
|
->setData($data)
|
||||||
|
->send();
|
||||||
|
|
||||||
|
|
||||||
|
$responseData = json_decode($response->getContent(), true);
|
||||||
|
$invoices = array_merge($invoices, $responseData['form_params']['data']['invoices']['entities']);
|
||||||
|
$page++;
|
||||||
|
} while ($page < ($responseData['form_params']['data']['invoices']['page_info']['total_pages'] + 1));
|
||||||
|
|
||||||
|
return $invoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInvoice(int $invoiceId = 1)
|
||||||
|
{
|
||||||
|
$page = 1;
|
||||||
|
$limit = 100;
|
||||||
|
$data = [
|
||||||
|
'form_params' => [
|
||||||
|
'query' => 'query accountInvoice($paginator: Paginator, $search: [Search], $sorter: [Sorter]) {
|
||||||
|
invoices(
|
||||||
|
id: ' . $invoiceId . '
|
||||||
|
paginator: $paginator
|
||||||
|
search: $search
|
||||||
|
sorter: $sorter
|
||||||
|
general_search_mode: ROOT_PLUS_RELATIONS
|
||||||
|
) {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
account_id
|
||||||
|
total_debits
|
||||||
|
void
|
||||||
|
remaining_due
|
||||||
|
date
|
||||||
|
due_date
|
||||||
|
end_date
|
||||||
|
delinquent
|
||||||
|
debits {
|
||||||
|
entities {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
service_id
|
||||||
|
service_name
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
credits {
|
||||||
|
entities {
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_info {
|
||||||
|
page
|
||||||
|
records_per_page
|
||||||
|
total_count
|
||||||
|
total_pages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}',
|
||||||
|
'variables' => [
|
||||||
|
'paginator' => [
|
||||||
|
'page' => $page,
|
||||||
|
'records_per_page' => $limit
|
||||||
|
],
|
||||||
|
'search' => [],
|
||||||
|
'sorter' => [
|
||||||
|
[
|
||||||
|
'attribute' => 'updated_at',
|
||||||
|
'direction' => 'ASC',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->client->createRequest()
|
||||||
|
->setMethod('POST')
|
||||||
|
->setData($data)
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$invoice = json_decode($response->getContent(), true);
|
||||||
|
|
||||||
|
return $invoice['form_params']['data']['invoices']['entities'][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeInvoices($invoices)
|
||||||
|
{
|
||||||
|
foreach ($invoices as $invoice) {
|
||||||
|
$this->storeInvoice($invoice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeInvoice($invoice)
|
||||||
|
{
|
||||||
|
\Yii::debug($invoice);
|
||||||
|
// $remainingDue is the Entire Invoice remaining to be paid amount, 0 = everything paid
|
||||||
|
$remainingDue = $invoice['remaining_due'];
|
||||||
|
// debits = charges on the account
|
||||||
|
// credits = payments on the account
|
||||||
|
|
||||||
|
foreach ($invoice['debits']['entities'] as $i => $rawItem) {
|
||||||
|
$invoiceItem = InvoiceItem::find()->where(['sonar_id' => (int)$rawItem['id']])->one();
|
||||||
|
if (null === $invoiceItem) { // create new invoice item
|
||||||
|
|
||||||
|
$account = Account::findOne(['sonar_id' => (int)$invoice['account_id']]);
|
||||||
|
$service = Service::findOne(['sonar_id' => (int)$rawItem['service_id']]);
|
||||||
|
\Yii::debug($rawItem);
|
||||||
|
if ($service && $account) {
|
||||||
|
\Yii::debug($invoice);
|
||||||
|
$payment = (isset($invoice['credits']['entities'][$i]['amount'])) ? $invoice['credits']['entities'][$i]['amount'] : 0;
|
||||||
|
// @todo check payment - i think it is wrong to assume we have the same credits and debits ^ CGS
|
||||||
|
$invoiceItem = new InvoiceItem([
|
||||||
|
'sonar_id' => (int)$rawItem['id'],
|
||||||
|
'account_id' => $account->id,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'name' => $rawItem['service_name'],
|
||||||
|
'status' => InvoiceItem::STATUS_OPEN,
|
||||||
|
'charge' => $rawItem['amount'],
|
||||||
|
'payment' => $payment,
|
||||||
|
'is_commissionable' => $service->hasCommission(),
|
||||||
|
]);
|
||||||
|
$invoiceItem->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the invoice item paid?
|
||||||
|
if ($invoiceItem && $remainingDue == 0) {
|
||||||
|
$invoiceItem->status = InvoiceItem::STATUS_PAYMENT_RECEIVED;
|
||||||
|
$invoiceItem->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapAccounts($accounts)
|
||||||
|
{
|
||||||
|
$mapped = [];
|
||||||
|
$i = 0;
|
||||||
|
$db = \Yii::$app->db;
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$mapped[$i]['sonar_id'] = (int)$account['id'];
|
||||||
|
$mapped[$i]['name'] = $account['name'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [
|
||||||
|
* 'id' => '132'
|
||||||
|
* 'quantity' => 1
|
||||||
|
* 'name_override' => 'Bradley'
|
||||||
|
* 'price_override' => 0
|
||||||
|
* 'price_override_reason' => 'testing'
|
||||||
|
* 'service' => [
|
||||||
|
* 'id' => '10'
|
||||||
|
* 'name' => 'Business Giga Speed Internet'
|
||||||
|
* 'amount' => 18000
|
||||||
|
* 'enabled' => true
|
||||||
|
* 'application' => 'DEBIT'
|
||||||
|
* 'type' => 'DATA'
|
||||||
|
* ]
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
$mapped[$i]['services'] = []; // init empty array
|
||||||
|
|
||||||
|
foreach ($account['account_services']['entities'] as $key => $account_service) {
|
||||||
|
$mapped[$i]['services'][$key]['sonar_id'] = (int)$account_service['service']['id'];
|
||||||
|
$mapped[$i]['services'][$key]['name'] = (!empty($account_service['name_override'])) ? $account_service['name_override'] : $account_service['service']['name'];
|
||||||
|
$mapped[$i]['services'][$key]['price'] = (!empty($account_service['price_override'])) ? $account_service['price_override'] : $account_service['service']['amount'];
|
||||||
|
if ($account_service['service']['application'] === 'CREDIT') {
|
||||||
|
$mapped[$i]['services'][$key]['price'] = -1 * $mapped[$i]['services'][$key]['price'];// store as a negative
|
||||||
|
}
|
||||||
|
|
||||||
|
// set to 0 if null after credit
|
||||||
|
if (null === $mapped[$i]['services'][$key]['price']) {
|
||||||
|
$mapped[$i]['services'][$key]['price'] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $account['custom_field_data']['entities'][0]['value'];
|
||||||
|
$salesAgent = SalesAgent::findOne(['name' => $name]);
|
||||||
|
if (null === $salesAgent) {
|
||||||
|
$salesAgent = new SalesAgent(['name' => $name]);
|
||||||
|
$salesAgent->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$mapped[$i]['sales_agent_id'] = $salesAgent->id;
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
return $mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeAccounts()
|
||||||
|
{
|
||||||
|
$page = 1;
|
||||||
|
do {
|
||||||
|
$batch = $this->getAccounts($page, 100);
|
||||||
|
$accounts = $this->mapAccounts($batch['accounts']['entities']);
|
||||||
|
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
$accountModel = Account::findOne(['sonar_id' => $account['sonar_id']]);
|
||||||
|
if (null === $accountModel) {
|
||||||
|
$accountModel = new Account([
|
||||||
|
'sonar_id' => $account['sonar_id'],
|
||||||
|
'name' => $account['name'],
|
||||||
|
'sales_agent_id' => $account['sales_agent_id'],
|
||||||
|
]);
|
||||||
|
$accountModel->save();
|
||||||
|
} else {
|
||||||
|
//$accountModel->sonar_id = $account['sonar_id'];
|
||||||
|
$accountModel->name = $account['name'];
|
||||||
|
$accountModel->sales_agent_id = $account['sales_agent_id'];
|
||||||
|
$accountModel->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($account['services'] as $rawServiceData) {
|
||||||
|
$serviceModel = Service::findOne(['sonar_id' => (int)$rawServiceData['sonar_id']]);
|
||||||
|
if (null === $serviceModel) {
|
||||||
|
$serviceModel = new Service([
|
||||||
|
'sonar_id' => $rawServiceData['sonar_id'],
|
||||||
|
'name' => $rawServiceData['name'],
|
||||||
|
'price' => $rawServiceData['price'],
|
||||||
|
'account_id' => $accountModel->id,
|
||||||
|
'active' => 1, // @todo pull active state in from sonar api
|
||||||
|
]);
|
||||||
|
$serviceModel->save();
|
||||||
|
} else {
|
||||||
|
$serviceModel->commission = $serviceModel->getFormattedDollar($serviceModel->commission, false);
|
||||||
|
$serviceModel->sonar_id = $rawServiceData['sonar_id'];
|
||||||
|
$serviceModel->name = $rawServiceData['name'];
|
||||||
|
$serviceModel->price = $rawServiceData['price'];
|
||||||
|
$serviceModel->account_id = $accountModel->id;
|
||||||
|
if (!empty($serviceModel->dirtyAttributes)) {
|
||||||
|
if (isset($serviceModel->dirtyAttributes['price'])) {
|
||||||
|
//Yii::$app->queue->push(new EmailJob([
|
||||||
|
// 'templateAlias' => EmailJob::PRICE_CHANGE,
|
||||||
|
// "email" => Yii::$app->params['adminEmail'],
|
||||||
|
// 'templateModel' => [
|
||||||
|
// "action_edit_url" => Yii::$app->urlManager->createAbsoluteUrl(
|
||||||
|
// ['service/update', 'id' => $serviceModel->id]
|
||||||
|
// ),
|
||||||
|
// ]
|
||||||
|
//]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$serviceModel->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$page++;
|
||||||
|
} while ($page < ($batch['accounts']['page_info']['total_pages'] + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processInvoices(int $invoiceId)
|
||||||
|
{
|
||||||
|
dump($this->getInvoice($invoiceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
4
common/config/.gitignore
vendored
Normal file
4
common/config/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
codeception-local.php
|
||||||
|
main-local.php
|
||||||
|
params-local.php
|
||||||
|
test-local.php
|
||||||
37
common/config/__autocomplete.php
Normal file
37
common/config/__autocomplete.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class only exists here for IDE (PHPStorm/Netbeans/...) autocompletion.
|
||||||
|
* This file is never included anywhere.
|
||||||
|
* Adjust this file to match classes configured in your application config, to enable IDE autocompletion for custom components.
|
||||||
|
* Example: A property phpdoc can be added in `__Application` class as `@property \vendor\package\Rollbar|__Rollbar $rollbar` and adding a class in this file
|
||||||
|
* ```php
|
||||||
|
* // @property of \vendor\package\Rollbar goes here
|
||||||
|
* class __Rollbar {
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class Yii {
|
||||||
|
/**
|
||||||
|
* @var \yii\web\Application|\yii\console\Application|__Application
|
||||||
|
*/
|
||||||
|
public static $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property yii\rbac\DbManager $authManager
|
||||||
|
* @property \Da\User\Model\User $user
|
||||||
|
* @property \common\components\SonarApiComponent $sonar
|
||||||
|
* @property \common\components\HubspotApiComponent $hubspot
|
||||||
|
* @property \common\components\PostmarkComponent $postmark
|
||||||
|
* @property \yii\queue\db\Queue $queue
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class __Application {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property app\models\User $identity
|
||||||
|
*/
|
||||||
|
class __WebUser {
|
||||||
|
}
|
||||||
5
common/config/bootstrap.php
Normal file
5
common/config/bootstrap.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
Yii::setAlias('@common', dirname(__DIR__));
|
||||||
|
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
|
||||||
|
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/api');
|
||||||
|
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');
|
||||||
44
common/config/main.php
Normal file
44
common/config/main.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use common\components\PostmarkComponent;
|
||||||
|
use common\components\SonarApiComponent;
|
||||||
|
use common\components\HubspotApiComponent;
|
||||||
|
use yii\caching\FileCache;
|
||||||
|
use yii\queue\db\Queue;
|
||||||
|
|
||||||
|
$params = array_merge(
|
||||||
|
require __DIR__ . '/params.php',
|
||||||
|
require __DIR__ . '/params-local.php'
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => $params['company_name'] . ' - ' . $params['product_name'],
|
||||||
|
'aliases' => [
|
||||||
|
'@bower' => '@vendor/bower-asset',
|
||||||
|
'@npm' => '@vendor/npm-asset',
|
||||||
|
],
|
||||||
|
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
|
||||||
|
'components' => [
|
||||||
|
'cache' => [
|
||||||
|
'class' => FileCache::class,
|
||||||
|
],
|
||||||
|
'sonar' => [
|
||||||
|
'class' => SonarApiComponent::class,
|
||||||
|
'baseUrl' => $params['sonar.url'] . '/api/graphql',
|
||||||
|
'bearerToken' => $params['sonar.bearerToken'],
|
||||||
|
],
|
||||||
|
'postmark' => [
|
||||||
|
'class' => PostmarkComponent::class,
|
||||||
|
'serverToken' => $params['postmark.serverToken'],
|
||||||
|
],
|
||||||
|
'authManager' => [
|
||||||
|
'class' => 'yii\rbac\DbManager',
|
||||||
|
],
|
||||||
|
'urlManager' => [
|
||||||
|
'enablePrettyUrl' => true,
|
||||||
|
'showScriptName' => false,
|
||||||
|
'rules' => [
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
];
|
||||||
14
common/config/params.php
Normal file
14
common/config/params.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'adminEmail' => 'admin@example.com',
|
||||||
|
'supportEmail' => 'support@example.com',
|
||||||
|
'senderEmail' => 'noreply@example.com',
|
||||||
|
'senderName' => 'Example.com mailer',
|
||||||
|
'user.passwordResetTokenExpire' => 3600,
|
||||||
|
'user.passwordMinLength' => 8,
|
||||||
|
'sonar.url' => 'https://yourname.sonar.software',
|
||||||
|
'sonar.bearerToken' => '',
|
||||||
|
'postmark.serverToken' => 'postmark-server-key',
|
||||||
|
'postmark.messageStream' => 'outbound',
|
||||||
|
'sentry.dsn' => 'https://asdf@o4507934844780544.ingest.us.sentry.io/4508006893158400',
|
||||||
|
];
|
||||||
11
common/config/test.php
Normal file
11
common/config/test.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'id' => 'app-common-tests',
|
||||||
|
'basePath' => dirname(__DIR__),
|
||||||
|
'components' => [
|
||||||
|
'user' => [
|
||||||
|
'class' => \yii\web\User::class,
|
||||||
|
'identityClass' => 'common\models\User',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
11
common/fixtures/AuthAssignmentFixture.php
Normal file
11
common/fixtures/AuthAssignmentFixture.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\fixtures;
|
||||||
|
|
||||||
|
use yii\test\ActiveFixture;
|
||||||
|
|
||||||
|
class AuthAssignmentFixture extends ActiveFixture
|
||||||
|
{
|
||||||
|
public $tableName = '{{%auth_assignment}}';
|
||||||
|
public $depends = [UserFixture::class];
|
||||||
|
}
|
||||||
11
common/fixtures/UserFixture.php
Normal file
11
common/fixtures/UserFixture.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\fixtures;
|
||||||
|
|
||||||
|
use yii\test\ActiveFixture;
|
||||||
|
|
||||||
|
class UserFixture extends ActiveFixture
|
||||||
|
{
|
||||||
|
public $modelClass = 'common\models\User';
|
||||||
|
public $depends = [];
|
||||||
|
}
|
||||||
12
common/fixtures/data/auth_assignment.php
Normal file
12
common/fixtures/data/auth_assignment.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'item_name' => 'admin',
|
||||||
|
'user_id' => 1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'item_name' => 'user',
|
||||||
|
'user_id' => 2,
|
||||||
|
],
|
||||||
|
];
|
||||||
22
common/fixtures/data/user.php
Normal file
22
common/fixtures/data/user.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
//password
|
||||||
|
'password_hash' => '$2y$13$k2hQ8aV/z6V0Y7pRbq1ufOUSaJI7EhNvvTUIoj2s/rxAmgyY95KPa',
|
||||||
|
'email' => 'admin@example.com',
|
||||||
|
'auth_key' => '1',
|
||||||
|
'status' => '1',
|
||||||
|
'created_at' => '1402312317',
|
||||||
|
'updated_at' => '1402312317',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
//password
|
||||||
|
'password_hash' => '$2y$13$k2hQ8aV/z6V0Y7pRbq1ufOUSaJI7EhNvvTUIoj2s/rxAmgyY95KPa',
|
||||||
|
'email' => 'user@example.com',
|
||||||
|
'auth_key' => '1',
|
||||||
|
'status' => '1',
|
||||||
|
'created_at' => '1402312317',
|
||||||
|
'updated_at' => '1402312317',
|
||||||
|
],
|
||||||
|
];
|
||||||
65
common/jobs/EmailJob.php
Normal file
65
common/jobs/EmailJob.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\jobs;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use yii\base\BaseObject;
|
||||||
|
use yii\queue\RetryableJobInterface;
|
||||||
|
|
||||||
|
class EmailJob extends BaseObject implements RetryableJobInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
const MAX_ATTEMPTS = 3;
|
||||||
|
const TTR = 600; // in seconds
|
||||||
|
|
||||||
|
public const PASSWORD_RESET = 'password-reset';
|
||||||
|
public const PASSWORD_HAS_BEEN_RESET = 'password-has-been-reset';
|
||||||
|
public const WELCOME_EMAIL = 'welcome-email';
|
||||||
|
public const VERIFY_EMAIL = 'verify-email';
|
||||||
|
public const ADMIN_NOTIFY = 'admin-new-user';
|
||||||
|
public const PRICE_CHANGE = 'price-change';
|
||||||
|
public const PAYOUT_NOTIFY = 'payout-notify';
|
||||||
|
public array $templateModel;
|
||||||
|
public string $email;
|
||||||
|
public string $templateAlias;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function execute($queue)
|
||||||
|
{
|
||||||
|
// Merge these values which are always on emails
|
||||||
|
$this->templateModel = array_merge($this->templateModel, [
|
||||||
|
"product" => Yii::$app->params['product_name'],
|
||||||
|
"product_name" => Yii::$app->params['product_name'],
|
||||||
|
"support_url" => Yii::$app->params['support_url'],
|
||||||
|
"product_url" => Yii::$app->urlManager->createAbsoluteUrl(['site/index']),
|
||||||
|
"company_name" => Yii::$app->params['company_name'],
|
||||||
|
"company_address" => Yii::$app->params['company_address'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Yii::$app->postmark->client->sendEmailWithTemplate(
|
||||||
|
Yii::$app->params['supportEmail'],
|
||||||
|
$this->email,
|
||||||
|
$this->templateAlias,
|
||||||
|
$this->templateModel,
|
||||||
|
messageStream: Yii::$app->params['postmark.messageStream']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getTtr()
|
||||||
|
{
|
||||||
|
return self::TTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function canRetry($attempt, $error)
|
||||||
|
{
|
||||||
|
return ($attempt < self::MAX_ATTEMPTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
common/models/AuthAssignment.php
Normal file
13
common/models/AuthAssignment.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\models;
|
||||||
|
|
||||||
|
use yii\db\ActiveRecord;
|
||||||
|
|
||||||
|
class AuthAssignment extends ActiveRecord
|
||||||
|
{
|
||||||
|
public static function tableName()
|
||||||
|
{
|
||||||
|
return '{{%auth_assignment}}';
|
||||||
|
}
|
||||||
|
}
|
||||||
79
common/models/LoginForm.php
Normal file
79
common/models/LoginForm.php
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\models;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use yii\base\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login form
|
||||||
|
*/
|
||||||
|
class LoginForm extends Model
|
||||||
|
{
|
||||||
|
public $email;
|
||||||
|
public $password;
|
||||||
|
public $rememberMe = true;
|
||||||
|
|
||||||
|
private $_user;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
// username and password are both required
|
||||||
|
[['email', 'password'], 'required'],
|
||||||
|
// rememberMe must be a boolean value
|
||||||
|
['rememberMe', 'boolean'],
|
||||||
|
// password is validated by validatePassword()
|
||||||
|
['password', 'validatePassword'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the password.
|
||||||
|
* This method serves as the inline validation for password.
|
||||||
|
*
|
||||||
|
* @param string $attribute the attribute currently being validated
|
||||||
|
* @param array $params the additional name-value pairs given in the rule
|
||||||
|
*/
|
||||||
|
public function validatePassword($attribute, $params)
|
||||||
|
{
|
||||||
|
if (!$this->hasErrors()) {
|
||||||
|
$user = $this->getUser();
|
||||||
|
if (!$user || !$user->validatePassword($this->password)) {
|
||||||
|
$this->addError($attribute, 'Incorrect email or password.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs in a user using the provided email and password.
|
||||||
|
*
|
||||||
|
* @return bool whether the user is logged in successfully
|
||||||
|
*/
|
||||||
|
public function login()
|
||||||
|
{
|
||||||
|
if ($this->validate()) {
|
||||||
|
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds user by [[email]]
|
||||||
|
*
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
protected function getUser()
|
||||||
|
{
|
||||||
|
if ($this->_user === null) {
|
||||||
|
$this->_user = User::findByEmail($this->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_user;
|
||||||
|
}
|
||||||
|
}
|
||||||
61
common/models/Meal.php
Normal file
61
common/models/Meal.php
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the model class for table "meal".
|
||||||
|
*
|
||||||
|
* @property int $id
|
||||||
|
* @property string $file_name
|
||||||
|
* @property int $calories
|
||||||
|
* @property int $protein
|
||||||
|
* @property int $fat
|
||||||
|
* @property int $carbohydrates
|
||||||
|
* @property int $fiber
|
||||||
|
* @property int $meal
|
||||||
|
* @property int $created_at
|
||||||
|
* @property int $updated_at
|
||||||
|
*/
|
||||||
|
class Meal extends \yii\db\ActiveRecord
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function tableName()
|
||||||
|
{
|
||||||
|
return 'meal';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[['file_name', 'calories', 'protein', 'fat', 'carbohydrates', 'fiber', 'meal', 'created_at', 'updated_at'], 'required'],
|
||||||
|
[['calories', 'protein', 'fat', 'carbohydrates', 'fiber', 'meal', 'created_at', 'updated_at'], 'integer'],
|
||||||
|
[['file_name'], 'string', 'max' => 255],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function attributeLabels()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => 'ID',
|
||||||
|
'file_name' => 'File Name',
|
||||||
|
'calories' => 'Calories',
|
||||||
|
'protein' => 'Protein',
|
||||||
|
'fat' => 'Fat',
|
||||||
|
'carbohydrates' => 'Carbohydrates',
|
||||||
|
'fiber' => 'Fiber',
|
||||||
|
'meal' => 'Meal',
|
||||||
|
'created_at' => 'Created At',
|
||||||
|
'updated_at' => 'Updated At',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
332
common/models/User.php
Normal file
332
common/models/User.php
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\models;
|
||||||
|
|
||||||
|
use common\jobs\EmailJob;
|
||||||
|
use Yii;
|
||||||
|
use yii\base\NotSupportedException;
|
||||||
|
use yii\behaviors\TimestampBehavior;
|
||||||
|
use yii\db\ActiveRecord;
|
||||||
|
use yii\db\AfterSaveEvent;
|
||||||
|
use yii\web\IdentityInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User model
|
||||||
|
*
|
||||||
|
* @property integer $id
|
||||||
|
* @property string $password_hash
|
||||||
|
* @property string $password_reset_token
|
||||||
|
* @property string $verification_token
|
||||||
|
* @property string $email
|
||||||
|
* @property string $first_name
|
||||||
|
* @property string $name
|
||||||
|
* @property string $auth_key
|
||||||
|
* @property integer $status
|
||||||
|
* @property integer $sales_agent_id
|
||||||
|
* @property integer $created_at
|
||||||
|
* @property integer $updated_at
|
||||||
|
* @property string $password write-only password
|
||||||
|
*/
|
||||||
|
class User extends ActiveRecord implements IdentityInterface
|
||||||
|
{
|
||||||
|
// User statuses
|
||||||
|
public const STATUS_UNVERIFIED = 10;
|
||||||
|
public const STATUS_INACTIVE = 0;
|
||||||
|
public const STATUS_ACTIVE = 1;
|
||||||
|
public const STATUS_VERIFIED = 2;
|
||||||
|
|
||||||
|
public const PAYOUT_INTERVAL_MONTHLY = 0;
|
||||||
|
public array $userStatusArray;
|
||||||
|
public string $role = '';
|
||||||
|
public bool $welcomeEmailSent = false;
|
||||||
|
public string $firstName;
|
||||||
|
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$this->userStatusArray = [
|
||||||
|
self::STATUS_UNVERIFIED => Yii::t('app', 'Unverified'),
|
||||||
|
self::STATUS_INACTIVE => Yii::t('app', 'Inactive'),
|
||||||
|
self::STATUS_ACTIVE => Yii::t('app', 'Active'),
|
||||||
|
self::STATUS_VERIFIED => Yii::t('app', 'Verified (not active)'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// register event
|
||||||
|
$this->on(self::EVENT_AFTER_INSERT, [$this, 'emailTrigger']);
|
||||||
|
$this->on(self::EVENT_AFTER_UPDATE, [$this, 'emailTrigger']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function emailTrigger(AfterSaveEvent $event)
|
||||||
|
{
|
||||||
|
if ($event->sender->status == self::STATUS_ACTIVE && !$event->sender->welcome_email_sent) {
|
||||||
|
Yii::$app->queue->push(new EmailJob([
|
||||||
|
'templateAlias' => EmailJob::WELCOME_EMAIL,
|
||||||
|
'email' => $event->sender->email,
|
||||||
|
'templateModel' => [
|
||||||
|
"name" => $event->sender->first_name,
|
||||||
|
"action_url" => Yii::$app->urlManager->createAbsoluteUrl(['site/login']),
|
||||||
|
]
|
||||||
|
]));
|
||||||
|
|
||||||
|
$event->sender->welcome_email_sent = true;
|
||||||
|
$event->sender->save(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function tableName()
|
||||||
|
{
|
||||||
|
return '{{%user}}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['status', 'default', 'value' => self::STATUS_UNVERIFIED],
|
||||||
|
[['email'], 'email'],
|
||||||
|
[['email'], 'unique'],
|
||||||
|
[['sales_agent_id', 'created_at', 'updated_at'], 'integer'],
|
||||||
|
[
|
||||||
|
'sales_agent_id',
|
||||||
|
'required',
|
||||||
|
'when' => function ($model) {
|
||||||
|
return $model->role === 'sales-agent';
|
||||||
|
},
|
||||||
|
'whenClient' => "function (attribute, value) {
|
||||||
|
return $('#role').val() == 'sales-agent';
|
||||||
|
}"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'status',
|
||||||
|
'in',
|
||||||
|
'range' => [
|
||||||
|
self::STATUS_ACTIVE,
|
||||||
|
self::STATUS_UNVERIFIED,
|
||||||
|
self::STATUS_VERIFIED,
|
||||||
|
self::STATUS_INACTIVE,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[['role'], 'string'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterSave($insert, $changedAttributes)
|
||||||
|
{
|
||||||
|
$auth = Yii::$app->authManager;
|
||||||
|
|
||||||
|
// delete exiting roles if set
|
||||||
|
$auth->revokeAll($this->id);
|
||||||
|
// assign new role
|
||||||
|
if (!empty($this->role)) {
|
||||||
|
$auth->assign($auth->getRole($this->role), $this->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::afterSave($insert, $changedAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterFind()
|
||||||
|
{
|
||||||
|
$rolesAssignedToUser = Yii::$app->authManager->getRolesByUser($this->id);
|
||||||
|
// we only use one role for the user
|
||||||
|
if (!empty($rolesAssignedToUser)) {
|
||||||
|
$this->role = array_key_first($rolesAssignedToUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::afterFind();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function behaviors()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TimestampBehavior::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get names for dropdown lists
|
||||||
|
*
|
||||||
|
* @param $dropdown
|
||||||
|
* @return array|mixed
|
||||||
|
*/
|
||||||
|
public function getStatusName($dropdown = false)
|
||||||
|
{
|
||||||
|
return $dropdown ? $this->userStatusArray : $this->userStatusArray[$this->status];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthAssignment()
|
||||||
|
{
|
||||||
|
return $this->hasOne(\common\models\AuthAssignment::class, ['user_id' => 'id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function findIdentity($id)
|
||||||
|
{
|
||||||
|
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds user by email
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @return ActiveRecord|array|null
|
||||||
|
*/
|
||||||
|
public static function findByEmail($email)
|
||||||
|
{
|
||||||
|
return static::findOne(['email' => $email, 'status' => self::STATUS_ACTIVE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function findIdentityByAccessToken($token, $type = null)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds user by password reset token
|
||||||
|
*
|
||||||
|
* @param string $token password reset token
|
||||||
|
* @return ActiveRecord|array|null
|
||||||
|
*/
|
||||||
|
public static function findByPasswordResetToken($token)
|
||||||
|
{
|
||||||
|
if (!static::isPasswordResetTokenValid($token)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static::findOne(['password_reset_token' => $token, 'status' => self::STATUS_ACTIVE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds user by verification email token
|
||||||
|
*
|
||||||
|
* @param string $token verify email token
|
||||||
|
* @return static|null
|
||||||
|
*/
|
||||||
|
public static function findByVerificationToken($token)
|
||||||
|
{
|
||||||
|
return static::findOne([
|
||||||
|
'verification_token' => $token,
|
||||||
|
'status' => self::STATUS_UNVERIFIED
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds out if password reset token is valid
|
||||||
|
*
|
||||||
|
* @param string $token password reset token
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isPasswordResetTokenValid($token)
|
||||||
|
{
|
||||||
|
if (empty($token)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = (int)substr($token, strrpos($token, '_') + 1);
|
||||||
|
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
|
||||||
|
return $timestamp + $expire >= time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->getPrimaryKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAuthKey()
|
||||||
|
{
|
||||||
|
return $this->auth_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateAuthKey($authKey)
|
||||||
|
{
|
||||||
|
return $this->getAuthKey() === $authKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates password
|
||||||
|
*
|
||||||
|
* @param string $password password to validate
|
||||||
|
* @return bool if password provided is valid for current user
|
||||||
|
*/
|
||||||
|
public function validatePassword($password)
|
||||||
|
{
|
||||||
|
return Yii::$app->security->validatePassword($password, $this->password_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates password hash from password and sets it to the model
|
||||||
|
*
|
||||||
|
* @param string $password
|
||||||
|
*/
|
||||||
|
public function setPassword($password)
|
||||||
|
{
|
||||||
|
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates "remember me" authentication key
|
||||||
|
*/
|
||||||
|
public function generateAuthKey()
|
||||||
|
{
|
||||||
|
$this->auth_key = Yii::$app->security->generateRandomString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates new password reset token
|
||||||
|
*/
|
||||||
|
public function generatePasswordResetToken()
|
||||||
|
{
|
||||||
|
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates new token for email verification
|
||||||
|
*/
|
||||||
|
public function generateEmailVerificationToken()
|
||||||
|
{
|
||||||
|
$this->verification_token = Yii::$app->security->generateRandomString() . '_' . time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes password reset token
|
||||||
|
*/
|
||||||
|
public function removePasswordResetToken()
|
||||||
|
{
|
||||||
|
$this->password_reset_token = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets query for [[SalesAgent]].
|
||||||
|
*
|
||||||
|
* @return \yii\db\ActiveQuery|yii\db\ActiveQuery
|
||||||
|
*/
|
||||||
|
public function getSalesAgent()
|
||||||
|
{
|
||||||
|
return $this->hasOne(SalesAgent::class, ['id' => 'sales_agent_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
75
common/models/search/User.php
Normal file
75
common/models/search/User.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\models\search;
|
||||||
|
|
||||||
|
use common\models\User as UserModel;
|
||||||
|
use Yii;
|
||||||
|
use yii\base\Model;
|
||||||
|
use yii\data\ActiveDataProvider;
|
||||||
|
use common\models\SalesAgent;
|
||||||
|
|
||||||
|
|
||||||
|
class User extends UserModel
|
||||||
|
{
|
||||||
|
public string $role = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[['id', 'status'], 'integer'],
|
||||||
|
[['email', 'first_name', 'role'], 'safe'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates data provider instance with search query applied
|
||||||
|
*
|
||||||
|
* @param array $params
|
||||||
|
*
|
||||||
|
* @return ActiveDataProvider
|
||||||
|
*/
|
||||||
|
public function search($params)
|
||||||
|
{
|
||||||
|
$query = UserModel::find()
|
||||||
|
->joinWith('authAssignment', 'salesAgent');
|
||||||
|
|
||||||
|
$dataProvider = new ActiveDataProvider([
|
||||||
|
'query' => $query,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$dataProvider->sort->attributes['salesAgentName'] = [
|
||||||
|
'asc' => ['sales_agent_id' => SORT_ASC],
|
||||||
|
'desc' => ['sales_agent_id' => SORT_DESC],
|
||||||
|
];
|
||||||
|
|
||||||
|
$dataProvider->sort->attributes['role'] = [
|
||||||
|
'asc' => ['auth_assignment.item_name' => SORT_ASC],
|
||||||
|
'desc' => ['auth_assignment.item_name' => SORT_DESC],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->load($params);
|
||||||
|
|
||||||
|
if (!$this->validate()) {
|
||||||
|
return $dataProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->salesAgentName)) {
|
||||||
|
$salesAgent = SalesAgent::find()->where(['name' => $this->salesAgentName])->one();
|
||||||
|
$this->sales_agent_id = $salesAgent ? $salesAgent->id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->andFilterWhere([
|
||||||
|
'id' => $this->id,
|
||||||
|
'status' => $this->status,
|
||||||
|
'sales_agent_id' => $this->sales_agent_id,
|
||||||
|
'auth_assignment.item_name' => $this->role
|
||||||
|
]);
|
||||||
|
|
||||||
|
$query->andFilterWhere(['like', 'email', $this->email]);
|
||||||
|
|
||||||
|
return $dataProvider;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
common/rbac/SalesAgentRule.php
Normal file
16
common/rbac/SalesAgentRule.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\rbac;
|
||||||
|
|
||||||
|
class SalesAgentRule extends \yii\rbac\Rule
|
||||||
|
{
|
||||||
|
public $name = 'assignedToSalesAgent';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function execute($user, $item, $params)
|
||||||
|
{
|
||||||
|
return isset($params['post']) && $params['post']->sales_agent_id == $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
common/tests/_bootstrap.php
Normal file
9
common/tests/_bootstrap.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||||
|
defined('YII_ENV') or define('YII_ENV', 'test');
|
||||||
|
defined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
|
||||||
|
require __DIR__ . '/../config/bootstrap.php';
|
||||||
23
common/tests/_data/user.php
Normal file
23
common/tests/_data/user.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR',
|
||||||
|
//password_0
|
||||||
|
'password_hash' => '$2y$13$.NVUBcghbQPiEcBh4LmI7.ctT3FDofwpgrKIAfwWvsr.wBM0l6Y7u',
|
||||||
|
'password_reset_token' => 'ExzkCOaYc1L8IOBs4wdTGGbgNiG3Wz1I_1402312317',
|
||||||
|
'created_at' => '1402312317',
|
||||||
|
'updated_at' => '1402312317',
|
||||||
|
'email' => 'nicole.paucek@schultz.info',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
//password
|
||||||
|
'password_hash' => '$2y$13$k2hQ8aV/z6V0Y7pRbq1ufOUSaJI7EhNvvTUIoj2s/rxAmgyY95KPa',
|
||||||
|
'email' => 'admin@example.com',
|
||||||
|
'auth_key' => '1',
|
||||||
|
'status' => '1',
|
||||||
|
'sales_agent_id' => '0',
|
||||||
|
'created_at' => '1402312317',
|
||||||
|
'updated_at' => '1402312317',
|
||||||
|
],
|
||||||
|
];
|
||||||
2
common/tests/_output/.gitignore
vendored
Normal file
2
common/tests/_output/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
1
common/tests/_support/.gitignore
vendored
Normal file
1
common/tests/_support/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
_generated
|
||||||
26
common/tests/_support/UnitTester.php
Normal file
26
common/tests/_support/UnitTester.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\tests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inherited Methods
|
||||||
|
* @method void wantToTest($text)
|
||||||
|
* @method void wantTo($text)
|
||||||
|
* @method void execute($callable)
|
||||||
|
* @method void expectTo($prediction)
|
||||||
|
* @method void verify($prediction)
|
||||||
|
* @method void amGoingTo($argumentation)
|
||||||
|
* @method void am($role)
|
||||||
|
* @method void lookForwardTo($achieveValue)
|
||||||
|
* @method void comment($description)
|
||||||
|
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
|
||||||
|
*
|
||||||
|
* @SuppressWarnings(PHPMD)
|
||||||
|
*/
|
||||||
|
class UnitTester extends \Codeception\Actor
|
||||||
|
{
|
||||||
|
use _generated\UnitTesterActions;
|
||||||
|
/**
|
||||||
|
* Define custom actions here
|
||||||
|
*/
|
||||||
|
}
|
||||||
7
common/tests/unit.suite.yml
Normal file
7
common/tests/unit.suite.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
suite_namespace: common\tests\unit
|
||||||
|
actor: UnitTester
|
||||||
|
bootstrap: false
|
||||||
|
modules:
|
||||||
|
enabled:
|
||||||
|
- Yii2:
|
||||||
|
part: fixtures
|
||||||
67
common/tests/unit/models/LoginFormTest.php
Normal file
67
common/tests/unit/models/LoginFormTest.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace unit\models;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use common\models\LoginForm;
|
||||||
|
use common\fixtures\UserFixture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login form test
|
||||||
|
*/
|
||||||
|
class LoginFormTest extends \Codeception\Test\Unit
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \common\tests\UnitTester
|
||||||
|
*/
|
||||||
|
protected $tester;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function _fixtures()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'user' => [
|
||||||
|
'class' => UserFixture::class,
|
||||||
|
'dataFile' => codecept_data_dir() . 'user.php'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoginNoUser()
|
||||||
|
{
|
||||||
|
$model = new LoginForm([
|
||||||
|
'email' => 'not_existing_username',
|
||||||
|
'password' => 'not_existing_password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
verify($model->login())->false();
|
||||||
|
verify(Yii::$app->user->isGuest)->true();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoginWrongPassword()
|
||||||
|
{
|
||||||
|
$model = new LoginForm([
|
||||||
|
'email' => 'nicole.paucek@schultz.info',
|
||||||
|
'password' => 'wrong_password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
verify($model->login())->false();
|
||||||
|
verify( $model->errors)->arrayHasKey('password');
|
||||||
|
verify(Yii::$app->user->isGuest)->true();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLoginCorrect()
|
||||||
|
{
|
||||||
|
$model = new LoginForm([
|
||||||
|
'email' => 'admin@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
verify($model->login())->true();
|
||||||
|
verify($model->errors)->arrayHasNotKey('password');
|
||||||
|
verify(Yii::$app->user->isGuest)->false();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
common/tests/unit/traits/FormattedDollarTraitTest.php
Normal file
45
common/tests/unit/traits/FormattedDollarTraitTest.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace unit\traits;
|
||||||
|
|
||||||
|
use common\traits\FormattedDollarTrait;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
use function PHPUnit\Framework\assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatted dollar trait test
|
||||||
|
*/
|
||||||
|
class FormattedDollarTraitTest extends \Codeception\Test\Unit
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider floatDataProvider
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testConvertToCents($test, $expected)
|
||||||
|
{
|
||||||
|
$mock = $this->getMockForTrait(FormattedDollarTrait::class);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $mock->convertToCents($test));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function floatDataProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[12.445, 1244],
|
||||||
|
[-13.678901234, -1367],
|
||||||
|
["-10.4", -1040],
|
||||||
|
["-10", -1000],
|
||||||
|
["11.445", 1144],
|
||||||
|
["533.3.3533.11,445", 533335331144],
|
||||||
|
["1,40032,0030.445", 140032003044],
|
||||||
|
[124.99, 12499],
|
||||||
|
[-1.4, -140],
|
||||||
|
[14, 1400],
|
||||||
|
[.99, 99],
|
||||||
|
[2.3, 230],
|
||||||
|
[-30, -3000],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
76
common/widgets/Alert.php
Normal file
76
common/widgets/Alert.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace common\widgets;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert widget renders a message from session flash. All flash messages are displayed
|
||||||
|
* in the sequence they were assigned using setFlash. You can set message as following:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* Yii::$app->session->setFlash('error', 'This is the message');
|
||||||
|
* Yii::$app->session->setFlash('success', 'This is the message');
|
||||||
|
* Yii::$app->session->setFlash('info', 'This is the message');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Multiple messages could be set as follows:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @author Kartik Visweswaran <kartikv2@gmail.com>
|
||||||
|
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||||
|
*/
|
||||||
|
class Alert extends \yii\bootstrap5\Widget
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array the alert types configuration for the flash messages.
|
||||||
|
* This array is setup as $key => $value, where:
|
||||||
|
* - key: the name of the session flash variable
|
||||||
|
* - value: the bootstrap alert type (i.e. danger, success, info, warning)
|
||||||
|
*/
|
||||||
|
public $alertTypes = [
|
||||||
|
'error' => 'alert-danger',
|
||||||
|
'danger' => 'alert-danger',
|
||||||
|
'success' => 'alert-success',
|
||||||
|
'info' => 'alert-info',
|
||||||
|
'warning' => 'alert-warning'
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* @var array the options for rendering the close button tag.
|
||||||
|
* Array will be passed to [[\yii\bootstrap\Alert::closeButton]].
|
||||||
|
*/
|
||||||
|
public $closeButton = [];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$session = Yii::$app->session;
|
||||||
|
$flashes = $session->getAllFlashes();
|
||||||
|
$appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : '';
|
||||||
|
|
||||||
|
foreach ($flashes as $type => $flash) {
|
||||||
|
if (!isset($this->alertTypes[$type])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ((array) $flash as $i => $message) {
|
||||||
|
echo \yii\bootstrap5\Alert::widget([
|
||||||
|
'body' => $message,
|
||||||
|
'closeButton' => $this->closeButton,
|
||||||
|
'options' => array_merge($this->options, [
|
||||||
|
'id' => $this->getId() . '-' . $type . '-' . $i,
|
||||||
|
'class' => $this->alertTypes[$type] . $appendClass,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$session->removeFlash($type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
composer.json
Normal file
57
composer.json
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"license": "proprietary",
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4.0",
|
||||||
|
"ext-json": "*",
|
||||||
|
"yiisoft/yii2": "~2.0.45",
|
||||||
|
"yiisoft/yii2-bootstrap5": "@dev",
|
||||||
|
"yiisoft/yii2-authclient": "^2.2",
|
||||||
|
"yiisoft/yii2-httpclient": "~2.0",
|
||||||
|
"cgsmith/yii2-flatpickr-widget": "~1.1",
|
||||||
|
"yiisoft/yii2-queue": "^2.3",
|
||||||
|
"wildbit/postmark-php": "^6.0",
|
||||||
|
"donatj/phpuseragentparser": "^1.9",
|
||||||
|
"twbs/bootstrap-icons": "^1.11",
|
||||||
|
"sentry/sentry": "^4.9",
|
||||||
|
"kartik-v/yii2-widget-select2": "^2.2",
|
||||||
|
"kartik-v/yii2-grid": "dev-master",
|
||||||
|
"kartik-v/yii2-editable": "^1.8",
|
||||||
|
"kartik-v/yii2-icons": "^1.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"codeception/codeception": "^5.1",
|
||||||
|
"codeception/lib-innerbrowser": "^3.0 || ^1.1",
|
||||||
|
"codeception/module-asserts": "^3.0 || ^1.1",
|
||||||
|
"codeception/module-filesystem": "^3.0 || ^1.1",
|
||||||
|
"codeception/module-yii2": "^1.1",
|
||||||
|
"codeception/specify": "^2.0",
|
||||||
|
"codeception/verify": "^3.0",
|
||||||
|
"deployer/deployer": "^7.4",
|
||||||
|
"phpunit/phpunit": "~9.5.0",
|
||||||
|
"symfony/browser-kit": "^6.0 || >=2.7 <=4.2.4",
|
||||||
|
"yiisoft/yii2-debug": "~2.1.0",
|
||||||
|
"yiisoft/yii2-faker": "~2.0.0",
|
||||||
|
"yiisoft/yii2-gii": "~2.2.0"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true,
|
||||||
|
"allow-plugins": {
|
||||||
|
"yiisoft/yii2-composer": true
|
||||||
|
},
|
||||||
|
"process-timeout": 1800,
|
||||||
|
"fxp-asset": {
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"phpunit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "composer",
|
||||||
|
"url": "https://asset-packagist.org"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
6917
composer.lock
generated
Normal file
6917
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
console/config/.gitignore
vendored
Normal file
3
console/config/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
main-local.php
|
||||||
|
params-local.php
|
||||||
|
test-local.php
|
||||||
2
console/config/bootstrap.php
Normal file
2
console/config/bootstrap.php
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?php
|
||||||
|
Yii::setAlias('@console', dirname(__DIR__));
|
||||||
58
console/config/main.php
Normal file
58
console/config/main.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use yii\console\controllers\FixtureController;
|
||||||
|
use yii\console\controllers\MigrateController;
|
||||||
|
use yii\log\FileTarget;
|
||||||
|
use function Sentry\init;
|
||||||
|
|
||||||
|
$params = array_merge(
|
||||||
|
require __DIR__ . '/../../common/config/params.php',
|
||||||
|
require __DIR__ . '/../../common/config/params-local.php',
|
||||||
|
require __DIR__ . '/params.php',
|
||||||
|
require __DIR__ . '/params-local.php'
|
||||||
|
);
|
||||||
|
|
||||||
|
init([
|
||||||
|
'dsn' => $params['sentry.dsn'],
|
||||||
|
// Specify a fixed sample rate
|
||||||
|
'traces_sample_rate' => 1.0,
|
||||||
|
// Set a sampling rate for profiling - this is relative to traces_sample_rate
|
||||||
|
'profiles_sample_rate' => 1.0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => 'app-console',
|
||||||
|
'basePath' => dirname(__DIR__),
|
||||||
|
'bootstrap' => ['log', 'queue'],
|
||||||
|
'controllerNamespace' => 'console\controllers',
|
||||||
|
'aliases' => [
|
||||||
|
'@bower' => '@vendor/bower-asset',
|
||||||
|
'@npm' => '@vendor/npm-asset',
|
||||||
|
],
|
||||||
|
'controllerMap' => [
|
||||||
|
'fixture' => [
|
||||||
|
'class' => FixtureController::class,
|
||||||
|
'namespace' => 'common\fixtures',
|
||||||
|
],
|
||||||
|
'migrate' => [
|
||||||
|
'class' => MigrateController::class,
|
||||||
|
'newFileOwnership' => '1000:1000', # Default WSL user id
|
||||||
|
'newFileMode' => 0660,
|
||||||
|
'migrationPath' => [
|
||||||
|
'@app/migrations',
|
||||||
|
'@yii/rbac/migrations',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'components' => [
|
||||||
|
'log' => [
|
||||||
|
'targets' => [
|
||||||
|
[
|
||||||
|
'class' => FileTarget::class,
|
||||||
|
'levels' => ['error', 'warning'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'params' => $params,
|
||||||
|
];
|
||||||
5
console/config/params.php
Normal file
5
console/config/params.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'adminEmail' => 'admin@example.com',
|
||||||
|
];
|
||||||
4
console/config/test.php
Normal file
4
console/config/test.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
96
console/controllers/CronController.php
Executable file
96
console/controllers/CronController.php
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace console\controllers;
|
||||||
|
|
||||||
|
use yii\console\{Controller, ExitCode};
|
||||||
|
|
||||||
|
// To create/edit crontab file: crontab -e
|
||||||
|
// To list: crontab -l
|
||||||
|
// // m h dom mon dow command
|
||||||
|
// */5 * * * * /var/www/html/yii cron/frequent
|
||||||
|
// */15 * * * * /var/www/html/yii cron/quarter
|
||||||
|
// */30 * * * * /var/www/html/yii cron/halfhourly
|
||||||
|
// 0 * * * * /var/www/html/yii cron/hourly
|
||||||
|
// 15 1 * * * /var/www/html/yii cron/overnight
|
||||||
|
// 15 3 * * 5 /var/www/html/yii cron/weekly
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CronController
|
||||||
|
*
|
||||||
|
* @package console\controllers
|
||||||
|
*/
|
||||||
|
class CronController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean whether to run the command interactively.
|
||||||
|
*/
|
||||||
|
public $interactive = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action Index
|
||||||
|
* @return int exit code
|
||||||
|
*/
|
||||||
|
public function actionIndex()
|
||||||
|
{
|
||||||
|
$this->stdout("Yes, service cron is running\n");
|
||||||
|
return ExitCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action Frequent
|
||||||
|
* Called every five minutes
|
||||||
|
* @return int exit code
|
||||||
|
*/
|
||||||
|
public function actionFrequent()
|
||||||
|
{
|
||||||
|
return ExitCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action Quarter
|
||||||
|
* Called every fifteen minutes
|
||||||
|
*
|
||||||
|
* @return int exit code
|
||||||
|
*/
|
||||||
|
public function actionQuarter()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
|
||||||
|
return ExitCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action Half Hourly
|
||||||
|
* Called every 30 minutes
|
||||||
|
*
|
||||||
|
* @return int exit code
|
||||||
|
*/
|
||||||
|
public function actionHalfhourly()
|
||||||
|
{
|
||||||
|
|
||||||
|
return ExitCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action Hourly
|
||||||
|
* @return int exit code
|
||||||
|
*/
|
||||||
|
public function actionHourly()
|
||||||
|
{
|
||||||
|
return ExitCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action Overnight
|
||||||
|
* Called every night
|
||||||
|
*
|
||||||
|
* @return int exit code
|
||||||
|
*/
|
||||||
|
public function actionOvernight()
|
||||||
|
{
|
||||||
|
|
||||||
|
return ExitCode::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
45
console/migrations/m221203_160610_create_user_table.php
Normal file
45
console/migrations/m221203_160610_create_user_table.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use common\models\User;
|
||||||
|
use yii\db\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the creation of table `{{%user}}`.
|
||||||
|
*/
|
||||||
|
class m221203_160610_create_user_table extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function safeUp()
|
||||||
|
{
|
||||||
|
$tableOptions = null;
|
||||||
|
if ($this->db->driverName === 'mysql') {
|
||||||
|
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
|
||||||
|
$tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->createTable('{{%user}}', [
|
||||||
|
'id' => $this->primaryKey(),
|
||||||
|
'auth_key' => $this->string(32)->notNull(),
|
||||||
|
'password_hash' => $this->string()->notNull(),
|
||||||
|
'password_reset_token' => $this->string()->unique(),
|
||||||
|
'verification_token' => $this->string()->defaultValue(null),
|
||||||
|
'first_name' => $this->string(64),
|
||||||
|
'email' => $this->string()->notNull()->unique(),
|
||||||
|
'status' => $this->smallInteger()->notNull()->defaultValue(User::STATUS_UNVERIFIED),
|
||||||
|
'welcome_email_sent' => $this->boolean()->defaultValue(false),
|
||||||
|
'created_at' => $this->integer()->notNull(),
|
||||||
|
'updated_at' => $this->integer()->notNull(),
|
||||||
|
|
||||||
|
], $tableOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function safeDown()
|
||||||
|
{
|
||||||
|
$this->dropTable('{{%user}}');
|
||||||
|
}
|
||||||
|
}
|
||||||
87
console/migrations/m230120_214209_init_rbac.php
Normal file
87
console/migrations/m230120_214209_init_rbac.php
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use yii\db\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class m230120_214209_init_rbac
|
||||||
|
*/
|
||||||
|
class m230120_214209_init_rbac extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$auth = Yii::$app->authManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permissions for future use?
|
||||||
|
* Maybe...
|
||||||
|
*/
|
||||||
|
// add "view meal" permission
|
||||||
|
$viewMeal = $auth->createPermission('viewMeal');
|
||||||
|
$viewMeal->description = 'View own meal records';
|
||||||
|
$auth->add($viewMeal);
|
||||||
|
|
||||||
|
// add "create meal" permission
|
||||||
|
$createMeal = $auth->createPermission('createMeal');
|
||||||
|
$createMeal->description = 'Create a new meal record';
|
||||||
|
$auth->add($createMeal);
|
||||||
|
|
||||||
|
// add "update meal" permission
|
||||||
|
$updateMeal = $auth->createPermission('updateMeal');
|
||||||
|
$updateMeal->description = 'Update own meal records';
|
||||||
|
$auth->add($updateMeal);
|
||||||
|
|
||||||
|
// add "delete meal" permission
|
||||||
|
$deleteMeal = $auth->createPermission('deleteMeal');
|
||||||
|
$deleteMeal->description = 'Delete own meal records';
|
||||||
|
$auth->add($deleteMeal);
|
||||||
|
|
||||||
|
|
||||||
|
// add "view all meals" permission (for admin)
|
||||||
|
$viewAllMeals = $auth->createPermission('viewAllMeals');
|
||||||
|
$viewAllMeals->description = 'View all meal records';
|
||||||
|
$auth->add($viewAllMeals);
|
||||||
|
|
||||||
|
// add "update all meals" permission (for admin)
|
||||||
|
$updateAllMeals = $auth->createPermission('updateAllMeals');
|
||||||
|
$updateAllMeals->description = 'Update any meal record';
|
||||||
|
$auth->add($updateAllMeals);
|
||||||
|
|
||||||
|
// add "delete all meals" permission (for admin)
|
||||||
|
$deleteAllMeals = $auth->createPermission('deleteAllMeals');
|
||||||
|
$deleteAllMeals->description = 'Delete any meal record';
|
||||||
|
$auth->add($deleteAllMeals);
|
||||||
|
|
||||||
|
// Add roles
|
||||||
|
$user = $auth->createRole('user');
|
||||||
|
$auth->add($user);
|
||||||
|
|
||||||
|
$admin = $auth->createRole('admin');
|
||||||
|
$auth->add($admin);
|
||||||
|
|
||||||
|
|
||||||
|
// Add permissions to roles (crucially important):
|
||||||
|
$auth->addChild($user, $viewMeal);
|
||||||
|
$auth->addChild($user, $createMeal);
|
||||||
|
$auth->addChild($user, $updateMeal);
|
||||||
|
$auth->addChild($user, $deleteMeal);
|
||||||
|
|
||||||
|
$auth->addChild($admin, $viewMeal);
|
||||||
|
$auth->addChild($admin, $createMeal);
|
||||||
|
$auth->addChild($admin, $updateMeal);
|
||||||
|
$auth->addChild($admin, $deleteMeal);
|
||||||
|
$auth->addChild($admin, $viewAllMeals);
|
||||||
|
$auth->addChild($admin, $updateAllMeals);
|
||||||
|
$auth->addChild($admin, $deleteAllMeals);
|
||||||
|
|
||||||
|
// Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId()
|
||||||
|
// usually implemented in your User model.
|
||||||
|
$auth->assign($admin, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$auth = Yii::$app->authManager;
|
||||||
|
|
||||||
|
$auth->removeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
47
console/migrations/m230210_155341_queue_table.php
Normal file
47
console/migrations/m230210_155341_queue_table.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use yii\db\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class m230210_155341_queue_table
|
||||||
|
*/
|
||||||
|
class m230210_155341_queue_table extends Migration
|
||||||
|
{
|
||||||
|
public $tableName = '{{%queue}}';
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function safeUp()
|
||||||
|
{
|
||||||
|
$tableOptions = null;
|
||||||
|
if ($this->db->driverName === 'mysql') {
|
||||||
|
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
|
||||||
|
$tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->createTable($this->tableName, [
|
||||||
|
'id' => $this->primaryKey(),
|
||||||
|
'channel' => $this->string()->notNull(),
|
||||||
|
'job' => $this->binary()->notNull(),
|
||||||
|
'pushed_at' => $this->integer()->notNull(),
|
||||||
|
'ttr' => $this->integer()->notNull(),
|
||||||
|
'delay' => $this->integer()->notNull(),
|
||||||
|
'priority' => $this->integer()->unsigned()->notNull()->defaultValue(1024),
|
||||||
|
'reserved_at' => $this->integer(),
|
||||||
|
'attempt' => $this->integer(),
|
||||||
|
'done_at' => $this->integer(),
|
||||||
|
], $tableOptions);
|
||||||
|
$this->createIndex('channel', $this->tableName, 'channel');
|
||||||
|
$this->createIndex('reserved_at', $this->tableName, 'reserved_at');
|
||||||
|
$this->createIndex('priority', $this->tableName, 'priority');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function safeDown()
|
||||||
|
{
|
||||||
|
$this->dropTable($this->tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
console/migrations/m250219_133939_create_meal_table.php
Normal file
36
console/migrations/m250219_133939_create_meal_table.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use yii\db\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the creation of table `{{%meal}}`.
|
||||||
|
*/
|
||||||
|
class m250219_133939_create_meal_table extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function safeUp()
|
||||||
|
{
|
||||||
|
$this->createTable('{{%meal}}', [
|
||||||
|
'id' => $this->primaryKey(),
|
||||||
|
'file_name' => $this->string()->notNull(),
|
||||||
|
'calories' => $this->integer()->notNull(),
|
||||||
|
'protein' => $this->integer()->notNull(),
|
||||||
|
'fat' => $this->integer()->notNull(),
|
||||||
|
'carbohydrates' => $this->integer()->notNull(),
|
||||||
|
'fiber' => $this->integer()->notNull(),
|
||||||
|
'meal' => $this->integer()->notNull(),
|
||||||
|
'created_at' => $this->integer()->notNull(),
|
||||||
|
'updated_at' => $this->integer()->notNull(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function safeDown()
|
||||||
|
{
|
||||||
|
$this->dropTable('{{%meal}}');
|
||||||
|
}
|
||||||
|
}
|
||||||
1
console/models/.gitkeep
Normal file
1
console/models/.gitkeep
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*
|
||||||
2
console/runtime/.gitignore
vendored
Normal file
2
console/runtime/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
54
deploy.php
Normal file
54
deploy.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
namespace Deployer;
|
||||||
|
|
||||||
|
require 'recipe/yii.php';
|
||||||
|
|
||||||
|
// Config
|
||||||
|
set('repository', 'git@github.com:cgsmith/calorie.git');
|
||||||
|
|
||||||
|
add('shared_files', [
|
||||||
|
//'yii',
|
||||||
|
'common/config/main-local.php',
|
||||||
|
'common/config/params-local.php',
|
||||||
|
'frontend/config/main-local.php',
|
||||||
|
'frontend/config/params-local.php',
|
||||||
|
]);
|
||||||
|
add('shared_dirs', []);
|
||||||
|
add('writable_dirs', []);
|
||||||
|
|
||||||
|
// Hosts
|
||||||
|
host('calorie')
|
||||||
|
->set('remote_user', 'root')
|
||||||
|
->set('deploy_path', '/var/www/calorie')
|
||||||
|
->set('environment', 'Production')
|
||||||
|
->setLabels([
|
||||||
|
'env' => 'prod',
|
||||||
|
]);
|
||||||
|
host('test.calorie')
|
||||||
|
->set('composer_options', '--verbose --prefer-dist --no-progress --no-interaction')
|
||||||
|
->set('remote_user', 'root')
|
||||||
|
->set('deploy_path', '/var/www/test.calorie')
|
||||||
|
->set('environment', 'Testing')
|
||||||
|
->setLabels([
|
||||||
|
'env' => 'test',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Tasks
|
||||||
|
task('init-app', function () {
|
||||||
|
run('cd {{release_or_current_path}} && {{bin/php}} init --env={{environment}} --overwrite=n');
|
||||||
|
});
|
||||||
|
|
||||||
|
desc('Restart yii queue workers');
|
||||||
|
task('yii:queue:restart', function () {
|
||||||
|
run('systemctl restart yii-queue@*');
|
||||||
|
});
|
||||||
|
|
||||||
|
task('deploy:prod', function() {
|
||||||
|
invoke('yii:queue:restart');
|
||||||
|
})->select('env=prod');
|
||||||
|
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
after('deploy:vendors', 'init-app');
|
||||||
|
after('deploy:failed', 'deploy:unlock');
|
||||||
|
after('deploy', 'deploy:prod');
|
||||||
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: frontend
|
||||||
|
ports:
|
||||||
|
- "20080:80"
|
||||||
|
environment:
|
||||||
|
- PHP_ENABLE_XDEBUG=1
|
||||||
|
volumes:
|
||||||
|
# Re-use local composer cache via host-volume
|
||||||
|
- ~/.composer-docker/cache:/root/.composer/cache:delegated
|
||||||
|
# Mount source-code for development
|
||||||
|
- ./:/app
|
||||||
|
extra_hosts: # https://stackoverflow.com/a/67158212/1106908
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
image: mysql:8
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 123
|
||||||
|
MYSQL_DATABASE: app
|
||||||
|
MYSQL_USER: app
|
||||||
|
MYSQL_PASSWORD: 123
|
||||||
|
ports:
|
||||||
|
- "20083:3306"
|
||||||
|
volumes:
|
||||||
|
- ./mysql-data/var/lib/mysql:/var/lib/mysql
|
||||||
16
environments/dev/common/config/codeception-local.php
Normal file
16
environments/dev/common/config/codeception-local.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return yii\helpers\ArrayHelper::merge(
|
||||||
|
require __DIR__ . '/main.php',
|
||||||
|
require __DIR__ . '/main-local.php',
|
||||||
|
require __DIR__ . '/test.php',
|
||||||
|
require __DIR__ . '/test-local.php',
|
||||||
|
[
|
||||||
|
'components' => [
|
||||||
|
'request' => [
|
||||||
|
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
|
||||||
|
'cookieValidationKey' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
23
environments/dev/common/config/main-local.php
Normal file
23
environments/dev/common/config/main-local.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'components' => [
|
||||||
|
'db' => [
|
||||||
|
'class' => \yii\db\Connection::class,
|
||||||
|
'dsn' => 'mysql:host=mysql;dbname=app',
|
||||||
|
'username' => 'app',
|
||||||
|
'password' => '123',
|
||||||
|
'charset' => 'utf8',
|
||||||
|
],
|
||||||
|
'queue' => [
|
||||||
|
'class' => \yii\queue\sync\Queue::class,
|
||||||
|
'handle' => true, // whether tasks should be executed immediately
|
||||||
|
],
|
||||||
|
'mailer' => [
|
||||||
|
'class' => \yii\symfonymailer\Mailer::class,
|
||||||
|
'viewPath' => '@common/mail',
|
||||||
|
// send all mails to a file by default.
|
||||||
|
'useFileTransport' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
8
environments/dev/common/config/params-local.php
Normal file
8
environments/dev/common/config/params-local.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'company_name' => 'Five Devs',
|
||||||
|
'product_name' => 'Calorie',
|
||||||
|
'sonar.url' => 'https://yourname.sonar.software', # no trailing slash
|
||||||
|
'sonar.bearerToken' => '',
|
||||||
|
];
|
||||||
9
environments/dev/common/config/test-local.php
Normal file
9
environments/dev/common/config/test-local.php
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'components' => [
|
||||||
|
'db' => [
|
||||||
|
'dsn' => 'mysql:host=localhost;dbname=yii2advanced_test',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
8
environments/dev/console/config/main-local.php
Normal file
8
environments/dev/console/config/main-local.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'bootstrap' => ['gii'],
|
||||||
|
'modules' => [
|
||||||
|
'gii' => 'yii\gii\Module',
|
||||||
|
],
|
||||||
|
];
|
||||||
4
environments/dev/console/config/params-local.php
Normal file
4
environments/dev/console/config/params-local.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
4
environments/dev/console/config/test-local.php
Normal file
4
environments/dev/console/config/test-local.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
11
environments/dev/frontend/config/codeception-local.php
Normal file
11
environments/dev/frontend/config/codeception-local.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return yii\helpers\ArrayHelper::merge(
|
||||||
|
require dirname(dirname(__DIR__)) . '/common/config/codeception-local.php',
|
||||||
|
require __DIR__ . '/main.php',
|
||||||
|
require __DIR__ . '/main-local.php',
|
||||||
|
require __DIR__ . '/test.php',
|
||||||
|
require __DIR__ . '/test-local.php',
|
||||||
|
[
|
||||||
|
]
|
||||||
|
);
|
||||||
27
environments/dev/frontend/config/main-local.php
Normal file
27
environments/dev/frontend/config/main-local.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$config = [
|
||||||
|
'components' => [
|
||||||
|
'request' => [
|
||||||
|
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
|
||||||
|
'cookieValidationKey' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!YII_ENV_TEST) {
|
||||||
|
// configuration adjustments for 'dev' environment
|
||||||
|
$config['bootstrap'][] = 'debug';
|
||||||
|
$config['modules']['debug'] = [
|
||||||
|
'class' => \yii\debug\Module::class,
|
||||||
|
'allowedIPs' => ['*'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$config['bootstrap'][] = 'gii';
|
||||||
|
$config['modules']['gii'] = [
|
||||||
|
'class' => \yii\gii\Module::class,
|
||||||
|
'allowedIPs' => ['*'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
||||||
4
environments/dev/frontend/config/params-local.php
Normal file
4
environments/dev/frontend/config/params-local.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
4
environments/dev/frontend/config/test-local.php
Normal file
4
environments/dev/frontend/config/test-local.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
];
|
||||||
28
environments/dev/frontend/web/index-test.php
Normal file
28
environments/dev/frontend/web/index-test.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// NOTE: Make sure this file is not accessible when deployed to production
|
||||||
|
if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
|
||||||
|
die('You are not allowed to access this file.');
|
||||||
|
}
|
||||||
|
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||||
|
defined('YII_ENV') or define('YII_ENV', 'test');
|
||||||
|
|
||||||
|
require __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
|
||||||
|
require __DIR__ . '/../../common/config/bootstrap.php';
|
||||||
|
require __DIR__ . '/../config/bootstrap.php';
|
||||||
|
|
||||||
|
|
||||||
|
$config = yii\helpers\ArrayHelper::merge(
|
||||||
|
require __DIR__ . '/../../common/config/main.php',
|
||||||
|
require __DIR__ . '/../../common/config/main-local.php',
|
||||||
|
require __DIR__ . '/../../common/config/test.php',
|
||||||
|
require __DIR__ . '/../../common/config/test-local.php',
|
||||||
|
require __DIR__ . '/../config/main.php',
|
||||||
|
require __DIR__ . '/../config/main-local.php',
|
||||||
|
require __DIR__ . '/../config/test.php',
|
||||||
|
require __DIR__ . '/../config/test-local.php'
|
||||||
|
);
|
||||||
|
|
||||||
|
(new yii\web\Application($config))->run();
|
||||||
18
environments/dev/frontend/web/index.php
Normal file
18
environments/dev/frontend/web/index.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||||
|
defined('YII_ENV') or define('YII_ENV', 'dev');
|
||||||
|
|
||||||
|
require __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
|
||||||
|
require __DIR__ . '/../../common/config/bootstrap.php';
|
||||||
|
require __DIR__ . '/../config/bootstrap.php';
|
||||||
|
|
||||||
|
$config = yii\helpers\ArrayHelper::merge(
|
||||||
|
require __DIR__ . '/../../common/config/main.php',
|
||||||
|
require __DIR__ . '/../../common/config/main-local.php',
|
||||||
|
require __DIR__ . '/../config/main.php',
|
||||||
|
require __DIR__ . '/../config/main-local.php'
|
||||||
|
);
|
||||||
|
|
||||||
|
(new yii\web\Application($config))->run();
|
||||||
2
environments/dev/frontend/web/robots.txt
Normal file
2
environments/dev/frontend/web/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user