[
  {
    "path": ".bowerrc",
    "content": "{\n    \"directory\" : \"vendor/bower-asset\"\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# yii console commands\n/yii\n/yii_test\n/yii_test.bat\n\n# phpstorm project files\n.idea\n\n# netbeans project files\nnbproject\n\n# zend studio for eclipse project files\n.buildpath\n.project\n.settings\n\n# windows thumbnail cache\nThumbs.db\n\n# composer vendor dir\n/vendor\n\n# composer itself is not needed\ncomposer.phar\n\n# Mac DS_Store Files\n.DS_Store\n\n# phpunit itself is not needed\nphpunit.phar\n# local phpunit config\n/phpunit.xml\n\n# vagrant runtime\n/.vagrant\n\n# ignore generated files\n/frontend/web/index.php\n/frontend/web/index-test.php\n/frontend/web/robots.txt\n/backend/web/index.php\n/backend/web/index-test.php\n/backend/web/robots.txt\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n * Neither the name of Yii Software LLC nor the names of its\n   contributors may be used to endorse or promote products derived\n   from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\nANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <a href=\"#\" target=\"_blank\">\n        <img src=\"https://avatars0.githubusercontent.com/u/993323\" height=\"100px\">\n    </a>\n    <h1 align=\"center\">FreeCodeTube - Yii2 Youtube Clone</h1>\n    <br>\n</p>\n\n#### The project was created while recording [video for FreeCodeCamp](https://youtu.be/whuIf33v2Ug)\n\nFeatures\n================\n\n - Login and Registration\n - Email confirmation\n - Upload videos\n - Provide thumbnail, title, description, tags\n - Status of the video: Published or Unlisted\n - Dashboard with analitics: \n    - Latest video\n    - Number of total views\n    - Number of total subscribers\n    - Latest subscribers\n - View videos\n - Leave a like/dislike\n - Find similar videos\n - Channel page\n - View videos only for specific channel\n - Subscribe on channel or unsubscribe\n - Sending email when user subscribes to channel\n - Global search to search videos by title, description or tags\n - History page\n\nDemo\n====\n\nIf you want to see working demo of the application [click here](https://freecodetube.thecodeholic.com/)\n\nInstallation\n============\n\n## Requirements\n\nThe minimum requirement by this project template is that your Web server supports PHP 5.6.0.\n\n## Installing using Composer\n\n##### Clone the repository from github.\n\n    git clone git@github.com:thecodeholic/Yii2-Youtube-Clone.git [YourDirectoryName]\n    \nThe command installs the project in a directory named `YourDirectoryName`. You can choose a different\ndirectory name if you want.\n\n##### Install dependencies\n\nFor this we need composer to be installed on our operating system. \nIf you do not have [Composer](http://getcomposer.org/), follow the instructions in the\n[Installing Yii](https://github.com/yiisoft/yii2/blob/master/docs/guide/start-installation.md#installing-via-composer) section of the definitive guide to install it.\n\nWith Composer installed, navigate to the project folder from command line and run\n\n    composer install\n\n\n## Preparing application\n\nFollow the steps from [yii2 advanced template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-installation.md#preparing-application)\nto prepare installation.\n\nAfter doing all the steps from [yii2 advanced template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-installation.md#preparing-application)\nopen `common/config/params-local.php` and add your frontend domain on key `frontendUrl`. \nExample:\n\n```php\nreturn [\n    'frontendUrl' => 'http://frontend.test/'\n];\n```\n"
  },
  {
    "path": "Vagrantfile",
    "content": "require 'yaml'\nrequire 'fileutils'\n\nrequired_plugins = %w( vagrant-hostmanager vagrant-vbguest )\nrequired_plugins.each do |plugin|\n    exec \"vagrant plugin install #{plugin}\" unless Vagrant.has_plugin? plugin\nend\n\ndomains = {\n  frontend: 'y2aa-frontend.test',\n  backend:  'y2aa-backend.test'\n}\n\nconfig = {\n  local: './vagrant/config/vagrant-local.yml',\n  example: './vagrant/config/vagrant-local.example.yml'\n}\n\n# copy config from example if local config not exists\nFileUtils.cp config[:example], config[:local] unless File.exist?(config[:local])\n# read config\noptions = YAML.load_file config[:local]\n\n# check github token\nif options['github_token'].nil? || options['github_token'].to_s.length != 40\n  puts \"You must place REAL GitHub token into configuration:\\n/yii2-app-advanced/vagrant/config/vagrant-local.yml\"\n  exit\nend\n\n# vagrant configurate\nVagrant.configure(2) do |config|\n  # select the box\n  config.vm.box = 'bento/ubuntu-16.04'\n\n  # should we ask about box updates?\n  config.vm.box_check_update = options['box_check_update']\n\n  config.vm.provider 'virtualbox' do |vb|\n    # machine cpus count\n    vb.cpus = options['cpus']\n    # machine memory size\n    vb.memory = options['memory']\n    # machine name (for VirtualBox UI)\n    vb.name = options['machine_name']\n  end\n\n  # machine name (for vagrant console)\n  config.vm.define options['machine_name']\n\n  # machine name (for guest machine console)\n  config.vm.hostname = options['machine_name']\n\n  # network settings\n  config.vm.network 'private_network', ip: options['ip']\n\n  # sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine)\n  config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant'\n\n  # disable folder '/vagrant' (guest machine)\n  config.vm.synced_folder '.', '/vagrant', disabled: true\n\n  # hosts settings (host machine)\n  config.vm.provision :hostmanager\n  config.hostmanager.enabled            = true\n  config.hostmanager.manage_host        = true\n  config.hostmanager.ignore_private_ip  = false\n  config.hostmanager.include_offline    = true\n  config.hostmanager.aliases            = domains.values\n\n  # provisioners\n  config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone']]\n  config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false\n  config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always'\n\n  # post-install message (vagrant console)\n  config.vm.post_up_message = \"Frontend URL: http://#{domains[:frontend]}\\nBackend URL: http://#{domains[:backend]}\"\nend\n"
  },
  {
    "path": "backend/Dockerfile",
    "content": "FROM yiisoftware/yii2-php:7.2-apache\n\n# Change document root for Apache\nRUN sed -i -e 's|/app/web|/app/backend/web|g' /etc/apache2/sites-available/000-default.conf"
  },
  {
    "path": "backend/assets/AppAsset.php",
    "content": "<?php\n\nnamespace backend\\assets;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * Main backend application asset bundle.\n */\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n    ];\n    public $js = [\n        'app.js'\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap4\\BootstrapAsset',\n    ];\n}\n"
  },
  {
    "path": "backend/assets/TagsInputAsset.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:12 AM\n */\n\nnamespace backend\\assets;\n\n\nuse yii\\web\\AssetBundle;\nuse yii\\web\\JqueryAsset;\n\n/**\n * Class TagsInputAsset\n *\n * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n * @package backend\\assets\n */\nclass TagsInputAsset extends AssetBundle\n{\n    public $basePath = '@webroot/tagsinput';\n    public $baseUrl = '@web/tagsinput';\n    public $css = [\n        'tagsinput.css'\n    ];\n\n    public $js = [\n        'tagsinput.js'\n    ];\n\n    public $depends = [\n        JqueryAsset::class\n    ];\n}"
  },
  {
    "path": "backend/codeception.yml",
    "content": "namespace: backend\\tests\nactor_suffix: Tester\npaths:\n    tests: tests\n    output: tests/_output\n    data: tests/_data\n    support: tests/_support\nbootstrap: _bootstrap.php\nsettings:\n    colors: true\n    memory_limit: 1024M\nmodules:\n    config:\n        Yii2:\n            configFile: 'config/codeception-local.php'\n"
  },
  {
    "path": "backend/config/.gitignore",
    "content": "codeception-local.php\nmain-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "backend/config/bootstrap.php",
    "content": "<?php\n"
  },
  {
    "path": "backend/config/main.php",
    "content": "<?php\n$params = array_merge(\n    require __DIR__ . '/../../common/config/params.php',\n    require __DIR__ . '/../../common/config/params-local.php',\n    require __DIR__ . '/params.php',\n    require __DIR__ . '/params-local.php'\n);\n\nreturn [\n    'id' => 'app-backend',\n    'name' => 'FreeCodeTube Studio',\n    'basePath' => dirname(__DIR__),\n    'controllerNamespace' => 'backend\\controllers',\n    'bootstrap' => ['log'],\n    'modules' => [],\n    'language' => 'en-US',\n    'components' => [\n        'request' => [\n            'csrfParam' => '_csrf-backend',\n        ],\n        'user' => [\n            'identityClass' => 'common\\models\\User',\n            'enableAutoLogin' => true,\n            'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],\n        ],\n        'session' => [\n            // this is the name of the session cookie used for login on the backend\n            'name' => 'advanced-backend',\n        ],\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n            ],\n        ],\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'rules' => [\n                'video/update/<id>' => 'video/update'\n            ],\n        ],\n        'assetManager' => [\n            'appendTimestamp' => true\n        ]\n\n    ],\n    'params' => $params,\n];\n"
  },
  {
    "path": "backend/config/params.php",
    "content": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n];\n"
  },
  {
    "path": "backend/config/test.php",
    "content": "<?php\nreturn [\n    'id' => 'app-backend-tests',\n    'components' => [\n        'assetManager' => [\n            'basePath' => __DIR__ . '/../web/assets',\n        ],\n        'urlManager' => [\n            'showScriptName' => true,\n        ],\n        'request' => [\n            'cookieValidationKey' => 'test',\n        ],\n    ],\n];\n"
  },
  {
    "path": "backend/controllers/CommentController.php",
    "content": "<?php\n\nnamespace backend\\controllers;\n\nuse Yii;\nuse common\\models\\Comment;\nuse backend\\models\\CommentSearch;\nuse yii\\filters\\AccessControl;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse yii\\filters\\VerbFilter;\n\n/**\n * CommentController implements the CRUD actions for Comment model.\n */\nclass CommentController extends Controller\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'roles' => ['@']\n                    ]\n                ]\n            ],\n            'verbs' => [\n                'class' => VerbFilter::className(),\n                'actions' => [\n                    'delete' => ['POST'],\n                ],\n            ],\n        ];\n    }\n\n    /**\n     * Lists all Comment models.\n     * @return mixed\n     */\n    public function actionIndex()\n    {\n        $searchModel = new CommentSearch();\n        $dataProvider = $searchModel->search(Yii::$app->request->queryParams, Yii::$app->user->id);\n\n        return $this->render('index', [\n            'searchModel' => $searchModel,\n            'dataProvider' => $dataProvider,\n        ]);\n    }\n\n    /**\n     * Displays a single Comment model.\n     * @param integer $id\n     * @return mixed\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    public function actionView($id)\n    {\n        return $this->render('view', [\n            'model' => $this->findModel($id),\n        ]);\n    }\n\n    /**\n     * Creates a new Comment model.\n     * If creation is successful, the browser will be redirected to the 'view' page.\n     * @return mixed\n     */\n    public function actionCreate()\n    {\n        $model = new Comment();\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        }\n\n        return $this->render('create', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Updates an existing Comment model.\n     * If update is successful, the browser will be redirected to the 'view' page.\n     * @param integer $id\n     * @return mixed\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    public function actionUpdate($id)\n    {\n        $model = $this->findModel($id);\n\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['view', 'id' => $model->id]);\n        }\n\n        return $this->render('update', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Deletes an existing Comment model.\n     * If deletion is successful, the browser will be redirected to the 'index' page.\n     * @param integer $id\n     * @return mixed\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    public function actionDelete($id)\n    {\n        $this->findModel($id)->delete();\n\n        return $this->redirect(['index']);\n    }\n\n    /**\n     * Finds the Comment model based on its primary key value.\n     * If the model is not found, a 404 HTTP exception will be thrown.\n     * @param integer $id\n     * @return Comment the loaded model\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    protected function findModel($id)\n    {\n        if (($model = Comment::findOne($id)) !== null) {\n            return $model;\n        }\n\n        throw new NotFoundHttpException('The requested page does not exist.');\n    }\n}\n"
  },
  {
    "path": "backend/controllers/SiteController.php",
    "content": "<?php\n\nnamespace backend\\controllers;\n\nuse common\\models\\Subscriber;\nuse common\\models\\Video;\nuse common\\models\\VideoView;\nuse Yii;\nuse yii\\web\\Controller;\nuse yii\\filters\\VerbFilter;\nuse yii\\filters\\AccessControl;\nuse common\\models\\LoginForm;\n\n/**\n * Site controller\n */\nclass SiteController extends Controller\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::className(),\n                'rules' => [\n                    [\n                        'actions' => ['login', 'error'],\n                        'allow' => true,\n                    ],\n                    [\n                        'actions' => ['logout', 'index'],\n                        'allow' => true,\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n            'verbs' => [\n                'class' => VerbFilter::className(),\n                'actions' => [\n                    'logout' => ['post'],\n                ],\n            ],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n        ];\n    }\n\n    /**\n     * Displays homepage.\n     *\n     * @return string\n     */\n    public function actionIndex()\n    {\n        $user = Yii::$app->user->identity;\n        $userId = $user->id;\n        $latestVideo = Video::find()\n            ->latest()\n            ->creator($userId)\n            ->limit(1)\n            ->one();\n        $numberOfView = VideoView::find()\n            ->alias('vv')\n            ->innerJoin(Video::tableName() . ' v',\n                'v.video_id = vv.video_id')\n            ->andWhere(['v.created_by' => $userId])\n            ->count();\n\n        $numberOfSubscribers = Yii::$app->cache->get('subscribers-'.$userId);\n        if (!$numberOfSubscribers) {\n            $numberOfSubscribers = $user->getSubscribers()->count();\n            Yii::$app->cache->set('subscribers-'.$userId, $numberOfSubscribers);\n        }\n\n\n        $subscribers = Subscriber::find()\n            ->with('user')\n            ->andWhere(['channel_id' => $userId])\n            ->orderBy('created_at DESC')\n            ->limit(3)\n            ->all();\n\n        return $this->render('index', [\n            'latestVideo' => $latestVideo,\n            'numberOfView' => $numberOfView,\n            'numberOfSubscribers' => $numberOfSubscribers,\n            'subscribers' => $subscribers,\n        ]);\n    }\n\n    /**\n     * Login action.\n     *\n     * @return string\n     */\n    public function actionLogin()\n    {\n        $this->layout = 'auth';\n        if (!Yii::$app->user->isGuest) {\n            return $this->goHome();\n        }\n\n        $model = new LoginForm();\n        if ($model->load(Yii::$app->request->post()) && $model->login()) {\n            return $this->goBack();\n        } else {\n            $model->password = '';\n\n            return $this->render('login', [\n                'model' => $model,\n            ]);\n        }\n    }\n\n    /**\n     * Logout action.\n     *\n     * @return string\n     */\n    public function actionLogout()\n    {\n        Yii::$app->user->logout();\n\n        return $this->goHome();\n    }\n}\n"
  },
  {
    "path": "backend/controllers/VideoController.php",
    "content": "<?php\n\nnamespace backend\\controllers;\n\nuse Yii;\nuse common\\models\\Video;\nuse yii\\data\\ActiveDataProvider;\nuse yii\\filters\\AccessControl;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\nuse yii\\filters\\VerbFilter;\nuse yii\\web\\UploadedFile;\n\n/**\n * VideoController implements the CRUD actions for Video model.\n */\nclass VideoController extends Controller\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'roles' => ['@']\n                    ]\n                ]\n            ],\n            'verbs' => [\n                'class' => VerbFilter::className(),\n                'actions' => [\n                    'delete' => ['POST'],\n                ],\n            ],\n        ];\n    }\n\n    /**\n     * Lists all Video models.\n     *\n     * @return mixed\n     */\n    public function actionIndex()\n    {\n        $dataProvider = new ActiveDataProvider([\n            'query' => Video::find()\n                ->creator(Yii::$app->user->id)\n                ->latest(),\n        ]);\n\n        return $this->render('index', [\n            'dataProvider' => $dataProvider,\n        ]);\n    }\n\n    /**\n     * Creates a new Video model.\n     * If creation is successful, the browser will be redirected to the 'view' page.\n     *\n     * @return mixed\n     */\n    public function actionCreate()\n    {\n        $model = new Video();\n\n        $model->video = UploadedFile::getInstanceByName('video');\n        if (Yii::$app->request->isPost && $model->save()) {\n            return $this->redirect(['update', 'id' => $model->video_id]);\n        }\n\n        return $this->render('create', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Updates an existing Video model.\n     * If update is successful, the browser will be redirected to the 'view' page.\n     *\n     * @param string $id\n     * @return mixed\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    public function actionUpdate($id)\n    {\n        $model = $this->findModel($id);\n\n        $model->thumbnail = UploadedFile::getInstanceByName('thumbnail');\n        if ($model->load(Yii::$app->request->post()) && $model->save()) {\n            return $this->redirect(['update', 'id' => $model->video_id]);\n        }\n\n        return $this->render('update', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Deletes an existing Video model.\n     * If deletion is successful, the browser will be redirected to the 'index' page.\n     *\n     * @param string $id\n     * @return mixed\n     * @throws \\Throwable\n     * @throws \\yii\\db\\StaleObjectException\n     * @throws \\yii\\web\\NotFoundHttpException if the model cannot be found\n     */\n    public function actionDelete($id)\n    {\n        $this->findModel($id)->delete();\n\n        return $this->redirect(['index']);\n    }\n\n    /**\n     * Finds the Video model based on its primary key value.\n     * If the model is not found, a 404 HTTP exception will be thrown.\n     *\n     * @param string $id\n     * @return Video the loaded model\n     * @throws NotFoundHttpException if the model cannot be found\n     */\n    protected function findModel($id)\n    {\n        if (( $model = Video::findOne($id) ) !== null) {\n            return $model;\n        }\n\n        throw new NotFoundHttpException('The requested page does not exist.');\n    }\n}\n"
  },
  {
    "path": "backend/models/.gitkeep",
    "content": "*\n"
  },
  {
    "path": "backend/models/CommentSearch.php",
    "content": "<?php\n\nnamespace backend\\models;\n\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\nuse common\\models\\Comment;\n\n/**\n * CommentSearch represents the model behind the search form of `common\\models\\Comment`.\n */\nclass CommentSearch extends Comment\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [['id', 'parent_id', 'pinned', 'created_at', 'updated_at', 'created_by'], 'integer'],\n            [['comment', 'video_id'], 'safe'],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function scenarios()\n    {\n        // bypass scenarios() implementation in the parent class\n        return Model::scenarios();\n    }\n\n    /**\n     * Creates data provider instance with search query applied\n     *\n     * @param array $params\n     *\n     * @return ActiveDataProvider\n     */\n    public function search($params, $userId)\n    {\n        $query = Comment::find()\n            ->with('video')\n            ->parent()\n            ->byChannel($userId)\n            ->latest();\n\n        // add conditions that should always apply here\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query,\n        ]);\n\n        $this->load($params);\n\n        if (!$this->validate()) {\n            // uncomment the following line if you do not want to return any records when validation fails\n            // $query->where('0=1');\n            return $dataProvider;\n        }\n\n        // grid filtering conditions\n        $query->andFilterWhere([\n            'id' => $this->id,\n            'parent_id' => $this->parent_id,\n            'pinned' => $this->pinned,\n            'created_at' => $this->created_at,\n            'updated_at' => $this->updated_at,\n            'created_by' => $this->created_by,\n        ]);\n\n        $query->andFilterWhere(['like', 'comment', $this->comment])\n            ->andFilterWhere(['like', 'video_id', $this->video_id]);\n\n        return $dataProvider;\n    }\n}\n"
  },
  {
    "path": "backend/runtime/.gitignore",
    "content": "*\n!.gitignore"
  },
  {
    "path": "backend/tests/_bootstrap.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\ndefined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');\n\nrequire_once YII_APP_BASE_PATH . '/vendor/autoload.php';\nrequire_once YII_APP_BASE_PATH . '/vendor/yiisoft/yii2/Yii.php';\nrequire_once YII_APP_BASE_PATH . '/common/config/bootstrap.php';\nrequire_once __DIR__ . '/../config/bootstrap.php';\n"
  },
  {
    "path": "backend/tests/_data/.gitignore",
    "content": ""
  },
  {
    "path": "backend/tests/_data/login_data.php",
    "content": "<?php\nreturn [\n    [\n        'username' => 'erau',\n        'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',\n        // password_0\n        'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne',\n        'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490',\n        'created_at' => '1392559490',\n        'updated_at' => '1392559490',\n        'email' => 'sfriesen@jenkins.info',\n    ],\n];\n"
  },
  {
    "path": "backend/tests/_output/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "backend/tests/_support/.gitignore",
    "content": "_generated\n"
  },
  {
    "path": "backend/tests/_support/FunctionalTester.php",
    "content": "<?php\nnamespace backend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)\n * @method void execute($callable)\n * @method void expectTo($prediction)\n * @method void expect($prediction)\n * @method void amGoingTo($argumentation)\n * @method void am($role)\n * @method void lookForwardTo($achieveValue)\n * @method void comment($description)\n * @method \\Codeception\\Lib\\Friend haveFriend($name, $actorClass = NULL)\n *\n * @SuppressWarnings(PHPMD)\n */\nclass FunctionalTester extends \\Codeception\\Actor\n{\n    use _generated\\FunctionalTesterActions;\n   /**\n    * Define custom actions here\n    */\n}\n"
  },
  {
    "path": "backend/tests/_support/UnitTester.php",
    "content": "<?php\nnamespace backend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)\n * @method void execute($callable)\n * @method void expectTo($prediction)\n * @method void expect($prediction)\n * @method void amGoingTo($argumentation)\n * @method void am($role)\n * @method void lookForwardTo($achieveValue)\n * @method void comment($description)\n * @method \\Codeception\\Lib\\Friend haveFriend($name, $actorClass = NULL)\n *\n * @SuppressWarnings(PHPMD)\n */\nclass UnitTester extends \\Codeception\\Actor\n{\n    use _generated\\UnitTesterActions;\n   /**\n    * Define custom actions here\n    */\n}\n"
  },
  {
    "path": "backend/tests/functional/LoginCest.php",
    "content": "<?php\n\nnamespace backend\\tests\\functional;\n\nuse backend\\tests\\FunctionalTester;\nuse common\\fixtures\\UserFixture;\n\n/**\n * Class LoginCest\n */\nclass LoginCest\n{\n    /**\n     * Load fixtures before db transaction begin\n     * Called in _before()\n     * @see \\Codeception\\Module\\Yii2::_before()\n     * @see \\Codeception\\Module\\Yii2::loadFixtures()\n     * @return array\n     */\n    public function _fixtures()\n    {\n        return [\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'login_data.php'\n            ]\n        ];\n    }\n    \n    /**\n     * @param FunctionalTester $I\n     */\n    public function loginUser(FunctionalTester $I)\n    {\n        $I->amOnPage('/site/login');\n        $I->fillField('Username', 'erau');\n        $I->fillField('Password', 'password_0');\n        $I->click('login-button');\n\n        $I->see('Logout (erau)', 'form button[type=submit]');\n        $I->dontSeeLink('Login');\n        $I->dontSeeLink('Signup');\n    }\n}\n"
  },
  {
    "path": "backend/tests/functional/_bootstrap.php",
    "content": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and use it in Cests.\n *\n * ```php\n * // Here _bootstrap.php\n * \\Codeception\\Util\\Fixtures::add('user1', ['name' => 'davert']);\n * ```\n *\n * In Cests\n *\n * ```php\n * \\Codeception\\Util\\Fixtures::get('user1');\n * ```\n */"
  },
  {
    "path": "backend/tests/functional.suite.yml",
    "content": "suite_namespace: backend\\tests\\functional\nactor: FunctionalTester\nmodules:\n    enabled:\n        - Yii2\n"
  },
  {
    "path": "backend/tests/unit/_bootstrap.php",
    "content": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and use it in Tests.\n *\n * ```php\n * // Here _bootstrap.php\n * \\Codeception\\Util\\Fixtures::add('user1', ['name' => 'davert']);\n * ```\n *\n * In Tests\n *\n * ```php\n * \\Codeception\\Util\\Fixtures::get('user1');\n * ```\n */\n"
  },
  {
    "path": "backend/tests/unit.suite.yml",
    "content": "suite_namespace: backend\\tests\\unit\nactor: UnitTester\n"
  },
  {
    "path": "backend/views/comment/_comment_item.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/14/2020\n * Time: 10:36 AM\n */\n\n/** @var $this \\yii\\web\\View */\n/** @var $model \\common\\models\\Comment */\n\n?>\n<div class=\"media media-comment comment-item\" data-id=\"<?php echo $model->id ?>\">\n    <img class=\"mr-3 comment-avatar\" src=\"/img/avatar.svg\" alt=\"\" style=\"width: 50px\">\n    <div class=\"media-body\">\n        <h6 class=\"mt-0\">\n            <?php echo \\common\\helpers\\Html::channelLink($model->createdBy) ?>\n            <small class=\"text-muted\">\n                <?php echo Yii::$app->formatter->asRelativeTime($model->created_at) ?>\n                <?php if ($model->created_at !== $model->updated_at): ?>\n                    (edited)\n                <?php endif; ?>\n            </small>\n        </h6>\n        <div class=\"comment-text\">\n            <div class=\"text-wrapper\">\n                <?php if ($model->mention): ?>\n                    <span class=\"badge bg-info\"><?php echo '@' . $model->mention ?></span>\n                <?php endif; ?>\n                <?php echo $model->comment ?>\n            </div>\n\n            <div class=\"bottom-actions my-2\">\n                <button data-action=\"<?php echo \\yii\\helpers\\Url::to(['/comment/reply']) ?>\"\n                        class=\"btn btn-sm btn-light btn-reply\">\n                    REPLY\n                </button>\n                <div class=\"btn-group comment-actions\">\n                    <button class=\"btn dropdown-toggle\" type=\"button\" id=\"dropdownMenuButton\"\n                            data-toggle=\"dropdown\">\n                        <i class=\"fas fa-ellipsis-v\"></i>\n                    </button>\n                    <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"dropdownMenuButton\">\n                        <a class=\"dropdown-item item-delete-comment\"\n                           href=\"<?php echo \\yii\\helpers\\Url::to(['/comment/delete', 'id' => $model->id]) ?>\">\n                            <i class=\"fas fa-trash\"></i>\n                            Delete\n                        </a>\n                    </div>\n                </div>\n            </div>\n            <div class=\"reply-section\">\n\n            </div>\n            <div class=\"sub-comments\">\n                <?php foreach ($model->comments as $comment): ?>\n                    <?php echo $this->render('_comment_item', ['model' => $comment]) ?>\n                <?php endforeach; ?>\n            </div>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "backend/views/comment/_form.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Comment */\n/* @var $form yii\\widgets\\ActiveForm */\n?>\n\n<div class=\"comment-form\">\n\n    <?php $form = ActiveForm::begin(); ?>\n\n    <?= $form->field($model, 'comment')->textarea(['rows' => 6]) ?>\n\n    <?= $form->field($model, 'video_id')->textInput(['maxlength' => true]) ?>\n\n    <?= $form->field($model, 'parent_id')->textInput() ?>\n\n    <?= $form->field($model, 'pinned')->textInput() ?>\n\n    <?= $form->field($model, 'created_at')->textInput() ?>\n\n    <?= $form->field($model, 'updated_at')->textInput() ?>\n\n    <?= $form->field($model, 'created_by')->textInput() ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n\n</div>\n"
  },
  {
    "path": "backend/views/comment/_item.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/14/2020\n * Time: 9:45 AM\n */\n\n/** @var $this \\yii\\web\\View */\n/** @var $model \\common\\models\\Comment */\n\n?>\n\n<div class=\"d-flex\">\n    <div class=\"comment-wrapper\">\n\n        <?php echo $this->render('_comment_item', [\n            'model' => $model\n        ]) ?>\n\n    </div>\n    <div class=\"video-wrapper\">\n        <?php echo $this->render('@backend/views/video/_video_item', [\n            'model' => $model->video\n        ]) ?>\n    </div>\n</div>\n\n\n\n\n"
  },
  {
    "path": "backend/views/comment/_search.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/* @var $this yii\\web\\View */\n/* @var $model backend\\models\\CommentSearch */\n/* @var $form yii\\widgets\\ActiveForm */\n?>\n\n<div class=\"comment-search\">\n\n    <?php $form = ActiveForm::begin([\n        'action' => ['index'],\n        'method' => 'get',\n    ]); ?>\n\n    <?= $form->field($model, 'id') ?>\n\n    <?= $form->field($model, 'comment') ?>\n\n    <?= $form->field($model, 'video_id') ?>\n\n    <?= $form->field($model, 'parent_id') ?>\n\n    <?= $form->field($model, 'pinned') ?>\n\n    <?php // echo $form->field($model, 'created_at') ?>\n\n    <?php // echo $form->field($model, 'updated_at') ?>\n\n    <?php // echo $form->field($model, 'created_by') ?>\n\n    <div class=\"form-group\">\n        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>\n        <?= Html::resetButton('Reset', ['class' => 'btn btn-outline-secondary']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n\n</div>\n"
  },
  {
    "path": "backend/views/comment/create.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Comment */\n\n$this->title = 'Create Comment';\n$this->params['breadcrumbs'][] = ['label' => 'Comments', 'url' => ['index']];\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"comment-create\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <?= $this->render('_form', [\n        'model' => $model,\n    ]) ?>\n\n</div>\n"
  },
  {
    "path": "backend/views/comment/index.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\grid\\GridView;\n\n/* @var $this yii\\web\\View */\n/* @var $searchModel backend\\models\\CommentSearch */\n/* @var $dataProvider yii\\data\\ActiveDataProvider */\n\n$this->title = 'Comments';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"comment-index\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <?php // echo $this->render('_search', ['model' => $searchModel]); ?>\n\n    <?= \\yii\\widgets\\ListView::widget([\n        'dataProvider' => $dataProvider,\n        'itemView' => '_item',\n        'pager' => [\n            'class' => \\yii\\bootstrap4\\LinkPager::class\n        ],\n        'layout' => '<div class=\"mt-4\">{items}</div>{pager}'\n    ]); ?>\n\n\n</div>\n"
  },
  {
    "path": "backend/views/comment/update.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Comment */\n\n$this->title = 'Update Comment: ' . $model->id;\n$this->params['breadcrumbs'][] = ['label' => 'Comments', 'url' => ['index']];\n$this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]];\n$this->params['breadcrumbs'][] = 'Update';\n?>\n<div class=\"comment-update\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <?= $this->render('_form', [\n        'model' => $model,\n    ]) ?>\n\n</div>\n"
  },
  {
    "path": "backend/views/comment/view.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\DetailView;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Comment */\n\n$this->title = $model->id;\n$this->params['breadcrumbs'][] = ['label' => 'Comments', 'url' => ['index']];\n$this->params['breadcrumbs'][] = $this->title;\n\\yii\\web\\YiiAsset::register($this);\n?>\n<div class=\"comment-view\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>\n        <?= Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>\n        <?= Html::a('Delete', ['delete', 'id' => $model->id], [\n            'class' => 'btn btn-danger',\n            'data' => [\n                'confirm' => 'Are you sure you want to delete this item?',\n                'method' => 'post',\n            ],\n        ]) ?>\n    </p>\n\n    <?= DetailView::widget([\n        'model' => $model,\n        'attributes' => [\n            'id',\n            'comment:ntext',\n            'video_id',\n            'parent_id',\n            'pinned',\n            'created_at',\n            'updated_at',\n            'created_by',\n        ],\n    ]) ?>\n\n</div>\n"
  },
  {
    "path": "backend/views/layouts/_header.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 9:20 AM\n */\n\nuse yii\\bootstrap4\\Nav;\nuse yii\\bootstrap4\\NavBar;\n\nNavBar::begin([\n    'brandLabel' => Yii::$app->name,\n    'brandUrl' => Yii::$app->homeUrl,\n    'options' => ['class' => 'navbar-expand-lg navbar-light bg-light shadow-sm'],\n    'innerContainerOptions' => [\n        'class' => 'container-fluid'\n    ]\n]);\n$menuItems = [\n    ['label' => 'Create', 'url' => ['/video/create']],\n];\nif (Yii::$app->user->isGuest) {\n    $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];\n} else {\n    $menuItems[] = [\n        'label' => 'Logout (' . Yii::$app->user->identity->username . ')',\n        'url' => ['/site/logout'],\n        'linkOptions' => [\n            'data-method' => 'post'\n        ]\n    ];\n}\necho Nav::widget([\n    'options' => ['class' => 'navbar-nav ml-auto'],\n    'items' => $menuItems,\n]);\nNavBar::end();\n"
  },
  {
    "path": "backend/views/layouts/_sidebar.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 9:20 AM\n */\n\n?>\n\n<aside class=\"shadow\">\n    <?php echo \\yii\\bootstrap4\\Nav::widget([\n    'options' => [\n        'class' => 'd-flex flex-column nav-pills'\n    ],\n    'items' => [\n        [\n            'label' => 'Dashboard',\n            'url' => ['/site/index']\n        ],\n        [\n            'label' => 'Videos',\n            'url' => ['/video/index']\n        ],\n        [\n            'label' => 'Comments',\n            'url' => ['/comment/index']\n        ]\n    ]\n]) ?>\n</aside>\n"
  },
  {
    "path": "backend/views/layouts/auth.php",
    "content": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse backend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\Nav;\nuse yii\\bootstrap4\\NavBar;\nuse yii\\widgets\\Breadcrumbs;\nuse common\\widgets\\Alert;\n\nAppAsset::register($this);\n$this->beginContent('@backend/views/layouts/base.php');\n?>\n<main class=\"d-flex\">\n    <div class=\"content-wrapper p-3\">\n        <?= Alert::widget() ?>\n        <?= $content ?>\n    </div>\n</main>\n<?php $this->endContent() ?>"
  },
  {
    "path": "backend/views/layouts/base.php",
    "content": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse backend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\Nav;\nuse yii\\bootstrap4\\NavBar;\nuse yii\\widgets\\Breadcrumbs;\nuse common\\widgets\\Alert;\n\nAppAsset::register($this);\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"<?= Yii::$app->language ?>\">\n<head>\n    <meta charset=\"<?= Yii::$app->charset ?>\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <?php $this->registerCsrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css\"\n            rel=\"stylesheet\">\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n\n<div class=\"wrap h-100 d-flex flex-column\">\n    <div class=\"wrap h-100 d-flex flex-column\">\n        <?php echo $this->render('_header') ?>\n\n        <?php echo $content ?>\n    </div>\n</div>\n\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n"
  },
  {
    "path": "backend/views/layouts/main.php",
    "content": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse backend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\Nav;\nuse yii\\bootstrap4\\NavBar;\nuse yii\\widgets\\Breadcrumbs;\nuse common\\widgets\\Alert;\n\nAppAsset::register($this);\n$this->beginContent('@backend/views/layouts/base.php');\n?>\n<main class=\"d-flex\">\n    <?php echo $this->render('_sidebar') ?>\n\n    <div class=\"content-wrapper p-3\">\n        <?= Alert::widget() ?>\n        <?= $content ?>\n    </div>\n</main>\n<?php $this->endContent() ?>"
  },
  {
    "path": "backend/views/site/error.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $name string */\n/* @var $message string */\n/* @var $exception Exception */\n\nuse yii\\helpers\\Html;\n\n$this->title = $name;\n?>\n<div class=\"site-error\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <div class=\"alert alert-danger\">\n        <?= nl2br(Html::encode($message)) ?>\n    </div>\n\n    <p>\n        The above error occurred while the Web server was processing your request.\n    </p>\n    <p>\n        Please contact us if you think this is a server error. Thank you.\n    </p>\n\n</div>\n"
  },
  {
    "path": "backend/views/site/index.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/** @var $latestVideo \\common\\models\\Video */\n/** @var $numberOfView integer */\n/** @var $numberOfSubscribers integer */\n/** @var $subscribers \\common\\models\\Subscriber[] */\n\n$this->title = Yii::$app->name;\n?>\n<div class=\"site-index d-flex\">\n    <div class=\"card m-2\" style=\"width: 14rem;\">\n        <?php if ($latestVideo): ?>\n            <div class=\"embed-responsive embed-responsive-16by9 mb-3\">\n                <video class=\"embed-responsive-item\"\n                       poster=\"<?php echo $latestVideo->getThumbnailLink() ?>\"\n                       src=\"<?php echo $latestVideo->getVideoLink() ?>\"></video>\n            </div>\n            <div class=\"card-body\">\n                <h6 class=\"card-title\"><?php echo $latestVideo->title ?></h6>\n                <p class=\"card-text\">\n                    Likes: <?php echo $latestVideo->getLikes()->count() ?><br>\n                    Views: <?php echo $latestVideo->getViews()->count() ?>\n                </p>\n                <a href=\"<?php echo \\yii\\helpers\\Url::to(['/video/update',\n                    'id' => $latestVideo->video_id]) ?>\"\n                   class=\"btn btn-primary\">\n                    Edit\n                </a>\n            </div>\n        <?php else: ?>\n            <div class=\"card-body\">\n                You don't have uploaded videos yet\n            </div>\n        <?php endif; ?>\n    </div>\n    <div class=\"card m-2\" style=\"width: 14rem;\">\n        <div class=\"card-body\">\n            <h6 class=\"card-title\">Total Views</h6>\n            <p class=\"card-text\" style=\"font-size: 48px\">\n                <?php echo $numberOfView ?>\n            </p>\n        </div>\n    </div>\n    <div class=\"card m-2\" style=\"width: 14rem;\">\n        <div class=\"card-body\">\n            <h6 class=\"card-title\">Total Subscribers</h6>\n            <p class=\"card-text\" style=\"font-size: 48px\">\n                <?php echo $numberOfSubscribers ?>\n            </p>\n        </div>\n    </div>\n    <div class=\"card m-2\" style=\"width: 14rem;\">\n        <div class=\"card-body\">\n            <h6 class=\"card-title\">Latest Subscribers</h6>\n            <?php foreach ($subscribers as $subscriber): ?>\n                <div>\n                    <?php echo $subscriber->user->username ?>\n                </div>\n            <?php endforeach; ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "backend/views/site/login.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\common\\models\\LoginForm */\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n$this->title = 'Login';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"site-login\">\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>Please fill out the following fields to login:</p>\n\n    <div class=\"row\">\n        <div class=\"col-lg-5\">\n            <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n                <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>\n\n                <?= $form->field($model, 'password')->passwordInput() ?>\n\n                <?= $form->field($model, 'rememberMe')->checkbox() ?>\n\n                <div class=\"form-group\">\n                    <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>\n                </div>\n\n            <?php ActiveForm::end(); ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "backend/views/video/_form.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Video */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n\n\\backend\\assets\\TagsInputAsset::register($this);\n?>\n\n<div class=\"video-form\">\n\n    <?php $form = ActiveForm::begin([\n        'options' => ['enctype' => 'multipart/form-data']\n    ]); ?>\n\n    <div class=\"row\">\n        <div class=\"col-sm-8\">\n\n            <?php echo $form->errorSummary($model) ?>\n\n            <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>\n\n            <?= $form->field($model, 'description')->textarea(['rows' => 6]) ?>\n\n            <div class=\"form-group\">\n                <label><?php echo $model->getAttributeLabel('thumbnail') ?></label>\n                <div class=\"custom-file\">\n                    <input type=\"file\" class=\"custom-file-input\"\n                           id=\"thumbnail\" name=\"thumbnail\">\n                    <label class=\"custom-file-label\" for=\"thumbnail\">Choose file</label>\n                </div>\n            </div>\n\n            <?= $form->field($model, 'tags', [\n                'inputOptions' => ['data-role' => 'tagsinput']\n            ])->textInput(['maxlength' => true]) ?>\n\n        </div>\n        <div class=\"col-sm-4\">\n\n            <div class=\"embed-responsive embed-responsive-16by9 mb-3\">\n                <video class=\"embed-responsive-item\"\n                       poster=\"<?php echo $model->getThumbnailLink() ?>\"\n                       src=\"<?php echo $model->getVideoLink() ?>\"\n                       controls></video>\n            </div>\n\n            <div class=\"mb-3\">\n                <div class=\"text-muted\">Video Link</div>\n                <a href=\"<?php echo $model->getVideoLink() ?>\">\n                    Open Video\n                </a>\n            </div>\n\n            <div class=\"mb-3\">\n                <div class=\"text-muted\">Video Name</div>\n                <?php echo $model->video_name ?>\n            </div>\n\n            <?= $form->field($model, 'status')->dropDownList($model->getStatusLabels()) ?>\n        </div>\n    </div>\n    <div class=\"form-group\">\n        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>\n    </div>\n\n    <?php ActiveForm::end(); ?>\n\n</div>\n"
  },
  {
    "path": "backend/views/video/_video_item.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:23 AM\n */\n\n/** @var $model \\common\\models\\Video */\n\nuse \\yii\\helpers\\StringHelper;\nuse yii\\helpers\\Url;\n\n?>\n\n<div class=\"media\">\n    <a href=\"<?php echo Url::to(['/video/update', 'id' => $model->video_id]) ?>\">\n        <div class=\"embed-responsive embed-responsive-16by9 mr-2\"\n             style=\"width: 120px\">\n            <video class=\"embed-responsive-item\"\n                   poster=\"<?php echo $model->getThumbnailLink() ?>\"\n                   src=\"<?php echo $model->getVideoLink() ?>\"></video>\n        </div>\n    </a>\n    <div class=\"media-body\">\n        <h6 class=\"mt-0\"><?php echo $model->title ?></h6>\n        <?php echo StringHelper::truncateWords($model->description, 10) ?>\n    </div>\n</div>\n"
  },
  {
    "path": "backend/views/video/create.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Video */\n\n$this->title = 'Create Video';\n$this->params['breadcrumbs'][] = ['label' => 'Videos', 'url' => ['index']];\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"video-create\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <div class=\"d-flex flex-column justify-content-center align-items-center\">\n\n        <div class=\"upload-icon\">\n            <i class=\"fas fa-upload\"></i>\n        </div>\n        <br>\n\n        <p class=\"m-0\">Drag and drop a file you want to upload\n        <p>\n\n        <p class=\"text-muted\">Your video will be private until you publish it</p>\n\n        <?php $form = \\yii\\bootstrap4\\ActiveForm::begin([\n            'options' => ['enctype' => 'multipart/form-data']\n        ]) ?>\n\n        <?php echo $form->errorSummary($model) ?>\n\n        <button class=\"btn btn-primary btn-file\">\n            Select File\n            <input type=\"file\" id=\"videoFile\" name=\"video\">\n        </button>\n        <?php \\yii\\bootstrap4\\ActiveForm::end() ?>\n    </div>\n\n</div>\n"
  },
  {
    "path": "backend/views/video/index.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\grid\\GridView;\n\n/* @var $this yii\\web\\View */\n/* @var $dataProvider yii\\data\\ActiveDataProvider */\n\n$this->title = 'Videos';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"video-index\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>\n        <?= Html::a('Create Video', ['create'], ['class' => 'btn btn-success']) ?>\n    </p>\n\n\n    <?= GridView::widget([\n        'dataProvider' => $dataProvider,\n        'columns' => [\n            ['class' => 'yii\\grid\\SerialColumn'],\n\n            [\n                'attribute' => 'title',\n                'content' => function ($model) {\n                    return $this->render('_video_item', ['model' => $model]);\n                }\n            ],\n            [\n                'attribute' => 'status',\n                'content' => function ($model) {\n                    return $model->getStatusLabels()[$model->status];\n                }\n            ],\n            //'has_thumbnail',\n            //'video_name',\n            'created_at:datetime',\n            'updated_at:datetime',\n            //'created_by',\n\n            [\n                'class' => 'yii\\grid\\ActionColumn',\n                'buttons' => [\n                    'delete' => function ($url) {\n                        return Html::a('Delete', $url, [\n                            'data-method' => 'post',\n                            'data-confirm' => 'Are you sure?'\n                        ]);\n                    }\n                ]\n            ],\n        ],\n    ]); ?>\n\n\n</div>\n"
  },
  {
    "path": "backend/views/video/update.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Video */\n\n$this->title = 'Update Video: ' . $model->title;\n$this->params['breadcrumbs'][] = ['label' => 'Videos', 'url' => ['index']];\n$this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->video_id]];\n$this->params['breadcrumbs'][] = 'Update';\n?>\n<div class=\"video-update\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <?= $this->render('_form', [\n        'model' => $model,\n    ]) ?>\n\n</div>\n"
  },
  {
    "path": "backend/web/app.js",
    "content": "/**\n * Created by TheCodeholic on 4/17/2020.\n */\n$(function () {\n  'use strict';\n  $('#videoFile').change(ev => {\n    $(ev.target).closest('form').trigger('submit');\n  })\n});"
  },
  {
    "path": "backend/web/assets/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "backend/web/css/site.css",
    "content": "html,\nbody {\n    height: 100%;\n}\n\nmain {\n    flex: 1;\n}\n.content-wrapper{\n    flex: 1;\n}\naside{\n    min-width: 200px;\n}\naside .nav-pills .nav-link {\n    border-radius: 0;\n    color: #444444;\n}\n\n\naside .nav-pills .nav-link:hover {\n    background: rgba(0, 0, 0, 0.05);\n}\n\naside .nav-pills .nav-link.active {\n    background: rgba(0, 0, 0, 0.05);\n    color: #b90000;\n    border-left: 4px solid #b90000;\n}\n\n\n.upload-icon {\n    width: 200px;\n    height: 200px;\n    border-radius: 50%;\n    background: #e0e0e0;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 70px;\n    color: #454545;\n}\n\n.btn-file {\n    position: relative;\n}\n.btn-file input {\n    position: absolute;\n    left: 0;\n    right: 0;\n    top: 0;\n    bottom: 0;\n    width: 100%;\n    height: 100%;\n    opacity: 0;\n}\n\n.comment-wrapper {\n    flex: 1;\n}\n\n.comment-wrapper .dropdown-toggle::after {\n    content: none;\n}"
  },
  {
    "path": "backend/web/tagsinput/tagsinput.css",
    "content": "/*\n * bootstrap-tagsinput v0.8.0\n * \n */\n\n.bootstrap-tagsinput {\n  background-color: #fff;\n  border: 1px solid #ccc;\n  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n  display: inline-block;\n  padding: 4px 6px;\n  color: #555;\n  vertical-align: middle;\n  border-radius: 4px;\n  width: 100%;\n  line-height: 22px;\n  cursor: text;\n}\n.bootstrap-tagsinput input {\n  border: none;\n  box-shadow: none;\n  outline: none;\n  background-color: transparent;\n  padding: 0 6px;\n  margin: 0;\n  width: auto;\n  max-width: inherit;\n}\n.bootstrap-tagsinput.form-control input::-moz-placeholder {\n  color: #777;\n  opacity: 1;\n}\n.bootstrap-tagsinput.form-control input:-ms-input-placeholder {\n  color: #777;\n}\n.bootstrap-tagsinput.form-control input::-webkit-input-placeholder {\n  color: #777;\n}\n.bootstrap-tagsinput input:focus {\n  border: none;\n  box-shadow: none;\n}\n.bootstrap-tagsinput .badge {\n  margin: 2px 0;\n  padding:5px 8px;\n}\n.bootstrap-tagsinput .badge [data-role=\"remove\"] {\n  margin-left: 8px;\n  cursor: pointer;\n}\n.bootstrap-tagsinput .badge [data-role=\"remove\"]:after {\n  content: \"×\";\n  padding: 0px 4px;\n  background-color:rgba(0, 0, 0, 0.1);\n  border-radius:50%;\n  font-size:13px\n}\n.bootstrap-tagsinput .badge [data-role=\"remove\"]:hover:after {\n\n  background-color:rgba(0, 0, 0, 0.62);}\n.bootstrap-tagsinput .badge [data-role=\"remove\"]:hover:active {\n  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n\n.tt-menu {\n    position: absolute;\n    top: 100%;\n    left: 0;\n    z-index: 1000;\n    display: none;\n    float: left;\n    min-width: 160px;\n    padding: 5px 0;\n    margin: 2px 0 0;\n    list-style: none;\n    font-size: 14px;\n    background-color: #ffffff;\n    border: 1px solid #cccccc;\n    border: 1px solid rgba(0, 0, 0, 0.15);\n    border-radius: 4px;\n    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n    background-clip: padding-box;\n    cursor: pointer;\n}\n\n.tt-suggestion {\n    display: block;\n    padding: 3px 20px;\n    clear: both;\n    font-weight: normal;\n    line-height: 1.428571429;\n    color: #333333;\n    white-space: nowrap;\n}\n\n.tt-suggestion:hover,\n.tt-suggestion:focus {\n  color: #ffffff;\n  text-decoration: none;\n  outline: 0;\n  background-color: #428bca;\n}\n"
  },
  {
    "path": "backend/web/tagsinput/tagsinput.js",
    "content": "/*\n * bootstrap-tagsinput v0.8.0\n * \n */\n\n(function ($) {\n  \"use strict\";\n\n  var defaultOptions = {\n    tagClass: function(item) {\n      return 'badge badge-info';\n    },\n    focusClass: 'focus',\n    itemValue: function(item) {\n      return item ? item.toString() : item;\n    },\n    itemText: function(item) {\n      return this.itemValue(item);\n    },\n    itemTitle: function(item) {\n      return null;\n    },\n    freeInput: true,\n    addOnBlur: true,\n    maxTags: undefined,\n    maxChars: undefined,\n    confirmKeys: [13, 44],\n    delimiter: ',',\n    delimiterRegex: null,\n    cancelConfirmKeysOnEmpty: false,\n    onTagExists: function(item, $tag) {\n      $tag.hide().fadeIn();\n    },\n    trimValue: false,\n    allowDuplicates: false,\n    triggerChange: true,\n    editOnBackspace: false\n  };\n\n  /**\n   * Constructor function\n   */\n  function TagsInput(element, options) {\n    this.isInit = true;\n    this.itemsArray = [];\n\n    this.$element = $(element);\n    this.$element.addClass('sr-only');\n\n    this.isSelect = (element.tagName === 'SELECT');\n    this.multiple = (this.isSelect && element.hasAttribute('multiple'));\n    this.objectItems = options && options.itemValue;\n    this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';\n    this.inputSize = Math.max(1, this.placeholderText.length);\n\n    this.$container = $('<div class=\"bootstrap-tagsinput\"></div>');\n    this.$input = $('<input type=\"text\" placeholder=\"' + this.placeholderText + '\"/>').appendTo(this.$container);\n\n    this.$element.before(this.$container);\n\n    this.build(options);\n    this.isInit = false;\n  }\n\n  TagsInput.prototype = {\n    constructor: TagsInput,\n\n    /**\n     * Adds the given item as a new tag. Pass true to dontPushVal to prevent\n     * updating the elements val()\n     */\n    add: function(item, dontPushVal, options) {\n      var self = this;\n\n      if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)\n        return;\n\n      // Ignore falsey values, except false\n      if (item !== false && !item)\n        return;\n\n      // Trim value\n      if (typeof item === \"string\" && self.options.trimValue) {\n        item = $.trim(item);\n      }\n\n      // Throw an error when trying to add an object while the itemValue option was not set\n      if (typeof item === \"object\" && !self.objectItems)\n        throw(\"Can't add objects when itemValue option is not set\");\n\n      // Ignore strings only containg whitespace\n      if (item.toString().match(/^\\s*$/))\n        return;\n\n      // If SELECT but not multiple, remove current tag\n      if (self.isSelect && !self.multiple && self.itemsArray.length > 0)\n        self.remove(self.itemsArray[0]);\n\n      if (typeof item === \"string\" && this.$element[0].tagName === 'INPUT') {\n        var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;\n        var items = item.split(delimiter);\n        if (items.length > 1) {\n          for (var i = 0; i < items.length; i++) {\n            this.add(items[i], true);\n          }\n\n          if (!dontPushVal)\n            self.pushVal(self.options.triggerChange);\n          return;\n        }\n      }\n\n      var itemValue = self.options.itemValue(item),\n          itemText = self.options.itemText(item),\n          tagClass = self.options.tagClass(item),\n          itemTitle = self.options.itemTitle(item);\n\n      // Ignore items allready added\n      var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];\n      if (existing && !self.options.allowDuplicates) {\n        // Invoke onTagExists\n        if (self.options.onTagExists) {\n          var $existingTag = $(\".badge\", self.$container).filter(function() { return $(this).data(\"item\") === existing; });\n          self.options.onTagExists(item, $existingTag);\n        }\n        return;\n      }\n\n      // if length greater than limit\n      if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)\n        return;\n\n      // raise beforeItemAdd arg\n      var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});\n      self.$element.trigger(beforeItemAddEvent);\n      if (beforeItemAddEvent.cancel)\n        return;\n\n      // register item in internal array and map\n      self.itemsArray.push(item);\n\n      // add a tag element\n\n      var $tag = $('<span class=\"' + htmlEncode(tagClass) + (itemTitle !== null ? ('\" title=\"' + itemTitle) : '') + '\">' + htmlEncode(itemText) + '<span data-role=\"remove\"></span></span>');\n      $tag.data('item', item);\n      self.findInputWrapper().before($tag);\n\n      // Check to see if the tag exists in its raw or uri-encoded form\n      var optionExists = (\n        $('option[value=\"' + encodeURIComponent(itemValue).replace(/\"/g, '\\\\\"') + '\"]', self.$element).length ||\n        $('option[value=\"' + htmlEncode(itemValue).replace(/\"/g, '\\\\\"') + '\"]', self.$element).length\n      );\n\n      // add <option /> if item represents a value not present in one of the <select />'s options\n      if (self.isSelect && !optionExists) {\n        var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');\n        $option.data('item', item);\n        $option.attr('value', itemValue);\n        self.$element.append($option);\n      }\n\n      if (!dontPushVal)\n        self.pushVal(self.options.triggerChange);\n\n      // Add class when reached maxTags\n      if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)\n        self.$container.addClass('bootstrap-tagsinput-max');\n\n      // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.\n      if ($('.typeahead, .twitter-typeahead', self.$container).length) {\n        self.$input.typeahead('val', '');\n      }\n\n      if (this.isInit) {\n        self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));\n      } else {\n        self.$element.trigger($.Event('itemAdded', { item: item, options: options }));\n      }\n    },\n\n    /**\n     * Removes the given item. Pass true to dontPushVal to prevent updating the\n     * elements val()\n     */\n    remove: function(item, dontPushVal, options) {\n      var self = this;\n\n      if (self.objectItems) {\n        if (typeof item === \"object\")\n          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  self.options.itemValue(item); } );\n        else\n          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  item; } );\n\n        item = item[item.length-1];\n      }\n\n      if (item) {\n        var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });\n        self.$element.trigger(beforeItemRemoveEvent);\n        if (beforeItemRemoveEvent.cancel)\n          return;\n\n        $('.badge', self.$container).filter(function() { return $(this).data('item') === item; }).remove();\n        $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();\n        if($.inArray(item, self.itemsArray) !== -1)\n          self.itemsArray.splice($.inArray(item, self.itemsArray), 1);\n      }\n\n      if (!dontPushVal)\n        self.pushVal(self.options.triggerChange);\n\n      // Remove class when reached maxTags\n      if (self.options.maxTags > self.itemsArray.length)\n        self.$container.removeClass('bootstrap-tagsinput-max');\n\n      self.$element.trigger($.Event('itemRemoved',  { item: item, options: options }));\n    },\n\n    /**\n     * Removes all items\n     */\n    removeAll: function() {\n      var self = this;\n\n      $('.badge', self.$container).remove();\n      $('option', self.$element).remove();\n\n      while(self.itemsArray.length > 0)\n        self.itemsArray.pop();\n\n      self.pushVal(self.options.triggerChange);\n    },\n\n    /**\n     * Refreshes the tags so they match the text/value of their corresponding\n     * item.\n     */\n    refresh: function() {\n      var self = this;\n      $('.badge', self.$container).each(function() {\n        var $tag = $(this),\n            item = $tag.data('item'),\n            itemValue = self.options.itemValue(item),\n            itemText = self.options.itemText(item),\n            tagClass = self.options.tagClass(item);\n\n          // Update tag's class and inner text\n          $tag.attr('class', null);\n          $tag.addClass('badge ' + htmlEncode(tagClass));\n          $tag.contents().filter(function() {\n            return this.nodeType == 3;\n          })[0].nodeValue = htmlEncode(itemText);\n\n          if (self.isSelect) {\n            var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });\n            option.attr('value', itemValue);\n          }\n      });\n    },\n\n    /**\n     * Returns the items added as tags\n     */\n    items: function() {\n      return this.itemsArray;\n    },\n\n    /**\n     * Assembly value by retrieving the value of each item, and set it on the\n     * element.\n     */\n    pushVal: function() {\n      var self = this,\n          val = $.map(self.items(), function(item) {\n            return self.options.itemValue(item).toString();\n          });\n\n      self.$element.val( val.join(self.options.delimiter) );\n\n      if (self.options.triggerChange)\n        self.$element.trigger('change');\n    },\n\n    /**\n     * Initializes the tags input behaviour on the element\n     */\n    build: function(options) {\n      var self = this;\n\n      self.options = $.extend({}, defaultOptions, options);\n      // When itemValue is set, freeInput should always be false\n      if (self.objectItems)\n        self.options.freeInput = false;\n\n      makeOptionItemFunction(self.options, 'itemValue');\n      makeOptionItemFunction(self.options, 'itemText');\n      makeOptionFunction(self.options, 'tagClass');\n\n      // Typeahead Bootstrap version 2.3.2\n      if (self.options.typeahead) {\n        var typeahead = self.options.typeahead || {};\n\n        makeOptionFunction(typeahead, 'source');\n\n        self.$input.typeahead($.extend({}, typeahead, {\n          source: function (query, process) {\n            function processItems(items) {\n              var texts = [];\n\n              for (var i = 0; i < items.length; i++) {\n                var text = self.options.itemText(items[i]);\n                map[text] = items[i];\n                texts.push(text);\n              }\n              process(texts);\n            }\n\n            this.map = {};\n            var map = this.map,\n                data = typeahead.source(query);\n\n            if ($.isFunction(data.success)) {\n              // support for Angular callbacks\n              data.success(processItems);\n            } else if ($.isFunction(data.then)) {\n              // support for Angular promises\n              data.then(processItems);\n            } else {\n              // support for functions and jquery promises\n              $.when(data)\n               .then(processItems);\n            }\n          },\n          updater: function (text) {\n            self.add(this.map[text]);\n            return this.map[text];\n          },\n          matcher: function (text) {\n            return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);\n          },\n          sorter: function (texts) {\n            return texts.sort();\n          },\n          highlighter: function (text) {\n            var regex = new RegExp( '(' + this.query + ')', 'gi' );\n            return text.replace( regex, \"<strong>$1</strong>\" );\n          }\n        }));\n      }\n\n      // typeahead.js\n      if (self.options.typeaheadjs) {\n        // Determine if main configurations were passed or simply a dataset\n        var typeaheadjs = self.options.typeaheadjs;\n        if (!$.isArray(typeaheadjs)) {\n            typeaheadjs = [null, typeaheadjs];\n        }\n\n        $.fn.typeahead.apply(self.$input, typeaheadjs).on('typeahead:selected', $.proxy(function (obj, datum, name) {\n          var index = 0;\n          typeaheadjs.some(function(dataset, _index) {\n            if (dataset.name === name) {\n              index = _index;\n              return true;\n            }\n            return false;\n          });\n\n          // @TODO Dep: https://github.com/corejavascript/typeahead.js/issues/89\n          if (typeaheadjs[index].valueKey) {\n            self.add(datum[typeaheadjs[index].valueKey]);\n          } else {\n            self.add(datum);\n          }\n\n          self.$input.typeahead('val', '');\n        }, self));\n      }\n\n      self.$container.on('click', $.proxy(function(event) {\n        if (! self.$element.attr('disabled')) {\n          self.$input.removeAttr('disabled');\n        }\n        self.$input.focus();\n      }, self));\n\n        if (self.options.addOnBlur && self.options.freeInput) {\n          self.$input.on('focusout', $.proxy(function(event) {\n              // HACK: only process on focusout when no typeahead opened, to\n              //       avoid adding the typeahead text as tag\n              if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {\n                self.add(self.$input.val());\n                self.$input.val('');\n              }\n          }, self));\n        }\n\n      // Toggle the 'focus' css class on the container when it has focus\n      self.$container.on({\n        focusin: function() {\n          self.$container.addClass(self.options.focusClass);\n        },\n        focusout: function() {\n          self.$container.removeClass(self.options.focusClass);\n        },\n      });\n\n      self.$container.on('keydown', 'input', $.proxy(function(event) {\n        var $input = $(event.target),\n            $inputWrapper = self.findInputWrapper();\n\n        if (self.$element.attr('disabled')) {\n          self.$input.attr('disabled', 'disabled');\n          return;\n        }\n\n        switch (event.which) {\n          // BACKSPACE\n          case 8:\n            if (doGetCaretPosition($input[0]) === 0) {\n              var prev = $inputWrapper.prev();\n              if (prev.length) {\n                if (self.options.editOnBackspace === true) {\n                  $input.val(prev.data('item'));\n                }\n                self.remove(prev.data('item'));\n              }\n            }\n            break;\n\n          // DELETE\n          case 46:\n            if (doGetCaretPosition($input[0]) === 0) {\n              var next = $inputWrapper.next();\n              if (next.length) {\n                self.remove(next.data('item'));\n              }\n            }\n            break;\n\n          // LEFT ARROW\n          case 37:\n            // Try to move the input before the previous tag\n            var $prevTag = $inputWrapper.prev();\n            if ($input.val().length === 0 && $prevTag[0]) {\n              $prevTag.before($inputWrapper);\n              $input.focus();\n            }\n            break;\n          // RIGHT ARROW\n          case 39:\n            // Try to move the input after the next tag\n            var $nextTag = $inputWrapper.next();\n            if ($input.val().length === 0 && $nextTag[0]) {\n              $nextTag.after($inputWrapper);\n              $input.focus();\n            }\n            break;\n         default:\n             // ignore\n         }\n\n        // Reset internal input's size\n        var textLength = $input.val().length,\n            wordSpace = Math.ceil(textLength / 5),\n            size = textLength + wordSpace + 1;\n        $input.attr('size', Math.max(this.inputSize, size));\n      }, self));\n\n      self.$container.on('keypress', 'input', $.proxy(function(event) {\n         var $input = $(event.target);\n\n         if (self.$element.attr('disabled')) {\n            self.$input.attr('disabled', 'disabled');\n            return;\n         }\n\n         var text = $input.val(),\n         maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;\n         if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {\n            // Only attempt to add a tag if there is data in the field\n            if (text.length !== 0) {\n               self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);\n               $input.val('');\n            }\n\n            // If the field is empty, let the event triggered fire as usual\n            if (self.options.cancelConfirmKeysOnEmpty === false) {\n                event.preventDefault();\n            }\n         }\n\n         // Reset internal input's size\n         var textLength = $input.val().length,\n            wordSpace = Math.ceil(textLength / 5),\n            size = textLength + wordSpace + 1;\n         $input.attr('size', Math.max(this.inputSize, size));\n      }, self));\n\n      // Remove icon clicked\n      self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {\n        if (self.$element.attr('disabled')) {\n          return;\n        }\n        self.remove($(event.target).closest('.badge').data('item'));\n      }, self));\n\n      // Only add existing value as tags when using strings as tags\n      if (self.options.itemValue === defaultOptions.itemValue) {\n        if (self.$element[0].tagName === 'INPUT') {\n            self.add(self.$element.val());\n        } else {\n          $('option', self.$element).each(function() {\n            self.add($(this).attr('value'), true);\n          });\n        }\n      }\n    },\n\n    /**\n     * Removes all tagsinput behaviour and unregsiter all event handlers\n     */\n    destroy: function() {\n      var self = this;\n\n      // Unbind events\n      self.$container.off('keypress', 'input');\n      self.$container.off('click', '[role=remove]');\n\n      self.$container.remove();\n      self.$element.removeData('tagsinput');\n      self.$element.show();\n    },\n\n    /**\n     * Sets focus on the tagsinput\n     */\n    focus: function() {\n      this.$input.focus();\n    },\n\n    /**\n     * Returns the internal input element\n     */\n    input: function() {\n      return this.$input;\n    },\n\n    /**\n     * Returns the element which is wrapped around the internal input. This\n     * is normally the $container, but typeahead.js moves the $input element.\n     */\n    findInputWrapper: function() {\n      var elt = this.$input[0],\n          container = this.$container[0];\n      while(elt && elt.parentNode !== container)\n        elt = elt.parentNode;\n\n      return $(elt);\n    }\n  };\n\n  /**\n   * Register JQuery plugin\n   */\n  $.fn.tagsinput = function(arg1, arg2, arg3) {\n    var results = [];\n\n    this.each(function() {\n      var tagsinput = $(this).data('tagsinput');\n      // Initialize a new tags input\n      if (!tagsinput) {\n          tagsinput = new TagsInput(this, arg1);\n          $(this).data('tagsinput', tagsinput);\n          results.push(tagsinput);\n\n          if (this.tagName === 'SELECT') {\n              $('option', $(this)).attr('selected', 'selected');\n          }\n\n          // Init tags from $(this).val()\n          $(this).val($(this).val());\n      } else if (!arg1 && !arg2) {\n          // tagsinput already exists\n          // no function, trying to init\n          results.push(tagsinput);\n      } else if(tagsinput[arg1] !== undefined) {\n          // Invoke function on existing tags input\n            if(tagsinput[arg1].length === 3 && arg3 !== undefined){\n               var retVal = tagsinput[arg1](arg2, null, arg3);\n            }else{\n               var retVal = tagsinput[arg1](arg2);\n            }\n          if (retVal !== undefined)\n              results.push(retVal);\n      }\n    });\n\n    if ( typeof arg1 == 'string') {\n      // Return the results from the invoked function calls\n      return results.length > 1 ? results : results[0];\n    } else {\n      return results;\n    }\n  };\n\n  $.fn.tagsinput.Constructor = TagsInput;\n\n  /**\n   * Most options support both a string or number as well as a function as\n   * option value. This function makes sure that the option with the given\n   * key in the given options is wrapped in a function\n   */\n  function makeOptionItemFunction(options, key) {\n    if (typeof options[key] !== 'function') {\n      var propertyName = options[key];\n      options[key] = function(item) { return item[propertyName]; };\n    }\n  }\n  function makeOptionFunction(options, key) {\n    if (typeof options[key] !== 'function') {\n      var value = options[key];\n      options[key] = function() { return value; };\n    }\n  }\n  /**\n   * HtmlEncodes the given value\n   */\n  var htmlEncodeContainer = $('<div />');\n  function htmlEncode(value) {\n    if (value) {\n      return htmlEncodeContainer.text(value).html();\n    } else {\n      return '';\n    }\n  }\n\n  /**\n   * Returns the position of the caret in the given input field\n   * http://flightschool.acylt.com/devnotes/caret-position-woes/\n   */\n  function doGetCaretPosition(oField) {\n    var iCaretPos = 0;\n    if (document.selection) {\n      oField.focus ();\n      var oSel = document.selection.createRange();\n      oSel.moveStart ('character', -oField.value.length);\n      iCaretPos = oSel.text.length;\n    } else if (oField.selectionStart || oField.selectionStart == '0') {\n      iCaretPos = oField.selectionStart;\n    }\n    return (iCaretPos);\n  }\n\n  /**\n    * Returns boolean indicates whether user has pressed an expected key combination.\n    * @param object keyPressEvent: JavaScript event object, refer\n    *     http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\n    * @param object lookupList: expected key combinations, as in:\n    *     [13, {which: 188, shiftKey: true}]\n    */\n  function keyCombinationInList(keyPressEvent, lookupList) {\n      var found = false;\n      $.each(lookupList, function (index, keyCombination) {\n          if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {\n              found = true;\n              return false;\n          }\n\n          if (keyPressEvent.which === keyCombination.which) {\n              var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,\n                  shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,\n                  ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;\n              if (alt && shift && ctrl) {\n                  found = true;\n                  return false;\n              }\n          }\n      });\n\n      return found;\n  }\n\n  /**\n   * Initialize tagsinput behaviour on inputs and selects which have\n   * data-role=tagsinput\n   */\n  $(function() {\n    $(\"input[data-role=tagsinput], select[multiple][data-role=tagsinput]\").tagsinput();\n  });\n})(window.jQuery);\n"
  },
  {
    "path": "codeception.yml",
    "content": "# global codeception file to run tests from all apps\ninclude:\n    - common\n    - frontend\n    - backend\npaths:\n    log: console/runtime/logs\nsettings:\n    colors: true"
  },
  {
    "path": "common/codeception.yml",
    "content": "namespace: common\\tests\nactor_suffix: Tester\npaths:\n    tests: tests\n    output: tests/_output\n    data: tests/_data\n    support: tests/_support\nbootstrap: _bootstrap.php\nsettings:\n    colors: true\n    memory_limit: 1024M\nmodules:\n    config:\n        Yii2:\n            configFile: 'config/codeception-local.php'\n"
  },
  {
    "path": "common/config/.gitignore",
    "content": "codeception-local.php\nmain-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "common/config/bootstrap.php",
    "content": "<?php\nYii::setAlias('@common', dirname(__DIR__));\nYii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');\nYii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');\nYii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');\n"
  },
  {
    "path": "common/config/main.php",
    "content": "<?php\nreturn [\n    'name' => 'FreeCodeTube',\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',\n    'components' => [\n        'cache' => [\n            'class' => 'yii\\caching\\FileCache',\n        ],\n    ],\n];\n"
  },
  {
    "path": "common/config/params.php",
    "content": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n    'supportEmail' => 'support@example.com',\n    'senderEmail' => 'noreply@example.com',\n    'senderName' => 'Example.com mailer',\n    'user.passwordResetTokenExpire' => 3600,\n];\n"
  },
  {
    "path": "common/config/test.php",
    "content": "<?php\nreturn [\n    'id' => 'app-common-tests',\n    'basePath' => dirname(__DIR__),\n    'components' => [\n        'user' => [\n            'class' => 'yii\\web\\User',\n            'identityClass' => 'common\\models\\User',\n        ],\n    ],\n];\n"
  },
  {
    "path": "common/fixtures/UserFixture.php",
    "content": "<?php\nnamespace common\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $modelClass = 'common\\models\\User';\n}"
  },
  {
    "path": "common/helpers/Html.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 10:18 AM\n */\n\nnamespace common\\helpers;\n\n\nuse yii\\helpers\\Url;\n\n/**\n * Class Html\n *\n * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n * @package common\\helpers\n */\nclass Html\n{\n    public static function channelLink($user, $schema = false)\n    {\n        return \\yii\\helpers\\Html::a($user->username,\n            Url::to(['/channel/view', 'username' => $user->username], $schema),\n            ['class' => 'text-dark channel-link']);\n    }\n}"
  },
  {
    "path": "common/mail/emailVerify-html.php",
    "content": "<?php\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$verifyLink = Yii::$app->urlManager->createAbsoluteUrl(['site/verify-email', 'token' => $user->verification_token]);\n?>\n<div class=\"verify-email\">\n    <p>Hello <?= Html::encode($user->username) ?>,</p>\n\n    <p>Follow the link below to verify your email:</p>\n\n    <p><?= Html::a(Html::encode($verifyLink), $verifyLink) ?></p>\n</div>\n"
  },
  {
    "path": "common/mail/emailVerify-text.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$verifyLink = Yii::$app->urlManager->createAbsoluteUrl(['site/verify-email', 'token' => $user->verification_token]);\n?>\nHello <?= $user->username ?>,\n\nFollow the link below to verify your email:\n\n<?= $verifyLink ?>\n"
  },
  {
    "path": "common/mail/layouts/html.php",
    "content": "<?php\nuse yii\\helpers\\Html;\n\n/* @var $this \\yii\\web\\View view component instance */\n/* @var $message \\yii\\mail\\MessageInterface the message being composed */\n/* @var $content string main view render result */\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=<?= Yii::$app->charset ?>\" />\n    <title><?= Html::encode($this->title) ?></title>\n    <?php $this->head() ?>\n</head>\n<body>\n    <?php $this->beginBody() ?>\n    <?= $content ?>\n    <?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n"
  },
  {
    "path": "common/mail/layouts/text.php",
    "content": "<?php\n\nuse yii\\helpers\\Html;\n\n/** @var \\yii\\web\\View $this view component instance */\n/** @var \\yii\\mail\\MessageInterface $message the message being composed */\n/** @var string $content main view render result */\n?>\n\n<?php $this->beginPage() ?>\n<?php $this->beginBody() ?>\n<?= $content ?>\n<?php $this->endBody() ?>\n<?php $this->endPage() ?>\n"
  },
  {
    "path": "common/mail/mention-html.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/15/2020\n * Time: 4:52 PM\n */\n/** @var $channel \\common\\models\\User */\n/** @var $user \\common\\models\\User */\n/** @var $comment string */\n?>\n\n<p>Hello <?php echo $channel->username ?></p>\n<p>User <?php echo \\common\\helpers\\Html::channelLink($user, true) ?>\n    has mention you in the following comment</p>\n\n<blockquote>\n    <?php echo $comment ?>\n</blockquote>\n\n<p>FreeCodeTube team</p>\n"
  },
  {
    "path": "common/mail/mention-text.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/15/2020\n * Time: 4:52 PM\n */\n?>\n<?php\n/**\n * User: TheCodeholic\n * Date: 11/15/2020\n * Time: 4:52 PM\n */\n/** @var $channel \\common\\models\\User */\n/** @var $user \\common\\models\\User */\n/** @var $comment string */\n?>\n\nHello <?php echo $channel->username ?>\nUser <?php echo \\common\\helpers\\Html::channelLink($user, true) ?>\n    has mention you in the following comment\n\n<?php echo $comment ?>\n\nFreeCodeTube team\n\n"
  },
  {
    "path": "common/mail/passwordResetToken-html.php",
    "content": "<?php\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);\n?>\n<div class=\"password-reset\">\n    <p>Hello <?= Html::encode($user->username) ?>,</p>\n\n    <p>Follow the link below to reset your password:</p>\n\n    <p><?= Html::a(Html::encode($resetLink), $resetLink) ?></p>\n</div>\n"
  },
  {
    "path": "common/mail/passwordResetToken-text.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);\n?>\nHello <?= $user->username ?>,\n\nFollow the link below to reset your password:\n\n<?= $resetLink ?>\n"
  },
  {
    "path": "common/mail/subscriber-html.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 10:27 AM\n */\n/** @var $channel \\common\\models\\User */\n/** @var $user \\common\\models\\User */\n?>\n\n<p>Hello <?php echo $channel->username ?></p>\n<p>User <?php echo \\common\\helpers\\Html::channelLink($user, true) ?>\n    has subscribed to you</p>\n\n<p>FreeCodeTube team</p>\n\n"
  },
  {
    "path": "common/mail/subscriber-text.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 10:27 AM\n */\n/** @var $channel \\common\\models\\User */\n/** @var $user \\common\\models\\User */\n?>\n\nHello <?php echo $channel->username ?>\nUser <?php echo $user->username ?> has subscribed to you\n\nFreeCodeTube team\n\n"
  },
  {
    "path": "common/models/Comment.php",
    "content": "<?php\n\nnamespace common\\models;\n\nuse Yii;\nuse yii\\behaviors\\BlameableBehavior;\nuse yii\\behaviors\\TimestampBehavior;\n\n/**\n * This is the model class for table \"{{%comment}}\".\n *\n * @property int $id\n * @property string $comment\n * @property string $video_id\n * @property int|null $parent_id\n * @property int|null $pinned\n * @property string $mention\n * @property int|null $created_at\n * @property int|null $updated_at\n * @property int|null $created_by\n *\n * @property User $createdBy\n * @property Comment $parent\n * @property Comment[] $comments\n * @property Video $video\n */\nclass Comment extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return '{{%comment}}';\n    }\n\n    public function behaviors()\n    {\n        return [\n            TimestampBehavior::class,\n            [\n                'class' => BlameableBehavior::class,\n                'updatedByAttribute' => false\n            ]\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [['comment', 'video_id'], 'required'],\n            [['comment'], 'string'],\n            [['parent_id', 'pinned', 'created_at', 'updated_at', 'created_by'], 'integer'],\n            [['video_id'], 'string', 'max' => 16],\n            [['mention'], 'string', 'max' => 255],\n            [['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['created_by' => 'id']],\n            [['parent_id'], 'exist', 'skipOnError' => true, 'targetClass' => Comment::className(), 'targetAttribute' => ['parent_id' => 'id']],\n            [['video_id'], 'exist', 'skipOnError' => true, 'targetClass' => Video::className(), 'targetAttribute' => ['video_id' => 'video_id']],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            'id' => 'ID',\n            'comment' => 'Comment',\n            'video_id' => 'Video ID',\n            'parent_id' => 'Parent ID',\n            'pinned' => 'Pinned',\n            'mention' => 'Mention',\n            'created_at' => 'Created At',\n            'updated_at' => 'Updated At',\n            'created_by' => 'Created By',\n        ];\n    }\n\n    /**\n     * Gets query for [[CreatedBy]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\UserQuery\n     */\n    public function getCreatedBy()\n    {\n        return $this->hasOne(User::className(), ['id' => 'created_by']);\n    }\n\n    /**\n     * Gets query for [[Parent]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\CommentQuery\n     */\n    public function getParent()\n    {\n        return $this->hasOne(Comment::className(), ['id' => 'parent_id']);\n    }\n\n    /**\n     * Gets query for [[Comments]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\CommentQuery\n     */\n    public function getComments()\n    {\n        return $this->hasMany(Comment::className(), ['parent_id' => 'id']);\n    }\n\n    /**\n     * Gets query for [[Video]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\VideoQuery\n     */\n    public function getVideo()\n    {\n        return $this->hasOne(Video::className(), ['video_id' => 'video_id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\query\\CommentQuery the active query used by this AR class.\n     */\n    public static function find()\n    {\n        return new \\common\\models\\query\\CommentQuery(get_called_class());\n    }\n\n    public function belongsTo($userId)\n    {\n        return $this->created_by === $userId;\n    }\n}\n"
  },
  {
    "path": "common/models/LoginForm.php",
    "content": "<?php\nnamespace common\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\n/**\n * Login form\n */\nclass LoginForm extends Model\n{\n    public $username;\n    public $password;\n    public $rememberMe = true;\n\n    private $_user;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            // username and password are both required\n            [['username', 'password'], 'required'],\n            // rememberMe must be a boolean value\n            ['rememberMe', 'boolean'],\n            // password is validated by validatePassword()\n            ['password', 'validatePassword'],\n        ];\n    }\n\n    /**\n     * Validates the password.\n     * This method serves as the inline validation for password.\n     *\n     * @param string $attribute the attribute currently being validated\n     * @param array $params the additional name-value pairs given in the rule\n     */\n    public function validatePassword($attribute, $params)\n    {\n        if (!$this->hasErrors()) {\n            $user = $this->getUser();\n            if (!$user || !$user->validatePassword($this->password)) {\n                $this->addError($attribute, 'Incorrect username or password.');\n            }\n        }\n    }\n\n    /**\n     * Logs in a user using the provided username and password.\n     *\n     * @return bool whether the user is logged in successfully\n     */\n    public function login()\n    {\n        if ($this->validate()) {\n            return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);\n        }\n        \n        return false;\n    }\n\n    /**\n     * Finds user by [[username]]\n     *\n     * @return User|null\n     */\n    protected function getUser()\n    {\n        if ($this->_user === null) {\n            $this->_user = User::findByUsername($this->username);\n        }\n\n        return $this->_user;\n    }\n}\n"
  },
  {
    "path": "common/models/Subscriber.php",
    "content": "<?php\n\nnamespace common\\models;\n\nuse Yii;\n\n/**\n * This is the model class for table \"{{%subscriber}}\".\n *\n * @property int $id\n * @property int|null $channel_id\n * @property int|null $user_id\n * @property int|null $created_at\n *\n * @property User $channel\n * @property User $user\n */\nclass Subscriber extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return '{{%subscriber}}';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [['channel_id', 'user_id', 'created_at'], 'integer'],\n            [['channel_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['channel_id' => 'id']],\n            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            'id' => 'ID',\n            'channel_id' => 'Channel ID',\n            'user_id' => 'User ID',\n            'created_at' => 'Created At',\n        ];\n    }\n\n    /**\n     * Gets query for [[Channel]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\UserQuery\n     */\n    public function getChannel()\n    {\n        return $this->hasOne(User::className(), ['id' => 'channel_id']);\n    }\n\n    /**\n     * Gets query for [[User]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\UserQuery\n     */\n    public function getUser()\n    {\n        return $this->hasOne(User::className(), ['id' => 'user_id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\query\\SubscriberQuery the active query used by this AR class.\n     */\n    public static function find()\n    {\n        return new \\common\\models\\query\\SubscriberQuery(get_called_class());\n    }\n}\n"
  },
  {
    "path": "common/models/User.php",
    "content": "<?php\nnamespace common\\models;\n\nuse Yii;\nuse yii\\base\\NotSupportedException;\nuse yii\\behaviors\\TimestampBehavior;\nuse yii\\db\\ActiveRecord;\nuse yii\\web\\IdentityInterface;\n\n/**\n * User model\n *\n * @property integer $id\n * @property string $username\n * @property string $password_hash\n * @property string $password_reset_token\n * @property string $verification_token\n * @property string $email\n * @property string $auth_key\n * @property integer $status\n * @property integer $created_at\n * @property integer $updated_at\n * @property string $password write-only password\n */\nclass User extends ActiveRecord implements IdentityInterface\n{\n    const STATUS_DELETED = 0;\n    const STATUS_INACTIVE = 9;\n    const STATUS_ACTIVE = 10;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return '{{%user}}';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function behaviors()\n    {\n        return [\n            TimestampBehavior::className(),\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            ['status', 'default', 'value' => self::STATUS_INACTIVE],\n            ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]],\n        ];\n    }\n\n    /**\n     * @return \\yii\\db\\ActiveQuery\n     * @throws \\yii\\base\\InvalidConfigException\n     * @author Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n     */\n    public function getSubscribers()\n    {\n        return $this->hasMany(User::class, ['id' => 'user_id'])\n            ->viaTable('subscriber', ['channel_id' => 'id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function findIdentity($id)\n    {\n        return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function findIdentityByAccessToken($token, $type = null)\n    {\n        throw new NotSupportedException('\"findIdentityByAccessToken\" is not implemented.');\n    }\n\n    /**\n     * Finds user by username\n     *\n     * @param string $username\n     * @return static|null\n     */\n    public static function findByUsername($username)\n    {\n        return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);\n    }\n\n    /**\n     * Finds user by password reset token\n     *\n     * @param string $token password reset token\n     * @return static|null\n     */\n    public static function findByPasswordResetToken($token)\n    {\n        if (!static::isPasswordResetTokenValid($token)) {\n            return null;\n        }\n\n        return static::findOne([\n            'password_reset_token' => $token,\n            'status' => self::STATUS_ACTIVE,\n        ]);\n    }\n\n    /**\n     * Finds user by verification email token\n     *\n     * @param string $token verify email token\n     * @return static|null\n     */\n    public static function findByVerificationToken($token) {\n        return static::findOne([\n            'verification_token' => $token,\n            'status' => self::STATUS_INACTIVE\n        ]);\n    }\n\n    /**\n     * Finds out if password reset token is valid\n     *\n     * @param string $token password reset token\n     * @return bool\n     */\n    public static function isPasswordResetTokenValid($token)\n    {\n        if (empty($token)) {\n            return false;\n        }\n\n        $timestamp = (int) substr($token, strrpos($token, '_') + 1);\n        $expire = Yii::$app->params['user.passwordResetTokenExpire'];\n        return $timestamp + $expire >= time();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getId()\n    {\n        return $this->getPrimaryKey();\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getAuthKey()\n    {\n        return $this->auth_key;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function validateAuthKey($authKey)\n    {\n        return $this->getAuthKey() === $authKey;\n    }\n\n    /**\n     * Validates password\n     *\n     * @param string $password password to validate\n     * @return bool if password provided is valid for current user\n     */\n    public function validatePassword($password)\n    {\n        return Yii::$app->security->validatePassword($password, $this->password_hash);\n    }\n\n    /**\n     * Generates password hash from password and sets it to the model\n     *\n     * @param string $password\n     */\n    public function setPassword($password)\n    {\n        $this->password_hash = Yii::$app->security->generatePasswordHash($password);\n    }\n\n    /**\n     * Generates \"remember me\" authentication key\n     */\n    public function generateAuthKey()\n    {\n        $this->auth_key = Yii::$app->security->generateRandomString();\n    }\n\n    /**\n     * Generates new password reset token\n     */\n    public function generatePasswordResetToken()\n    {\n        $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();\n    }\n\n    /**\n     * Generates new token for email verification\n     */\n    public function generateEmailVerificationToken()\n    {\n        $this->verification_token = Yii::$app->security->generateRandomString() . '_' . time();\n    }\n\n    /**\n     * Removes password reset token\n     */\n    public function removePasswordResetToken()\n    {\n        $this->password_reset_token = null;\n    }\n\n    public function isSubscribed($userId)\n    {\n        return Subscriber::find()->andWhere([\n            'channel_id' => $this->id,\n            'user_id' => $userId\n        ])->one();\n    }\n}\n"
  },
  {
    "path": "common/models/Video.php",
    "content": "<?php\n\nnamespace common\\models;\n\nuse Imagine\\Image\\Box;\nuse Yii;\nuse yii\\behaviors\\BlameableBehavior;\nuse yii\\behaviors\\TimestampBehavior;\nuse yii\\helpers\\FileHelper;\nuse yii\\imagine\\Image;\n\n/**\n * This is the model class for table \"{{%video}}\".\n *\n * @property string      $video_id\n * @property string      $title\n * @property string|null $description\n * @property string|null $tags\n * @property int|null    $status\n * @property int|null    $has_thumbnail\n * @property string|null $video_name\n * @property int|null    $created_at\n * @property int|null    $updated_at\n * @property int|null    $created_by\n *\n * @property User        $createdBy\n * @property \\common\\models\\VideoLike[]        $likes\n * @property \\common\\models\\VideoLike[]        $dislikes\n */\nclass Video extends \\yii\\db\\ActiveRecord\n{\n    const STATUS_UNLISTED = 0;\n    const STATUS_PUBLISHED = 1;\n\n    /**\n     * @var \\yii\\web\\UploadedFile\n     */\n    public $video;\n\n    /**\n     * @var \\yii\\web\\UploadedFile\n     */\n    public $thumbnail;\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return '{{%video}}';\n    }\n\n    public function behaviors()\n    {\n        return [\n            TimestampBehavior::class,\n            [\n                'class' => BlameableBehavior::class,\n                'updatedByAttribute' => false\n            ]\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [['video_id', 'title'], 'required'],\n            [['description'], 'string'],\n            [['status', 'has_thumbnail', 'created_at', 'updated_at', 'created_by'], 'integer'],\n            [['video_id'], 'string', 'max' => 16],\n            [['title', 'tags', 'video_name'], 'string', 'max' => 512],\n            [['video_id'], 'unique'],\n            ['has_thumbnail', 'default', 'value' => 0],\n            ['status', 'default', 'value' => self::STATUS_UNLISTED],\n            ['thumbnail', 'image', 'minWidth' => 1280],\n            ['video', 'file', 'extensions' => ['mp4']],\n            [['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['created_by' => 'id']],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            'video_id' => 'Video ID',\n            'title' => 'Title',\n            'description' => 'Description',\n            'tags' => 'Tags',\n            'status' => 'Status',\n            'has_thumbnail' => 'Has Thumbnail',\n            'video_name' => 'Video Name',\n            'created_at' => 'Created At',\n            'updated_at' => 'Updated At',\n            'created_by' => 'Created By',\n            'thumbnail' => 'Thumbnail'\n        ];\n    }\n\n    public function getStatusLabels()\n    {\n        return [\n            self::STATUS_UNLISTED => 'Unlisted',\n            self::STATUS_PUBLISHED => 'Published',\n        ];\n    }\n\n    /**\n     * Gets query for [[CreatedBy]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\UserQuery\n     */\n    public function getCreatedBy()\n    {\n        return $this->hasOne(User::className(), ['id' => 'created_by']);\n    }\n\n    /**\n     * @return \\yii\\db\\ActiveQuery\n     */\n    public function getViews()\n    {\n        return $this->hasMany(VideoView::class, ['video_id' => 'video_id']);\n    }\n\n    /**\n     * @return \\yii\\db\\ActiveQuery\n     */\n    public function getLikes()\n    {\n        return $this->hasMany(VideoLike::class, ['video_id' => 'video_id'])\n            ->liked();\n    }\n\n    /**\n     * @return \\yii\\db\\ActiveQuery\n     */\n    public function getDislikes()\n    {\n        return $this->hasMany(VideoLike::class, ['video_id' => 'video_id'])\n            ->disliked();\n    }\n\n    /**\n     * @return \\common\\models\\query\\CommentQuery\n     */\n    public function getComments()\n    {\n        return $this->hasMany(Comment::class, ['video_id' => 'video_id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\query\\VideoQuery the active query used by this AR class.\n     */\n    public static function find()\n    {\n        return new \\common\\models\\query\\VideoQuery(get_called_class());\n    }\n\n    public function save($runValidation = true, $attributeNames = null)\n    {\n        $isInsert = $this->isNewRecord;\n        if ($isInsert) {\n            $this->video_id = Yii::$app->security->generateRandomString(8);\n            $this->title = $this->video->name;\n            $this->video_name = $this->video->name;\n        }\n        if ($this->thumbnail) {\n            $this->has_thumbnail = 1;\n        }\n        $saved = parent::save($runValidation, $attributeNames);\n        if (!$saved) {\n            return false;\n        }\n        if ($isInsert) {\n            $videoPath = Yii::getAlias('@frontend/web/storage/videos/' . $this->video_id . '.mp4');\n            if (!is_dir(dirname($videoPath))) {\n                FileHelper::createDirectory(dirname($videoPath));\n            }\n            $this->video->saveAs($videoPath);\n        }\n        if ($this->thumbnail) {\n            $thumbnailPath = Yii::getAlias('@frontend/web/storage/thumbs/' . $this->video_id . '.jpg');\n            if (!is_dir(dirname($thumbnailPath))) {\n                FileHelper::createDirectory(dirname($thumbnailPath));\n            }\n            $this->thumbnail->saveAs($thumbnailPath);\n            Image::getImagine()\n                ->open($thumbnailPath)\n                ->thumbnail(new Box(1280, 1280))\n                ->save();\n        }\n\n        return true;\n    }\n\n    public function getVideoLink()\n    {\n        return Yii::$app->params['frontendUrl'] . 'storage/videos/' . $this->video_id . '.mp4';\n    }\n\n    public function getThumbnailLink()\n    {\n        return $this->has_thumbnail ?\n            Yii::$app->params['frontendUrl'] . 'storage/thumbs/' . $this->video_id . '.jpg'\n            : '';\n    }\n\n    public function afterDelete()\n    {\n        parent::afterDelete();\n        $videoPath = Yii::getAlias('@frontend/web/storage/videos/' . $this->video_id . '.mp4');\n        unlink($videoPath);\n\n        $thumbnailPath = Yii::getAlias('@frontend/web/storage/thumbs/' . $this->video_id . '.jpg');\n        if (file_exists($thumbnailPath)) {\n            unlink($thumbnailPath);\n        }\n    }\n\n    public function isLikedBy($userId)\n    {\n        return VideoLike::find()\n            ->userIdVideoId($userId, $this->video_id)\n            ->liked()\n            ->one();\n    }\n\n    public function isDislikedBy($userId)\n    {\n        return VideoLike::find()\n            ->userIdVideoId($userId, $this->video_id)\n            ->disliked()\n            ->one();\n    }\n\n    public function belongsTo($userId)\n    {\n        return $this->created_by === $userId;\n    }\n}\n"
  },
  {
    "path": "common/models/VideoLike.php",
    "content": "<?php\n\nnamespace common\\models;\n\nuse Yii;\n\n/**\n * This is the model class for table \"{{%video_like}}\".\n *\n * @property int $id\n * @property string $video_id\n * @property int $user_id\n * @property int|null $type\n * @property int|null $created_at\n *\n * @property User $user\n * @property Video $video\n */\nclass VideoLike extends \\yii\\db\\ActiveRecord\n{\n    const TYPE_LIKE = 1;\n    const TYPE_DISLIKE = 0;\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return '{{%video_like}}';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [['video_id', 'user_id'], 'required'],\n            [['user_id', 'type', 'created_at'], 'integer'],\n            [['video_id'], 'string', 'max' => 16],\n            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],\n            [['video_id'], 'exist', 'skipOnError' => true, 'targetClass' => Video::className(), 'targetAttribute' => ['video_id' => 'video_id']],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            'id' => 'ID',\n            'video_id' => 'Video ID',\n            'user_id' => 'User ID',\n            'type' => 'Type',\n            'created_at' => 'Created At',\n        ];\n    }\n\n    /**\n     * Gets query for [[User]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\UserQuery\n     */\n    public function getUser()\n    {\n        return $this->hasOne(User::className(), ['id' => 'user_id']);\n    }\n\n    /**\n     * Gets query for [[Video]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\VideoQuery\n     */\n    public function getVideo()\n    {\n        return $this->hasOne(Video::className(), ['video_id' => 'video_id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\query\\VideoLikeQuery the active query used by this AR class.\n     */\n    public static function find()\n    {\n        return new \\common\\models\\query\\VideoLikeQuery(get_called_class());\n    }\n}\n"
  },
  {
    "path": "common/models/VideoView.php",
    "content": "<?php\n\nnamespace common\\models;\n\nuse Yii;\n\n/**\n * This is the model class for table \"{{%video_view}}\".\n *\n * @property int $id\n * @property string $video_id\n * @property int|null $user_id\n * @property int|null $created_at\n *\n * @property User $user\n * @property Video $video\n */\nclass VideoView extends \\yii\\db\\ActiveRecord\n{\n    /**\n     * {@inheritdoc}\n     */\n    public static function tableName()\n    {\n        return '{{%video_view}}';\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            [['video_id'], 'required'],\n            [['user_id', 'created_at'], 'integer'],\n            [['video_id'], 'string', 'max' => 16],\n            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],\n            [['video_id'], 'exist', 'skipOnError' => true, 'targetClass' => Video::className(), 'targetAttribute' => ['video_id' => 'video_id']],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function attributeLabels()\n    {\n        return [\n            'id' => 'ID',\n            'video_id' => 'Video ID',\n            'user_id' => 'User ID',\n            'created_at' => 'Created At',\n        ];\n    }\n\n    /**\n     * Gets query for [[User]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\UserQuery\n     */\n    public function getUser()\n    {\n        return $this->hasOne(User::className(), ['id' => 'user_id']);\n    }\n\n    /**\n     * Gets query for [[Video]].\n     *\n     * @return \\yii\\db\\ActiveQuery|\\common\\models\\query\\VideoQuery\n     */\n    public function getVideo()\n    {\n        return $this->hasOne(Video::className(), ['video_id' => 'video_id']);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\query\\VideoViewQuery the active query used by this AR class.\n     */\n    public static function find()\n    {\n        return new \\common\\models\\query\\VideoViewQuery(get_called_class());\n    }\n}\n"
  },
  {
    "path": "common/models/query/CommentQuery.php",
    "content": "<?php\n\nnamespace common\\models\\query;\n\nuse common\\models\\Comment;\nuse common\\models\\Video;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\Comment]].\n *\n * @see \\common\\models\\Comment\n */\nclass CommentQuery extends \\yii\\db\\ActiveQuery\n{\n    /*public function active()\n    {\n        return $this->andWhere('[[status]]=1');\n    }*/\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\Comment[]|array\n     */\n    public function all($db = null)\n    {\n        return parent::all($db);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\Comment|array|null\n     */\n    public function one($db = null)\n    {\n        return parent::one($db);\n    }\n\n    public function videoId($videoId)\n    {\n        return $this->andWhere(['video_id' => $videoId]);\n    }\n\n    public function parent()\n    {\n        return $this->andWhere(['parent_id' => null]);\n    }\n\n    public function latest()\n    {\n        return $this->orderBy(\"pinned DESC, created_at DESC\");\n    }\n\n    public function byChannel($userId)\n    {\n        return $this->innerJoin(Video::tableName() . ' v', 'v.video_id = ' . Comment::tableName() . '.video_id')\n            ->andWhere(['v.created_by' => $userId]);\n    }\n}\n"
  },
  {
    "path": "common/models/query/SubscriberQuery.php",
    "content": "<?php\n\nnamespace common\\models\\query;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\Subscriber]].\n *\n * @see \\common\\models\\Subscriber\n */\nclass SubscriberQuery extends \\yii\\db\\ActiveQuery\n{\n    /*public function active()\n    {\n        return $this->andWhere('[[status]]=1');\n    }*/\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\Subscriber[]|array\n     */\n    public function all($db = null)\n    {\n        return parent::all($db);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\Subscriber|array|null\n     */\n    public function one($db = null)\n    {\n        return parent::one($db);\n    }\n}\n"
  },
  {
    "path": "common/models/query/VideoLikeQuery.php",
    "content": "<?php\n\nnamespace common\\models\\query;\n\nuse common\\models\\VideoLike;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\VideoLike]].\n *\n * @see \\common\\models\\VideoLike\n */\nclass VideoLikeQuery extends \\yii\\db\\ActiveQuery\n{\n    /*public function active()\n    {\n        return $this->andWhere('[[status]]=1');\n    }*/\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\VideoLike[]|array\n     */\n    public function all($db = null)\n    {\n        return parent::all($db);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\VideoLike|array|null\n     */\n    public function one($db = null)\n    {\n        return parent::one($db);\n    }\n\n    public function userIdVideoId($userId, $videoId)\n    {\n        return $this->andWhere([\n            'video_id' => $videoId,\n            'user_id' => $userId\n        ]);\n    }\n\n    public function liked()\n    {\n        return $this->andWhere(['type' => VideoLike::TYPE_LIKE]);\n    }\n\n    public function disliked()\n    {\n        return $this->andWhere(['type' => VideoLike::TYPE_DISLIKE]);\n    }\n}\n"
  },
  {
    "path": "common/models/query/VideoQuery.php",
    "content": "<?php\n\nnamespace common\\models\\query;\n\nuse common\\models\\Video;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\Video]].\n *\n * @see \\common\\models\\Video\n */\nclass VideoQuery extends \\yii\\db\\ActiveQuery\n{\n    /*public function active()\n    {\n        return $this->andWhere('[[status]]=1');\n    }*/\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\Video[]|array\n     */\n    public function all($db = null)\n    {\n        return parent::all($db);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\Video|array|null\n     */\n    public function one($db = null)\n    {\n        return parent::one($db);\n    }\n\n    public function creator($userId)\n    {\n        return $this->andWhere(['created_by' => $userId]);\n    }\n\n    public function latest()\n    {\n        return $this->orderBy(['created_at' => SORT_DESC]);\n    }\n\n    public function published()\n    {\n        return $this->andWhere(['status' => Video::STATUS_PUBLISHED]);\n    }\n\n    public function byKeyword($keyword)\n    {\n        return $this->andWhere(\"MATCH(title, description, tags)\n        AGAINST (:keyword)\", ['keyword' => $keyword]);\n    }\n}\n"
  },
  {
    "path": "common/models/query/VideoViewQuery.php",
    "content": "<?php\n\nnamespace common\\models\\query;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\VideoView]].\n *\n * @see \\common\\models\\VideoView\n */\nclass VideoViewQuery extends \\yii\\db\\ActiveQuery\n{\n    /*public function active()\n    {\n        return $this->andWhere('[[status]]=1');\n    }*/\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\VideoView[]|array\n     */\n    public function all($db = null)\n    {\n        return parent::all($db);\n    }\n\n    /**\n     * {@inheritdoc}\n     * @return \\common\\models\\VideoView|array|null\n     */\n    public function one($db = null)\n    {\n        return parent::one($db);\n    }\n}\n"
  },
  {
    "path": "common/tests/_bootstrap.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\ndefined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');\n\nrequire_once __DIR__ .  '/../../vendor/autoload.php';\nrequire_once __DIR__ .  '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n"
  },
  {
    "path": "common/tests/_data/user.php",
    "content": "<?php\n\nreturn [\n    [\n        'username' => 'bayer.hudson',\n        'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR',\n        //password_0\n        'password_hash' => '$2y$13$EjaPFBnZOQsHdGuHI.xvhuDp1fHpo8hKRSk6yshqa9c5EG8s3C3lO',\n        'password_reset_token' => 'ExzkCOaYc1L8IOBs4wdTGGbgNiG3Wz1I_1402312317',\n        'created_at' => '1402312317',\n        'updated_at' => '1402312317',\n        'email' => 'nicole.paucek@schultz.info',\n    ],\n];\n"
  },
  {
    "path": "common/tests/_output/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "common/tests/_support/.gitignore",
    "content": "_generated\n"
  },
  {
    "path": "common/tests/_support/UnitTester.php",
    "content": "<?php\nnamespace common\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)\n * @method void execute($callable)\n * @method void expectTo($prediction)\n * @method void expect($prediction)\n * @method void amGoingTo($argumentation)\n * @method void am($role)\n * @method void lookForwardTo($achieveValue)\n * @method void comment($description)\n * @method \\Codeception\\Lib\\Friend haveFriend($name, $actorClass = NULL)\n *\n * @SuppressWarnings(PHPMD)\n */\nclass UnitTester extends \\Codeception\\Actor\n{\n    use _generated\\UnitTesterActions;\n   /**\n    * Define custom actions here\n    */\n}\n"
  },
  {
    "path": "common/tests/unit/models/LoginFormTest.php",
    "content": "<?php\n\nnamespace common\\tests\\unit\\models;\n\nuse Yii;\nuse common\\models\\LoginForm;\nuse common\\fixtures\\UserFixture;\n\n/**\n * Login form test\n */\nclass LoginFormTest extends \\Codeception\\Test\\Unit\n{\n    /**\n     * @var \\common\\tests\\UnitTester\n     */\n    protected $tester;\n\n\n    /**\n     * @return array\n     */\n    public function _fixtures()\n    {\n        return [\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ]\n        ];\n    }\n\n    public function testLoginNoUser()\n    {\n        $model = new LoginForm([\n            'username' => 'not_existing_username',\n            'password' => 'not_existing_password',\n        ]);\n\n        expect('model should not login user', $model->login())->false();\n        expect('user should not be logged in', Yii::$app->user->isGuest)->true();\n    }\n\n    public function testLoginWrongPassword()\n    {\n        $model = new LoginForm([\n            'username' => 'bayer.hudson',\n            'password' => 'wrong_password',\n        ]);\n\n        expect('model should not login user', $model->login())->false();\n        expect('error message should be set', $model->errors)->hasKey('password');\n        expect('user should not be logged in', Yii::$app->user->isGuest)->true();\n    }\n\n    public function testLoginCorrect()\n    {\n        $model = new LoginForm([\n            'username' => 'bayer.hudson',\n            'password' => 'password_0',\n        ]);\n\n        expect('model should login user', $model->login())->true();\n        expect('error message should not be set', $model->errors)->hasntKey('password');\n        expect('user should be logged in', Yii::$app->user->isGuest)->false();\n    }\n}\n"
  },
  {
    "path": "common/tests/unit.suite.yml",
    "content": "suite_namespace: common\\tests\\unit\nactor: UnitTester\nbootstrap: false\nmodules:\n    enabled:\n        - Yii2:\n            part: fixtures\n"
  },
  {
    "path": "common/widgets/Alert.php",
    "content": "<?php\nnamespace common\\widgets;\n\nuse Yii;\n\n/**\n * Alert widget renders a message from session flash. All flash messages are displayed\n * in the sequence they were assigned using setFlash. You can set message as following:\n *\n * ```php\n * Yii::$app->session->setFlash('error', 'This is the message');\n * Yii::$app->session->setFlash('success', 'This is the message');\n * Yii::$app->session->setFlash('info', 'This is the message');\n * ```\n *\n * Multiple messages could be set as follows:\n *\n * ```php\n * Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']);\n * ```\n *\n * @author Kartik Visweswaran <kartikv2@gmail.com>\n * @author Alexander Makarov <sam@rmcreative.ru>\n */\nclass Alert extends \\yii\\bootstrap4\\Widget\n{\n    /**\n     * @var array the alert types configuration for the flash messages.\n     * This array is setup as $key => $value, where:\n     * - key: the name of the session flash variable\n     * - value: the bootstrap alert type (i.e. danger, success, info, warning)\n     */\n    public $alertTypes = [\n        'error'   => 'alert-danger',\n        'danger'  => 'alert-danger',\n        'success' => 'alert-success',\n        'info'    => 'alert-info',\n        'warning' => 'alert-warning'\n    ];\n    /**\n     * @var array the options for rendering the close button tag.\n     * Array will be passed to [[\\yii\\bootstrap\\Alert::closeButton]].\n     */\n    public $closeButton = [];\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function run()\n    {\n        $session = Yii::$app->session;\n        $flashes = $session->getAllFlashes();\n        $appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : '';\n\n        foreach ($flashes as $type => $flash) {\n            if (!isset($this->alertTypes[$type])) {\n                continue;\n            }\n\n            foreach ((array) $flash as $i => $message) {\n                echo \\yii\\bootstrap4\\Alert::widget([\n                    'body' => $message,\n                    'closeButton' => $this->closeButton,\n                    'options' => array_merge($this->options, [\n                        'id' => $this->getId() . '-' . $type . '-' . $i,\n                        'class' => $this->alertTypes[$type] . $appendClass,\n                    ]),\n                ]);\n            }\n\n            $session->removeFlash($type);\n        }\n    }\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"yiisoft/yii2-app-advanced\",\n    \"description\": \"Yii 2 Advanced Project Template\",\n    \"keywords\": [\"yii2\", \"framework\", \"advanced\", \"project template\"],\n    \"homepage\": \"http://www.yiiframework.com/\",\n    \"type\": \"project\",\n    \"license\": \"BSD-3-Clause\",\n    \"support\": {\n        \"issues\": \"https://github.com/yiisoft/yii2/issues?state=open\",\n        \"forum\": \"http://www.yiiframework.com/forum/\",\n        \"wiki\": \"http://www.yiiframework.com/wiki/\",\n        \"irc\": \"irc://irc.freenode.net/yii\",\n        \"source\": \"https://github.com/yiisoft/yii2\"\n    },\n    \"minimum-stability\": \"stable\",\n    \"require\": {\n        \"php\": \">=5.6.0\",\n        \"php-gd\": \"*\",\n        \"yiisoft/yii2\": \"~2.0.14\",\n        \"yiisoft/yii2-swiftmailer\": \"~2.0.0 || ~2.1.0\",\n        \"yiisoft/yii2-bootstrap4\": \"^2.0\",\n        \"yiisoft/yii2-imagine\": \"^2.2\"\n    },\n    \"require-dev\": {\n        \"yiisoft/yii2-debug\": \"~2.1.0\",\n        \"yiisoft/yii2-gii\": \"~2.1.0\",\n        \"yiisoft/yii2-faker\": \"~2.0.0\",\n        \"codeception/codeception\": \"^4.0\",\n        \"codeception/module-asserts\": \"^1.0\",\n        \"codeception/module-yii2\": \"^1.0\",\n        \"codeception/module-filesystem\": \"^1.0\",\n        \"codeception/verify\": \"~0.5.0 || ~1.1.0\",\n        \"symfony/browser-kit\": \">=2.7 <=4.2.4\"\n    },\n    \"config\": {\n        \"process-timeout\": 1800,\n        \"fxp-asset\": {\n            \"enabled\": false\n        }\n    },\n    \"repositories\": [\n        {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    ]\n}\n"
  },
  {
    "path": "console/config/.gitignore",
    "content": "main-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "console/config/bootstrap.php",
    "content": "<?php\n"
  },
  {
    "path": "console/config/main.php",
    "content": "<?php\n$params = array_merge(\n    require __DIR__ . '/../../common/config/params.php',\n    require __DIR__ . '/../../common/config/params-local.php',\n    require __DIR__ . '/params.php',\n    require __DIR__ . '/params-local.php'\n);\n\nreturn [\n    'id' => 'app-console',\n    'basePath' => dirname(__DIR__),\n    'bootstrap' => ['log'],\n    'controllerNamespace' => 'console\\controllers',\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm'   => '@vendor/npm-asset',\n    ],\n    'controllerMap' => [\n        'fixture' => [\n            'class' => 'yii\\console\\controllers\\FixtureController',\n            'namespace' => 'common\\fixtures',\n          ],\n    ],\n    'components' => [\n        'log' => [\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n            ],\n        ],\n    ],\n    'params' => $params,\n];\n"
  },
  {
    "path": "console/config/params.php",
    "content": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n];\n"
  },
  {
    "path": "console/config/test.php",
    "content": "<?php\nreturn [\n\n];"
  },
  {
    "path": "console/controllers/.gitkeep",
    "content": ""
  },
  {
    "path": "console/migrations/m130524_201442_init.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\nclass m130524_201442_init extends Migration\n{\n    public function up()\n    {\n        $tableOptions = null;\n        if ($this->db->driverName === 'mysql') {\n            // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci\n            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';\n        }\n\n        $this->createTable('{{%user}}', [\n            'id' => $this->primaryKey(),\n            'username' => $this->string()->notNull()->unique(),\n            'auth_key' => $this->string(32)->notNull(),\n            'password_hash' => $this->string()->notNull(),\n            'password_reset_token' => $this->string()->unique(),\n            'email' => $this->string()->notNull()->unique(),\n\n            'status' => $this->smallInteger()->notNull()->defaultValue(10),\n            'created_at' => $this->integer()->notNull(),\n            'updated_at' => $this->integer()->notNull(),\n        ], $tableOptions);\n    }\n\n    public function down()\n    {\n        $this->dropTable('{{%user}}');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m190124_110200_add_verification_token_column_to_user_table.php",
    "content": "<?php\n\nuse \\yii\\db\\Migration;\n\nclass m190124_110200_add_verification_token_column_to_user_table extends Migration\n{\n    public function up()\n    {\n        $this->addColumn('{{%user}}', 'verification_token', $this->string()->defaultValue(null));\n    }\n\n    public function down()\n    {\n        $this->dropColumn('{{%user}}', 'verification_token');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m200417_054237_create_videos_table.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%video}}`.\n * Has foreign keys to the tables:\n *\n * - `{{%user}}`\n */\nclass m200417_054237_create_videos_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->createTable('{{%video}}', [\n            'video_id' => $this->string(16)->notNull(),\n            'title' => $this->string(512)->notNull(),\n            'description' => $this->text(),\n            'tags' => $this->string(512),\n            'status' => $this->integer(1),\n            'has_thumbnail' => $this->boolean(),\n            'video_name' => $this->string(512),\n            'created_at' => $this->integer(11),\n            'updated_at' => $this->integer(11),\n            'created_by' => $this->integer(11),\n        ]);\n\n        $this->addPrimaryKey('PK_videos_video_id', '{{%video}}', 'video_id');\n\n        // creates index for column `created_by`\n        $this->createIndex(\n            '{{%idx-videos-created_by}}',\n            '{{%video}}',\n            'created_by'\n        );\n\n        // add foreign key for table `{{%user}}`\n        $this->addForeignKey(\n            '{{%fk-videos-created_by}}',\n            '{{%video}}',\n            'created_by',\n            '{{%user}}',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        // drops foreign key for table `{{%user}}`\n        $this->dropForeignKey(\n            '{{%fk-videos-created_by}}',\n            '{{%video}}'\n        );\n\n        // drops index for column `created_by`\n        $this->dropIndex(\n            '{{%idx-videos-created_by}}',\n            '{{%video}}'\n        );\n\n        $this->dropTable('{{%video}}');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m200418_050048_create_video_view_table.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%video_view}}`.\n * Has foreign keys to the tables:\n *\n * - `{{%video}}`\n * - `{{%user}}`\n */\nclass m200418_050048_create_video_view_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->createTable('{{%video_view}}', [\n            'id' => $this->primaryKey(),\n            'video_id' => $this->string(16)->notNull(),\n            'user_id' => $this->integer(11),\n            'created_at' => $this->integer(11),\n        ]);\n\n        // creates index for column `video_id`\n        $this->createIndex(\n            '{{%idx-video_view-video_id}}',\n            '{{%video_view}}',\n            'video_id'\n        );\n\n        // add foreign key for table `{{%video}}`\n        $this->addForeignKey(\n            '{{%fk-video_view-video_id}}',\n            '{{%video_view}}',\n            'video_id',\n            '{{%video}}',\n            'video_id',\n            'CASCADE'\n        );\n\n        // creates index for column `user_id`\n        $this->createIndex(\n            '{{%idx-video_view-user_id}}',\n            '{{%video_view}}',\n            'user_id'\n        );\n\n        // add foreign key for table `{{%user}}`\n        $this->addForeignKey(\n            '{{%fk-video_view-user_id}}',\n            '{{%video_view}}',\n            'user_id',\n            '{{%user}}',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        // drops foreign key for table `{{%video}}`\n        $this->dropForeignKey(\n            '{{%fk-video_view-video_id}}',\n            '{{%video_view}}'\n        );\n\n        // drops index for column `video_id`\n        $this->dropIndex(\n            '{{%idx-video_view-video_id}}',\n            '{{%video_view}}'\n        );\n\n        // drops foreign key for table `{{%user}}`\n        $this->dropForeignKey(\n            '{{%fk-video_view-user_id}}',\n            '{{%video_view}}'\n        );\n\n        // drops index for column `user_id`\n        $this->dropIndex(\n            '{{%idx-video_view-user_id}}',\n            '{{%video_view}}'\n        );\n\n        $this->dropTable('{{%video_view}}');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m200418_051244_create_video_like_table.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%video_like}}`.\n * Has foreign keys to the tables:\n *\n * - `{{%video}}`\n * - `{{%user}}`\n */\nclass m200418_051244_create_video_like_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->createTable('{{%video_like}}', [\n            'id' => $this->primaryKey(),\n            'video_id' => $this->string(16)->notNull(),\n            'user_id' => $this->integer(11)->notNull(),\n            'type' => $this->integer(1),\n            'created_at' => $this->integer(11),\n        ]);\n\n        // creates index for column `video_id`\n        $this->createIndex(\n            '{{%idx-video_like-video_id}}',\n            '{{%video_like}}',\n            'video_id'\n        );\n\n        // add foreign key for table `{{%video}}`\n        $this->addForeignKey(\n            '{{%fk-video_like-video_id}}',\n            '{{%video_like}}',\n            'video_id',\n            '{{%video}}',\n            'video_id',\n            'CASCADE'\n        );\n\n        // creates index for column `user_id`\n        $this->createIndex(\n            '{{%idx-video_like-user_id}}',\n            '{{%video_like}}',\n            'user_id'\n        );\n\n        // add foreign key for table `{{%user}}`\n        $this->addForeignKey(\n            '{{%fk-video_like-user_id}}',\n            '{{%video_like}}',\n            'user_id',\n            '{{%user}}',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        // drops foreign key for table `{{%video}}`\n        $this->dropForeignKey(\n            '{{%fk-video_like-video_id}}',\n            '{{%video_like}}'\n        );\n\n        // drops index for column `video_id`\n        $this->dropIndex(\n            '{{%idx-video_like-video_id}}',\n            '{{%video_like}}'\n        );\n\n        // drops foreign key for table `{{%user}}`\n        $this->dropForeignKey(\n            '{{%fk-video_like-user_id}}',\n            '{{%video_like}}'\n        );\n\n        // drops index for column `user_id`\n        $this->dropIndex(\n            '{{%idx-video_like-user_id}}',\n            '{{%video_like}}'\n        );\n\n        $this->dropTable('{{%video_like}}');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m200418_060320_create_subscriber_table.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%subscriber}}`.\n * Has foreign keys to the tables:\n *\n * - `{{%user}}`\n * - `{{%user}}`\n */\nclass m200418_060320_create_subscriber_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->createTable('{{%subscriber}}', [\n            'id' => $this->primaryKey(),\n            'channel_id' => $this->integer(11),\n            'user_id' => $this->integer(11),\n            'created_at' => $this->integer(11),\n        ]);\n\n        // creates index for column `channel_id`\n        $this->createIndex(\n            '{{%idx-subscriber-channel_id}}',\n            '{{%subscriber}}',\n            'channel_id'\n        );\n\n        // add foreign key for table `{{%user}}`\n        $this->addForeignKey(\n            '{{%fk-subscriber-channel_id}}',\n            '{{%subscriber}}',\n            'channel_id',\n            '{{%user}}',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `user_id`\n        $this->createIndex(\n            '{{%idx-subscriber-user_id}}',\n            '{{%subscriber}}',\n            'user_id'\n        );\n\n        // add foreign key for table `{{%user}}`\n        $this->addForeignKey(\n            '{{%fk-subscriber-user_id}}',\n            '{{%subscriber}}',\n            'user_id',\n            '{{%user}}',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        // drops foreign key for table `{{%user}}`\n        $this->dropForeignKey(\n            '{{%fk-subscriber-channel_id}}',\n            '{{%subscriber}}'\n        );\n\n        // drops index for column `channel_id`\n        $this->dropIndex(\n            '{{%idx-subscriber-channel_id}}',\n            '{{%subscriber}}'\n        );\n\n        // drops foreign key for table `{{%user}}`\n        $this->dropForeignKey(\n            '{{%fk-subscriber-user_id}}',\n            '{{%subscriber}}'\n        );\n\n        // drops index for column `user_id`\n        $this->dropIndex(\n            '{{%idx-subscriber-user_id}}',\n            '{{%subscriber}}'\n        );\n\n        $this->dropTable('{{%subscriber}}');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m200418_064142_create_fulltext_index_on_video.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Class m200418_064142_create_fulltext_index_on_video\n */\nclass m200418_064142_create_fulltext_index_on_video extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->execute(\"ALTER TABLE {{%video}} ADD FULLTEXT(title, description, tags)\");\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        echo \"m200418_064142_create_fulltext_index_on_video cannot be reverted.\\n\";\n\n        return false;\n    }\n\n    /*\n    // Use up()/down() to run migration code without a transaction.\n    public function up()\n    {\n\n    }\n\n    public function down()\n    {\n        echo \"m200418_064142_create_fulltext_index_on_video cannot be reverted.\\n\";\n\n        return false;\n    }\n    */\n}\n"
  },
  {
    "path": "console/migrations/m201112_042619_create_comment_table.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%comment}}`.\n * Has foreign keys to the tables:\n *\n * - `{{%video}}`\n * - `{{%comment}}`\n * - `{{%user}}`\n */\nclass m201112_042619_create_comment_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->createTable('{{%comment}}', [\n            'id' => $this->primaryKey(),\n            'comment' => $this->text()->notNull(),\n            'video_id' => $this->string(16)->notNull(),\n            'parent_id' => $this->integer(11),\n            'pinned' => $this->tinyInteger()->defaultValue(0),\n            'created_at' => $this->integer(11),\n            'updated_at' => $this->integer(11),\n            'created_by' => $this->integer(11),\n        ]);\n\n        // creates index for column `video_id`\n        $this->createIndex(\n            '{{%idx-comment-video_id}}',\n            '{{%comment}}',\n            'video_id'\n        );\n\n        // add foreign key for table `{{%video}}`\n        $this->addForeignKey(\n            '{{%fk-comment-video_id}}',\n            '{{%comment}}',\n            'video_id',\n            '{{%video}}',\n            'video_id',\n            'CASCADE'\n        );\n\n        // creates index for column `parent_id`\n        $this->createIndex(\n            '{{%idx-comment-parent_id}}',\n            '{{%comment}}',\n            'parent_id'\n        );\n\n        // add foreign key for table `{{%comment}}`\n        $this->addForeignKey(\n            '{{%fk-comment-parent_id}}',\n            '{{%comment}}',\n            'parent_id',\n            '{{%comment}}',\n            'id',\n            'CASCADE'\n        );\n\n        // creates index for column `created_by`\n        $this->createIndex(\n            '{{%idx-comment-created_by}}',\n            '{{%comment}}',\n            'created_by'\n        );\n\n        // add foreign key for table `{{%user}}`\n        $this->addForeignKey(\n            '{{%fk-comment-created_by}}',\n            '{{%comment}}',\n            'created_by',\n            '{{%user}}',\n            'id',\n            'CASCADE'\n        );\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        // drops foreign key for table `{{%video}}`\n        $this->dropForeignKey(\n            '{{%fk-comment-video_id}}',\n            '{{%comment}}'\n        );\n\n        // drops index for column `video_id`\n        $this->dropIndex(\n            '{{%idx-comment-video_id}}',\n            '{{%comment}}'\n        );\n\n        // drops foreign key for table `{{%comment}}`\n        $this->dropForeignKey(\n            '{{%fk-comment-parent_id}}',\n            '{{%comment}}'\n        );\n\n        // drops index for column `parent_id`\n        $this->dropIndex(\n            '{{%idx-comment-parent_id}}',\n            '{{%comment}}'\n        );\n\n        // drops foreign key for table `{{%user}}`\n        $this->dropForeignKey(\n            '{{%fk-comment-created_by}}',\n            '{{%comment}}'\n        );\n\n        // drops index for column `created_by`\n        $this->dropIndex(\n            '{{%idx-comment-created_by}}',\n            '{{%comment}}'\n        );\n\n        $this->dropTable('{{%comment}}');\n    }\n}\n"
  },
  {
    "path": "console/migrations/m201115_124738_add_mention_column_to_comment_table.php",
    "content": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles adding columns to table `{{%comment}}`.\n */\nclass m201115_124738_add_mention_column_to_comment_table extends Migration\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function safeUp()\n    {\n        $this->addColumn('{{%comment}}', 'mention', $this->string(255)->after('pinned'));\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function safeDown()\n    {\n        $this->dropColumn('{{%comment}}', 'mention');\n    }\n}\n"
  },
  {
    "path": "console/models/.gitkeep",
    "content": "*\n"
  },
  {
    "path": "console/runtime/.gitignore",
    "content": "*\n!.gitignore"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3.2'\n\nservices:\n\n  frontend:\n    build: frontend\n    ports:\n      - 20080:80\n    volumes:\n      # Re-use local composer cache via host-volume\n      - ~/.composer-docker/cache:/root/.composer/cache:delegated\n      # Mount source-code for development\n      - ./:/app\n\n  backend:\n    build: backend\n    ports:\n      - 21080:80\n    volumes:\n      # Re-use local composer cache via host-volume\n      - ~/.composer-docker/cache:/root/.composer/cache:delegated\n      # Mount source-code for development\n      - ./:/app\n\n  mysql:\n    image: mysql:5.7\n    environment:\n      - MYSQL_ROOT_PASSWORD=verysecret\n      - MYSQL_DATABASE=yii2advanced\n      - MYSQL_USER=yii2advanced\n      - MYSQL_PASSWORD=secret\n\n  #pgsql:\n  #  image: postgres:9.5\n  #  environment:\n  #    - POSTGRES_DB=yii2advanced\n  #    - POSTGRES_USER=yii2advanced\n  #    - POSTGRES_PASSWORD=secret"
  },
  {
    "path": "environments/dev/backend/config/codeception-local.php",
    "content": "<?php\n\nreturn yii\\helpers\\ArrayHelper::merge(\n    require dirname(dirname(__DIR__)) . '/common/config/codeception-local.php',\n    require __DIR__ . '/main.php',\n    require __DIR__ . '/main-local.php',\n    require __DIR__ . '/test.php',\n    require __DIR__ . '/test-local.php',\n    [\n    ]\n);\n"
  },
  {
    "path": "environments/dev/backend/config/main-local.php",
    "content": "<?php\n\n$config = [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n            'cookieValidationKey' => '',\n        ],\n    ],\n];\n\nif (!YII_ENV_TEST) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = [\n        'class' => 'yii\\debug\\Module',\n    ];\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n\nreturn $config;\n"
  },
  {
    "path": "environments/dev/backend/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/backend/config/test-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/backend/web/index-test.php",
    "content": "<?php\n\n// NOTE: Make sure this file is not accessible when deployed to production\nif (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {\n    die('You are not allowed to access this file.');\n}\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\n\nrequire __DIR__ . '/../../vendor/autoload.php';\nrequire __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../../common/config/bootstrap.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/../../common/config/main.php',\n    require __DIR__ . '/../../common/config/main-local.php',\n    require __DIR__ . '/../../common/config/test.php',\n    require __DIR__ . '/../../common/config/test-local.php',\n    require __DIR__ . '/../config/main.php',\n    require __DIR__ . '/../config/main-local.php',\n    require __DIR__ . '/../config/test.php',\n    require __DIR__ . '/../config/test-local.php'\n);\n\n(new yii\\web\\Application($config))->run();\n"
  },
  {
    "path": "environments/dev/backend/web/index.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR__ . '/../../vendor/autoload.php';\nrequire __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../../common/config/bootstrap.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/../../common/config/main.php',\n    require __DIR__ . '/../../common/config/main-local.php',\n    require __DIR__ . '/../config/main.php',\n    require __DIR__ . '/../config/main-local.php'\n);\n\n(new yii\\web\\Application($config))->run();\n"
  },
  {
    "path": "environments/dev/backend/web/robots.txt",
    "content": "User-agent: *\nDisallow: /"
  },
  {
    "path": "environments/dev/common/config/codeception-local.php",
    "content": "<?php\n\nreturn yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/main.php',\n    require __DIR__ . '/main-local.php',\n    require __DIR__ . '/test.php',\n    require __DIR__ . '/test-local.php',\n    [\n        'components' => [\n            'request' => [\n                // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n                'cookieValidationKey' => '',\n            ],\n        ],\n    ]\n);\n"
  },
  {
    "path": "environments/dev/common/config/main-local.php",
    "content": "<?php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=yii2advanced',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n            'viewPath' => '@common/mail',\n            // send all mails to a file by default. You have to set\n            // 'useFileTransport' to false and configure a transport\n            // for the mailer to send real emails.\n            'useFileTransport' => true,\n        ],\n    ],\n];\n"
  },
  {
    "path": "environments/dev/common/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/common/config/test-local.php",
    "content": "<?php\nreturn [\n    'components' => [\n        'db' => [\n            'dsn' => 'mysql:host=localhost;dbname=yii2advanced_test',\n        ],\n    ],\n];\n"
  },
  {
    "path": "environments/dev/console/config/main-local.php",
    "content": "<?php\nreturn [\n    'bootstrap' => ['gii'],\n    'modules' => [\n        'gii' => 'yii\\gii\\Module',\n    ],\n];\n"
  },
  {
    "path": "environments/dev/console/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/console/config/test-local.php",
    "content": "<?php\nreturn [\n];"
  },
  {
    "path": "environments/dev/frontend/config/codeception-local.php",
    "content": "<?php\n\nreturn yii\\helpers\\ArrayHelper::merge(\n    require dirname(dirname(__DIR__)) . '/common/config/codeception-local.php',\n    require __DIR__ . '/main.php',\n    require __DIR__ . '/main-local.php',\n    require __DIR__ . '/test.php',\n    require __DIR__ . '/test-local.php',\n    [\n    ]\n);\n"
  },
  {
    "path": "environments/dev/frontend/config/main-local.php",
    "content": "<?php\n\n$config = [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n            'cookieValidationKey' => '',\n        ],\n    ],\n];\n\nif (!YII_ENV_TEST) {\n    // configuration adjustments for 'dev' environment\n    $config['bootstrap'][] = 'debug';\n    $config['modules']['debug'] = [\n        'class' => 'yii\\debug\\Module',\n    ];\n\n    $config['bootstrap'][] = 'gii';\n    $config['modules']['gii'] = [\n        'class' => 'yii\\gii\\Module',\n    ];\n}\n\nreturn $config;\n"
  },
  {
    "path": "environments/dev/frontend/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/frontend/config/test-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/frontend/web/index-test.php",
    "content": "<?php\n\n// NOTE: Make sure this file is not accessible when deployed to production\nif (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {\n    die('You are not allowed to access this file.');\n}\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\n\nrequire __DIR__ . '/../../vendor/autoload.php';\nrequire __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../../common/config/bootstrap.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/../../common/config/main.php',\n    require __DIR__ . '/../../common/config/main-local.php',\n    require __DIR__ . '/../../common/config/test.php',\n    require __DIR__ . '/../../common/config/test-local.php',\n    require __DIR__ . '/../config/main.php',\n    require __DIR__ . '/../config/main-local.php',\n    require __DIR__ . '/../config/test.php',\n    require __DIR__ . '/../config/test-local.php'\n);\n\n(new yii\\web\\Application($config))->run();\n"
  },
  {
    "path": "environments/dev/frontend/web/index.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR__ . '/../../vendor/autoload.php';\nrequire __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../../common/config/bootstrap.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/../../common/config/main.php',\n    require __DIR__ . '/../../common/config/main-local.php',\n    require __DIR__ . '/../config/main.php',\n    require __DIR__ . '/../config/main-local.php'\n);\n\n(new yii\\web\\Application($config))->run();\n"
  },
  {
    "path": "environments/dev/frontend/web/robots.txt",
    "content": "User-agent: *\nDisallow: /"
  },
  {
    "path": "environments/dev/yii",
    "content": "#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/common/config/bootstrap.php';\nrequire __DIR__ . '/console/config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/common/config/main.php',\n    require __DIR__ . '/common/config/main-local.php',\n    require __DIR__ . '/console/config/main.php',\n    require __DIR__ . '/console/config/main-local.php'\n);\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n"
  },
  {
    "path": "environments/dev/yii_test",
    "content": "#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/common/config/bootstrap.php';\nrequire __DIR__ . '/console/config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/common/config/main.php',\n    require __DIR__ . '/common/config/main-local.php',\n    require __DIR__ . '/common/config/test.php',\n    require __DIR__ . '/common/config/test-local.php',\n    require __DIR__ . '/console/config/main.php',\n    require __DIR__ . '/console/config/main-local.php',\n    require __DIR__ . '/console/config/test.php',\n    require __DIR__ . '/console/config/test-local.php'\n);\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n"
  },
  {
    "path": "environments/dev/yii_test.bat",
    "content": "@echo off\n\nrem -------------------------------------------------------------\nrem  Yii command line bootstrap script for Windows.\nrem -------------------------------------------------------------\n\n@setlocal\n\nset YII_PATH=%~dp0\n\nif \"%PHP_COMMAND%\" == \"\" set PHP_COMMAND=php.exe\n\n\"%PHP_COMMAND%\" \"%YII_PATH%yii_test\" %*\n\n@endlocal\n"
  },
  {
    "path": "environments/index.php",
    "content": "<?php\n/**\n * The manifest of files that are local to specific environment.\n * This file returns a list of environments that the application\n * may be installed under. The returned data must be in the following\n * format:\n *\n * ```php\n * return [\n *     'environment name' => [\n *         'path' => 'directory storing the local files',\n *         'skipFiles'  => [\n *             // list of files that should only copied once and skipped if they already exist\n *         ],\n *         'setWritable' => [\n *             // list of directories that should be set writable\n *         ],\n *         'setExecutable' => [\n *             // list of files that should be set executable\n *         ],\n *         'setCookieValidationKey' => [\n *             // list of config files that need to be inserted with automatically generated cookie validation keys\n *         ],\n *         'createSymlink' => [\n *             // list of symlinks to be created. Keys are symlinks, and values are the targets.\n *         ],\n *     ],\n * ];\n * ```\n */\nreturn [\n    'Development' => [\n        'path' => 'dev',\n        'setWritable' => [\n            'backend/runtime',\n            'backend/web/assets',\n            'console/runtime',\n            'frontend/runtime',\n            'frontend/web/assets',\n        ],\n        'setExecutable' => [\n            'yii',\n            'yii_test',\n        ],\n        'setCookieValidationKey' => [\n            'backend/config/main-local.php',\n            'common/config/codeception-local.php',\n            'frontend/config/main-local.php',\n        ],\n    ],\n    'Production' => [\n        'path' => 'prod',\n        'setWritable' => [\n            'backend/runtime',\n            'backend/web/assets',\n            'console/runtime',\n            'frontend/runtime',\n            'frontend/web/assets',\n        ],\n        'setExecutable' => [\n            'yii',\n        ],\n        'setCookieValidationKey' => [\n            'backend/config/main-local.php',\n            'frontend/config/main-local.php',\n        ],\n    ],\n];\n"
  },
  {
    "path": "environments/prod/backend/config/main-local.php",
    "content": "<?php\nreturn [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n            'cookieValidationKey' => '',\n        ],\n    ],\n];\n"
  },
  {
    "path": "environments/prod/backend/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/backend/web/index.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\ndefined('YII_ENV') or define('YII_ENV', 'prod');\n\nrequire __DIR__ . '/../../vendor/autoload.php';\nrequire __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../../common/config/bootstrap.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/../../common/config/main.php',\n    require __DIR__ . '/../../common/config/main-local.php',\n    require __DIR__ . '/../config/main.php',\n    require __DIR__ . '/../config/main-local.php'\n);\n\n(new yii\\web\\Application($config))->run();\n"
  },
  {
    "path": "environments/prod/backend/web/robots.txt",
    "content": "User-agent: *\nDisallow: /"
  },
  {
    "path": "environments/prod/common/config/main-local.php",
    "content": "<?php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => 'mysql:host=localhost;dbname=yii2advanced',\n            'username' => 'root',\n            'password' => '',\n            'charset' => 'utf8',\n        ],\n        'mailer' => [\n            'class' => 'yii\\swiftmailer\\Mailer',\n            'viewPath' => '@common/mail',\n        ],\n    ],\n];\n"
  },
  {
    "path": "environments/prod/common/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/console/config/main-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/console/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/frontend/config/main-local.php",
    "content": "<?php\nreturn [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n            'cookieValidationKey' => '',\n        ],\n    ],\n];\n"
  },
  {
    "path": "environments/prod/frontend/config/params-local.php",
    "content": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/frontend/web/index.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\ndefined('YII_ENV') or define('YII_ENV', 'prod');\n\nrequire __DIR__ . '/../../vendor/autoload.php';\nrequire __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/../../common/config/bootstrap.php';\nrequire __DIR__ . '/../config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/../../common/config/main.php',\n    require __DIR__ . '/../../common/config/main-local.php',\n    require __DIR__ . '/../config/main.php',\n    require __DIR__ . '/../config/main-local.php'\n);\n\n(new yii\\web\\Application($config))->run();\n"
  },
  {
    "path": "environments/prod/frontend/web/robots.txt",
    "content": "User-agent: *\nAllow: /"
  },
  {
    "path": "environments/prod/yii",
    "content": "#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\ndefined('YII_ENV') or define('YII_ENV', 'prod');\n\nrequire __DIR__ . '/vendor/autoload.php';\nrequire __DIR__ . '/vendor/yiisoft/yii2/Yii.php';\nrequire __DIR__ . '/common/config/bootstrap.php';\nrequire __DIR__ . '/console/config/bootstrap.php';\n\n$config = yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/common/config/main.php',\n    require __DIR__ . '/common/config/main-local.php',\n    require __DIR__ . '/console/config/main.php',\n    require __DIR__ . '/console/config/main-local.php'\n);\n\n$application = new yii\\console\\Application($config);\n$exitCode = $application->run();\nexit($exitCode);\n"
  },
  {
    "path": "frontend/Dockerfile",
    "content": "FROM yiisoftware/yii2-php:7.2-apache\n\n# Change document root for Apache\nRUN sed -i -e 's|/app/web|/app/frontend/web|g' /etc/apache2/sites-available/000-default.conf"
  },
  {
    "path": "frontend/assets/AppAsset.php",
    "content": "<?php\n\nnamespace frontend\\assets;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * Main frontend application asset bundle.\n */\nclass AppAsset extends AssetBundle\n{\n    public $basePath = '@webroot';\n    public $baseUrl = '@web';\n    public $css = [\n        'css/site.css',\n    ];\n    public $js = [\n        'js/app.js'\n    ];\n    public $depends = [\n        'yii\\web\\YiiAsset',\n        'yii\\bootstrap4\\BootstrapAsset',\n    ];\n}\n"
  },
  {
    "path": "frontend/codeception.yml",
    "content": "namespace: frontend\\tests\nactor_suffix: Tester\npaths:\n    tests: tests\n    output: tests/_output\n    data: tests/_data\n    support: tests/_support\nbootstrap: _bootstrap.php\nsettings:\n    colors: true\n    memory_limit: 1024M\nmodules:\n    config:\n        Yii2:\n            configFile: 'config/codeception-local.php'\n"
  },
  {
    "path": "frontend/config/.gitignore",
    "content": "codeception-local.php\nmain-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "frontend/config/bootstrap.php",
    "content": "<?php\n"
  },
  {
    "path": "frontend/config/main.php",
    "content": "<?php\n$params = array_merge(\n    require __DIR__ . '/../../common/config/params.php',\n    require __DIR__ . '/../../common/config/params-local.php',\n    require __DIR__ . '/params.php',\n    require __DIR__ . '/params-local.php'\n);\n\nreturn [\n    'id' => 'app-frontend',\n    'name' => 'FreeCodeTube',\n    'basePath' => dirname(__DIR__),\n    'bootstrap' => ['log'],\n    'defaultRoute' => '/video/index',\n    'layout' => 'auth',\n    'controllerNamespace' => 'frontend\\controllers',\n    'components' => [\n        'request' => [\n            'csrfParam' => '_csrf-frontend',\n        ],\n        'user' => [\n            'identityClass' => 'common\\models\\User',\n            'enableAutoLogin' => true,\n            'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true],\n        ],\n        'session' => [\n            // this is the name of the session cookie used for login on the frontend\n            'name' => 'advanced-frontend',\n        ],\n        'log' => [\n            'traceLevel' => YII_DEBUG ? 3 : 0,\n            'targets' => [\n                [\n                    'class' => 'yii\\log\\FileTarget',\n                    'levels' => ['error', 'warning'],\n                ],\n            ],\n        ],\n        'errorHandler' => [\n            'errorAction' => 'site/error',\n        ],\n\n        'urlManager' => [\n            'enablePrettyUrl' => true,\n            'showScriptName' => false,\n            'rules' => [\n                '/c/<username>' => '/channel/view',\n                '/v/<id>' => '/video/view',\n            ],\n        ],\n\n        'assetManager' => [\n            'appendTimestamp' => true\n        ]\n\n    ],\n    'params' => $params,\n];\n"
  },
  {
    "path": "frontend/config/params.php",
    "content": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n];\n"
  },
  {
    "path": "frontend/config/test.php",
    "content": "<?php\nreturn [\n    'id' => 'app-frontend-tests',\n    'components' => [\n        'assetManager' => [\n            'basePath' => __DIR__ . '/../web/assets',\n        ],\n        'urlManager' => [\n            'showScriptName' => true,\n        ],\n        'request' => [\n            'cookieValidationKey' => 'test',\n        ],\n    ],\n];\n"
  },
  {
    "path": "frontend/controllers/ChannelController.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 9:48 AM\n */\n\nnamespace frontend\\controllers;\n\n\nuse common\\models\\Subscriber;\nuse common\\models\\User;\nuse common\\models\\Video;\nuse yii\\data\\ActiveDataProvider;\nuse yii\\filters\\AccessControl;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\n/**\n * Class ChannelController\n *\n * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n * @package frontend\\controllers\n */\nclass ChannelController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['subscribe'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'roles' => ['@']\n                    ]\n                ]\n            ],\n        ];\n    }\n\n    public function actionView($username)\n    {\n        $channel = $this->findChannel($username);\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => Video::find()->creator($channel->id)->published()\n        ]);\n\n        return $this->render('view', [\n            'channel' => $channel,\n            'dataProvider' => $dataProvider\n        ]);\n    }\n\n    public function actionSubscribe($username)\n    {\n        $channel = $this->findChannel($username);\n\n        $userId = \\Yii::$app->user->id;\n        $subscriber = $channel->isSubscribed($userId);\n        if (!$subscriber) {\n            $subscriber = new Subscriber();\n            $subscriber->channel_id = $channel->id;\n            $subscriber->user_id = $userId;\n            $subscriber->created_at = time();\n            $subscriber->save();\n            \\Yii::$app->mailer->compose([\n                'html' => 'subscriber-html', 'text' => 'subscriber-text'\n            ], [\n                'channel' => $channel,\n                'user' => \\Yii::$app->user->identity\n            ])\n                ->setFrom(\\Yii::$app->params['senderEmail'])\n                ->setTo($channel->email)\n                ->setSubject('You have new subscriber')\n                ->send();\n\n        } else {\n            $subscriber->delete();\n        }\n\n        return $this->renderAjax('_subscribe', [\n            'channel' => $channel\n        ]);\n    }\n\n    /**\n     * @param $username\n     * @return \\common\\models\\User|null\n     * @throws \\yii\\web\\NotFoundHttpException\n     * @author Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n     */\n    protected function findChannel($username)\n    {\n        $channel = User::findByUsername($username);\n        if (!$channel) {\n            throw new NotFoundHttpException(\"Channel does not exist\");\n        }\n\n        return $channel;\n    }\n}"
  },
  {
    "path": "frontend/controllers/CommentController.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/12/2020\n * Time: 8:58 AM\n */\n\nnamespace frontend\\controllers;\n\n\nuse common\\models\\Comment;\nuse common\\models\\User;\nuse yii\\filters\\AccessControl;\nuse yii\\filters\\ContentNegotiator;\nuse yii\\filters\\VerbFilter;\nuse yii\\web\\Controller;\nuse yii\\web\\ForbiddenHttpException;\nuse yii\\web\\NotFoundHttpException;\nuse yii\\web\\Response;\n\n/**\n * Class CommentController\n *\n * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n * @package frontend\\controllers\n */\nclass CommentController extends Controller\n{\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['create'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'roles' => ['@']\n                    ]\n                ]\n            ],\n            'content' => [\n                'class' => ContentNegotiator::class,\n                'only' => ['create', 'update', 'delete', 'reply', 'by-parent', 'pin'],\n                'formats' => [\n                    'application/json' => Response::FORMAT_JSON\n                ]\n            ],\n            'verb' => [\n                'class' => VerbFilter::class,\n                'actions' => [\n                    'delete' => ['POST']\n                ]\n            ]\n        ];\n    }\n\n    public function actionCreate($id)\n    {\n        $comment = new Comment();\n        $comment->video_id = $id;\n        if ($comment->load(\\Yii::$app->request->post(), '') && $comment->save()) {\n            return [\n                'success' => true,\n                'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [\n                    'model' => $comment,\n                ])\n            ];\n        }\n\n        return [\n            'success' => false,\n            'errors' => $comment->errors\n        ];\n    }\n\n    public function actionDelete($id)\n    {\n        $comment = $this->findModel($id);\n        if ($comment->belongsTo(\\Yii::$app->user->id) || $comment->video->belongsTo(\\Yii::$app->user->id)) {\n            $comment->delete();\n\n            return ['success' => true];\n        }\n        throw new ForbiddenHttpException();\n    }\n\n    public function actionUpdate($id)\n    {\n        $comment = $this->findModel($id);\n        if ($comment->belongsTo(\\Yii::$app->user->id)) {\n            $commentText = \\Yii::$app->request->post('comment');\n            $comment->comment = $commentText;\n            if ($comment->save()) {\n                return [\n                    'success' => true,\n                    'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [\n                        'model' => $comment,\n                    ])\n                ];\n            }\n\n            return [\n                'success' => false,\n                'errors' => $comment->errors\n            ];\n        }\n        throw new ForbiddenHttpException();\n    }\n\n    public function actionReply()\n    {\n        $parentId = \\Yii::$app->request->post('parent_id');\n        $parentComment = $this->findModel($parentId);\n\n        $commentText = \\Yii::$app->request->post('comment');\n        $mentionUsername = \\Yii::$app->request->post('mention');\n        if (strpos($commentText, '@' . $mentionUsername) !== 0) {\n            $mentionUsername = null;\n        } else {\n            $commentText = trim(str_replace('@' . $mentionUsername, '', $commentText));\n        }\n\n        if ($mentionUsername) {\n            $mentionUser = User::findByUsername($mentionUsername);\n            if (!$mentionUser) {\n                $mentionUsername = null;\n            } else {\n                $currentUser = \\Yii::$app->user->identity;\n                \\Yii::$app->mailer->compose([\n                    'html' => 'mention-html', 'text' => 'mention-text'\n                ], [\n                    'comment' => $commentText,\n                    'channel' => $mentionUser,\n                    'user' => $currentUser\n                ])\n                    ->setFrom(\\Yii::$app->params['senderEmail'])\n                    ->setTo($mentionUser->email)\n                    ->setSubject('User '.$currentUser->username.' mention you in a comment')\n                    ->send();\n            }\n        }\n\n        $comment = new Comment();\n        $comment->comment = $commentText;\n        $comment->mention = $mentionUsername;\n        $comment->video_id = $parentComment->video_id;\n\n        if ($parentComment->parent_id) {\n            $comment->parent_id = $parentComment->parent_id;\n        } else {\n            $comment->parent_id = $parentId;\n        }\n\n\n        if ($comment->save()) {\n            return [\n                'success' => true,\n                'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [\n                    'model' => $comment,\n                ])\n            ];\n        }\n\n        return [\n            'success' => false,\n            'errors' => $comment->errors\n        ];\n    }\n\n    public function actionByParent($id)\n    {\n        $parentComment = $this->findModel($id);\n\n        $finalContent = \"\";\n        foreach ($parentComment->comments as $comment) {\n            $finalContent .= $this->renderPartial('@frontend/views/video/_comment_item', [\n                'model' => $comment,\n            ]);\n        }\n\n        return [\n            'success' => true,\n            'comments' => $finalContent\n        ];\n    }\n\n    public function actionPin($id)\n    {\n        $comment = $this->findModel($id);\n        if ($comment->video->belongsTo(\\Yii::$app->user->id)) {\n            if ($comment->pinned) {\n                $comment->pinned = 0;\n            } else {\n                Comment::updateAll(['pinned' => 0], ['video_id' => $comment->video]);\n                $comment->pinned = 1;\n            }\n            if ($comment->save()) {\n                return [\n                    'success' => true,\n                    'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [\n                        'model' => $comment,\n                    ])\n                ];\n            }\n\n            return [\n                'success' => false,\n                'errors' => $comment->errors\n            ];\n        }\n\n        throw new ForbiddenHttpException();\n    }\n\n    protected function findModel($id)\n    {\n        $comment = Comment::findOne($id);\n        if (!$comment) {\n            throw new NotFoundHttpException;\n        }\n\n        return $comment;\n    }\n}"
  },
  {
    "path": "frontend/controllers/SiteController.php",
    "content": "<?php\nnamespace frontend\\controllers;\n\nuse frontend\\models\\ResendVerificationEmailForm;\nuse frontend\\models\\VerifyEmailForm;\nuse Yii;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\web\\BadRequestHttpException;\nuse yii\\web\\Controller;\nuse yii\\filters\\VerbFilter;\nuse yii\\filters\\AccessControl;\nuse common\\models\\LoginForm;\nuse frontend\\models\\PasswordResetRequestForm;\nuse frontend\\models\\ResetPasswordForm;\nuse frontend\\models\\SignupForm;\nuse frontend\\models\\ContactForm;\n\n/**\n * Site controller\n */\nclass SiteController extends Controller\n{\n    /**\n     * {@inheritdoc}\n     */\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::className(),\n                'only' => ['logout', 'signup'],\n                'rules' => [\n                    [\n                        'actions' => ['signup'],\n                        'allow' => true,\n                        'roles' => ['?'],\n                    ],\n                    [\n                        'actions' => ['logout'],\n                        'allow' => true,\n                        'roles' => ['@'],\n                    ],\n                ],\n            ],\n            'verbs' => [\n                'class' => VerbFilter::className(),\n                'actions' => [\n                    'logout' => ['post'],\n                ],\n            ],\n        ];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function actions()\n    {\n        return [\n            'error' => [\n                'class' => 'yii\\web\\ErrorAction',\n            ],\n            'captcha' => [\n                'class' => 'yii\\captcha\\CaptchaAction',\n                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,\n            ],\n        ];\n    }\n\n    /**\n     * Displays homepage.\n     *\n     * @return mixed\n     */\n    public function actionIndex()\n    {\n        return $this->render('index');\n    }\n\n    /**\n     * Logs in a user.\n     *\n     * @return mixed\n     */\n    public function actionLogin()\n    {\n        if (!Yii::$app->user->isGuest) {\n            return $this->goHome();\n        }\n\n        $model = new LoginForm();\n        if ($model->load(Yii::$app->request->post()) && $model->login()) {\n            return $this->goBack();\n        } else {\n            $model->password = '';\n\n            return $this->render('login', [\n                'model' => $model,\n            ]);\n        }\n    }\n\n    /**\n     * Logs out the current user.\n     *\n     * @return mixed\n     */\n    public function actionLogout()\n    {\n        Yii::$app->user->logout();\n\n        return $this->goHome();\n    }\n\n    /**\n     * Signs user up.\n     *\n     * @return mixed\n     */\n    public function actionSignup()\n    {\n        $model = new SignupForm();\n        if ($model->load(Yii::$app->request->post()) && $model->signup()) {\n            Yii::$app->session->setFlash('success', 'Thank you for registration. Please check your inbox for verification email.');\n            return $this->goHome();\n        }\n\n        return $this->render('signup', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Requests password reset.\n     *\n     * @return mixed\n     */\n    public function actionRequestPasswordReset()\n    {\n        $model = new PasswordResetRequestForm();\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            if ($model->sendEmail()) {\n                Yii::$app->session->setFlash('success', 'Check your email for further instructions.');\n\n                return $this->goHome();\n            } else {\n                Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for the provided email address.');\n            }\n        }\n\n        return $this->render('requestPasswordResetToken', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Resets password.\n     *\n     * @param string $token\n     * @return mixed\n     * @throws BadRequestHttpException\n     */\n    public function actionResetPassword($token)\n    {\n        try {\n            $model = new ResetPasswordForm($token);\n        } catch (InvalidArgumentException $e) {\n            throw new BadRequestHttpException($e->getMessage());\n        }\n\n        if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) {\n            Yii::$app->session->setFlash('success', 'New password saved.');\n\n            return $this->goHome();\n        }\n\n        return $this->render('resetPassword', [\n            'model' => $model,\n        ]);\n    }\n\n    /**\n     * Verify email address\n     *\n     * @param string $token\n     * @throws BadRequestHttpException\n     * @return yii\\web\\Response\n     */\n    public function actionVerifyEmail($token)\n    {\n        try {\n            $model = new VerifyEmailForm($token);\n        } catch (InvalidArgumentException $e) {\n            throw new BadRequestHttpException($e->getMessage());\n        }\n        if ($user = $model->verifyEmail()) {\n            if (Yii::$app->user->login($user)) {\n                Yii::$app->session->setFlash('success', 'Your email has been confirmed!');\n                return $this->goHome();\n            }\n        }\n\n        Yii::$app->session->setFlash('error', 'Sorry, we are unable to verify your account with provided token.');\n        return $this->goHome();\n    }\n\n    /**\n     * Resend verification email\n     *\n     * @return mixed\n     */\n    public function actionResendVerificationEmail()\n    {\n        $model = new ResendVerificationEmailForm();\n        if ($model->load(Yii::$app->request->post()) && $model->validate()) {\n            if ($model->sendEmail()) {\n                Yii::$app->session->setFlash('success', 'Check your email for further instructions.');\n                return $this->goHome();\n            }\n            Yii::$app->session->setFlash('error', 'Sorry, we are unable to resend verification email for the provided email address.');\n        }\n\n        return $this->render('resendVerificationEmail', [\n            'model' => $model\n        ]);\n    }\n}\n"
  },
  {
    "path": "frontend/controllers/VideoController.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:56 AM\n */\n\nnamespace frontend\\controllers;\n\n\nuse common\\models\\Comment;\nuse common\\models\\Video;\nuse common\\models\\VideoLike;\nuse common\\models\\VideoView;\nuse yii\\data\\ActiveDataProvider;\nuse yii\\filters\\AccessControl;\nuse yii\\filters\\VerbFilter;\nuse yii\\web\\Controller;\nuse yii\\web\\NotFoundHttpException;\n\n/**\n * Class VideoController\n *\n * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>\n * @package frontend\\controllers\n */\nclass VideoController extends Controller\n{\n\n    public function behaviors()\n    {\n        return [\n            'access' => [\n                'class' => AccessControl::class,\n                'only' => ['like', 'dislike', 'history'],\n                'rules' => [\n                    [\n                        'allow' => true,\n                        'roles' => ['@']\n                    ]\n                ]\n            ],\n            'verb' => [\n                'class' => VerbFilter::class,\n                'actions' => [\n                    'like' => ['post'],\n                    'dislike' => ['post'],\n                ]\n            ]\n        ];\n    }\n\n    public function actionIndex()\n    {\n        $this->layout = 'main';\n        $dataProvider = new ActiveDataProvider([\n            'query' => Video::find()->with('createdBy')->published()->latest(),\n        ]);\n\n        return $this->render('index', [\n            'dataProvider' => $dataProvider\n        ]);\n    }\n\n    public function actionView($id)\n    {\n        $this->layout = 'auth';\n        $video = $this->findVideo($id);\n\n        $videoView = new VideoView();\n        $videoView->video_id = $id;\n        $videoView->user_id = \\Yii::$app->user->id;\n        $videoView->created_at = time();\n        $videoView->save();\n\n        $similarVideos = Video::find()\n            ->published()\n            ->byKeyword($video->title)\n            ->andWhere(['NOT', ['video_id' => $id]])\n            ->limit(10)\n            ->all();\n\n        $comments = $video\n            ->getComments()\n            ->with(['createdBy'])\n            ->parent()\n            ->latest()\n            ->all();\n\n        return $this->render('view', [\n            'model' => $video,\n            'comments' => $comments,\n            'similarVideos' => $similarVideos\n        ]);\n    }\n\n    public function actionLike($id)\n    {\n        $video = $this->findVideo($id);\n        $userId = \\Yii::$app->user->id;\n\n        $videoLikeDislike = VideoLike::find()\n            ->userIdVideoId($userId, $id)\n            ->one();\n        if (!$videoLikeDislike) {\n            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_LIKE);\n        } else if ($videoLikeDislike->type == VideoLike::TYPE_LIKE) {\n            $videoLikeDislike->delete();\n        } else {\n            $videoLikeDislike->delete();\n            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_LIKE);\n        }\n\n        return $this->renderAjax('_buttons', [\n            'model' => $video\n        ]);\n    }\n\n    public function actionDislike($id)\n    {\n        $video = $this->findVideo($id);\n        $userId = \\Yii::$app->user->id;\n\n        $videoLikeDislike = VideoLike::find()\n            ->userIdVideoId($userId, $id)\n            ->one();\n        if (!$videoLikeDislike) {\n            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_DISLIKE);\n        } else if ($videoLikeDislike->type == VideoLike::TYPE_DISLIKE) {\n            $videoLikeDislike->delete();\n        } else {\n            $videoLikeDislike->delete();\n            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_DISLIKE);\n        }\n\n        return $this->renderAjax('_buttons', [\n            'model' => $video\n        ]);\n    }\n\n    public function actionSearch($keyword)\n    {\n        $this->layout = 'main';\n        $query = Video::find()\n            ->with('createdBy')\n            ->published()\n            ->latest();\n        if ($keyword) {\n            $query->byKeyword($keyword);\n        }\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query\n        ]);\n\n        return $this->render('search', [\n            'dataProvider' => $dataProvider\n        ]);\n    }\n\n    public function actionHistory()\n    {\n        $this->layout = 'main';\n        $query = Video::find()\n            ->alias('v')\n            ->innerJoin(\"(SELECT video_id, MAX(created_at) as max_date FROM video_view\n                    WHERE user_id = :userId\n                    GROUP BY video_id) vv\", 'vv.video_id = v.video_id', [\n                'userId' => \\Yii::$app->user->id\n            ])\n            ->orderBy(\"vv.max_date DESC\");\n\n        $dataProvider = new ActiveDataProvider([\n            'query' => $query\n        ]);\n\n        return $this->render('history', [\n            'dataProvider' => $dataProvider\n        ]);\n    }\n\n    protected function findVideo($id)\n    {\n        $video = Video::findOne($id);\n        if (!$video) {\n            throw new NotFoundHttpException(\"Video does not exit\");\n        }\n\n        return $video;\n    }\n\n    protected function saveLikeDislike($videoId, $userId, $type)\n    {\n        $videoLikeDislike = new VideoLike();\n        $videoLikeDislike->video_id = $videoId;\n        $videoLikeDislike->user_id = $userId;\n        $videoLikeDislike->type = $type;\n        $videoLikeDislike->created_at = time();\n        $videoLikeDislike->save();\n    }\n}"
  },
  {
    "path": "frontend/models/PasswordResetRequestForm.php",
    "content": "<?php\nnamespace frontend\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse common\\models\\User;\n\n/**\n * Password reset request form\n */\nclass PasswordResetRequestForm extends Model\n{\n    public $email;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            ['email', 'trim'],\n            ['email', 'required'],\n            ['email', 'email'],\n            ['email', 'exist',\n                'targetClass' => '\\common\\models\\User',\n                'filter' => ['status' => User::STATUS_ACTIVE],\n                'message' => 'There is no user with this email address.'\n            ],\n        ];\n    }\n\n    /**\n     * Sends an email with a link, for resetting the password.\n     *\n     * @return bool whether the email was send\n     */\n    public function sendEmail()\n    {\n        /* @var $user User */\n        $user = User::findOne([\n            'status' => User::STATUS_ACTIVE,\n            'email' => $this->email,\n        ]);\n\n        if (!$user) {\n            return false;\n        }\n        \n        if (!User::isPasswordResetTokenValid($user->password_reset_token)) {\n            $user->generatePasswordResetToken();\n            if (!$user->save()) {\n                return false;\n            }\n        }\n\n        return Yii::$app\n            ->mailer\n            ->compose(\n                ['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text'],\n                ['user' => $user]\n            )\n            ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])\n            ->setTo($this->email)\n            ->setSubject('Password reset for ' . Yii::$app->name)\n            ->send();\n    }\n}\n"
  },
  {
    "path": "frontend/models/ResendVerificationEmailForm.php",
    "content": "<?php\n\n\nnamespace frontend\\models;\n\nuse Yii;\nuse common\\models\\User;\nuse yii\\base\\Model;\n\nclass ResendVerificationEmailForm extends Model\n{\n    /**\n     * @var string\n     */\n    public $email;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            ['email', 'trim'],\n            ['email', 'required'],\n            ['email', 'email'],\n            ['email', 'exist',\n                'targetClass' => '\\common\\models\\User',\n                'filter' => ['status' => User::STATUS_INACTIVE],\n                'message' => 'There is no user with this email address.'\n            ],\n        ];\n    }\n\n    /**\n     * Sends confirmation email to user\n     *\n     * @return bool whether the email was sent\n     */\n    public function sendEmail()\n    {\n        $user = User::findOne([\n            'email' => $this->email,\n            'status' => User::STATUS_INACTIVE\n        ]);\n\n        if ($user === null) {\n            return false;\n        }\n\n        return Yii::$app\n            ->mailer\n            ->compose(\n                ['html' => 'emailVerify-html', 'text' => 'emailVerify-text'],\n                ['user' => $user]\n            )\n            ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])\n            ->setTo($this->email)\n            ->setSubject('Account registration at ' . Yii::$app->name)\n            ->send();\n    }\n}\n"
  },
  {
    "path": "frontend/models/ResetPasswordForm.php",
    "content": "<?php\nnamespace frontend\\models;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\nuse common\\models\\User;\n\n/**\n * Password reset form\n */\nclass ResetPasswordForm extends Model\n{\n    public $password;\n\n    /**\n     * @var \\common\\models\\User\n     */\n    private $_user;\n\n\n    /**\n     * Creates a form model given a token.\n     *\n     * @param string $token\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     * @throws InvalidArgumentException if token is empty or not valid\n     */\n    public function __construct($token, $config = [])\n    {\n        if (empty($token) || !is_string($token)) {\n            throw new InvalidArgumentException('Password reset token cannot be blank.');\n        }\n        $this->_user = User::findByPasswordResetToken($token);\n        if (!$this->_user) {\n            throw new InvalidArgumentException('Wrong password reset token.');\n        }\n        parent::__construct($config);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            ['password', 'required'],\n            ['password', 'string', 'min' => 6],\n        ];\n    }\n\n    /**\n     * Resets password.\n     *\n     * @return bool if password was reset.\n     */\n    public function resetPassword()\n    {\n        $user = $this->_user;\n        $user->setPassword($this->password);\n        $user->removePasswordResetToken();\n\n        return $user->save(false);\n    }\n}\n"
  },
  {
    "path": "frontend/models/SignupForm.php",
    "content": "<?php\nnamespace frontend\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse common\\models\\User;\n\n/**\n * Signup form\n */\nclass SignupForm extends Model\n{\n    public $username;\n    public $email;\n    public $password;\n\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rules()\n    {\n        return [\n            ['username', 'trim'],\n            ['username', 'required'],\n            ['username', 'unique', 'targetClass' => '\\common\\models\\User', 'message' => 'This username has already been taken.'],\n            ['username', 'string', 'min' => 2, 'max' => 255],\n\n            ['email', 'trim'],\n            ['email', 'required'],\n            ['email', 'email'],\n            ['email', 'string', 'max' => 255],\n            ['email', 'unique', 'targetClass' => '\\common\\models\\User', 'message' => 'This email address has already been taken.'],\n\n            ['password', 'required'],\n            ['password', 'string', 'min' => 6],\n        ];\n    }\n\n    /**\n     * Signs user up.\n     *\n     * @return bool whether the creating new account was successful and email was sent\n     */\n    public function signup()\n    {\n        if (!$this->validate()) {\n            return null;\n        }\n        \n        $user = new User();\n        $user->username = $this->username;\n        $user->email = $this->email;\n        $user->setPassword($this->password);\n        $user->generateAuthKey();\n        $user->generateEmailVerificationToken();\n        return $user->save() && $this->sendEmail($user);\n\n    }\n\n    /**\n     * Sends confirmation email to user\n     * @param User $user user model to with email should be send\n     * @return bool whether the email was sent\n     */\n    protected function sendEmail($user)\n    {\n        return Yii::$app\n            ->mailer\n            ->compose(\n                ['html' => 'emailVerify-html', 'text' => 'emailVerify-text'],\n                ['user' => $user]\n            )\n            ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])\n            ->setTo($this->email)\n            ->setSubject('Account registration at ' . Yii::$app->name)\n            ->send();\n    }\n}\n"
  },
  {
    "path": "frontend/models/VerifyEmailForm.php",
    "content": "<?php\n\nnamespace frontend\\models;\n\nuse common\\models\\User;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\n\nclass VerifyEmailForm extends Model\n{\n    /**\n     * @var string\n     */\n    public $token;\n\n    /**\n     * @var User\n     */\n    private $_user;\n\n\n    /**\n     * Creates a form model with given token.\n     *\n     * @param string $token\n     * @param array $config name-value pairs that will be used to initialize the object properties\n     * @throws InvalidArgumentException if token is empty or not valid\n     */\n    public function __construct($token, array $config = [])\n    {\n        if (empty($token) || !is_string($token)) {\n            throw new InvalidArgumentException('Verify email token cannot be blank.');\n        }\n        $this->_user = User::findByVerificationToken($token);\n        if (!$this->_user) {\n            throw new InvalidArgumentException('Wrong verify email token.');\n        }\n        parent::__construct($config);\n    }\n\n    /**\n     * Verify email\n     *\n     * @return User|null the saved model or null if saving fails\n     */\n    public function verifyEmail()\n    {\n        $user = $this->_user;\n        $user->status = User::STATUS_ACTIVE;\n        return $user->save(false) ? $user : null;\n    }\n}\n"
  },
  {
    "path": "frontend/runtime/.gitignore",
    "content": "*\n!.gitignore"
  },
  {
    "path": "frontend/tests/_bootstrap.php",
    "content": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\ndefined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');\n\nrequire_once YII_APP_BASE_PATH . '/vendor/autoload.php';\nrequire_once YII_APP_BASE_PATH . '/vendor/yiisoft/yii2/Yii.php';\nrequire_once YII_APP_BASE_PATH . '/common/config/bootstrap.php';\nrequire_once __DIR__ . '/../config/bootstrap.php';\n"
  },
  {
    "path": "frontend/tests/_data/login_data.php",
    "content": "<?php\nreturn [\n    [\n        'username' => 'erau',\n        'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',\n        // password_0\n        'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne',\n        'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490',\n        'created_at' => '1392559490',\n        'updated_at' => '1392559490',\n        'email' => 'sfriesen@jenkins.info',\n    ],\n    [\n        'username' => 'test.test',\n        'auth_key' => 'O87GkY3_UfmMHYkyezZ7QLfmkKNsllzT',\n        // Test1234\n        'password_hash' => 'O87GkY3_UfmMHYkyezZ7QLfmkKNsllzT',\n        'email' => 'test@mail.com',\n        'status' => '9',\n        'created_at' => '1548675330',\n        'updated_at' => '1548675330',\n        'verification_token' => '4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330',\n    ],\n];\n"
  },
  {
    "path": "frontend/tests/_data/user.php",
    "content": "<?php\n\nreturn [\n    [\n        'username' => 'okirlin',\n        'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv',\n        'password_hash' => '$2y$13$CXT0Rkle1EMJ/c1l5bylL.EylfmQ39O5JlHJVFpNn618OUS1HwaIi',\n        'password_reset_token' => 't5GU9NwpuGYSfb7FEZMAxqtuz2PkEvv_' . time(),\n        'created_at' => '1391885313',\n        'updated_at' => '1391885313',\n        'email' => 'brady.renner@rutherford.com',\n    ],\n    [\n        'username' => 'troy.becker',\n        'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS3Lp',\n        'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2',\n        'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_' . time(),\n        'created_at' => '1391885313',\n        'updated_at' => '1391885313',\n        'email' => 'nicolas.dianna@hotmail.com',\n        'status' => '0',\n    ],\n    [\n        'username' => 'test.test',\n        'auth_key' => 'O87GkY3_UfmMHYkyezZ7QLfmkKNsllzT',\n        //Test1234\n        'password_hash' => '$2y$13$d17z0w/wKC4LFwtzBcmx6up4jErQuandJqhzKGKczfWuiEhLBtQBK',\n        'email' => 'test@mail.com',\n        'status' => '9',\n        'created_at' => '1548675330',\n        'updated_at' => '1548675330',\n        'verification_token' => '4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330',\n    ],\n    [\n        'username' => 'test2.test',\n        'auth_key' => '4XXdVqi3rDpa_a6JH6zqVreFxUPcUPvJ',\n        //Test1234\n        'password_hash' => '$2y$13$d17z0w/wKC4LFwtzBcmx6up4jErQuandJqhzKGKczfWuiEhLBtQBK',\n        'email' => 'test2@mail.com',\n        'status' => '10',\n        'created_at' => '1548675330',\n        'updated_at' => '1548675330',\n        'verification_token' => 'already_used_token_1548675330',\n    ],\n];\n"
  },
  {
    "path": "frontend/tests/_output/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "frontend/tests/_support/.gitignore",
    "content": "_generated\n"
  },
  {
    "path": "frontend/tests/_support/FunctionalTester.php",
    "content": "<?php\nnamespace frontend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)\n * @method void execute($callable)\n * @method void expectTo($prediction)\n * @method void expect($prediction)\n * @method void amGoingTo($argumentation)\n * @method void am($role)\n * @method void lookForwardTo($achieveValue)\n * @method void comment($description)\n * @method \\Codeception\\Lib\\Friend haveFriend($name, $actorClass = NULL)\n *\n * @SuppressWarnings(PHPMD)\n */\nclass FunctionalTester extends \\Codeception\\Actor\n{\n    use _generated\\FunctionalTesterActions;\n\n\n    public function seeValidationError($message)\n    {\n        $this->see($message, '.help-block');\n    }\n\n    public function dontSeeValidationError($message)\n    {\n        $this->dontSee($message, '.help-block');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/_support/UnitTester.php",
    "content": "<?php\nnamespace frontend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)\n * @method void execute($callable)\n * @method void expectTo($prediction)\n * @method void expect($prediction)\n * @method void amGoingTo($argumentation)\n * @method void am($role)\n * @method void lookForwardTo($achieveValue)\n * @method void comment($description)\n * @method \\Codeception\\Lib\\Friend haveFriend($name, $actorClass = NULL)\n *\n * @SuppressWarnings(PHPMD)\n */\nclass UnitTester extends \\Codeception\\Actor\n{\n    use _generated\\UnitTesterActions;\n   /**\n    * Define custom actions here\n    */\n}\n"
  },
  {
    "path": "frontend/tests/acceptance/HomeCest.php",
    "content": "<?php\nnamespace frontend\\tests\\acceptance;\n\nuse frontend\\tests\\AcceptanceTester;\nuse yii\\helpers\\Url;\n\nclass HomeCest\n{\n    public function checkHome(AcceptanceTester $I)\n    {\n        $I->amOnPage(Url::toRoute('/site/index'));\n        $I->see('My Application');\n\n        $I->seeLink('About');\n        $I->click('About');\n        $I->wait(2); // wait for page to be opened\n\n        $I->see('This is the About page.');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/acceptance/_bootstrap.php",
    "content": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and use it in Cepts.\n *\n * ```php\n * // Here _bootstrap.php\n * \\Codeception\\Util\\Fixtures::add('user1', ['name' => 'davert']);\n * ```\n *\n * In Cept\n *\n * ```php\n * \\Codeception\\Util\\Fixtures::get('user1');\n * ```\n */"
  },
  {
    "path": "frontend/tests/acceptance.suite.yml.example",
    "content": "suite_namespace: frontend\\tests\\acceptance\nactor: AcceptanceTester\nmodules:\n    enabled:\n        - WebDriver:\n            url: http://localhost:8080\n            browser: firefox\n        - Yii2:\n            part: init\n"
  },
  {
    "path": "frontend/tests/functional/AboutCest.php",
    "content": "<?php\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\nclass AboutCest\n{\n    public function checkAbout(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/about');\n        $I->see('About', 'h1');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/functional/ContactCest.php",
    "content": "<?php\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\n/* @var $scenario \\Codeception\\Scenario */\n\nclass ContactCest\n{\n    public function _before(FunctionalTester $I)\n    {\n        $I->amOnPage(['site/contact']);\n    }\n\n    public function checkContact(FunctionalTester $I)\n    {\n        $I->see('Contact', 'h1');\n    }\n\n    public function checkContactSubmitNoData(FunctionalTester $I)\n    {\n        $I->submitForm('#contact-form', []);\n        $I->see('Contact', 'h1');\n        $I->seeValidationError('Name cannot be blank');\n        $I->seeValidationError('Email cannot be blank');\n        $I->seeValidationError('Subject cannot be blank');\n        $I->seeValidationError('Body cannot be blank');\n        $I->seeValidationError('The verification code is incorrect');\n    }\n\n    public function checkContactSubmitNotCorrectEmail(FunctionalTester $I)\n    {\n        $I->submitForm('#contact-form', [\n            'ContactForm[name]' => 'tester',\n            'ContactForm[email]' => 'tester.email',\n            'ContactForm[subject]' => 'test subject',\n            'ContactForm[body]' => 'test content',\n            'ContactForm[verifyCode]' => 'testme',\n        ]);\n        $I->seeValidationError('Email is not a valid email address.');\n        $I->dontSeeValidationError('Name cannot be blank');\n        $I->dontSeeValidationError('Subject cannot be blank');\n        $I->dontSeeValidationError('Body cannot be blank');\n        $I->dontSeeValidationError('The verification code is incorrect');\n    }\n\n    public function checkContactSubmitCorrectData(FunctionalTester $I)\n    {\n        $I->submitForm('#contact-form', [\n            'ContactForm[name]' => 'tester',\n            'ContactForm[email]' => 'tester@example.com',\n            'ContactForm[subject]' => 'test subject',\n            'ContactForm[body]' => 'test content',\n            'ContactForm[verifyCode]' => 'testme',\n        ]);\n        $I->seeEmailIsSent();\n        $I->see('Thank you for contacting us. We will respond to you as soon as possible.');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/functional/HomeCest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\nclass HomeCest\n{\n    public function checkOpen(FunctionalTester $I)\n    {\n        $I->amOnPage(\\Yii::$app->homeUrl);\n        $I->see('My Application');\n        $I->seeLink('About');\n        $I->click('About');\n        $I->see('This is the About page.');\n    }\n}"
  },
  {
    "path": "frontend/tests/functional/LoginCest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\nuse common\\fixtures\\UserFixture;\n\nclass LoginCest\n{\n    /**\n     * Load fixtures before db transaction begin\n     * Called in _before()\n     * @see \\Codeception\\Module\\Yii2::_before()\n     * @see \\Codeception\\Module\\Yii2::loadFixtures()\n     * @return array\n     */\n    public function _fixtures()\n    {\n        return [\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'login_data.php',\n            ],\n        ];\n    }\n\n    public function _before(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/login');\n    }\n\n    protected function formParams($login, $password)\n    {\n        return [\n            'LoginForm[username]' => $login,\n            'LoginForm[password]' => $password,\n        ];\n    }\n\n    public function checkEmpty(FunctionalTester $I)\n    {\n        $I->submitForm('#login-form', $this->formParams('', ''));\n        $I->seeValidationError('Username cannot be blank.');\n        $I->seeValidationError('Password cannot be blank.');\n    }\n\n    public function checkWrongPassword(FunctionalTester $I)\n    {\n        $I->submitForm('#login-form', $this->formParams('admin', 'wrong'));\n        $I->seeValidationError('Incorrect username or password.');\n    }\n\n    public function checkInactiveAccount(FunctionalTester $I)\n    {\n        $I->submitForm('#login-form', $this->formParams('test.test', 'Test1234'));\n        $I->seeValidationError('Incorrect username or password');\n    }\n\n    public function checkValidLogin(FunctionalTester $I)\n    {\n        $I->submitForm('#login-form', $this->formParams('erau', 'password_0'));\n        $I->see('Logout (erau)', 'form button[type=submit]');\n        $I->dontSeeLink('Login');\n        $I->dontSeeLink('Signup');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/functional/ResendVerificationEmailCest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\tests\\FunctionalTester;\n\nclass ResendVerificationEmailCest\n{\n    protected $formId = '#resend-verification-email-form';\n\n\n    /**\n     * Load fixtures before db transaction begin\n     * Called in _before()\n     * @see \\Codeception\\Module\\Yii2::_before()\n     * @see \\Codeception\\Module\\Yii2::loadFixtures()\n     * @return array\n     */\n    public function _fixtures()\n    {\n        return [\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php',\n            ],\n        ];\n    }\n\n    public function _before(FunctionalTester $I)\n    {\n        $I->amOnRoute('/site/resend-verification-email');\n    }\n\n    protected function formParams($email)\n    {\n        return [\n            'ResendVerificationEmailForm[email]' => $email\n        ];\n    }\n\n    public function checkPage(FunctionalTester $I)\n    {\n        $I->see('Resend verification email', 'h1');\n        $I->see('Please fill out your email. A verification email will be sent there.');\n    }\n\n    public function checkEmptyField(FunctionalTester $I)\n    {\n        $I->submitForm($this->formId, $this->formParams(''));\n        $I->seeValidationError('Email cannot be blank.');\n    }\n\n    public function checkWrongEmailFormat(FunctionalTester $I)\n    {\n        $I->submitForm($this->formId, $this->formParams('abcd.com'));\n        $I->seeValidationError('Email is not a valid email address.');\n    }\n\n    public function checkWrongEmail(FunctionalTester $I)\n    {\n        $I->submitForm($this->formId, $this->formParams('wrong@email.com'));\n        $I->seeValidationError('There is no user with this email address.');\n    }\n\n    public function checkAlreadyVerifiedEmail(FunctionalTester $I)\n    {\n        $I->submitForm($this->formId, $this->formParams('test2@mail.com'));\n        $I->seeValidationError('There is no user with this email address.');\n    }\n\n    public function checkSendSuccessfully(FunctionalTester $I)\n    {\n        $I->submitForm($this->formId, $this->formParams('test@mail.com'));\n        $I->canSeeEmailIsSent();\n        $I->seeRecord('common\\models\\User', [\n            'email' => 'test@mail.com',\n            'username' => 'test.test',\n            'status' => \\common\\models\\User::STATUS_INACTIVE\n        ]);\n        $I->see('Check your email for further instructions.');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/functional/SignupCest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\nclass SignupCest\n{\n    protected $formId = '#form-signup';\n\n\n    public function _before(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/signup');\n    }\n\n    public function signupWithEmptyFields(FunctionalTester $I)\n    {\n        $I->see('Signup', 'h1');\n        $I->see('Please fill out the following fields to signup:');\n        $I->submitForm($this->formId, []);\n        $I->seeValidationError('Username cannot be blank.');\n        $I->seeValidationError('Email cannot be blank.');\n        $I->seeValidationError('Password cannot be blank.');\n\n    }\n\n    public function signupWithWrongEmail(FunctionalTester $I)\n    {\n        $I->submitForm(\n            $this->formId, [\n            'SignupForm[username]'  => 'tester',\n            'SignupForm[email]'     => 'ttttt',\n            'SignupForm[password]'  => 'tester_password',\n        ]\n        );\n        $I->dontSee('Username cannot be blank.', '.help-block');\n        $I->dontSee('Password cannot be blank.', '.help-block');\n        $I->see('Email is not a valid email address.', '.help-block');\n    }\n\n    public function signupSuccessfully(FunctionalTester $I)\n    {\n        $I->submitForm($this->formId, [\n            'SignupForm[username]' => 'tester',\n            'SignupForm[email]' => 'tester.email@example.com',\n            'SignupForm[password]' => 'tester_password',\n        ]);\n\n        $I->seeRecord('common\\models\\User', [\n            'username' => 'tester',\n            'email' => 'tester.email@example.com',\n            'status' => \\common\\models\\User::STATUS_INACTIVE\n        ]);\n\n        $I->seeEmailIsSent();\n        $I->see('Thank you for registration. Please check your inbox for verification email.');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/functional/VerifyEmailCest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\tests\\FunctionalTester;\n\nclass VerifyEmailCest\n{\n    /**\n     * Load fixtures before db transaction begin\n     * Called in _before()\n     * @see \\Codeception\\Module\\Yii2::_before()\n     * @see \\Codeception\\Module\\Yii2::loadFixtures()\n     * @return array\n     */\n    public function _fixtures()\n    {\n        return [\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php',\n            ],\n        ];\n    }\n\n    public function checkEmptyToken(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/verify-email', ['token' => '']);\n        $I->canSee('Bad Request', 'h1');\n        $I->canSee('Verify email token cannot be blank.');\n    }\n\n    public function checkInvalidToken(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/verify-email', ['token' => 'wrong_token']);\n        $I->canSee('Bad Request', 'h1');\n        $I->canSee('Wrong verify email token.');\n    }\n\n    public function checkNoToken(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/verify-email');\n        $I->canSee('Bad Request', 'h1');\n        $I->canSee('Missing required parameters: token');\n    }\n\n    public function checkAlreadyActivatedToken(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/verify-email', ['token' => 'already_used_token_1548675330']);\n        $I->canSee('Bad Request', 'h1');\n        $I->canSee('Wrong verify email token.');\n    }\n\n    public function checkSuccessVerification(FunctionalTester $I)\n    {\n        $I->amOnRoute('site/verify-email', ['token' => '4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330']);\n        $I->canSee('Your email has been confirmed!');\n        $I->canSee('Congratulations!', 'h1');\n        $I->see('Logout (test.test)', 'form button[type=submit]');\n\n        $I->seeRecord('common\\models\\User', [\n           'username' => 'test.test',\n           'email' => 'test@mail.com',\n           'status' => \\common\\models\\User::STATUS_ACTIVE\n        ]);\n    }\n}\n"
  },
  {
    "path": "frontend/tests/functional/_bootstrap.php",
    "content": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and use it in Cests.\n *\n * ```php\n * // Here _bootstrap.php\n * \\Codeception\\Util\\Fixtures::add('user1', ['name' => 'davert']);\n * ```\n *\n * In Cests\n *\n * ```php\n * \\Codeception\\Util\\Fixtures::get('user1');\n * ```\n */"
  },
  {
    "path": "frontend/tests/functional.suite.yml",
    "content": "suite_namespace: frontend\\tests\\functional\nactor: FunctionalTester\nmodules:\n    enabled:\n        - Filesystem\n        - Yii2\n"
  },
  {
    "path": "frontend/tests/unit/_bootstrap.php",
    "content": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and use it in Tests.\n *\n * ```php\n * // Here _bootstrap.php\n * \\Codeception\\Util\\Fixtures::add('user1', ['name' => 'davert']);\n * ```\n *\n * In Tests\n *\n * ```php\n * \\Codeception\\Util\\Fixtures::get('user1');\n * ```\n */\n"
  },
  {
    "path": "frontend/tests/unit/models/ContactFormTest.php",
    "content": "<?php\nnamespace frontend\\tests\\unit\\models;\n\nuse frontend\\models\\ContactForm;\nuse yii\\mail\\MessageInterface;\n\nclass ContactFormTest extends \\Codeception\\Test\\Unit\n{\n    public function testSendEmail()\n    {\n        $model = new ContactForm();\n\n        $model->attributes = [\n            'name' => 'Tester',\n            'email' => 'tester@example.com',\n            'subject' => 'very important letter subject',\n            'body' => 'body of current message',\n        ];\n\n        expect_that($model->sendEmail('admin@example.com'));\n\n        // using Yii2 module actions to check email was sent\n        $this->tester->seeEmailIsSent();\n\n        /** @var MessageInterface  $emailMessage */\n        $emailMessage = $this->tester->grabLastSentEmail();\n        expect('valid email is sent', $emailMessage)->isInstanceOf('yii\\mail\\MessageInterface');\n        expect($emailMessage->getTo())->hasKey('admin@example.com');\n        expect($emailMessage->getFrom())->hasKey('noreply@example.com');\n        expect($emailMessage->getReplyTo())->hasKey('tester@example.com');\n        expect($emailMessage->getSubject())->equals('very important letter subject');\n        expect($emailMessage->toString())->stringContainsString('body of current message');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/unit/models/PasswordResetRequestFormTest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\nuse Yii;\nuse frontend\\models\\PasswordResetRequestForm;\nuse common\\fixtures\\UserFixture as UserFixture;\nuse common\\models\\User;\n\nclass PasswordResetRequestFormTest extends \\Codeception\\Test\\Unit\n{\n    /**\n     * @var \\frontend\\tests\\UnitTester\n     */\n    protected $tester;\n\n\n    public function _before()\n    {\n        $this->tester->haveFixtures([\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ]\n        ]);\n    }\n\n    public function testSendMessageWithWrongEmailAddress()\n    {\n        $model = new PasswordResetRequestForm();\n        $model->email = 'not-existing-email@example.com';\n        expect_not($model->sendEmail());\n    }\n\n    public function testNotSendEmailsToInactiveUser()\n    {\n        $user = $this->tester->grabFixture('user', 1);\n        $model = new PasswordResetRequestForm();\n        $model->email = $user['email'];\n        expect_not($model->sendEmail());\n    }\n\n    public function testSendEmailSuccessfully()\n    {\n        $userFixture = $this->tester->grabFixture('user', 0);\n        \n        $model = new PasswordResetRequestForm();\n        $model->email = $userFixture['email'];\n        $user = User::findOne(['password_reset_token' => $userFixture['password_reset_token']]);\n\n        expect_that($model->sendEmail());\n        expect_that($user->password_reset_token);\n\n        $emailMessage = $this->tester->grabLastSentEmail();\n        expect('valid email is sent', $emailMessage)->isInstanceOf('yii\\mail\\MessageInterface');\n        expect($emailMessage->getTo())->hasKey($model->email);\n        expect($emailMessage->getFrom())->hasKey(Yii::$app->params['supportEmail']);\n    }\n}\n"
  },
  {
    "path": "frontend/tests/unit/models/ResendVerificationEmailFormTest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\n\nuse Codeception\\Test\\Unit;\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\ResendVerificationEmailForm;\n\nclass ResendVerificationEmailFormTest extends Unit\n{\n    /**\n     * @var \\frontend\\tests\\UnitTester\n     */\n    protected $tester;\n\n\n    public function _before()\n    {\n        $this->tester->haveFixtures([\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ]\n        ]);\n    }\n\n    public function testWrongEmailAddress()\n    {\n        $model = new ResendVerificationEmailForm();\n        $model->attributes = [\n            'email' => 'aaa@bbb.cc'\n        ];\n\n        expect($model->validate())->false();\n        expect($model->hasErrors())->true();\n        expect($model->getFirstError('email'))->equals('There is no user with this email address.');\n    }\n\n    public function testEmptyEmailAddress()\n    {\n        $model = new ResendVerificationEmailForm();\n        $model->attributes = [\n            'email' => ''\n        ];\n\n        expect($model->validate())->false();\n        expect($model->hasErrors())->true();\n        expect($model->getFirstError('email'))->equals('Email cannot be blank.');\n    }\n\n    public function testResendToActiveUser()\n    {\n        $model = new ResendVerificationEmailForm();\n        $model->attributes = [\n            'email' => 'test2@mail.com'\n        ];\n\n        expect($model->validate())->false();\n        expect($model->hasErrors())->true();\n        expect($model->getFirstError('email'))->equals('There is no user with this email address.');\n    }\n\n    public function testSuccessfullyResend()\n    {\n        $model = new ResendVerificationEmailForm();\n        $model->attributes = [\n            'email' => 'test@mail.com'\n        ];\n\n        expect($model->validate())->true();\n        expect($model->hasErrors())->false();\n\n        expect($model->sendEmail())->true();\n        $this->tester->seeEmailIsSent();\n\n        $mail = $this->tester->grabLastSentEmail();\n\n        expect('valid email is sent', $mail)->isInstanceOf('yii\\mail\\MessageInterface');\n        expect($mail->getTo())->hasKey('test@mail.com');\n        expect($mail->getFrom())->hasKey(\\Yii::$app->params['supportEmail']);\n        expect($mail->getSubject())->equals('Account registration at ' . \\Yii::$app->name);\n        expect($mail->toString())->stringContainsString('4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/unit/models/ResetPasswordFormTest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\ResetPasswordForm;\n\nclass ResetPasswordFormTest extends \\Codeception\\Test\\Unit\n{\n    /**\n     * @var \\frontend\\tests\\UnitTester\n     */\n    protected $tester;\n\n\n    public function _before()\n    {\n        $this->tester->haveFixtures([\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ],\n        ]);\n    }\n\n    public function testResetWrongToken()\n    {\n        $this->tester->expectException('\\yii\\base\\InvalidArgumentException', function() {\n            new ResetPasswordForm('');\n        });\n\n        $this->tester->expectException('\\yii\\base\\InvalidArgumentException', function() {\n            new ResetPasswordForm('notexistingtoken_1391882543');\n        });\n    }\n\n    public function testResetCorrectToken()\n    {\n        $user = $this->tester->grabFixture('user', 0);\n        $form = new ResetPasswordForm($user['password_reset_token']);\n        expect_that($form->resetPassword());\n    }\n\n}\n"
  },
  {
    "path": "frontend/tests/unit/models/SignupFormTest.php",
    "content": "<?php\nnamespace frontend\\tests\\unit\\models;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\SignupForm;\n\nclass SignupFormTest extends \\Codeception\\Test\\Unit\n{\n    /**\n     * @var \\frontend\\tests\\UnitTester\n     */\n    protected $tester;\n\n\n    public function _before()\n    {\n        $this->tester->haveFixtures([\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ]\n        ]);\n    }\n\n    public function testCorrectSignup()\n    {\n        $model = new SignupForm([\n            'username' => 'some_username',\n            'email' => 'some_email@example.com',\n            'password' => 'some_password',\n        ]);\n\n        $user = $model->signup();\n        expect($user)->true();\n\n        /** @var \\common\\models\\User $user */\n        $user = $this->tester->grabRecord('common\\models\\User', [\n            'username' => 'some_username',\n            'email' => 'some_email@example.com',\n            'status' => \\common\\models\\User::STATUS_INACTIVE\n        ]);\n\n        $this->tester->seeEmailIsSent();\n\n        $mail = $this->tester->grabLastSentEmail();\n\n        expect($mail)->isInstanceOf('yii\\mail\\MessageInterface');\n        expect($mail->getTo())->hasKey('some_email@example.com');\n        expect($mail->getFrom())->hasKey(\\Yii::$app->params['supportEmail']);\n        expect($mail->getSubject())->equals('Account registration at ' . \\Yii::$app->name);\n        expect($mail->toString())->stringContainsString($user->verification_token);\n    }\n\n    public function testNotCorrectSignup()\n    {\n        $model = new SignupForm([\n            'username' => 'troy.becker',\n            'email' => 'nicolas.dianna@hotmail.com',\n            'password' => 'some_password',\n        ]);\n\n        expect_not($model->signup());\n        expect_that($model->getErrors('username'));\n        expect_that($model->getErrors('email'));\n\n        expect($model->getFirstError('username'))\n            ->equals('This username has already been taken.');\n        expect($model->getFirstError('email'))\n            ->equals('This email address has already been taken.');\n    }\n}\n"
  },
  {
    "path": "frontend/tests/unit/models/VerifyEmailFormTest.php",
    "content": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\VerifyEmailForm;\n\nclass VerifyEmailFormTest extends \\Codeception\\Test\\Unit\n{\n    /**\n     * @var \\frontend\\tests\\UnitTester\n     */\n    protected $tester;\n\n\n    public function _before()\n    {\n        $this->tester->haveFixtures([\n            'user' => [\n                'class' => UserFixture::className(),\n                'dataFile' => codecept_data_dir() . 'user.php'\n            ]\n        ]);\n    }\n\n    public function testVerifyWrongToken()\n    {\n        $this->tester->expectException('\\yii\\base\\InvalidArgumentException', function() {\n            new VerifyEmailForm('');\n        });\n\n        $this->tester->expectException('\\yii\\base\\InvalidArgumentException', function() {\n            new VerifyEmailForm('notexistingtoken_1391882543');\n        });\n    }\n\n    public function testAlreadyActivatedToken()\n    {\n        $this->tester->expectException('\\yii\\base\\InvalidArgumentException', function() {\n            new VerifyEmailForm('already_used_token_1548675330');\n        });\n    }\n\n    public function testVerifyCorrectToken()\n    {\n        $model = new VerifyEmailForm('4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330');\n        $user = $model->verifyEmail();\n        expect($user)->isInstanceOf('common\\models\\User');\n\n        expect($user->username)->equals('test.test');\n        expect($user->email)->equals('test@mail.com');\n        expect($user->status)->equals(\\common\\models\\User::STATUS_ACTIVE);\n        expect($user->validatePassword('Test1234'))->true();\n    }\n}\n"
  },
  {
    "path": "frontend/tests/unit.suite.yml",
    "content": "suite_namespace: frontend\\tests\\unit\nactor: UnitTester\nmodules:\n    enabled:\n        - Yii2:\n            part: [orm, email, fixtures]\n        - Asserts\n"
  },
  {
    "path": "frontend/views/channel/_subscribe.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 9:58 AM\n */\n\nuse yii\\helpers\\Url;\n\n/** @var $channel \\common\\models\\User */\n?>\n\n<a class=\"btn <?php echo $channel->isSubscribed(Yii::$app->user->id)\n    ? 'btn-secondary' : 'btn-danger' ?>\"\n   href=\"<?php echo Url::to(['channel/subscribe', 'username' => $channel->username]) ?>\"\n   data-method=\"post\" data-pjax=\"1\">\n    Subscribe <i class=\"far fa-bell\"></i>\n</a> <?php echo $channel->getSubscribers()->count() ?>\n"
  },
  {
    "path": "frontend/views/channel/view.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 9:50 AM\n */\n\nuse yii\\helpers\\Url;\n\n/** @var $this \\yii\\web\\View */\n/** @var $channel \\common\\models\\User */\n/**@var $dataProvider \\yii\\data\\ActiveDataProvider */\n\n$this->title = 'Channel ' . $channel->username . ' | ' . Yii::$app->name;\n?>\n\n<div class=\"jumbotron\">\n    <h1 class=\"display-4\"><?php echo $channel->username ?></h1>\n    <hr class=\"my-4\">\n    <?php \\yii\\widgets\\Pjax::begin() ?>\n    <?php echo $this->render('_subscribe', [\n        'channel' => $channel\n    ]) ?>\n    <?php \\yii\\widgets\\Pjax::end() ?>\n</div>\n\n<?php echo \\yii\\widgets\\ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '@frontend/views/video/_video_item',\n    'layout' => '<div class=\"d-flex flex-wrap\">{items}</div>{pager}',\n    'itemOptions' => [\n        'tag' => false\n    ]\n]) ?>\n"
  },
  {
    "path": "frontend/views/layouts/_header.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 9:20 AM\n */\n\nuse yii\\bootstrap4\\Nav;\nuse yii\\bootstrap4\\NavBar;\nuse yii\\helpers\\Url;\n\nNavBar::begin([\n    'brandLabel' => Yii::$app->name,\n    'brandUrl' => Yii::$app->homeUrl,\n    'options' => ['class' => 'navbar-expand-lg navbar-light bg-light shadow-sm'],\n    'innerContainerOptions' => [\n        'class' => 'container-fluid'\n    ]\n]);\nif (Yii::$app->user->isGuest) {\n    $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];\n    $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];\n} else {\n    $menuItems[] = [\n        'label' => 'Logout (' . Yii::$app->user->identity->username . ')',\n        'url' => ['/site/logout'],\n        'linkOptions' => [\n            'data-method' => 'post'\n        ]\n    ];\n}\n?>\n  <form action=\"<?php echo Url::to(['/video/search']) ?>\"\n        class=\"form-inline my-2 my-lg-0\">\n    <input class=\"form-control mr-sm-2\" type=\"search\" placeholder=\"Search\"\n           name=\"keyword\"\n           value=\"<?php echo Yii::$app->request->get('keyword') ?>\">\n    <button class=\"btn btn-outline-success my-2 my-sm-0\">Search</button>\n  </form>\n<?php\necho Nav::widget([\n    'options' => ['class' => 'navbar-nav ml-auto'],\n    'items' => $menuItems,\n]);\nNavBar::end();\n"
  },
  {
    "path": "frontend/views/layouts/_sidebar.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 9:20 AM\n */\n\n?>\n\n<aside class=\"shadow\">\n    <?php echo \\yii\\bootstrap4\\Nav::widget([\n    'options' => [\n        'class' => 'd-flex flex-column nav-pills'\n    ],\n    'encodeLabels' => false,\n    'items' => [\n        [\n            'label' => '<i class=\"fas fa-home\"></i> Home',\n            'url' => ['/video/index']\n        ],\n        [\n            'label' => '<i class=\"fas fa-history\"></i> History',\n            'url' => ['/video/history']\n        ]\n    ]\n]) ?>\n</aside>\n"
  },
  {
    "path": "frontend/views/layouts/auth.php",
    "content": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse common\\widgets\\Alert;\n\n$this->beginContent('@frontend/views/layouts/base.php');\n?>\n<main class=\"d-flex\">\n    <div class=\"content-wrapper p-3\">\n        <?= Alert::widget() ?>\n        <?= $content ?>\n    </div>\n</main>\n<?php $this->endContent() ?>"
  },
  {
    "path": "frontend/views/layouts/base.php",
    "content": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse frontend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\n\nAppAsset::register($this);\n?>\n<?php $this->beginPage() ?>\n<!DOCTYPE html>\n<html lang=\"<?= Yii::$app->language ?>\">\n<head>\n    <meta charset=\"<?= Yii::$app->charset ?>\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <?php $this->registerCsrfMetaTags() ?>\n    <title><?= Html::encode($this->title) ?></title>\n    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css\"\n            rel=\"stylesheet\">\n    <?php $this->head() ?>\n</head>\n<body>\n<?php $this->beginBody() ?>\n\n<div class=\"wrap h-100 d-flex flex-column\">\n    <div class=\"wrap h-100 d-flex flex-column\">\n        <?php echo $this->render('_header') ?>\n\n        <?php echo $content ?>\n    </div>\n</div>\n\n<?php $this->endBody() ?>\n</body>\n</html>\n<?php $this->endPage() ?>\n"
  },
  {
    "path": "frontend/views/layouts/main.php",
    "content": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse common\\widgets\\Alert;\n\n$this->beginContent('@frontend/views/layouts/base.php');\n?>\n<main class=\"d-flex\">\n    <?php echo $this->render('_sidebar') ?>\n\n    <div class=\"content-wrapper p-3\">\n        <?= Alert::widget() ?>\n        <?= $content ?>\n    </div>\n</main>\n<?php $this->endContent() ?>"
  },
  {
    "path": "frontend/views/site/error.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $name string */\n/* @var $message string */\n/* @var $exception Exception */\n\nuse yii\\helpers\\Html;\n\n$this->title = $name;\n?>\n<div class=\"site-error\">\n\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <div class=\"alert alert-danger\">\n        <?= nl2br(Html::encode($message)) ?>\n    </div>\n\n    <p>\n        The above error occurred while the Web server was processing your request.\n    </p>\n    <p>\n        Please contact us if you think this is a server error. Thank you.\n    </p>\n\n</div>\n"
  },
  {
    "path": "frontend/views/site/index.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n\n$this->title = 'My Yii Application';\n?>\n<div class=\"site-index\">\n\n    <div class=\"jumbotron\">\n        <h1>Congratulations!</h1>\n\n        <p class=\"lead\">You have successfully created your Yii-powered application.</p>\n\n        <p><a class=\"btn btn-lg btn-success\" href=\"http://www.yiiframework.com\">Get started with Yii</a></p>\n    </div>\n\n    <div class=\"body-content\">\n\n        <div class=\"row\">\n            <div class=\"col-lg-4\">\n                <h2>Heading</h2>\n\n                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et\n                    dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip\n                    ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu\n                    fugiat nulla pariatur.</p>\n\n                <p><a class=\"btn btn-default\" href=\"http://www.yiiframework.com/doc/\">Yii Documentation &raquo;</a></p>\n            </div>\n            <div class=\"col-lg-4\">\n                <h2>Heading</h2>\n\n                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et\n                    dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip\n                    ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu\n                    fugiat nulla pariatur.</p>\n\n                <p><a class=\"btn btn-default\" href=\"http://www.yiiframework.com/forum/\">Yii Forum &raquo;</a></p>\n            </div>\n            <div class=\"col-lg-4\">\n                <h2>Heading</h2>\n\n                <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et\n                    dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip\n                    ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu\n                    fugiat nulla pariatur.</p>\n\n                <p><a class=\"btn btn-default\" href=\"http://www.yiiframework.com/extensions/\">Yii Extensions &raquo;</a></p>\n            </div>\n        </div>\n\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/site/login.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\common\\models\\LoginForm */\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n$this->title = 'Login';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"site-login\">\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>Please fill out the following fields to login:</p>\n\n    <div class=\"row\">\n        <div class=\"col-lg-5\">\n            <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>\n\n                <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>\n\n                <?= $form->field($model, 'password')->passwordInput() ?>\n\n                <?= $form->field($model, 'rememberMe')->checkbox() ?>\n\n                <div style=\"color:#999;margin:1em 0\">\n                    If you forgot your password you can <?= Html::a('reset it', ['site/request-password-reset']) ?>.\n                    <br>\n                    Need new verification email? <?= Html::a('Resend', ['site/resend-verification-email']) ?>\n                </div>\n\n                <div class=\"form-group\">\n                    <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>\n                </div>\n\n            <?php ActiveForm::end(); ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/site/requestPasswordResetToken.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\frontend\\models\\PasswordResetRequestForm */\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n$this->title = 'Request password reset';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"site-request-password-reset\">\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>Please fill out your email. A link to reset password will be sent there.</p>\n\n    <div class=\"row\">\n        <div class=\"col-lg-5\">\n            <?php $form = ActiveForm::begin(['id' => 'request-password-reset-form']); ?>\n\n                <?= $form->field($model, 'email')->textInput(['autofocus' => true]) ?>\n\n                <div class=\"form-group\">\n                    <?= Html::submitButton('Send', ['class' => 'btn btn-primary']) ?>\n                </div>\n\n            <?php ActiveForm::end(); ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/site/resendVerificationEmail.php",
    "content": "<?php\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\frontend\\models\\ResetPasswordForm */\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n$this->title = 'Resend verification email';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"site-resend-verification-email\">\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>Please fill out your email. A verification email will be sent there.</p>\n\n    <div class=\"row\">\n        <div class=\"col-lg-5\">\n            <?php $form = ActiveForm::begin(['id' => 'resend-verification-email-form']); ?>\n\n            <?= $form->field($model, 'email')->textInput(['autofocus' => true]) ?>\n\n            <div class=\"form-group\">\n                <?= Html::submitButton('Send', ['class' => 'btn btn-primary']) ?>\n            </div>\n\n            <?php ActiveForm::end(); ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/site/resetPassword.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\frontend\\models\\ResetPasswordForm */\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n$this->title = 'Reset password';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"site-reset-password\">\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>Please choose your new password:</p>\n\n    <div class=\"row\">\n        <div class=\"col-lg-5\">\n            <?php $form = ActiveForm::begin(['id' => 'reset-password-form']); ?>\n\n                <?= $form->field($model, 'password')->passwordInput(['autofocus' => true]) ?>\n\n                <div class=\"form-group\">\n                    <?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>\n                </div>\n\n            <?php ActiveForm::end(); ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/site/signup.php",
    "content": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\frontend\\models\\SignupForm */\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n$this->title = 'Signup';\n$this->params['breadcrumbs'][] = $this->title;\n?>\n<div class=\"site-signup\">\n    <h1><?= Html::encode($this->title) ?></h1>\n\n    <p>Please fill out the following fields to signup:</p>\n\n    <div class=\"row\">\n        <div class=\"col-lg-5\">\n            <?php $form = ActiveForm::begin(['id' => 'form-signup']); ?>\n\n                <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>\n\n                <?= $form->field($model, 'email') ?>\n\n                <?= $form->field($model, 'password')->passwordInput() ?>\n\n                <div class=\"form-group\">\n                    <?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>\n                </div>\n\n            <?php ActiveForm::end(); ?>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/video/_buttons.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 9:23 AM\n */\n/** @var $model \\common\\models\\Video */\n\nuse yii\\helpers\\Url;\n\n?>\n<a href=\"<?php echo Url::to(['/video/like', 'id' => $model->video_id]) ?>\"\n   class=\"btn btn-sm <?php echo $model->isLikedBy(Yii::$app->user->id) ? 'btn-outline-primary': 'btn-outline-secondary' ?>\"\n   data-method=\"post\" data-pjax=\"1\">\n    <i class=\"fas fa-thumbs-up\"></i> <?php echo $model->getLikes()->count() ?>\n</a>\n<a href=\"<?php echo Url::to(['/video/dislike', 'id' => $model->video_id]) ?>\"\n   class=\"btn btn-sm <?php echo $model->isDislikedBy(Yii::$app->user->id) ? 'btn-outline-primary': 'btn-outline-secondary' ?>\"\n   data-method=\"post\" data-pjax=\"1\">\n    <i class=\"fas fa-thumbs-down\"></i> <?php echo $model->getDislikes()->count() ?>\n</a>\n"
  },
  {
    "path": "frontend/views/video/_comment_item.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/12/2020\n * Time: 9:19 AM\n */\n\n/** @var $model \\common\\models\\Comment */\n\n$subCommentCount = $model->getComments()->count();\n?>\n\n<div class=\"comment-item <?php echo $model->pinned ? 'pinned-comment' : ''?>\" data-parent-id=\"<?php echo $model->parent_id ?>\" data-id=\"<?php echo $model->id ?>\">\n    <div class=\"media media-comment\">\n        <img class=\"mr-3 comment-avatar\" src=\"/img/avatar.svg\" alt=\"\">\n        <div class=\"media-body\">\n            <h6 class=\"mt-0\">\n                <?php if ($model->pinned): ?>\n                    <div class=\"pinned-text text-muted mb-1\">\n                        <i class=\"fas fa-thumbtack\"></i> Pinned comment\n                    </div>\n                <?php endif; ?>\n                <?php echo \\common\\helpers\\Html::channelLink($model->createdBy) ?>\n                <small class=\"text-muted\">\n                    <?php echo Yii::$app->formatter->asRelativeTime($model->created_at) ?>\n                    <?php if ($model->created_at !== $model->updated_at): ?>\n                        (edited)\n                    <?php endif; ?>\n                </small>\n            </h6>\n            <div class=\"comment-text\">\n                <div class=\"text-wrapper\">\n                    <?php if ($model->mention): ?>\n                        <span class=\"badge bg-info\"><?php echo '@'.$model->mention ?></span>\n                    <?php endif; ?>\n                    <?php echo $model->comment ?>\n                </div>\n\n                <div class=\"bottom-actions my-2\">\n                    <button data-action=\"<?php echo \\yii\\helpers\\Url::to(['/comment/reply']) ?>\"\n                            class=\"btn btn-sm btn-light btn-reply\">\n                        REPLY\n                    </button>\n                </div>\n                <div class=\"reply-section\">\n\n                </div>\n                <?php if ($subCommentCount): ?>\n                    <div class=\"mb-2\">\n                        <a href=\"<?php echo \\yii\\helpers\\Url::to(['/comment/by-parent', 'id' => $model->id]) ?>\"\n                           class=\"view-sub-comments\">View <?php echo $subCommentCount ?> replies</a>\n                    </div>\n                <?php endif; ?>\n                <div class=\"sub-comments\">\n\n                </div>\n            </div>\n            <?php if ($model->belongsTo(Yii::$app->user->id) || $model->video->belongsTo(Yii::$app->user->id)): ?>\n                <div class=\"dropdown comment-actions\">\n                    <button class=\"btn dropdown-toggle\" type=\"button\" id=\"dropdownMenuButton\"\n                            data-toggle=\"dropdown\">\n                        <i class=\"fas fa-ellipsis-v\"></i>\n                    </button>\n                    <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"dropdownMenuButton\">\n                        <?php if (!$model->parent_id && $model->video->belongsTo(Yii::$app->user->id)): ?>\n                            <a class=\"dropdown-item item-pin-comment\"\n                               data-pinned=\"<?php echo $model->pinned ?>\"\n                               href=\"<?php echo \\yii\\helpers\\Url::to(['/comment/pin', 'id' => $model->id]) ?>\">\n                                <i class=\"fas fa-thumbtack\"></i>\n                                <?php if ($model->pinned): ?>\n                                    Unpin\n                                <?php else: ?>\n                                    Pin\n                                <?php endif; ?>\n                            </a>\n                        <?php endif; ?>\n\n                        <?php if ($model->belongsTo(Yii::$app->user->id)): ?>\n                            <a class=\"dropdown-item item-edit-comment\" href=\"#\">\n                                <i class=\"fas fa-edit\"></i>\n                                Edit\n                            </a>\n                        <?php endif; ?>\n                        <a class=\"dropdown-item item-delete-comment\"\n                           href=\"<?php echo \\yii\\helpers\\Url::to(['/comment/delete', 'id' => $model->id]) ?>\">\n                            <i class=\"fas fa-trash\"></i>\n                            Delete\n                        </a>\n                    </div>\n                </div>\n            <?php endif; ?>\n        </div>\n    </div>\n    <div class=\"media media-input\">\n        <img class=\"mr-3 comment-avatar\" src=\"/img/avatar.svg\" alt=\"\">\n        <div class=\"media-body\">\n            <form class=\"comment-edit-section\" method=\"post\"\n                  action=\"<?php echo \\yii\\helpers\\Url::to(['/comment/update', 'id' => $model->id]) ?>\">\n            <textarea rows=\"1\"\n                      class=\"form-control\"\n                      name=\"comment\"\n                      placeholder=\"Add a public comment\"></textarea>\n                <div class=\"action-buttons text-right mt-2\">\n                    <button type=\"button\" class=\"btn btn-light btn-cancel\">Cancel</button>\n                    <button class=\"btn btn-primary btn-save\">Save</button>\n                </div>\n            </form>\n        </div>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/video/_video_item.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 12:00 PM\n */\n\nuse yii\\helpers\\Url;\n\n/** @var $model \\common\\models\\Video */\n?>\n\n<div class=\"card m-3\" style=\"width: 14rem;\">\n    <a href=\"<?php echo Url::to(['/video/view', 'id' => $model->video_id]) ?>\">\n        <div class=\"embed-responsive embed-responsive-16by9\">\n            <video class=\"embed-responsive-item\"\n                   poster=\"<?php echo $model->getThumbnailLink() ?>\"\n                   src=\"<?php echo $model->getVideoLink() ?>\"></video>\n        </div>\n    </a>\n    <div class=\"card-bod p-2\">\n        <h6 class=\"card-title m-0\"><?php echo $model->title ?></h6>\n        <p class=\"text-muted card-text m-0\">\n            <?php echo \\common\\helpers\\Html::channelLink($model->createdBy) ?>\n        </p>\n        <p class=\"text-muted card-text m-0\">\n            <?php echo $model->getViews()->count() ?> views .\n            <?php echo Yii::$app->formatter->asRelativeTime($model->created_at) ?>\n        </p>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/views/video/history.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:56 AM\n */\n/** @var $dataProvider \\yii\\data\\ActiveDataProvider */\n\n$this->title = 'My Visited videos | '. Yii::$app->name;\n?>\n<h1>My Visited videos</h1>\n<?php echo \\yii\\widgets\\ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_video_item',\n    'layout' => '<div class=\"d-flex flex-wrap\">{items}</div>{pager}',\n    'itemOptions' => [\n        'tag' => false\n    ]\n]) ?>\n"
  },
  {
    "path": "frontend/views/video/index.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:56 AM\n */\n/** @var $this \\yii\\web\\View */\n/** @var $dataProvider \\yii\\data\\ActiveDataProvider */\n\n$this->title = 'Popular videos | '. Yii::$app->name;\n?>\n\n<?php echo \\yii\\widgets\\ListView::widget([\n    'dataProvider' => $dataProvider,\n    'pager' => [\n        'class' => \\yii\\bootstrap4\\LinkPager::class,\n    ],\n    'itemView' => '_video_item',\n    'layout' => '<div class=\"d-flex flex-wrap\">{items}</div>{pager}',\n    'itemOptions' => [\n        'tag' => false\n    ]\n]) ?>\n"
  },
  {
    "path": "frontend/views/video/search.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:56 AM\n */\n/** @var $dataProvider \\yii\\data\\ActiveDataProvider */\n\n$this->title = 'Search videos | '. Yii::$app->name;\n?>\n<h1>Found videos</h1>\n<?php echo \\yii\\widgets\\ListView::widget([\n    'dataProvider' => $dataProvider,\n    'itemView' => '_video_item',\n    'layout' => '<div class=\"d-flex flex-wrap\">{items}</div>{pager}',\n    'itemOptions' => [\n        'tag' => false\n    ]\n]) ?>\n"
  },
  {
    "path": "frontend/views/video/view.php",
    "content": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 8:48 AM\n */\n\nuse yii\\helpers\\Html;\nuse yii\\helpers\\Url;\n\n/** @var $this \\yii\\web\\View */\n/** @var $model \\common\\models\\Video */\n/** @var $similarVideos \\common\\models\\Video[] */\n/** @var $comments \\common\\models\\Comment[] */\n\n$this->title = $model->title . ' | ' . Yii::$app->name;\n?>\n<div class=\"row\">\n    <div class=\"col-sm-8\">\n        <div class=\"embed-responsive embed-responsive-16by9\">\n            <video class=\"embed-responsive-item\"\n                   poster=\"<?php echo $model->getThumbnailLink() ?>\"\n                   src=\"<?php echo $model->getVideoLink() ?>\"\n                   controls></video>\n        </div>\n        <h6 class=\"mt-2\"><?php echo $model->title ?></h6>\n        <div class=\"d-flex justify-content-between align-items-center\">\n            <div>\n                <?php echo $model->getViews()->count() ?> views •\n                <?php echo Yii::$app->formatter->asDate($model->created_at) ?>\n            </div>\n            <div>\n                <?php \\yii\\widgets\\Pjax::begin() ?>\n                <?php echo $this->render('_buttons', [\n                    'model' => $model\n                ]) ?>\n                <?php \\yii\\widgets\\Pjax::end() ?>\n            </div>\n        </div>\n        <div>\n            <p>\n                <?php echo \\common\\helpers\\Html::channelLink($model->createdBy) ?>\n            </p>\n            <?php echo Html::encode($model->description) ?>\n        </div>\n        <div class=\"comments mt-5\">\n            <h4 class=\"mb-3\"> <span id=\"comment-count\"><?php echo $model->getComments()->count() ?></span> Comments</h4>\n            <div class=\"create-comment mb-4\">\n                <div class=\"media\">\n                    <img class=\"mr-3 comment-avatar\" src=\"/img/avatar.svg\" alt=\"\">\n                    <div class=\"media-body\">\n                        <form class=\"create-comment-form\" method=\"post\"\n                              action=\"<?php echo Url::to(['/comment/create', 'id' => $model->video_id]) ?>\">\n                            <input type=\"hidden\" name=\"video_id\" value=\"<?php echo $model->video_id ?>\">\n                            <textarea rows=\"1\"\n                                      class=\"form-control comment-input\"\n                                      name=\"comment\"\n                                      placeholder=\"Add a public comment\"></textarea>\n                            <div class=\"action-buttons text-right mt-2\">\n                                <button type=\"button\" class=\"btn btn-light btn-cancel\">Cancel</button>\n                                <button class=\"btn btn-primary btn-save\">Comment</button>\n                            </div>\n                        </form>\n                    </div>\n                </div>\n            </div>\n            <div id=\"comments-wrapper\" class=\"comments-wrapper\">\n                <?php foreach ($comments as $comment) {\n                    echo $this->render('_comment_item', [\n                        'model' => $comment,\n                    ]);\n                } ?>\n            </div>\n        </div>\n    </div>\n    <div class=\"col-sm-4\">\n        <?php foreach ($similarVideos as $similarVideo): ?>\n            <div class=\"media mb-3\">\n                <a href=\"<?php echo Url::to(['/video/view', 'id' => $similarVideo->video_id]) ?>\">\n                    <div class=\"embed-responsive embed-responsive-16by9 mr-2\"\n                         style=\"width: 120px\">\n                        <video class=\"embed-responsive-item\"\n                               poster=\"<?php echo $similarVideo->getThumbnailLink() ?>\"\n                               src=\"<?php echo $similarVideo->getVideoLink() ?>\"></video>\n                    </div>\n                </a>\n                <div class=\"media-body\">\n                    <h6 class=\"m-0\"><?php echo $similarVideo->title ?></h6>\n                    <div class=\"text-muted\">\n                        <p class=\"m-0\">\n                            <?php echo \\common\\helpers\\Html::channelLink($similarVideo->createdBy) ?>\n                        </p>\n                        <small>\n                            <?php echo $similarVideo->getViews()->count() ?> views •\n                            <?php echo Yii::$app->formatter->asRelativeTime($similarVideo->created_at) ?>\n                        </small>\n                    </div>\n                </div>\n            </div>\n        <?php endforeach; ?>\n    </div>\n</div>\n"
  },
  {
    "path": "frontend/web/assets/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "frontend/web/css/site.css",
    "content": "html,\nbody {\n    height: 100%;\n}\n\nmain {\n    flex: 1;\n}\n\n.content-wrapper {\n    flex: 1;\n}\n\naside {\n    min-width: 200px;\n}\n\naside .nav-pills .nav-link {\n    border-radius: 0;\n    color: #444444;\n}\n\n\naside .nav-pills .nav-link:hover {\n    background: rgba(0, 0, 0, 0.05);\n}\n\naside .nav-pills .nav-link.active {\n    background: rgba(0, 0, 0, 0.05);\n    color: #b90000;\n    border-left: 4px solid #b90000;\n}\n\n.content-wrapper {\n    margin-bottom: 200px;\n}\n\n.create-comment .action-buttons {\n    display: none;\n}\n\n.create-comment.focused .action-buttons {\n    display: block;\n}\n\n.comment-item {\n    position: relative;\n}\n\n.comment-avatar {\n    width: 50px;\n}\n\n.sub-comments .comment-avatar {\n    width: 24px;\n}\n\n.comment-actions {\n    position: absolute;\n    right: 0;\n    top: 0;\n}\n\n.comment-actions .dropdown-toggle:after {\n    display: none;\n}\n\n.comment-item .media-input {\n    display: none;\n}\n\n.comment-item.edit .media-comment {\n    display: none;\n}\n\n.comment-item.edit .media-input {\n    display: flex;\n}"
  },
  {
    "path": "frontend/web/js/app.js",
    "content": "$(function () {\n  const $createCommentForm = $('.create-comment-form');\n  const $commentsWrapper = $('#comments-wrapper')\n  const $commentCount = $('#comment-count')\n\n  initCommentForm($createCommentForm, false, false,\n      () => {\n        resetForm();\n      }, () => {\n        $.ajax({\n          method: $createCommentForm.attr('method'),\n          url: $createCommentForm.attr('action'),\n          data: $createCommentForm.serializeArray(),\n          success: function (res) {\n            if (res.success) {\n              const $pinnedComment = $commentsWrapper.find('.pinned-comment');\n              const $tmp = $(res.comment);\n              if ($pinnedComment.length) {\n                $tmp.insertAfter($pinnedComment);\n              } else {\n                $commentsWrapper.prepend($tmp);\n              }\n\n              resetForm();\n              $commentCount.text(parseInt($commentCount.text()) + 1);\n              initComment($tmp);\n            }\n          }\n        })\n      });\n  initComments();\n\n  function resetForm() {\n    const $textarea = $createCommentForm.find('textarea')\n    $textarea.val('').attr('rows', '1')\n    $textarea\n        .closest('.create-comment')\n        .removeClass('focused')\n  }\n\n  function initCommentForm($form,\n                           placeholder = false,\n                           btnSaveText = false,\n                           cancelCb = false,\n                           submitCb = false) {\n    const $cancel = $form.find('.btn-cancel')\n    const $save = $form.find('.btn-save')\n    const $textarea = $form.find('textarea')\n\n    if (placeholder) {\n      $textarea.attr('placeholder', placeholder);\n    }\n    if (btnSaveText) {\n      $save.text(btnSaveText);\n    }\n\n    $textarea.click(function () {\n      console.log(\"111\");\n      $textarea\n          .attr('rows', '2')\n          .closest('.create-comment')\n          .addClass('focused');\n    });\n    $cancel.click(ev => {\n      if (cancelCb && typeof cancelCb === 'function') {\n        cancelCb();\n      }\n    });\n    $form.submit(ev => {\n      ev.preventDefault();\n      if (submitCb && typeof submitCb === 'function') {\n        submitCb();\n      }\n    })\n\n  }\n\n  function initComment($comment) {\n    const $delete = $comment.find('.item-delete-comment')\n    const $edit = $comment.find('.item-edit-comment');\n    const $pin = $comment.find('.item-pin-comment');\n    const $cancel = $comment.find('.btn-cancel');\n    const $form = $comment.find('.comment-edit-section');\n    const $textWrapper = $comment.find('.text-wrapper');\n    const $input = $comment.find('textarea');\n    const $reply = $comment.find('.btn-reply');\n    const $replySection = $comment.find('.reply-section');\n    const $subCommentsSection = $comment.find('.sub-comments');\n    const $viewSubComments = $comment.find('.view-sub-comments');\n    const $channelLink = $comment.find('.channel-link');\n    let replyFormDisplayed = false;\n    let commentsLoaded = false;\n    let commentsCollapsed = true;\n    let mentionUsername = null;\n\n    $pin.on('click', onCommentPin)\n\n    $delete.on('click', onDeleteClick);\n\n    $edit.on('click', ev => {\n      ev.preventDefault();\n      $comment.addClass('edit');\n      $input.val($textWrapper.text().trim());\n    });\n\n    $cancel.on('click', ev => {\n      mentionUsername = null;\n      $comment.removeClass('edit');\n    });\n\n    $form.on('submit', onEditFormSubmit)\n\n    $reply.on('click', onReplyClick);\n\n    $viewSubComments.on('click', loadSubComments)\n\n    function onCommentPin(ev) {\n      ev.preventDefault();\n\n      const pinned = $pin.data('pinned');\n      if (pinned) {\n        if (!confirm(\"Are you sure you want to unpin this comment?\")) {\n          return;\n        }\n      } else {\n        if (!confirm(\"You are about to pin the comment. Are you sure you want to unpin any other comments?\")) {\n          return;\n        }\n      }\n\n      $.ajax({\n        method: 'post',\n        url: $pin.attr('href'),\n        success: (res) => {\n          if (res.success) {\n            $comment.remove();\n            $commentsWrapper.find('.pinned-text').remove();\n            $commentsWrapper.prepend(res.comment);\n            const $firstComment = $commentsWrapper.find('.comment-item').eq(0);\n            initComment($firstComment);\n          }\n        }\n      })\n    }\n\n    function onEditFormSubmit(ev) {\n      ev.preventDefault();\n\n      $.ajax({\n        method: $form.attr('method'),\n        url: $form.attr('action'),\n        data: $form.serializeArray(),\n        success: function (res) {\n          if (res.success) {\n            $comment.removeClass('edit')\n            $textWrapper.text($input.val());\n            const $div = $('<div>');\n            $div.html(res.comment);\n            const $newComment = $div.find('>div');\n            $comment.replaceWith($newComment);\n            initComment($newComment);\n          }\n        }\n      })\n    }\n\n    function onReplyClick(ev) {\n      const isParentComment = !$comment.data('parent-id');\n\n      if (replyFormDisplayed) {\n        return;\n      }\n      const $newForm = $createCommentForm.clone();\n      $replySection.append($newForm);\n      const $textarea = $newForm.find('textarea');\n      replyFormDisplayed = true;\n\n      if (!isParentComment) {\n        mentionUsername = $channelLink.text()\n        $textarea.val('@' + mentionUsername + ' ');\n        $textarea[0].focus();\n      }\n\n      $textarea.click(ev => {\n        ev.stopImmediatePropagation();\n      })\n\n      initCommentForm(\n          $newForm,\n          'Add a public reply...',\n          'Reply',\n          () => {\n            $newForm.remove();\n            replyFormDisplayed = false;\n          },\n          () => {\n            const value = $textarea.val();\n            if (value.indexOf('@'+mentionUsername) !== 0) {\n              mentionUsername = null;\n            }\n            $.ajax({\n              method: 'post',\n              url: $reply.data('action'),\n              data: {\n                comment: value,\n                mention: mentionUsername,\n                parent_id: $reply.closest('.comment-item').data('id')\n              },\n              success: (res) => {\n                console.log(res);\n                if (res.success) {\n                  $newForm.remove();\n                  replyFormDisplayed = false;\n\n                  let $parentSubCommentSection = $subCommentsSection;\n                  if ($comment.closest('.sub-comments').length) {\n                    $parentSubCommentSection = $comment.closest('.sub-comments');\n                  }\n                  $parentSubCommentSection.append(res.comment);\n                  const $lastComment = $parentSubCommentSection.find('>.comment-item').last();\n                  initComment($lastComment);\n\n                  $commentCount.text(parseInt($commentCount.text()) + 1);\n                } else {\n                  const commentErrors = res.errors.comment;\n                  if (commentErrors) {\n                    const $error = $('<small class=\"text-danger\">');\n                    $error.html(commentErrors[0]);\n                    $error.insertAfter($textarea);\n                  }\n                }\n              },\n            })\n          }\n      )\n    }\n\n    function loadSubComments(ev) {\n      ev.preventDefault();\n      commentsCollapsed = !commentsCollapsed;\n\n      if (commentsCollapsed) {\n        $subCommentsSection.css('display', 'none');\n      } else {\n        $subCommentsSection.css('display', 'block');\n      }\n\n      if (commentsLoaded) {\n        return;\n      }\n      $.ajax({\n        url: $viewSubComments.attr('href'),\n        success: (res) => {\n          console.log(res);\n          if (res.success) {\n            commentsLoaded = true;\n            $subCommentsSection.append(res.comments);\n            const $subComments = $subCommentsSection.find('.comment-item');\n            $subComments.each((ind, comment) => {\n              initComment($(comment))\n            })\n          }\n        }\n      })\n    }\n  }\n\n  function initComments() {\n    const $comments = $('.comment-item');\n    $comments.each((ind, comment) => {\n      const $comment = $(comment);\n      initComment($comment);\n    })\n  }\n\n  function onDeleteClick(ev) {\n    ev.preventDefault();\n    const $delete = $(ev.target);\n\n    if (confirm('Are you sure you want to delete that comment?')) {\n      $.ajax({\n        method: 'post',\n        url: $delete.attr('href'),\n        success: function (res) {\n          if (res.success)\n            $delete.closest('.comment-item').remove();\n          $commentCount.text(parseInt($commentCount.text()) - 1);\n        }\n      })\n    }\n  }\n});"
  },
  {
    "path": "frontend/web/storage/.gitignore",
    "content": "*\n!.gitignore"
  },
  {
    "path": "init",
    "content": "#!/usr/bin/env php\n<?php\n/**\n * Yii Application Initialization Tool\n *\n * In order to run in non-interactive mode:\n *\n * init --env=Development --overwrite=n\n */\n\nif (!extension_loaded('openssl')) {\n    die('The OpenSSL PHP extension is required by Yii2.');\n}\n\n$params = getParams();\n$root = str_replace('\\\\', '/', __DIR__);\n$envs = require \"$root/environments/index.php\";\n$envNames = array_keys($envs);\n\necho \"Yii Application Initialization Tool v1.0\\n\\n\";\n\n$envName = null;\nif (empty($params['env']) || $params['env'] === true) {\n    echo \"Which environment do you want the application to be initialized in?\\n\\n\";\n    foreach ($envNames as $i => $name) {\n        echo \"  [$i] $name\\n\";\n    }\n    echo \"\\n  Your choice [0-\" . (count($envs) - 1) . ', or \"q\" to quit] ';\n    $answer = trim(fgets(STDIN));\n\n    if (!ctype_digit($answer) || !in_array($answer, range(0, count($envs) - 1))) {\n        echo \"\\n  Quit initialization.\\n\";\n        exit(0);\n    }\n\n    if (isset($envNames[$answer])) {\n        $envName = $envNames[$answer];\n    }\n} else {\n    $envName = $params['env'];\n}\n\nif (!in_array($envName, $envNames, true)) {\n    $envsList = implode(', ', $envNames);\n    echo \"\\n  $envName is not a valid environment. Try one of the following: $envsList. \\n\";\n    exit(2);\n}\n\n$env = $envs[$envName];\n\nif (empty($params['env'])) {\n    echo \"\\n  Initialize the application under '{$envNames[$answer]}' environment? [yes|no] \";\n    $answer = trim(fgets(STDIN));\n    if (strncasecmp($answer, 'y', 1)) {\n        echo \"\\n  Quit initialization.\\n\";\n        exit(0);\n    }\n}\n\n$rootPath = \"$root/environments/{$env['path']}\";\nif (!is_dir($rootPath)) {\n    printError(\"$rootPath directory does not exist. Check path in $envName environment.\");\n    exit(3);\n}\n\necho \"\\n  Start initialization ...\\n\\n\";\n\n$files = getFileList($rootPath);\nif (isset($env['skipFiles'])) {\n    $skipFiles = $env['skipFiles'];\n    array_walk($skipFiles, function(&$value) use($env, $root) { $value = \"$root/$value\"; });\n    $files = array_diff($files, array_intersect_key($env['skipFiles'], array_filter($skipFiles, 'file_exists')));\n}\n$all = false;\nforeach ($files as $file) {\n    if (!copyFile($root, \"environments/{$env['path']}/$file\", $file, $all, $params)) {\n        break;\n    }\n}\n\n$callbacks = ['setCookieValidationKey', 'setWritable', 'setExecutable', 'createSymlink'];\nforeach ($callbacks as $callback) {\n    if (!empty($env[$callback])) {\n        $callback($root, $env[$callback]);\n    }\n}\n\necho \"\\n  ... initialization completed.\\n\\n\";\n\nfunction getFileList($root, $basePath = '')\n{\n    $files = [];\n    $handle = opendir($root);\n    while (($path = readdir($handle)) !== false) {\n        if ($path === '.git' || $path === '.svn' || $path === '.' || $path === '..') {\n            continue;\n        }\n        $fullPath = \"$root/$path\";\n        $relativePath = $basePath === '' ? $path : \"$basePath/$path\";\n        if (is_dir($fullPath)) {\n            $files = array_merge($files, getFileList($fullPath, $relativePath));\n        } else {\n            $files[] = $relativePath;\n        }\n    }\n    closedir($handle);\n    return $files;\n}\n\nfunction copyFile($root, $source, $target, &$all, $params)\n{\n    if (!is_file($root . '/' . $source)) {\n        echo \"       skip $target ($source not exist)\\n\";\n        return true;\n    }\n    if (is_file($root . '/' . $target)) {\n        if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) {\n            echo \"  unchanged $target\\n\";\n            return true;\n        }\n        if ($all) {\n            echo \"  overwrite $target\\n\";\n        } else {\n            echo \"      exist $target\\n\";\n            echo \"            ...overwrite? [Yes|No|All|Quit] \";\n\n\n            $answer = !empty($params['overwrite']) ? $params['overwrite'] : trim(fgets(STDIN));\n            if (!strncasecmp($answer, 'q', 1)) {\n                return false;\n            } else {\n                if (!strncasecmp($answer, 'y', 1)) {\n                    echo \"  overwrite $target\\n\";\n                } else {\n                    if (!strncasecmp($answer, 'a', 1)) {\n                        echo \"  overwrite $target\\n\";\n                        $all = true;\n                    } else {\n                        echo \"       skip $target\\n\";\n                        return true;\n                    }\n                }\n            }\n        }\n        file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source));\n        return true;\n    }\n    echo \"   generate $target\\n\";\n    @mkdir(dirname($root . '/' . $target), 0777, true);\n    file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source));\n    return true;\n}\n\nfunction getParams()\n{\n    $rawParams = [];\n    if (isset($_SERVER['argv'])) {\n        $rawParams = $_SERVER['argv'];\n        array_shift($rawParams);\n    }\n\n    $params = [];\n    foreach ($rawParams as $param) {\n        if (preg_match('/^--([\\w-]*\\w)(=(.*))?$/', $param, $matches)) {\n            $name = $matches[1];\n            $params[$name] = isset($matches[3]) ? $matches[3] : true;\n        } else {\n            $params[] = $param;\n        }\n    }\n    return $params;\n}\n\nfunction setWritable($root, $paths)\n{\n    foreach ($paths as $writable) {\n        if (is_dir(\"$root/$writable\")) {\n            if (@chmod(\"$root/$writable\", 0777)) {\n                echo \"      chmod 0777 $writable\\n\";\n            } else {\n                printError(\"Operation chmod not permitted for directory $writable.\");\n            }\n        } else {\n            printError(\"Directory $writable does not exist.\");\n        }\n    }\n}\n\nfunction setExecutable($root, $paths)\n{\n    foreach ($paths as $executable) {\n        if (file_exists(\"$root/$executable\")) {\n            if (@chmod(\"$root/$executable\", 0755)) {\n                echo \"      chmod 0755 $executable\\n\";\n            } else {\n                printError(\"Operation chmod not permitted for $executable.\");\n            }\n        } else {\n            printError(\"$executable does not exist.\");\n        }\n    }\n}\n\nfunction setCookieValidationKey($root, $paths)\n{\n    foreach ($paths as $file) {\n        echo \"   generate cookie validation key in $file\\n\";\n        $file = $root . '/' . $file;\n        $length = 32;\n        $bytes = openssl_random_pseudo_bytes($length);\n        $key = strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.');\n        $content = preg_replace('/((\"|\\')cookieValidationKey(\"|\\')\\s*=>\\s*)(\"\"|\\'\\')/', \"\\\\1'$key'\", file_get_contents($file));\n        file_put_contents($file, $content);\n    }\n}\n\nfunction createSymlink($root, $links)\n{\n    foreach ($links as $link => $target) {\n        //first removing folders to avoid errors if the folder already exists\n        @rmdir($root . \"/\" . $link);\n        //next removing existing symlink in order to update the target\n        if (is_link($root . \"/\" . $link)) {\n            @unlink($root . \"/\" . $link);\n        }\n        if (@symlink($root . \"/\" . $target, $root . \"/\" . $link)) {\n            echo \"      symlink $root/$target $root/$link\\n\";\n        } else {\n            printError(\"Cannot create symlink $root/$target $root/$link.\");\n        }\n    }\n}\n\n/**\n * Prints error message.\n * @param string $message message\n */\nfunction printError($message)\n{\n    echo \"\\n  \" . formatMessage(\"Error. $message\", ['fg-red']) . \" \\n\";\n}\n\n/**\n * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream.\n *\n * - windows without ansicon\n * - not tty consoles\n *\n * @return boolean true if the stream supports ANSI colors, otherwise false.\n */\nfunction ansiColorsSupported()\n{\n    return DIRECTORY_SEPARATOR === '\\\\'\n        ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON'\n        : function_exists('posix_isatty') && @posix_isatty(STDOUT);\n}\n\n/**\n * Get ANSI code of style.\n * @param string $name style name\n * @return integer ANSI code of style.\n */\nfunction getStyleCode($name)\n{\n    $styles = [\n        'bold' => 1,\n        'fg-black' => 30,\n        'fg-red' => 31,\n        'fg-green' => 32,\n        'fg-yellow' => 33,\n        'fg-blue' => 34,\n        'fg-magenta' => 35,\n        'fg-cyan' => 36,\n        'fg-white' => 37,\n        'bg-black' => 40,\n        'bg-red' => 41,\n        'bg-green' => 42,\n        'bg-yellow' => 43,\n        'bg-blue' => 44,\n        'bg-magenta' => 45,\n        'bg-cyan' => 46,\n        'bg-white' => 47,\n    ];\n    return $styles[$name];\n}\n\n/**\n * Formats message using styles if STDOUT supports it.\n * @param string $message message\n * @param string[] $styles styles\n * @return string formatted message.\n */\nfunction formatMessage($message, $styles)\n{\n    if (empty($styles) || !ansiColorsSupported()) {\n        return $message;\n    }\n\n    return sprintf(\"\\x1b[%sm\", implode(';', array_map('getStyleCode', $styles))) . $message . \"\\x1b[0m\";\n}\n"
  },
  {
    "path": "init.bat",
    "content": "@echo off\n\nrem -------------------------------------------------------------\nrem  Yii command line init script for Windows.\nrem -------------------------------------------------------------\n\n@setlocal\n\nset YII_PATH=%~dp0\n\nif \"%PHP_COMMAND%\" == \"\" set PHP_COMMAND=php.exe\n\n\"%PHP_COMMAND%\" \"%YII_PATH%init\" %*\n\n@endlocal\n"
  },
  {
    "path": "requirements.php",
    "content": "<?php\n/**\n * Application requirement checker script.\n *\n * In order to run this script use the following console command:\n * php requirements.php\n *\n * In order to run this script from the web, you should copy it to the web root.\n * If you are using Linux you can create a hard link instead, using the following command:\n * ln ../../requirements.php requirements.php\n */\n\n// you may need to adjust this path to the correct Yii framework path\n// uncomment and adjust the following line if Yii is not located at the default path\n//$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2';\nif (!isset($frameworkPath)) {\n    $searchPaths = array(\n        dirname(__FILE__) . '/vendor/yiisoft/yii2',\n        dirname(__FILE__) . '/../../vendor/yiisoft/yii2',\n    );\n    foreach ($searchPaths as $path) {\n        if (is_dir($path)) {\n            $frameworkPath = $path;\n            break;\n        }\n    }\n}\nif (!isset($frameworkPath) || !is_dir($frameworkPath)) {\n    $message = \"<h1>Error</h1>\\n\\n\"\n        . \"<p><strong>The path to yii framework seems to be incorrect.</strong></p>\\n\"\n        . '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title=\"' . __FILE__ . '\">' . basename(__FILE__) . \"</abbr>.</p>\\n\"\n        . '<p>Please refer to the <abbr title=\"' . dirname(dirname(dirname(__FILE__))) . \"/README.md\\\">README</abbr> on how to install Yii.</p>\\n\";\n    if (!empty($_SERVER['argv'])) {\n        // do not print HTML when used in console mode\n        echo strip_tags($message);\n    } else {\n        echo $message;\n    }\n    exit(1);\n}\n\nrequire_once $frameworkPath . '/requirements/YiiRequirementChecker.php';\n$requirementsChecker = new YiiRequirementChecker();\n\n$gdMemo = $imagickMemo = 'Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required for image CAPTCHA.';\n$gdOK = $imagickOK = false;\n\nif (extension_loaded('imagick')) {\n    $imagick = new Imagick();\n    $imagickFormats = $imagick->queryFormats('PNG');\n    if (in_array('PNG', $imagickFormats)) {\n        $imagickOK = true;\n    } else {\n        $imagickMemo = 'Imagick extension should be installed with PNG support in order to be used for image CAPTCHA.';\n    }\n}\n\nif (extension_loaded('gd')) {\n    $gdInfo = gd_info();\n    if (!empty($gdInfo['FreeType Support'])) {\n        $gdOK = true;\n    } else {\n        $gdMemo = 'GD extension should be installed with FreeType support in order to be used for image CAPTCHA.';\n    }\n}\n\n/**\n * Adjust requirements according to your application specifics.\n */\n$requirements = array(\n    // Database :\n    array(\n        'name' => 'PDO extension',\n        'mandatory' => true,\n        'condition' => extension_loaded('pdo'),\n        'by' => 'All DB-related classes',\n    ),\n    array(\n        'name' => 'PDO SQLite extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('pdo_sqlite'),\n        'by' => 'All DB-related classes',\n        'memo' => 'Required for SQLite database.',\n    ),\n    array(\n        'name' => 'PDO MySQL extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('pdo_mysql'),\n        'by' => 'All DB-related classes',\n        'memo' => 'Required for MySQL database.',\n    ),\n    array(\n        'name' => 'PDO PostgreSQL extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('pdo_pgsql'),\n        'by' => 'All DB-related classes',\n        'memo' => 'Required for PostgreSQL database.',\n    ),\n    // Cache :\n    array(\n        'name' => 'Memcache extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('memcache') || extension_loaded('memcached'),\n        'by' => '<a href=\"http://www.yiiframework.com/doc-2.0/yii-caching-memcache.html\">MemCache</a>',\n        'memo' => extension_loaded('memcached') ? 'To use memcached set <a href=\"http://www.yiiframework.com/doc-2.0/yii-caching-memcache.html#$useMemcached-detail\">MemCache::useMemcached</a> to <code>true</code>.' : ''\n    ),\n    array(\n        'name' => 'APC extension',\n        'mandatory' => false,\n        'condition' => extension_loaded('apc'),\n        'by' => '<a href=\"http://www.yiiframework.com/doc-2.0/yii-caching-apccache.html\">ApcCache</a>',\n    ),\n    // CAPTCHA:\n    array(\n        'name' => 'GD PHP extension with FreeType support',\n        'mandatory' => false,\n        'condition' => $gdOK,\n        'by' => '<a href=\"http://www.yiiframework.com/doc-2.0/yii-captcha-captcha.html\">Captcha</a>',\n        'memo' => $gdMemo,\n    ),\n    array(\n        'name' => 'ImageMagick PHP extension with PNG support',\n        'mandatory' => false,\n        'condition' => $imagickOK,\n        'by' => '<a href=\"http://www.yiiframework.com/doc-2.0/yii-captcha-captcha.html\">Captcha</a>',\n        'memo' => $imagickMemo,\n    ),\n    // PHP ini :\n    'phpExposePhp' => array(\n        'name' => 'Expose PHP',\n        'mandatory' => false,\n        'condition' => $requirementsChecker->checkPhpIniOff(\"expose_php\"),\n        'by' => 'Security reasons',\n        'memo' => '\"expose_php\" should be disabled at php.ini',\n    ),\n    'phpAllowUrlInclude' => array(\n        'name' => 'PHP allow url include',\n        'mandatory' => false,\n        'condition' => $requirementsChecker->checkPhpIniOff(\"allow_url_include\"),\n        'by' => 'Security reasons',\n        'memo' => '\"allow_url_include\" should be disabled at php.ini',\n    ),\n    'phpSmtp' => array(\n        'name' => 'PHP mail SMTP',\n        'mandatory' => false,\n        'condition' => strlen(ini_get('SMTP')) > 0,\n        'by' => 'Email sending',\n        'memo' => 'PHP mail SMTP server required',\n    ),\n);\n\n$result = $requirementsChecker->checkYii()->check($requirements)->getResult();\n$requirementsChecker->render();\n\nexit($result['summary']['errors'] === 0 ? 0 : 1);\n"
  },
  {
    "path": "sample.php",
    "content": "<?php\n\n$countries = [\"Georgia\", \"USA\", \"India\"];\n\nforeach ($countries as $country) {\n\n}"
  },
  {
    "path": "vagrant/config/.gitignore",
    "content": "# local configuration\nvagrant-local.yml"
  },
  {
    "path": "vagrant/config/vagrant-local.example.yml",
    "content": "# Your personal GitHub token\ngithub_token: <your-personal-github-token>\n# Read more: https://github.com/blog/1509-personal-api-tokens\n# You can generate it here: https://github.com/settings/tokens\n\n# Guest OS timezone\ntimezone: Europe/London\n\n# Are we need check box updates for every 'vagrant up'?\nbox_check_update: false\n\n# Virtual machine name\nmachine_name: y2aa\n\n# Virtual machine IP\nip: 192.168.83.137\n\n# Virtual machine CPU cores number\ncpus: 1\n\n# Virtual machine RAM\nmemory: 1024\n"
  },
  {
    "path": "vagrant/nginx/app.conf",
    "content": "server {\n   charset utf-8;\n   client_max_body_size 128M;\n   sendfile off;\n\n   listen 80; ## listen for ipv4\n   #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n   server_name y2aa-frontend.test;\n   root        /app/frontend/web/;\n   index       index.php;\n\n   access_log  /app/vagrant/nginx/log/frontend-access.log;\n   error_log   /app/vagrant/nginx/log/frontend-error.log;\n\n   location / {\n       # Redirect everything that isn't a real file to index.php\n       try_files $uri $uri/ /index.php$is_args$args;\n   }\n\n   # uncomment to avoid processing of calls to non-existing static files by Yii\n   #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n   #    try_files $uri =404;\n   #}\n   #error_page 404 /404.html;\n\n   location ~ \\.php$ {\n       include fastcgi_params;\n       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n       #fastcgi_pass   127.0.0.1:9000;\n       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;\n       try_files $uri =404;\n   }\n\n   location ~ /\\.(ht|svn|git) {\n       deny all;\n   }\n}\n\nserver {\n   charset utf-8;\n   client_max_body_size 128M;\n   sendfile off;\n\n   listen 80; ## listen for ipv4\n   #listen [::]:80 default_server ipv6only=on; ## listen for ipv6\n\n   server_name y2aa-backend.test;\n   root        /app/backend/web/;\n   index       index.php;\n\n   access_log  /app/vagrant/nginx/log/backend-access.log;\n   error_log   /app/vagrant/nginx/log/backend-error.log;\n\n   location / {\n       # Redirect everything that isn't a real file to index.php\n       try_files $uri $uri/ /index.php$is_args$args;\n   }\n\n   # uncomment to avoid processing of calls to non-existing static files by Yii\n   #location ~ \\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {\n   #    try_files $uri =404;\n   #}\n   #error_page 404 /404.html;\n\n   location ~ \\.php$ {\n       include fastcgi_params;\n       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n       #fastcgi_pass   127.0.0.1:9000;\n       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;\n       try_files $uri =404;\n   }\n\n   location ~ /\\.(ht|svn|git) {\n       deny all;\n   }\n}\n"
  },
  {
    "path": "vagrant/nginx/log/.gitignore",
    "content": "# nginx logs\nbackend-access.log\nbackend-error.log\nfrontend-access.log\nfrontend-error.log"
  },
  {
    "path": "vagrant/provision/always-as-root.sh",
    "content": "#!/usr/bin/env bash\n\nsource /app/vagrant/provision/common.sh\n\n#== Provision script ==\n\ninfo \"Provision-script user: `whoami`\"\n\ninfo \"Restart web-stack\"\nservice php7.0-fpm restart\nservice nginx restart\nservice mysql restart"
  },
  {
    "path": "vagrant/provision/common.sh",
    "content": "#!/usr/bin/env bash\n\n#== Bash helpers ==\n\nfunction info {\n  echo \" \"\n  echo \"--> $1\"\n  echo \" \"\n}\n"
  },
  {
    "path": "vagrant/provision/once-as-root.sh",
    "content": "#!/usr/bin/env bash\n\nsource /app/vagrant/provision/common.sh\n\n#== Import script args ==\n\ntimezone=$(echo \"$1\")\n\n#== Provision script ==\n\ninfo \"Provision-script user: `whoami`\"\n\nexport DEBIAN_FRONTEND=noninteractive\n\ninfo \"Configure timezone\"\ntimedatectl set-timezone ${timezone} --no-ask-password\n\ninfo \"Prepare root password for MySQL\"\ndebconf-set-selections <<< \"mysql-community-server mysql-community-server/root-pass password \\\"''\\\"\"\ndebconf-set-selections <<< \"mysql-community-server mysql-community-server/re-root-pass password \\\"''\\\"\"\necho \"Done!\"\n\ninfo \"Update OS software\"\napt-get update\napt-get upgrade -y\n\ninfo \"Install additional software\"\napt-get install -y php7.0-curl php7.0-cli php7.0-intl php7.0-mysqlnd php7.0-gd php7.0-fpm php7.0-mbstring php7.0-xml unzip nginx mysql-server-5.7 php.xdebug\n\ninfo \"Configure MySQL\"\nsed -i \"s/.*bind-address.*/bind-address = 0.0.0.0/\" /etc/mysql/mysql.conf.d/mysqld.cnf\nmysql -uroot <<< \"CREATE USER 'root'@'%' IDENTIFIED BY ''\"\nmysql -uroot <<< \"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'\"\nmysql -uroot <<< \"DROP USER 'root'@'localhost'\"\nmysql -uroot <<< \"FLUSH PRIVILEGES\"\necho \"Done!\"\n\ninfo \"Configure PHP-FPM\"\nsed -i 's/user = www-data/user = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf\nsed -i 's/group = www-data/group = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf\nsed -i 's/owner = www-data/owner = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf\ncat << EOF > /etc/php/7.0/mods-available/xdebug.ini\nzend_extension=xdebug.so\nxdebug.remote_enable=1\nxdebug.remote_connect_back=1\nxdebug.remote_port=9000\nxdebug.remote_autostart=1\nEOF\necho \"Done!\"\n\ninfo \"Configure NGINX\"\nsed -i 's/user www-data/user vagrant/g' /etc/nginx/nginx.conf\necho \"Done!\"\n\ninfo \"Enabling site configuration\"\nln -s /app/vagrant/nginx/app.conf /etc/nginx/sites-enabled/app.conf\necho \"Done!\"\n\ninfo \"Initailize databases for MySQL\"\nmysql -uroot <<< \"CREATE DATABASE yii2advanced\"\nmysql -uroot <<< \"CREATE DATABASE yii2advanced_test\"\necho \"Done!\"\n\ninfo \"Install composer\"\ncurl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer"
  },
  {
    "path": "vagrant/provision/once-as-vagrant.sh",
    "content": "#!/usr/bin/env bash\n\nsource /app/vagrant/provision/common.sh\n\n#== Import script args ==\n\ngithub_token=$(echo \"$1\")\n\n#== Provision script ==\n\ninfo \"Provision-script user: `whoami`\"\n\ninfo \"Configure composer\"\ncomposer config --global github-oauth.github.com ${github_token}\necho \"Done!\"\n\ninfo \"Install project dependencies\"\ncd /app\ncomposer --no-progress --prefer-dist install\n\ninfo \"Init project\"\n./init --env=Development --overwrite=y\n\ninfo \"Apply migrations\"\n./yii migrate --interactive=0\n./yii_test migrate --interactive=0\n\ninfo \"Create bash-alias 'app' for vagrant user\"\necho 'alias app=\"cd /app\"' | tee /home/vagrant/.bash_aliases\n\ninfo \"Enabling colorized prompt for guest console\"\nsed -i \"s/#force_color_prompt=yes/force_color_prompt=yes/\" /home/vagrant/.bashrc\n"
  },
  {
    "path": "yii.bat",
    "content": "@echo off\n\nrem -------------------------------------------------------------\nrem  Yii command line bootstrap script for Windows.\nrem -------------------------------------------------------------\n\n@setlocal\n\nset YII_PATH=%~dp0\n\nif \"%PHP_COMMAND%\" == \"\" set PHP_COMMAND=php.exe\n\n\"%PHP_COMMAND%\" \"%YII_PATH%yii\" %*\n\n@endlocal\n"
  }
]