master d8d6c35890b5 cached
237 files
254.7 KB
71.3k tokens
310 symbols
1 requests
Download .txt
Showing preview only (307K chars total). Download the full file or copy to clipboard to get everything.
Repository: thecodeholic/Yii2-Youtube-Clone
Branch: master
Commit: d8d6c35890b5
Files: 237
Total size: 254.7 KB

Directory structure:
gitextract_etck1_vw/

├── .bowerrc
├── .gitignore
├── LICENSE.md
├── README.md
├── Vagrantfile
├── backend/
│   ├── Dockerfile
│   ├── assets/
│   │   ├── AppAsset.php
│   │   └── TagsInputAsset.php
│   ├── codeception.yml
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── controllers/
│   │   ├── CommentController.php
│   │   ├── SiteController.php
│   │   └── VideoController.php
│   ├── models/
│   │   ├── .gitkeep
│   │   └── CommentSearch.php
│   ├── runtime/
│   │   └── .gitignore
│   ├── tests/
│   │   ├── _bootstrap.php
│   │   ├── _data/
│   │   │   ├── .gitignore
│   │   │   └── login_data.php
│   │   ├── _output/
│   │   │   └── .gitignore
│   │   ├── _support/
│   │   │   ├── .gitignore
│   │   │   ├── FunctionalTester.php
│   │   │   └── UnitTester.php
│   │   ├── functional/
│   │   │   ├── LoginCest.php
│   │   │   └── _bootstrap.php
│   │   ├── functional.suite.yml
│   │   ├── unit/
│   │   │   └── _bootstrap.php
│   │   └── unit.suite.yml
│   ├── views/
│   │   ├── comment/
│   │   │   ├── _comment_item.php
│   │   │   ├── _form.php
│   │   │   ├── _item.php
│   │   │   ├── _search.php
│   │   │   ├── create.php
│   │   │   ├── index.php
│   │   │   ├── update.php
│   │   │   └── view.php
│   │   ├── layouts/
│   │   │   ├── _header.php
│   │   │   ├── _sidebar.php
│   │   │   ├── auth.php
│   │   │   ├── base.php
│   │   │   └── main.php
│   │   ├── site/
│   │   │   ├── error.php
│   │   │   ├── index.php
│   │   │   └── login.php
│   │   └── video/
│   │       ├── _form.php
│   │       ├── _video_item.php
│   │       ├── create.php
│   │       ├── index.php
│   │       └── update.php
│   └── web/
│       ├── app.js
│       ├── assets/
│       │   └── .gitignore
│       ├── css/
│       │   └── site.css
│       └── tagsinput/
│           ├── tagsinput.css
│           └── tagsinput.js
├── codeception.yml
├── common/
│   ├── codeception.yml
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── fixtures/
│   │   └── UserFixture.php
│   ├── helpers/
│   │   └── Html.php
│   ├── mail/
│   │   ├── emailVerify-html.php
│   │   ├── emailVerify-text.php
│   │   ├── layouts/
│   │   │   ├── html.php
│   │   │   └── text.php
│   │   ├── mention-html.php
│   │   ├── mention-text.php
│   │   ├── passwordResetToken-html.php
│   │   ├── passwordResetToken-text.php
│   │   ├── subscriber-html.php
│   │   └── subscriber-text.php
│   ├── models/
│   │   ├── Comment.php
│   │   ├── LoginForm.php
│   │   ├── Subscriber.php
│   │   ├── User.php
│   │   ├── Video.php
│   │   ├── VideoLike.php
│   │   ├── VideoView.php
│   │   └── query/
│   │       ├── CommentQuery.php
│   │       ├── SubscriberQuery.php
│   │       ├── VideoLikeQuery.php
│   │       ├── VideoQuery.php
│   │       └── VideoViewQuery.php
│   ├── tests/
│   │   ├── _bootstrap.php
│   │   ├── _data/
│   │   │   └── user.php
│   │   ├── _output/
│   │   │   └── .gitignore
│   │   ├── _support/
│   │   │   ├── .gitignore
│   │   │   └── UnitTester.php
│   │   ├── unit/
│   │   │   └── models/
│   │   │       └── LoginFormTest.php
│   │   └── unit.suite.yml
│   └── widgets/
│       └── Alert.php
├── composer.json
├── console/
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── controllers/
│   │   └── .gitkeep
│   ├── migrations/
│   │   ├── m130524_201442_init.php
│   │   ├── m190124_110200_add_verification_token_column_to_user_table.php
│   │   ├── m200417_054237_create_videos_table.php
│   │   ├── m200418_050048_create_video_view_table.php
│   │   ├── m200418_051244_create_video_like_table.php
│   │   ├── m200418_060320_create_subscriber_table.php
│   │   ├── m200418_064142_create_fulltext_index_on_video.php
│   │   ├── m201112_042619_create_comment_table.php
│   │   └── m201115_124738_add_mention_column_to_comment_table.php
│   ├── models/
│   │   └── .gitkeep
│   └── runtime/
│       └── .gitignore
├── docker-compose.yml
├── environments/
│   ├── dev/
│   │   ├── backend/
│   │   │   ├── config/
│   │   │   │   ├── codeception-local.php
│   │   │   │   ├── main-local.php
│   │   │   │   ├── params-local.php
│   │   │   │   └── test-local.php
│   │   │   └── web/
│   │   │       ├── index-test.php
│   │   │       ├── index.php
│   │   │       └── robots.txt
│   │   ├── common/
│   │   │   └── config/
│   │   │       ├── codeception-local.php
│   │   │       ├── main-local.php
│   │   │       ├── params-local.php
│   │   │       └── test-local.php
│   │   ├── console/
│   │   │   └── config/
│   │   │       ├── main-local.php
│   │   │       ├── params-local.php
│   │   │       └── test-local.php
│   │   ├── frontend/
│   │   │   ├── config/
│   │   │   │   ├── codeception-local.php
│   │   │   │   ├── main-local.php
│   │   │   │   ├── params-local.php
│   │   │   │   └── test-local.php
│   │   │   └── web/
│   │   │       ├── index-test.php
│   │   │       ├── index.php
│   │   │       └── robots.txt
│   │   ├── yii
│   │   ├── yii_test
│   │   └── yii_test.bat
│   ├── index.php
│   └── prod/
│       ├── backend/
│       │   ├── config/
│       │   │   ├── main-local.php
│       │   │   └── params-local.php
│       │   └── web/
│       │       ├── index.php
│       │       └── robots.txt
│       ├── common/
│       │   └── config/
│       │       ├── main-local.php
│       │       └── params-local.php
│       ├── console/
│       │   └── config/
│       │       ├── main-local.php
│       │       └── params-local.php
│       ├── frontend/
│       │   ├── config/
│       │   │   ├── main-local.php
│       │   │   └── params-local.php
│       │   └── web/
│       │       ├── index.php
│       │       └── robots.txt
│       └── yii
├── frontend/
│   ├── Dockerfile
│   ├── assets/
│   │   └── AppAsset.php
│   ├── codeception.yml
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── controllers/
│   │   ├── ChannelController.php
│   │   ├── CommentController.php
│   │   ├── SiteController.php
│   │   └── VideoController.php
│   ├── models/
│   │   ├── PasswordResetRequestForm.php
│   │   ├── ResendVerificationEmailForm.php
│   │   ├── ResetPasswordForm.php
│   │   ├── SignupForm.php
│   │   └── VerifyEmailForm.php
│   ├── runtime/
│   │   └── .gitignore
│   ├── tests/
│   │   ├── _bootstrap.php
│   │   ├── _data/
│   │   │   ├── login_data.php
│   │   │   └── user.php
│   │   ├── _output/
│   │   │   └── .gitignore
│   │   ├── _support/
│   │   │   ├── .gitignore
│   │   │   ├── FunctionalTester.php
│   │   │   └── UnitTester.php
│   │   ├── acceptance/
│   │   │   ├── HomeCest.php
│   │   │   └── _bootstrap.php
│   │   ├── acceptance.suite.yml.example
│   │   ├── functional/
│   │   │   ├── AboutCest.php
│   │   │   ├── ContactCest.php
│   │   │   ├── HomeCest.php
│   │   │   ├── LoginCest.php
│   │   │   ├── ResendVerificationEmailCest.php
│   │   │   ├── SignupCest.php
│   │   │   ├── VerifyEmailCest.php
│   │   │   └── _bootstrap.php
│   │   ├── functional.suite.yml
│   │   ├── unit/
│   │   │   ├── _bootstrap.php
│   │   │   └── models/
│   │   │       ├── ContactFormTest.php
│   │   │       ├── PasswordResetRequestFormTest.php
│   │   │       ├── ResendVerificationEmailFormTest.php
│   │   │       ├── ResetPasswordFormTest.php
│   │   │       ├── SignupFormTest.php
│   │   │       └── VerifyEmailFormTest.php
│   │   └── unit.suite.yml
│   ├── views/
│   │   ├── channel/
│   │   │   ├── _subscribe.php
│   │   │   └── view.php
│   │   ├── layouts/
│   │   │   ├── _header.php
│   │   │   ├── _sidebar.php
│   │   │   ├── auth.php
│   │   │   ├── base.php
│   │   │   └── main.php
│   │   ├── site/
│   │   │   ├── error.php
│   │   │   ├── index.php
│   │   │   ├── login.php
│   │   │   ├── requestPasswordResetToken.php
│   │   │   ├── resendVerificationEmail.php
│   │   │   ├── resetPassword.php
│   │   │   └── signup.php
│   │   └── video/
│   │       ├── _buttons.php
│   │       ├── _comment_item.php
│   │       ├── _video_item.php
│   │       ├── history.php
│   │       ├── index.php
│   │       ├── search.php
│   │       └── view.php
│   └── web/
│       ├── assets/
│       │   └── .gitignore
│       ├── css/
│       │   └── site.css
│       ├── js/
│       │   └── app.js
│       └── storage/
│           └── .gitignore
├── init
├── init.bat
├── requirements.php
├── sample.php
├── vagrant/
│   ├── config/
│   │   ├── .gitignore
│   │   └── vagrant-local.example.yml
│   ├── nginx/
│   │   ├── app.conf
│   │   └── log/
│   │       └── .gitignore
│   └── provision/
│       ├── always-as-root.sh
│       ├── common.sh
│       ├── once-as-root.sh
│       └── once-as-vagrant.sh
└── yii.bat

================================================
FILE CONTENTS
================================================

================================================
FILE: .bowerrc
================================================
{
    "directory" : "vendor/bower-asset"
}


================================================
FILE: .gitignore
================================================
# yii console commands
/yii
/yii_test
/yii_test.bat

# phpstorm project files
.idea

# netbeans project files
nbproject

# zend studio for eclipse project files
.buildpath
.project
.settings

# windows thumbnail cache
Thumbs.db

# composer vendor dir
/vendor

# composer itself is not needed
composer.phar

# Mac DS_Store Files
.DS_Store

# phpunit itself is not needed
phpunit.phar
# local phpunit config
/phpunit.xml

# vagrant runtime
/.vagrant

# ignore generated files
/frontend/web/index.php
/frontend/web/index-test.php
/frontend/web/robots.txt
/backend/web/index.php
/backend/web/index-test.php
/backend/web/robots.txt


================================================
FILE: LICENSE.md
================================================
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in
   the documentation and/or other materials provided with the
   distribution.
 * Neither the name of Yii Software LLC nor the names of its
   contributors may be used to endorse or promote products derived
   from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
<p align="center">
    <a href="#" target="_blank">
        <img src="https://avatars0.githubusercontent.com/u/993323" height="100px">
    </a>
    <h1 align="center">FreeCodeTube - Yii2 Youtube Clone</h1>
    <br>
</p>

#### The project was created while recording [video for FreeCodeCamp](https://youtu.be/whuIf33v2Ug)

Features
================

 - Login and Registration
 - Email confirmation
 - Upload videos
 - Provide thumbnail, title, description, tags
 - Status of the video: Published or Unlisted
 - Dashboard with analitics: 
    - Latest video
    - Number of total views
    - Number of total subscribers
    - Latest subscribers
 - View videos
 - Leave a like/dislike
 - Find similar videos
 - Channel page
 - View videos only for specific channel
 - Subscribe on channel or unsubscribe
 - Sending email when user subscribes to channel
 - Global search to search videos by title, description or tags
 - History page

Demo
====

If you want to see working demo of the application [click here](https://freecodetube.thecodeholic.com/)

Installation
============

## Requirements

The minimum requirement by this project template is that your Web server supports PHP 5.6.0.

## Installing using Composer

##### Clone the repository from github.

    git clone git@github.com:thecodeholic/Yii2-Youtube-Clone.git [YourDirectoryName]
    
The command installs the project in a directory named `YourDirectoryName`. You can choose a different
directory name if you want.

##### Install dependencies

For this we need composer to be installed on our operating system. 
If you do not have [Composer](http://getcomposer.org/), follow the instructions in the
[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.

With Composer installed, navigate to the project folder from command line and run

    composer install


## Preparing application

Follow the steps from [yii2 advanced template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-installation.md#preparing-application)
to prepare installation.

After doing all the steps from [yii2 advanced template](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-installation.md#preparing-application)
open `common/config/params-local.php` and add your frontend domain on key `frontendUrl`. 
Example:

```php
return [
    'frontendUrl' => 'http://frontend.test/'
];
```


================================================
FILE: Vagrantfile
================================================
require 'yaml'
require 'fileutils'

required_plugins = %w( vagrant-hostmanager vagrant-vbguest )
required_plugins.each do |plugin|
    exec "vagrant plugin install #{plugin}" unless Vagrant.has_plugin? plugin
end

domains = {
  frontend: 'y2aa-frontend.test',
  backend:  'y2aa-backend.test'
}

config = {
  local: './vagrant/config/vagrant-local.yml',
  example: './vagrant/config/vagrant-local.example.yml'
}

# copy config from example if local config not exists
FileUtils.cp config[:example], config[:local] unless File.exist?(config[:local])
# read config
options = YAML.load_file config[:local]

# check github token
if options['github_token'].nil? || options['github_token'].to_s.length != 40
  puts "You must place REAL GitHub token into configuration:\n/yii2-app-advanced/vagrant/config/vagrant-local.yml"
  exit
end

# vagrant configurate
Vagrant.configure(2) do |config|
  # select the box
  config.vm.box = 'bento/ubuntu-16.04'

  # should we ask about box updates?
  config.vm.box_check_update = options['box_check_update']

  config.vm.provider 'virtualbox' do |vb|
    # machine cpus count
    vb.cpus = options['cpus']
    # machine memory size
    vb.memory = options['memory']
    # machine name (for VirtualBox UI)
    vb.name = options['machine_name']
  end

  # machine name (for vagrant console)
  config.vm.define options['machine_name']

  # machine name (for guest machine console)
  config.vm.hostname = options['machine_name']

  # network settings
  config.vm.network 'private_network', ip: options['ip']

  # sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine)
  config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant'

  # disable folder '/vagrant' (guest machine)
  config.vm.synced_folder '.', '/vagrant', disabled: true

  # hosts settings (host machine)
  config.vm.provision :hostmanager
  config.hostmanager.enabled            = true
  config.hostmanager.manage_host        = true
  config.hostmanager.ignore_private_ip  = false
  config.hostmanager.include_offline    = true
  config.hostmanager.aliases            = domains.values

  # provisioners
  config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone']]
  config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false
  config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always'

  # post-install message (vagrant console)
  config.vm.post_up_message = "Frontend URL: http://#{domains[:frontend]}\nBackend URL: http://#{domains[:backend]}"
end


================================================
FILE: backend/Dockerfile
================================================
FROM yiisoftware/yii2-php:7.2-apache

# Change document root for Apache
RUN sed -i -e 's|/app/web|/app/backend/web|g' /etc/apache2/sites-available/000-default.conf

================================================
FILE: backend/assets/AppAsset.php
================================================
<?php

namespace backend\assets;

use yii\web\AssetBundle;

/**
 * Main backend application asset bundle.
 */
class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
    ];
    public $js = [
        'app.js'
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap4\BootstrapAsset',
    ];
}


================================================
FILE: backend/assets/TagsInputAsset.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/17/2020
 * Time: 11:12 AM
 */

namespace backend\assets;


use yii\web\AssetBundle;
use yii\web\JqueryAsset;

/**
 * Class TagsInputAsset
 *
 * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>
 * @package backend\assets
 */
class TagsInputAsset extends AssetBundle
{
    public $basePath = '@webroot/tagsinput';
    public $baseUrl = '@web/tagsinput';
    public $css = [
        'tagsinput.css'
    ];

    public $js = [
        'tagsinput.js'
    ];

    public $depends = [
        JqueryAsset::class
    ];
}

================================================
FILE: backend/codeception.yml
================================================
namespace: backend\tests
actor_suffix: Tester
paths:
    tests: tests
    output: tests/_output
    data: tests/_data
    support: tests/_support
bootstrap: _bootstrap.php
settings:
    colors: true
    memory_limit: 1024M
modules:
    config:
        Yii2:
            configFile: 'config/codeception-local.php'


================================================
FILE: backend/config/.gitignore
================================================
codeception-local.php
main-local.php
params-local.php
test-local.php


================================================
FILE: backend/config/bootstrap.php
================================================
<?php


================================================
FILE: backend/config/main.php
================================================
<?php
$params = array_merge(
    require __DIR__ . '/../../common/config/params.php',
    require __DIR__ . '/../../common/config/params-local.php',
    require __DIR__ . '/params.php',
    require __DIR__ . '/params-local.php'
);

return [
    'id' => 'app-backend',
    'name' => 'FreeCodeTube Studio',
    'basePath' => dirname(__DIR__),
    'controllerNamespace' => 'backend\controllers',
    'bootstrap' => ['log'],
    'modules' => [],
    'language' => 'en-US',
    'components' => [
        'request' => [
            'csrfParam' => '_csrf-backend',
        ],
        'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true],
        ],
        'session' => [
            // this is the name of the session cookie used for login on the backend
            'name' => 'advanced-backend',
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],

        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                'video/update/<id>' => 'video/update'
            ],
        ],
        'assetManager' => [
            'appendTimestamp' => true
        ]

    ],
    'params' => $params,
];


================================================
FILE: backend/config/params.php
================================================
<?php
return [
    'adminEmail' => 'admin@example.com',
];


================================================
FILE: backend/config/test.php
================================================
<?php
return [
    'id' => 'app-backend-tests',
    'components' => [
        'assetManager' => [
            'basePath' => __DIR__ . '/../web/assets',
        ],
        'urlManager' => [
            'showScriptName' => true,
        ],
        'request' => [
            'cookieValidationKey' => 'test',
        ],
    ],
];


================================================
FILE: backend/controllers/CommentController.php
================================================
<?php

namespace backend\controllers;

use Yii;
use common\models\Comment;
use backend\models\CommentSearch;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * CommentController implements the CRUD actions for Comment model.
 */
class CommentController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@']
                    ]
                ]
            ],
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Lists all Comment models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new CommentSearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams, Yii::$app->user->id);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Displays a single Comment model.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new Comment model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new Comment();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

    /**
     * Updates an existing Comment model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('update', [
            'model' => $model,
        ]);
    }

    /**
     * Deletes an existing Comment model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionDelete($id)
    {
        $this->findModel($id)->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the Comment model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return Comment the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = Comment::findOne($id)) !== null) {
            return $model;
        }

        throw new NotFoundHttpException('The requested page does not exist.');
    }
}


================================================
FILE: backend/controllers/SiteController.php
================================================
<?php

namespace backend\controllers;

use common\models\Subscriber;
use common\models\Video;
use common\models\VideoView;
use Yii;
use yii\web\Controller;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use common\models\LoginForm;

/**
 * Site controller
 */
class SiteController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'actions' => ['login', 'error'],
                        'allow' => true,
                    ],
                    [
                        'actions' => ['logout', 'index'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'logout' => ['post'],
                ],
            ],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
        ];
    }

    /**
     * Displays homepage.
     *
     * @return string
     */
    public function actionIndex()
    {
        $user = Yii::$app->user->identity;
        $userId = $user->id;
        $latestVideo = Video::find()
            ->latest()
            ->creator($userId)
            ->limit(1)
            ->one();
        $numberOfView = VideoView::find()
            ->alias('vv')
            ->innerJoin(Video::tableName() . ' v',
                'v.video_id = vv.video_id')
            ->andWhere(['v.created_by' => $userId])
            ->count();

        $numberOfSubscribers = Yii::$app->cache->get('subscribers-'.$userId);
        if (!$numberOfSubscribers) {
            $numberOfSubscribers = $user->getSubscribers()->count();
            Yii::$app->cache->set('subscribers-'.$userId, $numberOfSubscribers);
        }


        $subscribers = Subscriber::find()
            ->with('user')
            ->andWhere(['channel_id' => $userId])
            ->orderBy('created_at DESC')
            ->limit(3)
            ->all();

        return $this->render('index', [
            'latestVideo' => $latestVideo,
            'numberOfView' => $numberOfView,
            'numberOfSubscribers' => $numberOfSubscribers,
            'subscribers' => $subscribers,
        ]);
    }

    /**
     * Login action.
     *
     * @return string
     */
    public function actionLogin()
    {
        $this->layout = 'auth';
        if (!Yii::$app->user->isGuest) {
            return $this->goHome();
        }

        $model = new LoginForm();
        if ($model->load(Yii::$app->request->post()) && $model->login()) {
            return $this->goBack();
        } else {
            $model->password = '';

            return $this->render('login', [
                'model' => $model,
            ]);
        }
    }

    /**
     * Logout action.
     *
     * @return string
     */
    public function actionLogout()
    {
        Yii::$app->user->logout();

        return $this->goHome();
    }
}


================================================
FILE: backend/controllers/VideoController.php
================================================
<?php

namespace backend\controllers;

use Yii;
use common\models\Video;
use yii\data\ActiveDataProvider;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;

/**
 * VideoController implements the CRUD actions for Video model.
 */
class VideoController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@']
                    ]
                ]
            ],
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Lists all Video models.
     *
     * @return mixed
     */
    public function actionIndex()
    {
        $dataProvider = new ActiveDataProvider([
            'query' => Video::find()
                ->creator(Yii::$app->user->id)
                ->latest(),
        ]);

        return $this->render('index', [
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Creates a new Video model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     *
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new Video();

        $model->video = UploadedFile::getInstanceByName('video');
        if (Yii::$app->request->isPost && $model->save()) {
            return $this->redirect(['update', 'id' => $model->video_id]);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

    /**
     * Updates an existing Video model.
     * If update is successful, the browser will be redirected to the 'view' page.
     *
     * @param string $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        $model->thumbnail = UploadedFile::getInstanceByName('thumbnail');
        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['update', 'id' => $model->video_id]);
        }

        return $this->render('update', [
            'model' => $model,
        ]);
    }

    /**
     * Deletes an existing Video model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     *
     * @param string $id
     * @return mixed
     * @throws \Throwable
     * @throws \yii\db\StaleObjectException
     * @throws \yii\web\NotFoundHttpException if the model cannot be found
     */
    public function actionDelete($id)
    {
        $this->findModel($id)->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the Video model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     *
     * @param string $id
     * @return Video the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (( $model = Video::findOne($id) ) !== null) {
            return $model;
        }

        throw new NotFoundHttpException('The requested page does not exist.');
    }
}


================================================
FILE: backend/models/.gitkeep
================================================
*


================================================
FILE: backend/models/CommentSearch.php
================================================
<?php

namespace backend\models;

use yii\base\Model;
use yii\data\ActiveDataProvider;
use common\models\Comment;

/**
 * CommentSearch represents the model behind the search form of `common\models\Comment`.
 */
class CommentSearch extends Comment
{
    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['id', 'parent_id', 'pinned', 'created_at', 'updated_at', 'created_by'], 'integer'],
            [['comment', 'video_id'], 'safe'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params, $userId)
    {
        $query = Comment::find()
            ->with('video')
            ->parent()
            ->byChannel($userId)
            ->latest();

        // add conditions that should always apply here

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);

        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1');
            return $dataProvider;
        }

        // grid filtering conditions
        $query->andFilterWhere([
            'id' => $this->id,
            'parent_id' => $this->parent_id,
            'pinned' => $this->pinned,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
            'created_by' => $this->created_by,
        ]);

        $query->andFilterWhere(['like', 'comment', $this->comment])
            ->andFilterWhere(['like', 'video_id', $this->video_id]);

        return $dataProvider;
    }
}


================================================
FILE: backend/runtime/.gitignore
================================================
*
!.gitignore

================================================
FILE: backend/tests/_bootstrap.php
================================================
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');
defined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');

require_once YII_APP_BASE_PATH . '/vendor/autoload.php';
require_once YII_APP_BASE_PATH . '/vendor/yiisoft/yii2/Yii.php';
require_once YII_APP_BASE_PATH . '/common/config/bootstrap.php';
require_once __DIR__ . '/../config/bootstrap.php';


================================================
FILE: backend/tests/_data/.gitignore
================================================


================================================
FILE: backend/tests/_data/login_data.php
================================================
<?php
return [
    [
        'username' => 'erau',
        'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',
        // password_0
        'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne',
        'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490',
        'created_at' => '1392559490',
        'updated_at' => '1392559490',
        'email' => 'sfriesen@jenkins.info',
    ],
];


================================================
FILE: backend/tests/_output/.gitignore
================================================
*
!.gitignore


================================================
FILE: backend/tests/_support/.gitignore
================================================
_generated


================================================
FILE: backend/tests/_support/FunctionalTester.php
================================================
<?php
namespace backend\tests;

/**
 * Inherited Methods
 * @method void wantToTest($text)
 * @method void wantTo($text)
 * @method void execute($callable)
 * @method void expectTo($prediction)
 * @method void expect($prediction)
 * @method void amGoingTo($argumentation)
 * @method void am($role)
 * @method void lookForwardTo($achieveValue)
 * @method void comment($description)
 * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
 *
 * @SuppressWarnings(PHPMD)
 */
class FunctionalTester extends \Codeception\Actor
{
    use _generated\FunctionalTesterActions;
   /**
    * Define custom actions here
    */
}


================================================
FILE: backend/tests/_support/UnitTester.php
================================================
<?php
namespace backend\tests;

/**
 * Inherited Methods
 * @method void wantToTest($text)
 * @method void wantTo($text)
 * @method void execute($callable)
 * @method void expectTo($prediction)
 * @method void expect($prediction)
 * @method void amGoingTo($argumentation)
 * @method void am($role)
 * @method void lookForwardTo($achieveValue)
 * @method void comment($description)
 * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
 *
 * @SuppressWarnings(PHPMD)
 */
class UnitTester extends \Codeception\Actor
{
    use _generated\UnitTesterActions;
   /**
    * Define custom actions here
    */
}


================================================
FILE: backend/tests/functional/LoginCest.php
================================================
<?php

namespace backend\tests\functional;

use backend\tests\FunctionalTester;
use common\fixtures\UserFixture;

/**
 * Class LoginCest
 */
class LoginCest
{
    /**
     * Load fixtures before db transaction begin
     * Called in _before()
     * @see \Codeception\Module\Yii2::_before()
     * @see \Codeception\Module\Yii2::loadFixtures()
     * @return array
     */
    public function _fixtures()
    {
        return [
            'user' => [
                'class' => UserFixture::className(),
                'dataFile' => codecept_data_dir() . 'login_data.php'
            ]
        ];
    }
    
    /**
     * @param FunctionalTester $I
     */
    public function loginUser(FunctionalTester $I)
    {
        $I->amOnPage('/site/login');
        $I->fillField('Username', 'erau');
        $I->fillField('Password', 'password_0');
        $I->click('login-button');

        $I->see('Logout (erau)', 'form button[type=submit]');
        $I->dontSeeLink('Login');
        $I->dontSeeLink('Signup');
    }
}


================================================
FILE: backend/tests/functional/_bootstrap.php
================================================
<?php
/**
 * Here you can initialize variables via \Codeception\Util\Fixtures class
 * to store data in global array and use it in Cests.
 *
 * ```php
 * // Here _bootstrap.php
 * \Codeception\Util\Fixtures::add('user1', ['name' => 'davert']);
 * ```
 *
 * In Cests
 *
 * ```php
 * \Codeception\Util\Fixtures::get('user1');
 * ```
 */

================================================
FILE: backend/tests/functional.suite.yml
================================================
suite_namespace: backend\tests\functional
actor: FunctionalTester
modules:
    enabled:
        - Yii2


================================================
FILE: backend/tests/unit/_bootstrap.php
================================================
<?php
/**
 * Here you can initialize variables via \Codeception\Util\Fixtures class
 * to store data in global array and use it in Tests.
 *
 * ```php
 * // Here _bootstrap.php
 * \Codeception\Util\Fixtures::add('user1', ['name' => 'davert']);
 * ```
 *
 * In Tests
 *
 * ```php
 * \Codeception\Util\Fixtures::get('user1');
 * ```
 */


================================================
FILE: backend/tests/unit.suite.yml
================================================
suite_namespace: backend\tests\unit
actor: UnitTester


================================================
FILE: backend/views/comment/_comment_item.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 11/14/2020
 * Time: 10:36 AM
 */

/** @var $this \yii\web\View */
/** @var $model \common\models\Comment */

?>
<div class="media media-comment comment-item" data-id="<?php echo $model->id ?>">
    <img class="mr-3 comment-avatar" src="/img/avatar.svg" alt="" style="width: 50px">
    <div class="media-body">
        <h6 class="mt-0">
            <?php echo \common\helpers\Html::channelLink($model->createdBy) ?>
            <small class="text-muted">
                <?php echo Yii::$app->formatter->asRelativeTime($model->created_at) ?>
                <?php if ($model->created_at !== $model->updated_at): ?>
                    (edited)
                <?php endif; ?>
            </small>
        </h6>
        <div class="comment-text">
            <div class="text-wrapper">
                <?php if ($model->mention): ?>
                    <span class="badge bg-info"><?php echo '@' . $model->mention ?></span>
                <?php endif; ?>
                <?php echo $model->comment ?>
            </div>

            <div class="bottom-actions my-2">
                <button data-action="<?php echo \yii\helpers\Url::to(['/comment/reply']) ?>"
                        class="btn btn-sm btn-light btn-reply">
                    REPLY
                </button>
                <div class="btn-group comment-actions">
                    <button class="btn dropdown-toggle" type="button" id="dropdownMenuButton"
                            data-toggle="dropdown">
                        <i class="fas fa-ellipsis-v"></i>
                    </button>
                    <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
                        <a class="dropdown-item item-delete-comment"
                           href="<?php echo \yii\helpers\Url::to(['/comment/delete', 'id' => $model->id]) ?>">
                            <i class="fas fa-trash"></i>
                            Delete
                        </a>
                    </div>
                </div>
            </div>
            <div class="reply-section">

            </div>
            <div class="sub-comments">
                <?php foreach ($model->comments as $comment): ?>
                    <?php echo $this->render('_comment_item', ['model' => $comment]) ?>
                <?php endforeach; ?>
            </div>
        </div>
    </div>
</div>


================================================
FILE: backend/views/comment/_form.php
================================================
<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model common\models\Comment */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="comment-form">

    <?php $form = ActiveForm::begin(); ?>

    <?= $form->field($model, 'comment')->textarea(['rows' => 6]) ?>

    <?= $form->field($model, 'video_id')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'parent_id')->textInput() ?>

    <?= $form->field($model, 'pinned')->textInput() ?>

    <?= $form->field($model, 'created_at')->textInput() ?>

    <?= $form->field($model, 'updated_at')->textInput() ?>

    <?= $form->field($model, 'created_by')->textInput() ?>

    <div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>


================================================
FILE: backend/views/comment/_item.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 11/14/2020
 * Time: 9:45 AM
 */

/** @var $this \yii\web\View */
/** @var $model \common\models\Comment */

?>

<div class="d-flex">
    <div class="comment-wrapper">

        <?php echo $this->render('_comment_item', [
            'model' => $model
        ]) ?>

    </div>
    <div class="video-wrapper">
        <?php echo $this->render('@backend/views/video/_video_item', [
            'model' => $model->video
        ]) ?>
    </div>
</div>






================================================
FILE: backend/views/comment/_search.php
================================================
<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model backend\models\CommentSearch */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="comment-search">

    <?php $form = ActiveForm::begin([
        'action' => ['index'],
        'method' => 'get',
    ]); ?>

    <?= $form->field($model, 'id') ?>

    <?= $form->field($model, 'comment') ?>

    <?= $form->field($model, 'video_id') ?>

    <?= $form->field($model, 'parent_id') ?>

    <?= $form->field($model, 'pinned') ?>

    <?php // echo $form->field($model, 'created_at') ?>

    <?php // echo $form->field($model, 'updated_at') ?>

    <?php // echo $form->field($model, 'created_by') ?>

    <div class="form-group">
        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
        <?= Html::resetButton('Reset', ['class' => 'btn btn-outline-secondary']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>


================================================
FILE: backend/views/comment/create.php
================================================
<?php

use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $model common\models\Comment */

$this->title = 'Create Comment';
$this->params['breadcrumbs'][] = ['label' => 'Comments', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="comment-create">

    <h1><?= Html::encode($this->title) ?></h1>

    <?= $this->render('_form', [
        'model' => $model,
    ]) ?>

</div>


================================================
FILE: backend/views/comment/index.php
================================================
<?php

use yii\helpers\Html;
use yii\grid\GridView;

/* @var $this yii\web\View */
/* @var $searchModel backend\models\CommentSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */

$this->title = 'Comments';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="comment-index">

    <h1><?= Html::encode($this->title) ?></h1>

    <?php // echo $this->render('_search', ['model' => $searchModel]); ?>

    <?= \yii\widgets\ListView::widget([
        'dataProvider' => $dataProvider,
        'itemView' => '_item',
        'pager' => [
            'class' => \yii\bootstrap4\LinkPager::class
        ],
        'layout' => '<div class="mt-4">{items}</div>{pager}'
    ]); ?>


</div>


================================================
FILE: backend/views/comment/update.php
================================================
<?php

use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $model common\models\Comment */

$this->title = 'Update Comment: ' . $model->id;
$this->params['breadcrumbs'][] = ['label' => 'Comments', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="comment-update">

    <h1><?= Html::encode($this->title) ?></h1>

    <?= $this->render('_form', [
        'model' => $model,
    ]) ?>

</div>


================================================
FILE: backend/views/comment/view.php
================================================
<?php

use yii\helpers\Html;
use yii\widgets\DetailView;

/* @var $this yii\web\View */
/* @var $model common\models\Comment */

$this->title = $model->id;
$this->params['breadcrumbs'][] = ['label' => 'Comments', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
\yii\web\YiiAsset::register($this);
?>
<div class="comment-view">

    <h1><?= Html::encode($this->title) ?></h1>

    <p>
        <?= Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
        <?= Html::a('Delete', ['delete', 'id' => $model->id], [
            'class' => 'btn btn-danger',
            'data' => [
                'confirm' => 'Are you sure you want to delete this item?',
                'method' => 'post',
            ],
        ]) ?>
    </p>

    <?= DetailView::widget([
        'model' => $model,
        'attributes' => [
            'id',
            'comment:ntext',
            'video_id',
            'parent_id',
            'pinned',
            'created_at',
            'updated_at',
            'created_by',
        ],
    ]) ?>

</div>


================================================
FILE: backend/views/layouts/_header.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/17/2020
 * Time: 9:20 AM
 */

use yii\bootstrap4\Nav;
use yii\bootstrap4\NavBar;

NavBar::begin([
    'brandLabel' => Yii::$app->name,
    'brandUrl' => Yii::$app->homeUrl,
    'options' => ['class' => 'navbar-expand-lg navbar-light bg-light shadow-sm'],
    'innerContainerOptions' => [
        'class' => 'container-fluid'
    ]
]);
$menuItems = [
    ['label' => 'Create', 'url' => ['/video/create']],
];
if (Yii::$app->user->isGuest) {
    $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
} else {
    $menuItems[] = [
        'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
        'url' => ['/site/logout'],
        'linkOptions' => [
            'data-method' => 'post'
        ]
    ];
}
echo Nav::widget([
    'options' => ['class' => 'navbar-nav ml-auto'],
    'items' => $menuItems,
]);
NavBar::end();


================================================
FILE: backend/views/layouts/_sidebar.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/17/2020
 * Time: 9:20 AM
 */

?>

<aside class="shadow">
    <?php echo \yii\bootstrap4\Nav::widget([
    'options' => [
        'class' => 'd-flex flex-column nav-pills'
    ],
    'items' => [
        [
            'label' => 'Dashboard',
            'url' => ['/site/index']
        ],
        [
            'label' => 'Videos',
            'url' => ['/video/index']
        ],
        [
            'label' => 'Comments',
            'url' => ['/comment/index']
        ]
    ]
]) ?>
</aside>


================================================
FILE: backend/views/layouts/auth.php
================================================
<?php

/* @var $this \yii\web\View */

/* @var $content string */

use backend\assets\AppAsset;
use yii\helpers\Html;
use yii\bootstrap4\Nav;
use yii\bootstrap4\NavBar;
use yii\widgets\Breadcrumbs;
use common\widgets\Alert;

AppAsset::register($this);
$this->beginContent('@backend/views/layouts/base.php');
?>
<main class="d-flex">
    <div class="content-wrapper p-3">
        <?= Alert::widget() ?>
        <?= $content ?>
    </div>
</main>
<?php $this->endContent() ?>

================================================
FILE: backend/views/layouts/base.php
================================================
<?php

/* @var $this \yii\web\View */

/* @var $content string */

use backend\assets\AppAsset;
use yii\helpers\Html;
use yii\bootstrap4\Nav;
use yii\bootstrap4\NavBar;
use yii\widgets\Breadcrumbs;
use common\widgets\Alert;

AppAsset::register($this);
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
    <meta charset="<?= Yii::$app->charset ?>">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <?php $this->registerCsrfMetaTags() ?>
    <title><?= Html::encode($this->title) ?></title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css"
            rel="stylesheet">
    <?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>

<div class="wrap h-100 d-flex flex-column">
    <div class="wrap h-100 d-flex flex-column">
        <?php echo $this->render('_header') ?>

        <?php echo $content ?>
    </div>
</div>

<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>


================================================
FILE: backend/views/layouts/main.php
================================================
<?php

/* @var $this \yii\web\View */

/* @var $content string */

use backend\assets\AppAsset;
use yii\helpers\Html;
use yii\bootstrap4\Nav;
use yii\bootstrap4\NavBar;
use yii\widgets\Breadcrumbs;
use common\widgets\Alert;

AppAsset::register($this);
$this->beginContent('@backend/views/layouts/base.php');
?>
<main class="d-flex">
    <?php echo $this->render('_sidebar') ?>

    <div class="content-wrapper p-3">
        <?= Alert::widget() ?>
        <?= $content ?>
    </div>
</main>
<?php $this->endContent() ?>

================================================
FILE: backend/views/site/error.php
================================================
<?php

/* @var $this yii\web\View */
/* @var $name string */
/* @var $message string */
/* @var $exception Exception */

use yii\helpers\Html;

$this->title = $name;
?>
<div class="site-error">

    <h1><?= Html::encode($this->title) ?></h1>

    <div class="alert alert-danger">
        <?= nl2br(Html::encode($message)) ?>
    </div>

    <p>
        The above error occurred while the Web server was processing your request.
    </p>
    <p>
        Please contact us if you think this is a server error. Thank you.
    </p>

</div>


================================================
FILE: backend/views/site/index.php
================================================
<?php

/* @var $this yii\web\View */
/** @var $latestVideo \common\models\Video */
/** @var $numberOfView integer */
/** @var $numberOfSubscribers integer */
/** @var $subscribers \common\models\Subscriber[] */

$this->title = Yii::$app->name;
?>
<div class="site-index d-flex">
    <div class="card m-2" style="width: 14rem;">
        <?php if ($latestVideo): ?>
            <div class="embed-responsive embed-responsive-16by9 mb-3">
                <video class="embed-responsive-item"
                       poster="<?php echo $latestVideo->getThumbnailLink() ?>"
                       src="<?php echo $latestVideo->getVideoLink() ?>"></video>
            </div>
            <div class="card-body">
                <h6 class="card-title"><?php echo $latestVideo->title ?></h6>
                <p class="card-text">
                    Likes: <?php echo $latestVideo->getLikes()->count() ?><br>
                    Views: <?php echo $latestVideo->getViews()->count() ?>
                </p>
                <a href="<?php echo \yii\helpers\Url::to(['/video/update',
                    'id' => $latestVideo->video_id]) ?>"
                   class="btn btn-primary">
                    Edit
                </a>
            </div>
        <?php else: ?>
            <div class="card-body">
                You don't have uploaded videos yet
            </div>
        <?php endif; ?>
    </div>
    <div class="card m-2" style="width: 14rem;">
        <div class="card-body">
            <h6 class="card-title">Total Views</h6>
            <p class="card-text" style="font-size: 48px">
                <?php echo $numberOfView ?>
            </p>
        </div>
    </div>
    <div class="card m-2" style="width: 14rem;">
        <div class="card-body">
            <h6 class="card-title">Total Subscribers</h6>
            <p class="card-text" style="font-size: 48px">
                <?php echo $numberOfSubscribers ?>
            </p>
        </div>
    </div>
    <div class="card m-2" style="width: 14rem;">
        <div class="card-body">
            <h6 class="card-title">Latest Subscribers</h6>
            <?php foreach ($subscribers as $subscriber): ?>
                <div>
                    <?php echo $subscriber->user->username ?>
                </div>
            <?php endforeach; ?>
        </div>
    </div>
</div>


================================================
FILE: backend/views/site/login.php
================================================
<?php

/* @var $this yii\web\View */
/* @var $form yii\bootstrap4\ActiveForm */
/* @var $model \common\models\LoginForm */

use yii\helpers\Html;
use yii\bootstrap4\ActiveForm;

$this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-login">
    <h1><?= Html::encode($this->title) ?></h1>

    <p>Please fill out the following fields to login:</p>

    <div class="row">
        <div class="col-lg-5">
            <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>

                <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>

                <?= $form->field($model, 'password')->passwordInput() ?>

                <?= $form->field($model, 'rememberMe')->checkbox() ?>

                <div class="form-group">
                    <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
                </div>

            <?php ActiveForm::end(); ?>
        </div>
    </div>
</div>


================================================
FILE: backend/views/video/_form.php
================================================
<?php

use yii\helpers\Html;
use yii\bootstrap4\ActiveForm;

/* @var $this yii\web\View */
/* @var $model common\models\Video */
/* @var $form yii\bootstrap4\ActiveForm */

\backend\assets\TagsInputAsset::register($this);
?>

<div class="video-form">

    <?php $form = ActiveForm::begin([
        'options' => ['enctype' => 'multipart/form-data']
    ]); ?>

    <div class="row">
        <div class="col-sm-8">

            <?php echo $form->errorSummary($model) ?>

            <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>

            <?= $form->field($model, 'description')->textarea(['rows' => 6]) ?>

            <div class="form-group">
                <label><?php echo $model->getAttributeLabel('thumbnail') ?></label>
                <div class="custom-file">
                    <input type="file" class="custom-file-input"
                           id="thumbnail" name="thumbnail">
                    <label class="custom-file-label" for="thumbnail">Choose file</label>
                </div>
            </div>

            <?= $form->field($model, 'tags', [
                'inputOptions' => ['data-role' => 'tagsinput']
            ])->textInput(['maxlength' => true]) ?>

        </div>
        <div class="col-sm-4">

            <div class="embed-responsive embed-responsive-16by9 mb-3">
                <video class="embed-responsive-item"
                       poster="<?php echo $model->getThumbnailLink() ?>"
                       src="<?php echo $model->getVideoLink() ?>"
                       controls></video>
            </div>

            <div class="mb-3">
                <div class="text-muted">Video Link</div>
                <a href="<?php echo $model->getVideoLink() ?>">
                    Open Video
                </a>
            </div>

            <div class="mb-3">
                <div class="text-muted">Video Name</div>
                <?php echo $model->video_name ?>
            </div>

            <?= $form->field($model, 'status')->dropDownList($model->getStatusLabels()) ?>
        </div>
    </div>
    <div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>


================================================
FILE: backend/views/video/_video_item.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/17/2020
 * Time: 11:23 AM
 */

/** @var $model \common\models\Video */

use \yii\helpers\StringHelper;
use yii\helpers\Url;

?>

<div class="media">
    <a href="<?php echo Url::to(['/video/update', 'id' => $model->video_id]) ?>">
        <div class="embed-responsive embed-responsive-16by9 mr-2"
             style="width: 120px">
            <video class="embed-responsive-item"
                   poster="<?php echo $model->getThumbnailLink() ?>"
                   src="<?php echo $model->getVideoLink() ?>"></video>
        </div>
    </a>
    <div class="media-body">
        <h6 class="mt-0"><?php echo $model->title ?></h6>
        <?php echo StringHelper::truncateWords($model->description, 10) ?>
    </div>
</div>


================================================
FILE: backend/views/video/create.php
================================================
<?php

use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $model common\models\Video */

$this->title = 'Create Video';
$this->params['breadcrumbs'][] = ['label' => 'Videos', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="video-create">

    <h1><?= Html::encode($this->title) ?></h1>

    <div class="d-flex flex-column justify-content-center align-items-center">

        <div class="upload-icon">
            <i class="fas fa-upload"></i>
        </div>
        <br>

        <p class="m-0">Drag and drop a file you want to upload
        <p>

        <p class="text-muted">Your video will be private until you publish it</p>

        <?php $form = \yii\bootstrap4\ActiveForm::begin([
            'options' => ['enctype' => 'multipart/form-data']
        ]) ?>

        <?php echo $form->errorSummary($model) ?>

        <button class="btn btn-primary btn-file">
            Select File
            <input type="file" id="videoFile" name="video">
        </button>
        <?php \yii\bootstrap4\ActiveForm::end() ?>
    </div>

</div>


================================================
FILE: backend/views/video/index.php
================================================
<?php

use yii\helpers\Html;
use yii\grid\GridView;

/* @var $this yii\web\View */
/* @var $dataProvider yii\data\ActiveDataProvider */

$this->title = 'Videos';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="video-index">

    <h1><?= Html::encode($this->title) ?></h1>

    <p>
        <?= Html::a('Create Video', ['create'], ['class' => 'btn btn-success']) ?>
    </p>


    <?= GridView::widget([
        'dataProvider' => $dataProvider,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],

            [
                'attribute' => 'title',
                'content' => function ($model) {
                    return $this->render('_video_item', ['model' => $model]);
                }
            ],
            [
                'attribute' => 'status',
                'content' => function ($model) {
                    return $model->getStatusLabels()[$model->status];
                }
            ],
            //'has_thumbnail',
            //'video_name',
            'created_at:datetime',
            'updated_at:datetime',
            //'created_by',

            [
                'class' => 'yii\grid\ActionColumn',
                'buttons' => [
                    'delete' => function ($url) {
                        return Html::a('Delete', $url, [
                            'data-method' => 'post',
                            'data-confirm' => 'Are you sure?'
                        ]);
                    }
                ]
            ],
        ],
    ]); ?>


</div>


================================================
FILE: backend/views/video/update.php
================================================
<?php

use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $model common\models\Video */

$this->title = 'Update Video: ' . $model->title;
$this->params['breadcrumbs'][] = ['label' => 'Videos', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->video_id]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="video-update">

    <h1><?= Html::encode($this->title) ?></h1>

    <?= $this->render('_form', [
        'model' => $model,
    ]) ?>

</div>


================================================
FILE: backend/web/app.js
================================================
/**
 * Created by TheCodeholic on 4/17/2020.
 */
$(function () {
  'use strict';
  $('#videoFile').change(ev => {
    $(ev.target).closest('form').trigger('submit');
  })
});

================================================
FILE: backend/web/assets/.gitignore
================================================
*
!.gitignore


================================================
FILE: backend/web/css/site.css
================================================
html,
body {
    height: 100%;
}

main {
    flex: 1;
}
.content-wrapper{
    flex: 1;
}
aside{
    min-width: 200px;
}
aside .nav-pills .nav-link {
    border-radius: 0;
    color: #444444;
}


aside .nav-pills .nav-link:hover {
    background: rgba(0, 0, 0, 0.05);
}

aside .nav-pills .nav-link.active {
    background: rgba(0, 0, 0, 0.05);
    color: #b90000;
    border-left: 4px solid #b90000;
}


.upload-icon {
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: #e0e0e0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 70px;
    color: #454545;
}

.btn-file {
    position: relative;
}
.btn-file input {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
}

.comment-wrapper {
    flex: 1;
}

.comment-wrapper .dropdown-toggle::after {
    content: none;
}

================================================
FILE: backend/web/tagsinput/tagsinput.css
================================================
/*
 * bootstrap-tagsinput v0.8.0
 * 
 */

.bootstrap-tagsinput {
  background-color: #fff;
  border: 1px solid #ccc;
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
  display: inline-block;
  padding: 4px 6px;
  color: #555;
  vertical-align: middle;
  border-radius: 4px;
  width: 100%;
  line-height: 22px;
  cursor: text;
}
.bootstrap-tagsinput input {
  border: none;
  box-shadow: none;
  outline: none;
  background-color: transparent;
  padding: 0 6px;
  margin: 0;
  width: auto;
  max-width: inherit;
}
.bootstrap-tagsinput.form-control input::-moz-placeholder {
  color: #777;
  opacity: 1;
}
.bootstrap-tagsinput.form-control input:-ms-input-placeholder {
  color: #777;
}
.bootstrap-tagsinput.form-control input::-webkit-input-placeholder {
  color: #777;
}
.bootstrap-tagsinput input:focus {
  border: none;
  box-shadow: none;
}
.bootstrap-tagsinput .badge {
  margin: 2px 0;
  padding:5px 8px;
}
.bootstrap-tagsinput .badge [data-role="remove"] {
  margin-left: 8px;
  cursor: pointer;
}
.bootstrap-tagsinput .badge [data-role="remove"]:after {
  content: "×";
  padding: 0px 4px;
  background-color:rgba(0, 0, 0, 0.1);
  border-radius:50%;
  font-size:13px
}
.bootstrap-tagsinput .badge [data-role="remove"]:hover:after {

  background-color:rgba(0, 0, 0, 0.62);}
.bootstrap-tagsinput .badge [data-role="remove"]:hover:active {
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}

.tt-menu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1000;
    display: none;
    float: left;
    min-width: 160px;
    padding: 5px 0;
    margin: 2px 0 0;
    list-style: none;
    font-size: 14px;
    background-color: #ffffff;
    border: 1px solid #cccccc;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 4px;
    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
    background-clip: padding-box;
    cursor: pointer;
}

.tt-suggestion {
    display: block;
    padding: 3px 20px;
    clear: both;
    font-weight: normal;
    line-height: 1.428571429;
    color: #333333;
    white-space: nowrap;
}

.tt-suggestion:hover,
.tt-suggestion:focus {
  color: #ffffff;
  text-decoration: none;
  outline: 0;
  background-color: #428bca;
}


================================================
FILE: backend/web/tagsinput/tagsinput.js
================================================
/*
 * bootstrap-tagsinput v0.8.0
 * 
 */

(function ($) {
  "use strict";

  var defaultOptions = {
    tagClass: function(item) {
      return 'badge badge-info';
    },
    focusClass: 'focus',
    itemValue: function(item) {
      return item ? item.toString() : item;
    },
    itemText: function(item) {
      return this.itemValue(item);
    },
    itemTitle: function(item) {
      return null;
    },
    freeInput: true,
    addOnBlur: true,
    maxTags: undefined,
    maxChars: undefined,
    confirmKeys: [13, 44],
    delimiter: ',',
    delimiterRegex: null,
    cancelConfirmKeysOnEmpty: false,
    onTagExists: function(item, $tag) {
      $tag.hide().fadeIn();
    },
    trimValue: false,
    allowDuplicates: false,
    triggerChange: true,
    editOnBackspace: false
  };

  /**
   * Constructor function
   */
  function TagsInput(element, options) {
    this.isInit = true;
    this.itemsArray = [];

    this.$element = $(element);
    this.$element.addClass('sr-only');

    this.isSelect = (element.tagName === 'SELECT');
    this.multiple = (this.isSelect && element.hasAttribute('multiple'));
    this.objectItems = options && options.itemValue;
    this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
    this.inputSize = Math.max(1, this.placeholderText.length);

    this.$container = $('<div class="bootstrap-tagsinput"></div>');
    this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);

    this.$element.before(this.$container);

    this.build(options);
    this.isInit = false;
  }

  TagsInput.prototype = {
    constructor: TagsInput,

    /**
     * Adds the given item as a new tag. Pass true to dontPushVal to prevent
     * updating the elements val()
     */
    add: function(item, dontPushVal, options) {
      var self = this;

      if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
        return;

      // Ignore falsey values, except false
      if (item !== false && !item)
        return;

      // Trim value
      if (typeof item === "string" && self.options.trimValue) {
        item = $.trim(item);
      }

      // Throw an error when trying to add an object while the itemValue option was not set
      if (typeof item === "object" && !self.objectItems)
        throw("Can't add objects when itemValue option is not set");

      // Ignore strings only containg whitespace
      if (item.toString().match(/^\s*$/))
        return;

      // If SELECT but not multiple, remove current tag
      if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
        self.remove(self.itemsArray[0]);

      if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
        var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
        var items = item.split(delimiter);
        if (items.length > 1) {
          for (var i = 0; i < items.length; i++) {
            this.add(items[i], true);
          }

          if (!dontPushVal)
            self.pushVal(self.options.triggerChange);
          return;
        }
      }

      var itemValue = self.options.itemValue(item),
          itemText = self.options.itemText(item),
          tagClass = self.options.tagClass(item),
          itemTitle = self.options.itemTitle(item);

      // Ignore items allready added
      var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
      if (existing && !self.options.allowDuplicates) {
        // Invoke onTagExists
        if (self.options.onTagExists) {
          var $existingTag = $(".badge", self.$container).filter(function() { return $(this).data("item") === existing; });
          self.options.onTagExists(item, $existingTag);
        }
        return;
      }

      // if length greater than limit
      if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
        return;

      // raise beforeItemAdd arg
      var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
      self.$element.trigger(beforeItemAddEvent);
      if (beforeItemAddEvent.cancel)
        return;

      // register item in internal array and map
      self.itemsArray.push(item);

      // add a tag element

      var $tag = $('<span class="' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' + htmlEncode(itemText) + '<span data-role="remove"></span></span>');
      $tag.data('item', item);
      self.findInputWrapper().before($tag);

      // Check to see if the tag exists in its raw or uri-encoded form
      var optionExists = (
        $('option[value="' + encodeURIComponent(itemValue).replace(/"/g, '\\"') + '"]', self.$element).length ||
        $('option[value="' + htmlEncode(itemValue).replace(/"/g, '\\"') + '"]', self.$element).length
      );

      // add <option /> if item represents a value not present in one of the <select />'s options
      if (self.isSelect && !optionExists) {
        var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
        $option.data('item', item);
        $option.attr('value', itemValue);
        self.$element.append($option);
      }

      if (!dontPushVal)
        self.pushVal(self.options.triggerChange);

      // Add class when reached maxTags
      if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
        self.$container.addClass('bootstrap-tagsinput-max');

      // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
      if ($('.typeahead, .twitter-typeahead', self.$container).length) {
        self.$input.typeahead('val', '');
      }

      if (this.isInit) {
        self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
      } else {
        self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
      }
    },

    /**
     * Removes the given item. Pass true to dontPushVal to prevent updating the
     * elements val()
     */
    remove: function(item, dontPushVal, options) {
      var self = this;

      if (self.objectItems) {
        if (typeof item === "object")
          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  self.options.itemValue(item); } );
        else
          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  item; } );

        item = item[item.length-1];
      }

      if (item) {
        var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
        self.$element.trigger(beforeItemRemoveEvent);
        if (beforeItemRemoveEvent.cancel)
          return;

        $('.badge', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
        $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
        if($.inArray(item, self.itemsArray) !== -1)
          self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
      }

      if (!dontPushVal)
        self.pushVal(self.options.triggerChange);

      // Remove class when reached maxTags
      if (self.options.maxTags > self.itemsArray.length)
        self.$container.removeClass('bootstrap-tagsinput-max');

      self.$element.trigger($.Event('itemRemoved',  { item: item, options: options }));
    },

    /**
     * Removes all items
     */
    removeAll: function() {
      var self = this;

      $('.badge', self.$container).remove();
      $('option', self.$element).remove();

      while(self.itemsArray.length > 0)
        self.itemsArray.pop();

      self.pushVal(self.options.triggerChange);
    },

    /**
     * Refreshes the tags so they match the text/value of their corresponding
     * item.
     */
    refresh: function() {
      var self = this;
      $('.badge', self.$container).each(function() {
        var $tag = $(this),
            item = $tag.data('item'),
            itemValue = self.options.itemValue(item),
            itemText = self.options.itemText(item),
            tagClass = self.options.tagClass(item);

          // Update tag's class and inner text
          $tag.attr('class', null);
          $tag.addClass('badge ' + htmlEncode(tagClass));
          $tag.contents().filter(function() {
            return this.nodeType == 3;
          })[0].nodeValue = htmlEncode(itemText);

          if (self.isSelect) {
            var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
            option.attr('value', itemValue);
          }
      });
    },

    /**
     * Returns the items added as tags
     */
    items: function() {
      return this.itemsArray;
    },

    /**
     * Assembly value by retrieving the value of each item, and set it on the
     * element.
     */
    pushVal: function() {
      var self = this,
          val = $.map(self.items(), function(item) {
            return self.options.itemValue(item).toString();
          });

      self.$element.val( val.join(self.options.delimiter) );

      if (self.options.triggerChange)
        self.$element.trigger('change');
    },

    /**
     * Initializes the tags input behaviour on the element
     */
    build: function(options) {
      var self = this;

      self.options = $.extend({}, defaultOptions, options);
      // When itemValue is set, freeInput should always be false
      if (self.objectItems)
        self.options.freeInput = false;

      makeOptionItemFunction(self.options, 'itemValue');
      makeOptionItemFunction(self.options, 'itemText');
      makeOptionFunction(self.options, 'tagClass');

      // Typeahead Bootstrap version 2.3.2
      if (self.options.typeahead) {
        var typeahead = self.options.typeahead || {};

        makeOptionFunction(typeahead, 'source');

        self.$input.typeahead($.extend({}, typeahead, {
          source: function (query, process) {
            function processItems(items) {
              var texts = [];

              for (var i = 0; i < items.length; i++) {
                var text = self.options.itemText(items[i]);
                map[text] = items[i];
                texts.push(text);
              }
              process(texts);
            }

            this.map = {};
            var map = this.map,
                data = typeahead.source(query);

            if ($.isFunction(data.success)) {
              // support for Angular callbacks
              data.success(processItems);
            } else if ($.isFunction(data.then)) {
              // support for Angular promises
              data.then(processItems);
            } else {
              // support for functions and jquery promises
              $.when(data)
               .then(processItems);
            }
          },
          updater: function (text) {
            self.add(this.map[text]);
            return this.map[text];
          },
          matcher: function (text) {
            return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
          },
          sorter: function (texts) {
            return texts.sort();
          },
          highlighter: function (text) {
            var regex = new RegExp( '(' + this.query + ')', 'gi' );
            return text.replace( regex, "<strong>$1</strong>" );
          }
        }));
      }

      // typeahead.js
      if (self.options.typeaheadjs) {
        // Determine if main configurations were passed or simply a dataset
        var typeaheadjs = self.options.typeaheadjs;
        if (!$.isArray(typeaheadjs)) {
            typeaheadjs = [null, typeaheadjs];
        }

        $.fn.typeahead.apply(self.$input, typeaheadjs).on('typeahead:selected', $.proxy(function (obj, datum, name) {
          var index = 0;
          typeaheadjs.some(function(dataset, _index) {
            if (dataset.name === name) {
              index = _index;
              return true;
            }
            return false;
          });

          // @TODO Dep: https://github.com/corejavascript/typeahead.js/issues/89
          if (typeaheadjs[index].valueKey) {
            self.add(datum[typeaheadjs[index].valueKey]);
          } else {
            self.add(datum);
          }

          self.$input.typeahead('val', '');
        }, self));
      }

      self.$container.on('click', $.proxy(function(event) {
        if (! self.$element.attr('disabled')) {
          self.$input.removeAttr('disabled');
        }
        self.$input.focus();
      }, self));

        if (self.options.addOnBlur && self.options.freeInput) {
          self.$input.on('focusout', $.proxy(function(event) {
              // HACK: only process on focusout when no typeahead opened, to
              //       avoid adding the typeahead text as tag
              if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
                self.add(self.$input.val());
                self.$input.val('');
              }
          }, self));
        }

      // Toggle the 'focus' css class on the container when it has focus
      self.$container.on({
        focusin: function() {
          self.$container.addClass(self.options.focusClass);
        },
        focusout: function() {
          self.$container.removeClass(self.options.focusClass);
        },
      });

      self.$container.on('keydown', 'input', $.proxy(function(event) {
        var $input = $(event.target),
            $inputWrapper = self.findInputWrapper();

        if (self.$element.attr('disabled')) {
          self.$input.attr('disabled', 'disabled');
          return;
        }

        switch (event.which) {
          // BACKSPACE
          case 8:
            if (doGetCaretPosition($input[0]) === 0) {
              var prev = $inputWrapper.prev();
              if (prev.length) {
                if (self.options.editOnBackspace === true) {
                  $input.val(prev.data('item'));
                }
                self.remove(prev.data('item'));
              }
            }
            break;

          // DELETE
          case 46:
            if (doGetCaretPosition($input[0]) === 0) {
              var next = $inputWrapper.next();
              if (next.length) {
                self.remove(next.data('item'));
              }
            }
            break;

          // LEFT ARROW
          case 37:
            // Try to move the input before the previous tag
            var $prevTag = $inputWrapper.prev();
            if ($input.val().length === 0 && $prevTag[0]) {
              $prevTag.before($inputWrapper);
              $input.focus();
            }
            break;
          // RIGHT ARROW
          case 39:
            // Try to move the input after the next tag
            var $nextTag = $inputWrapper.next();
            if ($input.val().length === 0 && $nextTag[0]) {
              $nextTag.after($inputWrapper);
              $input.focus();
            }
            break;
         default:
             // ignore
         }

        // Reset internal input's size
        var textLength = $input.val().length,
            wordSpace = Math.ceil(textLength / 5),
            size = textLength + wordSpace + 1;
        $input.attr('size', Math.max(this.inputSize, size));
      }, self));

      self.$container.on('keypress', 'input', $.proxy(function(event) {
         var $input = $(event.target);

         if (self.$element.attr('disabled')) {
            self.$input.attr('disabled', 'disabled');
            return;
         }

         var text = $input.val(),
         maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
         if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
            // Only attempt to add a tag if there is data in the field
            if (text.length !== 0) {
               self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
               $input.val('');
            }

            // If the field is empty, let the event triggered fire as usual
            if (self.options.cancelConfirmKeysOnEmpty === false) {
                event.preventDefault();
            }
         }

         // Reset internal input's size
         var textLength = $input.val().length,
            wordSpace = Math.ceil(textLength / 5),
            size = textLength + wordSpace + 1;
         $input.attr('size', Math.max(this.inputSize, size));
      }, self));

      // Remove icon clicked
      self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
        if (self.$element.attr('disabled')) {
          return;
        }
        self.remove($(event.target).closest('.badge').data('item'));
      }, self));

      // Only add existing value as tags when using strings as tags
      if (self.options.itemValue === defaultOptions.itemValue) {
        if (self.$element[0].tagName === 'INPUT') {
            self.add(self.$element.val());
        } else {
          $('option', self.$element).each(function() {
            self.add($(this).attr('value'), true);
          });
        }
      }
    },

    /**
     * Removes all tagsinput behaviour and unregsiter all event handlers
     */
    destroy: function() {
      var self = this;

      // Unbind events
      self.$container.off('keypress', 'input');
      self.$container.off('click', '[role=remove]');

      self.$container.remove();
      self.$element.removeData('tagsinput');
      self.$element.show();
    },

    /**
     * Sets focus on the tagsinput
     */
    focus: function() {
      this.$input.focus();
    },

    /**
     * Returns the internal input element
     */
    input: function() {
      return this.$input;
    },

    /**
     * Returns the element which is wrapped around the internal input. This
     * is normally the $container, but typeahead.js moves the $input element.
     */
    findInputWrapper: function() {
      var elt = this.$input[0],
          container = this.$container[0];
      while(elt && elt.parentNode !== container)
        elt = elt.parentNode;

      return $(elt);
    }
  };

  /**
   * Register JQuery plugin
   */
  $.fn.tagsinput = function(arg1, arg2, arg3) {
    var results = [];

    this.each(function() {
      var tagsinput = $(this).data('tagsinput');
      // Initialize a new tags input
      if (!tagsinput) {
          tagsinput = new TagsInput(this, arg1);
          $(this).data('tagsinput', tagsinput);
          results.push(tagsinput);

          if (this.tagName === 'SELECT') {
              $('option', $(this)).attr('selected', 'selected');
          }

          // Init tags from $(this).val()
          $(this).val($(this).val());
      } else if (!arg1 && !arg2) {
          // tagsinput already exists
          // no function, trying to init
          results.push(tagsinput);
      } else if(tagsinput[arg1] !== undefined) {
          // Invoke function on existing tags input
            if(tagsinput[arg1].length === 3 && arg3 !== undefined){
               var retVal = tagsinput[arg1](arg2, null, arg3);
            }else{
               var retVal = tagsinput[arg1](arg2);
            }
          if (retVal !== undefined)
              results.push(retVal);
      }
    });

    if ( typeof arg1 == 'string') {
      // Return the results from the invoked function calls
      return results.length > 1 ? results : results[0];
    } else {
      return results;
    }
  };

  $.fn.tagsinput.Constructor = TagsInput;

  /**
   * Most options support both a string or number as well as a function as
   * option value. This function makes sure that the option with the given
   * key in the given options is wrapped in a function
   */
  function makeOptionItemFunction(options, key) {
    if (typeof options[key] !== 'function') {
      var propertyName = options[key];
      options[key] = function(item) { return item[propertyName]; };
    }
  }
  function makeOptionFunction(options, key) {
    if (typeof options[key] !== 'function') {
      var value = options[key];
      options[key] = function() { return value; };
    }
  }
  /**
   * HtmlEncodes the given value
   */
  var htmlEncodeContainer = $('<div />');
  function htmlEncode(value) {
    if (value) {
      return htmlEncodeContainer.text(value).html();
    } else {
      return '';
    }
  }

  /**
   * Returns the position of the caret in the given input field
   * http://flightschool.acylt.com/devnotes/caret-position-woes/
   */
  function doGetCaretPosition(oField) {
    var iCaretPos = 0;
    if (document.selection) {
      oField.focus ();
      var oSel = document.selection.createRange();
      oSel.moveStart ('character', -oField.value.length);
      iCaretPos = oSel.text.length;
    } else if (oField.selectionStart || oField.selectionStart == '0') {
      iCaretPos = oField.selectionStart;
    }
    return (iCaretPos);
  }

  /**
    * Returns boolean indicates whether user has pressed an expected key combination.
    * @param object keyPressEvent: JavaScript event object, refer
    *     http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
    * @param object lookupList: expected key combinations, as in:
    *     [13, {which: 188, shiftKey: true}]
    */
  function keyCombinationInList(keyPressEvent, lookupList) {
      var found = false;
      $.each(lookupList, function (index, keyCombination) {
          if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
              found = true;
              return false;
          }

          if (keyPressEvent.which === keyCombination.which) {
              var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
                  shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
                  ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
              if (alt && shift && ctrl) {
                  found = true;
                  return false;
              }
          }
      });

      return found;
  }

  /**
   * Initialize tagsinput behaviour on inputs and selects which have
   * data-role=tagsinput
   */
  $(function() {
    $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
  });
})(window.jQuery);


================================================
FILE: codeception.yml
================================================
# global codeception file to run tests from all apps
include:
    - common
    - frontend
    - backend
paths:
    log: console/runtime/logs
settings:
    colors: true

================================================
FILE: common/codeception.yml
================================================
namespace: common\tests
actor_suffix: Tester
paths:
    tests: tests
    output: tests/_output
    data: tests/_data
    support: tests/_support
bootstrap: _bootstrap.php
settings:
    colors: true
    memory_limit: 1024M
modules:
    config:
        Yii2:
            configFile: 'config/codeception-local.php'


================================================
FILE: common/config/.gitignore
================================================
codeception-local.php
main-local.php
params-local.php
test-local.php


================================================
FILE: common/config/bootstrap.php
================================================
<?php
Yii::setAlias('@common', dirname(__DIR__));
Yii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');
Yii::setAlias('@backend', dirname(dirname(__DIR__)) . '/backend');
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');


================================================
FILE: common/config/main.php
================================================
<?php
return [
    'name' => 'FreeCodeTube',
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
    ],
];


================================================
FILE: common/config/params.php
================================================
<?php
return [
    'adminEmail' => 'admin@example.com',
    'supportEmail' => 'support@example.com',
    'senderEmail' => 'noreply@example.com',
    'senderName' => 'Example.com mailer',
    'user.passwordResetTokenExpire' => 3600,
];


================================================
FILE: common/config/test.php
================================================
<?php
return [
    'id' => 'app-common-tests',
    'basePath' => dirname(__DIR__),
    'components' => [
        'user' => [
            'class' => 'yii\web\User',
            'identityClass' => 'common\models\User',
        ],
    ],
];


================================================
FILE: common/fixtures/UserFixture.php
================================================
<?php
namespace common\fixtures;

use yii\test\ActiveFixture;

class UserFixture extends ActiveFixture
{
    public $modelClass = 'common\models\User';
}

================================================
FILE: common/helpers/Html.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/18/2020
 * Time: 10:18 AM
 */

namespace common\helpers;


use yii\helpers\Url;

/**
 * Class Html
 *
 * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>
 * @package common\helpers
 */
class Html
{
    public static function channelLink($user, $schema = false)
    {
        return \yii\helpers\Html::a($user->username,
            Url::to(['/channel/view', 'username' => $user->username], $schema),
            ['class' => 'text-dark channel-link']);
    }
}

================================================
FILE: common/mail/emailVerify-html.php
================================================
<?php
use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $user common\models\User */

$verifyLink = Yii::$app->urlManager->createAbsoluteUrl(['site/verify-email', 'token' => $user->verification_token]);
?>
<div class="verify-email">
    <p>Hello <?= Html::encode($user->username) ?>,</p>

    <p>Follow the link below to verify your email:</p>

    <p><?= Html::a(Html::encode($verifyLink), $verifyLink) ?></p>
</div>


================================================
FILE: common/mail/emailVerify-text.php
================================================
<?php

/* @var $this yii\web\View */
/* @var $user common\models\User */

$verifyLink = Yii::$app->urlManager->createAbsoluteUrl(['site/verify-email', 'token' => $user->verification_token]);
?>
Hello <?= $user->username ?>,

Follow the link below to verify your email:

<?= $verifyLink ?>


================================================
FILE: common/mail/layouts/html.php
================================================
<?php
use yii\helpers\Html;

/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\MessageInterface the message being composed */
/* @var $content string main view render result */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
    <title><?= Html::encode($this->title) ?></title>
    <?php $this->head() ?>
</head>
<body>
    <?php $this->beginBody() ?>
    <?= $content ?>
    <?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>


================================================
FILE: common/mail/layouts/text.php
================================================
<?php

use yii\helpers\Html;

/** @var \yii\web\View $this view component instance */
/** @var \yii\mail\MessageInterface $message the message being composed */
/** @var string $content main view render result */
?>

<?php $this->beginPage() ?>
<?php $this->beginBody() ?>
<?= $content ?>
<?php $this->endBody() ?>
<?php $this->endPage() ?>


================================================
FILE: common/mail/mention-html.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 11/15/2020
 * Time: 4:52 PM
 */
/** @var $channel \common\models\User */
/** @var $user \common\models\User */
/** @var $comment string */
?>

<p>Hello <?php echo $channel->username ?></p>
<p>User <?php echo \common\helpers\Html::channelLink($user, true) ?>
    has mention you in the following comment</p>

<blockquote>
    <?php echo $comment ?>
</blockquote>

<p>FreeCodeTube team</p>


================================================
FILE: common/mail/mention-text.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 11/15/2020
 * Time: 4:52 PM
 */
?>
<?php
/**
 * User: TheCodeholic
 * Date: 11/15/2020
 * Time: 4:52 PM
 */
/** @var $channel \common\models\User */
/** @var $user \common\models\User */
/** @var $comment string */
?>

Hello <?php echo $channel->username ?>
User <?php echo \common\helpers\Html::channelLink($user, true) ?>
    has mention you in the following comment

<?php echo $comment ?>

FreeCodeTube team



================================================
FILE: common/mail/passwordResetToken-html.php
================================================
<?php
use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $user common\models\User */

$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
?>
<div class="password-reset">
    <p>Hello <?= Html::encode($user->username) ?>,</p>

    <p>Follow the link below to reset your password:</p>

    <p><?= Html::a(Html::encode($resetLink), $resetLink) ?></p>
</div>


================================================
FILE: common/mail/passwordResetToken-text.php
================================================
<?php

/* @var $this yii\web\View */
/* @var $user common\models\User */

$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
?>
Hello <?= $user->username ?>,

Follow the link below to reset your password:

<?= $resetLink ?>


================================================
FILE: common/mail/subscriber-html.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/18/2020
 * Time: 10:27 AM
 */
/** @var $channel \common\models\User */
/** @var $user \common\models\User */
?>

<p>Hello <?php echo $channel->username ?></p>
<p>User <?php echo \common\helpers\Html::channelLink($user, true) ?>
    has subscribed to you</p>

<p>FreeCodeTube team</p>



================================================
FILE: common/mail/subscriber-text.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/18/2020
 * Time: 10:27 AM
 */
/** @var $channel \common\models\User */
/** @var $user \common\models\User */
?>

Hello <?php echo $channel->username ?>
User <?php echo $user->username ?> has subscribed to you

FreeCodeTube team



================================================
FILE: common/models/Comment.php
================================================
<?php

namespace common\models;

use Yii;
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;

/**
 * This is the model class for table "{{%comment}}".
 *
 * @property int $id
 * @property string $comment
 * @property string $video_id
 * @property int|null $parent_id
 * @property int|null $pinned
 * @property string $mention
 * @property int|null $created_at
 * @property int|null $updated_at
 * @property int|null $created_by
 *
 * @property User $createdBy
 * @property Comment $parent
 * @property Comment[] $comments
 * @property Video $video
 */
class Comment extends \yii\db\ActiveRecord
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%comment}}';
    }

    public function behaviors()
    {
        return [
            TimestampBehavior::class,
            [
                'class' => BlameableBehavior::class,
                'updatedByAttribute' => false
            ]
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['comment', 'video_id'], 'required'],
            [['comment'], 'string'],
            [['parent_id', 'pinned', 'created_at', 'updated_at', 'created_by'], 'integer'],
            [['video_id'], 'string', 'max' => 16],
            [['mention'], 'string', 'max' => 255],
            [['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['created_by' => 'id']],
            [['parent_id'], 'exist', 'skipOnError' => true, 'targetClass' => Comment::className(), 'targetAttribute' => ['parent_id' => 'id']],
            [['video_id'], 'exist', 'skipOnError' => true, 'targetClass' => Video::className(), 'targetAttribute' => ['video_id' => 'video_id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'comment' => 'Comment',
            'video_id' => 'Video ID',
            'parent_id' => 'Parent ID',
            'pinned' => 'Pinned',
            'mention' => 'Mention',
            'created_at' => 'Created At',
            'updated_at' => 'Updated At',
            'created_by' => 'Created By',
        ];
    }

    /**
     * Gets query for [[CreatedBy]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\UserQuery
     */
    public function getCreatedBy()
    {
        return $this->hasOne(User::className(), ['id' => 'created_by']);
    }

    /**
     * Gets query for [[Parent]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\CommentQuery
     */
    public function getParent()
    {
        return $this->hasOne(Comment::className(), ['id' => 'parent_id']);
    }

    /**
     * Gets query for [[Comments]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\CommentQuery
     */
    public function getComments()
    {
        return $this->hasMany(Comment::className(), ['parent_id' => 'id']);
    }

    /**
     * Gets query for [[Video]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\VideoQuery
     */
    public function getVideo()
    {
        return $this->hasOne(Video::className(), ['video_id' => 'video_id']);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\query\CommentQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new \common\models\query\CommentQuery(get_called_class());
    }

    public function belongsTo($userId)
    {
        return $this->created_by === $userId;
    }
}


================================================
FILE: common/models/LoginForm.php
================================================
<?php
namespace common\models;

use Yii;
use yii\base\Model;

/**
 * Login form
 */
class LoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;

    private $_user;


    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            // username and password are both required
            [['username', 'password'], 'required'],
            // rememberMe must be a boolean value
            ['rememberMe', 'boolean'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
        ];
    }

    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }

    /**
     * Logs in a user using the provided username and password.
     *
     * @return bool whether the user is logged in successfully
     */
    public function login()
    {
        if ($this->validate()) {
            return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
        }
        
        return false;
    }

    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    protected function getUser()
    {
        if ($this->_user === null) {
            $this->_user = User::findByUsername($this->username);
        }

        return $this->_user;
    }
}


================================================
FILE: common/models/Subscriber.php
================================================
<?php

namespace common\models;

use Yii;

/**
 * This is the model class for table "{{%subscriber}}".
 *
 * @property int $id
 * @property int|null $channel_id
 * @property int|null $user_id
 * @property int|null $created_at
 *
 * @property User $channel
 * @property User $user
 */
class Subscriber extends \yii\db\ActiveRecord
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%subscriber}}';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['channel_id', 'user_id', 'created_at'], 'integer'],
            [['channel_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['channel_id' => 'id']],
            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'channel_id' => 'Channel ID',
            'user_id' => 'User ID',
            'created_at' => 'Created At',
        ];
    }

    /**
     * Gets query for [[Channel]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\UserQuery
     */
    public function getChannel()
    {
        return $this->hasOne(User::className(), ['id' => 'channel_id']);
    }

    /**
     * Gets query for [[User]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\UserQuery
     */
    public function getUser()
    {
        return $this->hasOne(User::className(), ['id' => 'user_id']);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\query\SubscriberQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new \common\models\query\SubscriberQuery(get_called_class());
    }
}


================================================
FILE: common/models/User.php
================================================
<?php
namespace common\models;

use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;

/**
 * User model
 *
 * @property integer $id
 * @property string $username
 * @property string $password_hash
 * @property string $password_reset_token
 * @property string $verification_token
 * @property string $email
 * @property string $auth_key
 * @property integer $status
 * @property integer $created_at
 * @property integer $updated_at
 * @property string $password write-only password
 */
class User extends ActiveRecord implements IdentityInterface
{
    const STATUS_DELETED = 0;
    const STATUS_INACTIVE = 9;
    const STATUS_ACTIVE = 10;


    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%user}}';
    }

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            TimestampBehavior::className(),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['status', 'default', 'value' => self::STATUS_INACTIVE],
            ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]],
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     * @throws \yii\base\InvalidConfigException
     * @author Zura Sekhniashvili <zurasekhniashvili@gmail.com>
     */
    public function getSubscribers()
    {
        return $this->hasMany(User::class, ['id' => 'user_id'])
            ->viaTable('subscriber', ['channel_id' => 'id']);
    }

    /**
     * {@inheritdoc}
     */
    public static function findIdentity($id)
    {
        return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
    }

    /**
     * {@inheritdoc}
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
    }

    /**
     * Finds user by username
     *
     * @param string $username
     * @return static|null
     */
    public static function findByUsername($username)
    {
        return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]);
    }

    /**
     * Finds user by password reset token
     *
     * @param string $token password reset token
     * @return static|null
     */
    public static function findByPasswordResetToken($token)
    {
        if (!static::isPasswordResetTokenValid($token)) {
            return null;
        }

        return static::findOne([
            'password_reset_token' => $token,
            'status' => self::STATUS_ACTIVE,
        ]);
    }

    /**
     * Finds user by verification email token
     *
     * @param string $token verify email token
     * @return static|null
     */
    public static function findByVerificationToken($token) {
        return static::findOne([
            'verification_token' => $token,
            'status' => self::STATUS_INACTIVE
        ]);
    }

    /**
     * Finds out if password reset token is valid
     *
     * @param string $token password reset token
     * @return bool
     */
    public static function isPasswordResetTokenValid($token)
    {
        if (empty($token)) {
            return false;
        }

        $timestamp = (int) substr($token, strrpos($token, '_') + 1);
        $expire = Yii::$app->params['user.passwordResetTokenExpire'];
        return $timestamp + $expire >= time();
    }

    /**
     * {@inheritdoc}
     */
    public function getId()
    {
        return $this->getPrimaryKey();
    }

    /**
     * {@inheritdoc}
     */
    public function getAuthKey()
    {
        return $this->auth_key;
    }

    /**
     * {@inheritdoc}
     */
    public function validateAuthKey($authKey)
    {
        return $this->getAuthKey() === $authKey;
    }

    /**
     * Validates password
     *
     * @param string $password password to validate
     * @return bool if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    /**
     * Generates password hash from password and sets it to the model
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }

    /**
     * Generates "remember me" authentication key
     */
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }

    /**
     * Generates new password reset token
     */
    public function generatePasswordResetToken()
    {
        $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
    }

    /**
     * Generates new token for email verification
     */
    public function generateEmailVerificationToken()
    {
        $this->verification_token = Yii::$app->security->generateRandomString() . '_' . time();
    }

    /**
     * Removes password reset token
     */
    public function removePasswordResetToken()
    {
        $this->password_reset_token = null;
    }

    public function isSubscribed($userId)
    {
        return Subscriber::find()->andWhere([
            'channel_id' => $this->id,
            'user_id' => $userId
        ])->one();
    }
}


================================================
FILE: common/models/Video.php
================================================
<?php

namespace common\models;

use Imagine\Image\Box;
use Yii;
use yii\behaviors\BlameableBehavior;
use yii\behaviors\TimestampBehavior;
use yii\helpers\FileHelper;
use yii\imagine\Image;

/**
 * This is the model class for table "{{%video}}".
 *
 * @property string      $video_id
 * @property string      $title
 * @property string|null $description
 * @property string|null $tags
 * @property int|null    $status
 * @property int|null    $has_thumbnail
 * @property string|null $video_name
 * @property int|null    $created_at
 * @property int|null    $updated_at
 * @property int|null    $created_by
 *
 * @property User        $createdBy
 * @property \common\models\VideoLike[]        $likes
 * @property \common\models\VideoLike[]        $dislikes
 */
class Video extends \yii\db\ActiveRecord
{
    const STATUS_UNLISTED = 0;
    const STATUS_PUBLISHED = 1;

    /**
     * @var \yii\web\UploadedFile
     */
    public $video;

    /**
     * @var \yii\web\UploadedFile
     */
    public $thumbnail;

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%video}}';
    }

    public function behaviors()
    {
        return [
            TimestampBehavior::class,
            [
                'class' => BlameableBehavior::class,
                'updatedByAttribute' => false
            ]
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['video_id', 'title'], 'required'],
            [['description'], 'string'],
            [['status', 'has_thumbnail', 'created_at', 'updated_at', 'created_by'], 'integer'],
            [['video_id'], 'string', 'max' => 16],
            [['title', 'tags', 'video_name'], 'string', 'max' => 512],
            [['video_id'], 'unique'],
            ['has_thumbnail', 'default', 'value' => 0],
            ['status', 'default', 'value' => self::STATUS_UNLISTED],
            ['thumbnail', 'image', 'minWidth' => 1280],
            ['video', 'file', 'extensions' => ['mp4']],
            [['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['created_by' => 'id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'video_id' => 'Video ID',
            'title' => 'Title',
            'description' => 'Description',
            'tags' => 'Tags',
            'status' => 'Status',
            'has_thumbnail' => 'Has Thumbnail',
            'video_name' => 'Video Name',
            'created_at' => 'Created At',
            'updated_at' => 'Updated At',
            'created_by' => 'Created By',
            'thumbnail' => 'Thumbnail'
        ];
    }

    public function getStatusLabels()
    {
        return [
            self::STATUS_UNLISTED => 'Unlisted',
            self::STATUS_PUBLISHED => 'Published',
        ];
    }

    /**
     * Gets query for [[CreatedBy]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\UserQuery
     */
    public function getCreatedBy()
    {
        return $this->hasOne(User::className(), ['id' => 'created_by']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getViews()
    {
        return $this->hasMany(VideoView::class, ['video_id' => 'video_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getLikes()
    {
        return $this->hasMany(VideoLike::class, ['video_id' => 'video_id'])
            ->liked();
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getDislikes()
    {
        return $this->hasMany(VideoLike::class, ['video_id' => 'video_id'])
            ->disliked();
    }

    /**
     * @return \common\models\query\CommentQuery
     */
    public function getComments()
    {
        return $this->hasMany(Comment::class, ['video_id' => 'video_id']);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\query\VideoQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new \common\models\query\VideoQuery(get_called_class());
    }

    public function save($runValidation = true, $attributeNames = null)
    {
        $isInsert = $this->isNewRecord;
        if ($isInsert) {
            $this->video_id = Yii::$app->security->generateRandomString(8);
            $this->title = $this->video->name;
            $this->video_name = $this->video->name;
        }
        if ($this->thumbnail) {
            $this->has_thumbnail = 1;
        }
        $saved = parent::save($runValidation, $attributeNames);
        if (!$saved) {
            return false;
        }
        if ($isInsert) {
            $videoPath = Yii::getAlias('@frontend/web/storage/videos/' . $this->video_id . '.mp4');
            if (!is_dir(dirname($videoPath))) {
                FileHelper::createDirectory(dirname($videoPath));
            }
            $this->video->saveAs($videoPath);
        }
        if ($this->thumbnail) {
            $thumbnailPath = Yii::getAlias('@frontend/web/storage/thumbs/' . $this->video_id . '.jpg');
            if (!is_dir(dirname($thumbnailPath))) {
                FileHelper::createDirectory(dirname($thumbnailPath));
            }
            $this->thumbnail->saveAs($thumbnailPath);
            Image::getImagine()
                ->open($thumbnailPath)
                ->thumbnail(new Box(1280, 1280))
                ->save();
        }

        return true;
    }

    public function getVideoLink()
    {
        return Yii::$app->params['frontendUrl'] . 'storage/videos/' . $this->video_id . '.mp4';
    }

    public function getThumbnailLink()
    {
        return $this->has_thumbnail ?
            Yii::$app->params['frontendUrl'] . 'storage/thumbs/' . $this->video_id . '.jpg'
            : '';
    }

    public function afterDelete()
    {
        parent::afterDelete();
        $videoPath = Yii::getAlias('@frontend/web/storage/videos/' . $this->video_id . '.mp4');
        unlink($videoPath);

        $thumbnailPath = Yii::getAlias('@frontend/web/storage/thumbs/' . $this->video_id . '.jpg');
        if (file_exists($thumbnailPath)) {
            unlink($thumbnailPath);
        }
    }

    public function isLikedBy($userId)
    {
        return VideoLike::find()
            ->userIdVideoId($userId, $this->video_id)
            ->liked()
            ->one();
    }

    public function isDislikedBy($userId)
    {
        return VideoLike::find()
            ->userIdVideoId($userId, $this->video_id)
            ->disliked()
            ->one();
    }

    public function belongsTo($userId)
    {
        return $this->created_by === $userId;
    }
}


================================================
FILE: common/models/VideoLike.php
================================================
<?php

namespace common\models;

use Yii;

/**
 * This is the model class for table "{{%video_like}}".
 *
 * @property int $id
 * @property string $video_id
 * @property int $user_id
 * @property int|null $type
 * @property int|null $created_at
 *
 * @property User $user
 * @property Video $video
 */
class VideoLike extends \yii\db\ActiveRecord
{
    const TYPE_LIKE = 1;
    const TYPE_DISLIKE = 0;

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%video_like}}';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['video_id', 'user_id'], 'required'],
            [['user_id', 'type', 'created_at'], 'integer'],
            [['video_id'], 'string', 'max' => 16],
            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
            [['video_id'], 'exist', 'skipOnError' => true, 'targetClass' => Video::className(), 'targetAttribute' => ['video_id' => 'video_id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'video_id' => 'Video ID',
            'user_id' => 'User ID',
            'type' => 'Type',
            'created_at' => 'Created At',
        ];
    }

    /**
     * Gets query for [[User]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\UserQuery
     */
    public function getUser()
    {
        return $this->hasOne(User::className(), ['id' => 'user_id']);
    }

    /**
     * Gets query for [[Video]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\VideoQuery
     */
    public function getVideo()
    {
        return $this->hasOne(Video::className(), ['video_id' => 'video_id']);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\query\VideoLikeQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new \common\models\query\VideoLikeQuery(get_called_class());
    }
}


================================================
FILE: common/models/VideoView.php
================================================
<?php

namespace common\models;

use Yii;

/**
 * This is the model class for table "{{%video_view}}".
 *
 * @property int $id
 * @property string $video_id
 * @property int|null $user_id
 * @property int|null $created_at
 *
 * @property User $user
 * @property Video $video
 */
class VideoView extends \yii\db\ActiveRecord
{
    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%video_view}}';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            [['video_id'], 'required'],
            [['user_id', 'created_at'], 'integer'],
            [['video_id'], 'string', 'max' => 16],
            [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']],
            [['video_id'], 'exist', 'skipOnError' => true, 'targetClass' => Video::className(), 'targetAttribute' => ['video_id' => 'video_id']],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'video_id' => 'Video ID',
            'user_id' => 'User ID',
            'created_at' => 'Created At',
        ];
    }

    /**
     * Gets query for [[User]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\UserQuery
     */
    public function getUser()
    {
        return $this->hasOne(User::className(), ['id' => 'user_id']);
    }

    /**
     * Gets query for [[Video]].
     *
     * @return \yii\db\ActiveQuery|\common\models\query\VideoQuery
     */
    public function getVideo()
    {
        return $this->hasOne(Video::className(), ['video_id' => 'video_id']);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\query\VideoViewQuery the active query used by this AR class.
     */
    public static function find()
    {
        return new \common\models\query\VideoViewQuery(get_called_class());
    }
}


================================================
FILE: common/models/query/CommentQuery.php
================================================
<?php

namespace common\models\query;

use common\models\Comment;
use common\models\Video;

/**
 * This is the ActiveQuery class for [[\common\models\Comment]].
 *
 * @see \common\models\Comment
 */
class CommentQuery extends \yii\db\ActiveQuery
{
    /*public function active()
    {
        return $this->andWhere('[[status]]=1');
    }*/

    /**
     * {@inheritdoc}
     * @return \common\models\Comment[]|array
     */
    public function all($db = null)
    {
        return parent::all($db);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\Comment|array|null
     */
    public function one($db = null)
    {
        return parent::one($db);
    }

    public function videoId($videoId)
    {
        return $this->andWhere(['video_id' => $videoId]);
    }

    public function parent()
    {
        return $this->andWhere(['parent_id' => null]);
    }

    public function latest()
    {
        return $this->orderBy("pinned DESC, created_at DESC");
    }

    public function byChannel($userId)
    {
        return $this->innerJoin(Video::tableName() . ' v', 'v.video_id = ' . Comment::tableName() . '.video_id')
            ->andWhere(['v.created_by' => $userId]);
    }
}


================================================
FILE: common/models/query/SubscriberQuery.php
================================================
<?php

namespace common\models\query;

/**
 * This is the ActiveQuery class for [[\common\models\Subscriber]].
 *
 * @see \common\models\Subscriber
 */
class SubscriberQuery extends \yii\db\ActiveQuery
{
    /*public function active()
    {
        return $this->andWhere('[[status]]=1');
    }*/

    /**
     * {@inheritdoc}
     * @return \common\models\Subscriber[]|array
     */
    public function all($db = null)
    {
        return parent::all($db);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\Subscriber|array|null
     */
    public function one($db = null)
    {
        return parent::one($db);
    }
}


================================================
FILE: common/models/query/VideoLikeQuery.php
================================================
<?php

namespace common\models\query;

use common\models\VideoLike;

/**
 * This is the ActiveQuery class for [[\common\models\VideoLike]].
 *
 * @see \common\models\VideoLike
 */
class VideoLikeQuery extends \yii\db\ActiveQuery
{
    /*public function active()
    {
        return $this->andWhere('[[status]]=1');
    }*/

    /**
     * {@inheritdoc}
     * @return \common\models\VideoLike[]|array
     */
    public function all($db = null)
    {
        return parent::all($db);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\VideoLike|array|null
     */
    public function one($db = null)
    {
        return parent::one($db);
    }

    public function userIdVideoId($userId, $videoId)
    {
        return $this->andWhere([
            'video_id' => $videoId,
            'user_id' => $userId
        ]);
    }

    public function liked()
    {
        return $this->andWhere(['type' => VideoLike::TYPE_LIKE]);
    }

    public function disliked()
    {
        return $this->andWhere(['type' => VideoLike::TYPE_DISLIKE]);
    }
}


================================================
FILE: common/models/query/VideoQuery.php
================================================
<?php

namespace common\models\query;

use common\models\Video;

/**
 * This is the ActiveQuery class for [[\common\models\Video]].
 *
 * @see \common\models\Video
 */
class VideoQuery extends \yii\db\ActiveQuery
{
    /*public function active()
    {
        return $this->andWhere('[[status]]=1');
    }*/

    /**
     * {@inheritdoc}
     * @return \common\models\Video[]|array
     */
    public function all($db = null)
    {
        return parent::all($db);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\Video|array|null
     */
    public function one($db = null)
    {
        return parent::one($db);
    }

    public function creator($userId)
    {
        return $this->andWhere(['created_by' => $userId]);
    }

    public function latest()
    {
        return $this->orderBy(['created_at' => SORT_DESC]);
    }

    public function published()
    {
        return $this->andWhere(['status' => Video::STATUS_PUBLISHED]);
    }

    public function byKeyword($keyword)
    {
        return $this->andWhere("MATCH(title, description, tags)
        AGAINST (:keyword)", ['keyword' => $keyword]);
    }
}


================================================
FILE: common/models/query/VideoViewQuery.php
================================================
<?php

namespace common\models\query;

/**
 * This is the ActiveQuery class for [[\common\models\VideoView]].
 *
 * @see \common\models\VideoView
 */
class VideoViewQuery extends \yii\db\ActiveQuery
{
    /*public function active()
    {
        return $this->andWhere('[[status]]=1');
    }*/

    /**
     * {@inheritdoc}
     * @return \common\models\VideoView[]|array
     */
    public function all($db = null)
    {
        return parent::all($db);
    }

    /**
     * {@inheritdoc}
     * @return \common\models\VideoView|array|null
     */
    public function one($db = null)
    {
        return parent::one($db);
    }
}


================================================
FILE: common/tests/_bootstrap.php
================================================
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');
defined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__.'/../../');

require_once __DIR__ .  '/../../vendor/autoload.php';
require_once __DIR__ .  '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../config/bootstrap.php';



================================================
FILE: common/tests/_data/user.php
================================================
<?php

return [
    [
        'username' => 'bayer.hudson',
        'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR',
        //password_0
        'password_hash' => '$2y$13$EjaPFBnZOQsHdGuHI.xvhuDp1fHpo8hKRSk6yshqa9c5EG8s3C3lO',
        'password_reset_token' => 'ExzkCOaYc1L8IOBs4wdTGGbgNiG3Wz1I_1402312317',
        'created_at' => '1402312317',
        'updated_at' => '1402312317',
        'email' => 'nicole.paucek@schultz.info',
    ],
];


================================================
FILE: common/tests/_output/.gitignore
================================================
*
!.gitignore


================================================
FILE: common/tests/_support/.gitignore
================================================
_generated


================================================
FILE: common/tests/_support/UnitTester.php
================================================
<?php
namespace common\tests;

/**
 * Inherited Methods
 * @method void wantToTest($text)
 * @method void wantTo($text)
 * @method void execute($callable)
 * @method void expectTo($prediction)
 * @method void expect($prediction)
 * @method void amGoingTo($argumentation)
 * @method void am($role)
 * @method void lookForwardTo($achieveValue)
 * @method void comment($description)
 * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
 *
 * @SuppressWarnings(PHPMD)
 */
class UnitTester extends \Codeception\Actor
{
    use _generated\UnitTesterActions;
   /**
    * Define custom actions here
    */
}


================================================
FILE: common/tests/unit/models/LoginFormTest.php
================================================
<?php

namespace common\tests\unit\models;

use Yii;
use common\models\LoginForm;
use common\fixtures\UserFixture;

/**
 * Login form test
 */
class LoginFormTest extends \Codeception\Test\Unit
{
    /**
     * @var \common\tests\UnitTester
     */
    protected $tester;


    /**
     * @return array
     */
    public function _fixtures()
    {
        return [
            'user' => [
                'class' => UserFixture::className(),
                'dataFile' => codecept_data_dir() . 'user.php'
            ]
        ];
    }

    public function testLoginNoUser()
    {
        $model = new LoginForm([
            'username' => 'not_existing_username',
            'password' => 'not_existing_password',
        ]);

        expect('model should not login user', $model->login())->false();
        expect('user should not be logged in', Yii::$app->user->isGuest)->true();
    }

    public function testLoginWrongPassword()
    {
        $model = new LoginForm([
            'username' => 'bayer.hudson',
            'password' => 'wrong_password',
        ]);

        expect('model should not login user', $model->login())->false();
        expect('error message should be set', $model->errors)->hasKey('password');
        expect('user should not be logged in', Yii::$app->user->isGuest)->true();
    }

    public function testLoginCorrect()
    {
        $model = new LoginForm([
            'username' => 'bayer.hudson',
            'password' => 'password_0',
        ]);

        expect('model should login user', $model->login())->true();
        expect('error message should not be set', $model->errors)->hasntKey('password');
        expect('user should be logged in', Yii::$app->user->isGuest)->false();
    }
}


================================================
FILE: common/tests/unit.suite.yml
================================================
suite_namespace: common\tests\unit
actor: UnitTester
bootstrap: false
modules:
    enabled:
        - Yii2:
            part: fixtures


================================================
FILE: common/widgets/Alert.php
================================================
<?php
namespace common\widgets;

use Yii;

/**
 * Alert widget renders a message from session flash. All flash messages are displayed
 * in the sequence they were assigned using setFlash. You can set message as following:
 *
 * ```php
 * Yii::$app->session->setFlash('error', 'This is the message');
 * Yii::$app->session->setFlash('success', 'This is the message');
 * Yii::$app->session->setFlash('info', 'This is the message');
 * ```
 *
 * Multiple messages could be set as follows:
 *
 * ```php
 * Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']);
 * ```
 *
 * @author Kartik Visweswaran <kartikv2@gmail.com>
 * @author Alexander Makarov <sam@rmcreative.ru>
 */
class Alert extends \yii\bootstrap4\Widget
{
    /**
     * @var array the alert types configuration for the flash messages.
     * This array is setup as $key => $value, where:
     * - key: the name of the session flash variable
     * - value: the bootstrap alert type (i.e. danger, success, info, warning)
     */
    public $alertTypes = [
        'error'   => 'alert-danger',
        'danger'  => 'alert-danger',
        'success' => 'alert-success',
        'info'    => 'alert-info',
        'warning' => 'alert-warning'
    ];
    /**
     * @var array the options for rendering the close button tag.
     * Array will be passed to [[\yii\bootstrap\Alert::closeButton]].
     */
    public $closeButton = [];


    /**
     * {@inheritdoc}
     */
    public function run()
    {
        $session = Yii::$app->session;
        $flashes = $session->getAllFlashes();
        $appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : '';

        foreach ($flashes as $type => $flash) {
            if (!isset($this->alertTypes[$type])) {
                continue;
            }

            foreach ((array) $flash as $i => $message) {
                echo \yii\bootstrap4\Alert::widget([
                    'body' => $message,
                    'closeButton' => $this->closeButton,
                    'options' => array_merge($this->options, [
                        'id' => $this->getId() . '-' . $type . '-' . $i,
                        'class' => $this->alertTypes[$type] . $appendClass,
                    ]),
                ]);
            }

            $session->removeFlash($type);
        }
    }
}


================================================
FILE: composer.json
================================================
{
    "name": "yiisoft/yii2-app-advanced",
    "description": "Yii 2 Advanced Project Template",
    "keywords": ["yii2", "framework", "advanced", "project template"],
    "homepage": "http://www.yiiframework.com/",
    "type": "project",
    "license": "BSD-3-Clause",
    "support": {
        "issues": "https://github.com/yiisoft/yii2/issues?state=open",
        "forum": "http://www.yiiframework.com/forum/",
        "wiki": "http://www.yiiframework.com/wiki/",
        "irc": "irc://irc.freenode.net/yii",
        "source": "https://github.com/yiisoft/yii2"
    },
    "minimum-stability": "stable",
    "require": {
        "php": ">=5.6.0",
        "php-gd": "*",
        "yiisoft/yii2": "~2.0.14",
        "yiisoft/yii2-swiftmailer": "~2.0.0 || ~2.1.0",
        "yiisoft/yii2-bootstrap4": "^2.0",
        "yiisoft/yii2-imagine": "^2.2"
    },
    "require-dev": {
        "yiisoft/yii2-debug": "~2.1.0",
        "yiisoft/yii2-gii": "~2.1.0",
        "yiisoft/yii2-faker": "~2.0.0",
        "codeception/codeception": "^4.0",
        "codeception/module-asserts": "^1.0",
        "codeception/module-yii2": "^1.0",
        "codeception/module-filesystem": "^1.0",
        "codeception/verify": "~0.5.0 || ~1.1.0",
        "symfony/browser-kit": ">=2.7 <=4.2.4"
    },
    "config": {
        "process-timeout": 1800,
        "fxp-asset": {
            "enabled": false
        }
    },
    "repositories": [
        {
            "type": "composer",
            "url": "https://asset-packagist.org"
        }
    ]
}


================================================
FILE: console/config/.gitignore
================================================
main-local.php
params-local.php
test-local.php


================================================
FILE: console/config/bootstrap.php
================================================
<?php


================================================
FILE: console/config/main.php
================================================
<?php
$params = array_merge(
    require __DIR__ . '/../../common/config/params.php',
    require __DIR__ . '/../../common/config/params-local.php',
    require __DIR__ . '/params.php',
    require __DIR__ . '/params-local.php'
);

return [
    'id' => 'app-console',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'controllerNamespace' => 'console\controllers',
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    'controllerMap' => [
        'fixture' => [
            'class' => 'yii\console\controllers\FixtureController',
            'namespace' => 'common\fixtures',
          ],
    ],
    'components' => [
        'log' => [
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
    ],
    'params' => $params,
];


================================================
FILE: console/config/params.php
================================================
<?php
return [
    'adminEmail' => 'admin@example.com',
];


================================================
FILE: console/config/test.php
================================================
<?php
return [

];

================================================
FILE: console/controllers/.gitkeep
================================================


================================================
FILE: console/migrations/m130524_201442_init.php
================================================
<?php

use yii\db\Migration;

class m130524_201442_init extends Migration
{
    public function up()
    {
        $tableOptions = null;
        if ($this->db->driverName === 'mysql') {
            // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
        }

        $this->createTable('{{%user}}', [
            'id' => $this->primaryKey(),
            'username' => $this->string()->notNull()->unique(),
            'auth_key' => $this->string(32)->notNull(),
            'password_hash' => $this->string()->notNull(),
            'password_reset_token' => $this->string()->unique(),
            'email' => $this->string()->notNull()->unique(),

            'status' => $this->smallInteger()->notNull()->defaultValue(10),
            'created_at' => $this->integer()->notNull(),
            'updated_at' => $this->integer()->notNull(),
        ], $tableOptions);
    }

    public function down()
    {
        $this->dropTable('{{%user}}');
    }
}


================================================
FILE: console/migrations/m190124_110200_add_verification_token_column_to_user_table.php
================================================
<?php

use \yii\db\Migration;

class m190124_110200_add_verification_token_column_to_user_table extends Migration
{
    public function up()
    {
        $this->addColumn('{{%user}}', 'verification_token', $this->string()->defaultValue(null));
    }

    public function down()
    {
        $this->dropColumn('{{%user}}', 'verification_token');
    }
}


================================================
FILE: console/migrations/m200417_054237_create_videos_table.php
================================================
<?php

use yii\db\Migration;

/**
 * Handles the creation of table `{{%video}}`.
 * Has foreign keys to the tables:
 *
 * - `{{%user}}`
 */
class m200417_054237_create_videos_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->createTable('{{%video}}', [
            'video_id' => $this->string(16)->notNull(),
            'title' => $this->string(512)->notNull(),
            'description' => $this->text(),
            'tags' => $this->string(512),
            'status' => $this->integer(1),
            'has_thumbnail' => $this->boolean(),
            'video_name' => $this->string(512),
            'created_at' => $this->integer(11),
            'updated_at' => $this->integer(11),
            'created_by' => $this->integer(11),
        ]);

        $this->addPrimaryKey('PK_videos_video_id', '{{%video}}', 'video_id');

        // creates index for column `created_by`
        $this->createIndex(
            '{{%idx-videos-created_by}}',
            '{{%video}}',
            'created_by'
        );

        // add foreign key for table `{{%user}}`
        $this->addForeignKey(
            '{{%fk-videos-created_by}}',
            '{{%video}}',
            'created_by',
            '{{%user}}',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        // drops foreign key for table `{{%user}}`
        $this->dropForeignKey(
            '{{%fk-videos-created_by}}',
            '{{%video}}'
        );

        // drops index for column `created_by`
        $this->dropIndex(
            '{{%idx-videos-created_by}}',
            '{{%video}}'
        );

        $this->dropTable('{{%video}}');
    }
}


================================================
FILE: console/migrations/m200418_050048_create_video_view_table.php
================================================
<?php

use yii\db\Migration;

/**
 * Handles the creation of table `{{%video_view}}`.
 * Has foreign keys to the tables:
 *
 * - `{{%video}}`
 * - `{{%user}}`
 */
class m200418_050048_create_video_view_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->createTable('{{%video_view}}', [
            'id' => $this->primaryKey(),
            'video_id' => $this->string(16)->notNull(),
            'user_id' => $this->integer(11),
            'created_at' => $this->integer(11),
        ]);

        // creates index for column `video_id`
        $this->createIndex(
            '{{%idx-video_view-video_id}}',
            '{{%video_view}}',
            'video_id'
        );

        // add foreign key for table `{{%video}}`
        $this->addForeignKey(
            '{{%fk-video_view-video_id}}',
            '{{%video_view}}',
            'video_id',
            '{{%video}}',
            'video_id',
            'CASCADE'
        );

        // creates index for column `user_id`
        $this->createIndex(
            '{{%idx-video_view-user_id}}',
            '{{%video_view}}',
            'user_id'
        );

        // add foreign key for table `{{%user}}`
        $this->addForeignKey(
            '{{%fk-video_view-user_id}}',
            '{{%video_view}}',
            'user_id',
            '{{%user}}',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        // drops foreign key for table `{{%video}}`
        $this->dropForeignKey(
            '{{%fk-video_view-video_id}}',
            '{{%video_view}}'
        );

        // drops index for column `video_id`
        $this->dropIndex(
            '{{%idx-video_view-video_id}}',
            '{{%video_view}}'
        );

        // drops foreign key for table `{{%user}}`
        $this->dropForeignKey(
            '{{%fk-video_view-user_id}}',
            '{{%video_view}}'
        );

        // drops index for column `user_id`
        $this->dropIndex(
            '{{%idx-video_view-user_id}}',
            '{{%video_view}}'
        );

        $this->dropTable('{{%video_view}}');
    }
}


================================================
FILE: console/migrations/m200418_051244_create_video_like_table.php
================================================
<?php

use yii\db\Migration;

/**
 * Handles the creation of table `{{%video_like}}`.
 * Has foreign keys to the tables:
 *
 * - `{{%video}}`
 * - `{{%user}}`
 */
class m200418_051244_create_video_like_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->createTable('{{%video_like}}', [
            'id' => $this->primaryKey(),
            'video_id' => $this->string(16)->notNull(),
            'user_id' => $this->integer(11)->notNull(),
            'type' => $this->integer(1),
            'created_at' => $this->integer(11),
        ]);

        // creates index for column `video_id`
        $this->createIndex(
            '{{%idx-video_like-video_id}}',
            '{{%video_like}}',
            'video_id'
        );

        // add foreign key for table `{{%video}}`
        $this->addForeignKey(
            '{{%fk-video_like-video_id}}',
            '{{%video_like}}',
            'video_id',
            '{{%video}}',
            'video_id',
            'CASCADE'
        );

        // creates index for column `user_id`
        $this->createIndex(
            '{{%idx-video_like-user_id}}',
            '{{%video_like}}',
            'user_id'
        );

        // add foreign key for table `{{%user}}`
        $this->addForeignKey(
            '{{%fk-video_like-user_id}}',
            '{{%video_like}}',
            'user_id',
            '{{%user}}',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        // drops foreign key for table `{{%video}}`
        $this->dropForeignKey(
            '{{%fk-video_like-video_id}}',
            '{{%video_like}}'
        );

        // drops index for column `video_id`
        $this->dropIndex(
            '{{%idx-video_like-video_id}}',
            '{{%video_like}}'
        );

        // drops foreign key for table `{{%user}}`
        $this->dropForeignKey(
            '{{%fk-video_like-user_id}}',
            '{{%video_like}}'
        );

        // drops index for column `user_id`
        $this->dropIndex(
            '{{%idx-video_like-user_id}}',
            '{{%video_like}}'
        );

        $this->dropTable('{{%video_like}}');
    }
}


================================================
FILE: console/migrations/m200418_060320_create_subscriber_table.php
================================================
<?php

use yii\db\Migration;

/**
 * Handles the creation of table `{{%subscriber}}`.
 * Has foreign keys to the tables:
 *
 * - `{{%user}}`
 * - `{{%user}}`
 */
class m200418_060320_create_subscriber_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->createTable('{{%subscriber}}', [
            'id' => $this->primaryKey(),
            'channel_id' => $this->integer(11),
            'user_id' => $this->integer(11),
            'created_at' => $this->integer(11),
        ]);

        // creates index for column `channel_id`
        $this->createIndex(
            '{{%idx-subscriber-channel_id}}',
            '{{%subscriber}}',
            'channel_id'
        );

        // add foreign key for table `{{%user}}`
        $this->addForeignKey(
            '{{%fk-subscriber-channel_id}}',
            '{{%subscriber}}',
            'channel_id',
            '{{%user}}',
            'id',
            'CASCADE'
        );

        // creates index for column `user_id`
        $this->createIndex(
            '{{%idx-subscriber-user_id}}',
            '{{%subscriber}}',
            'user_id'
        );

        // add foreign key for table `{{%user}}`
        $this->addForeignKey(
            '{{%fk-subscriber-user_id}}',
            '{{%subscriber}}',
            'user_id',
            '{{%user}}',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        // drops foreign key for table `{{%user}}`
        $this->dropForeignKey(
            '{{%fk-subscriber-channel_id}}',
            '{{%subscriber}}'
        );

        // drops index for column `channel_id`
        $this->dropIndex(
            '{{%idx-subscriber-channel_id}}',
            '{{%subscriber}}'
        );

        // drops foreign key for table `{{%user}}`
        $this->dropForeignKey(
            '{{%fk-subscriber-user_id}}',
            '{{%subscriber}}'
        );

        // drops index for column `user_id`
        $this->dropIndex(
            '{{%idx-subscriber-user_id}}',
            '{{%subscriber}}'
        );

        $this->dropTable('{{%subscriber}}');
    }
}


================================================
FILE: console/migrations/m200418_064142_create_fulltext_index_on_video.php
================================================
<?php

use yii\db\Migration;

/**
 * Class m200418_064142_create_fulltext_index_on_video
 */
class m200418_064142_create_fulltext_index_on_video extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->execute("ALTER TABLE {{%video}} ADD FULLTEXT(title, description, tags)");
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        echo "m200418_064142_create_fulltext_index_on_video cannot be reverted.\n";

        return false;
    }

    /*
    // Use up()/down() to run migration code without a transaction.
    public function up()
    {

    }

    public function down()
    {
        echo "m200418_064142_create_fulltext_index_on_video cannot be reverted.\n";

        return false;
    }
    */
}


================================================
FILE: console/migrations/m201112_042619_create_comment_table.php
================================================
<?php

use yii\db\Migration;

/**
 * Handles the creation of table `{{%comment}}`.
 * Has foreign keys to the tables:
 *
 * - `{{%video}}`
 * - `{{%comment}}`
 * - `{{%user}}`
 */
class m201112_042619_create_comment_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->createTable('{{%comment}}', [
            'id' => $this->primaryKey(),
            'comment' => $this->text()->notNull(),
            'video_id' => $this->string(16)->notNull(),
            'parent_id' => $this->integer(11),
            'pinned' => $this->tinyInteger()->defaultValue(0),
            'created_at' => $this->integer(11),
            'updated_at' => $this->integer(11),
            'created_by' => $this->integer(11),
        ]);

        // creates index for column `video_id`
        $this->createIndex(
            '{{%idx-comment-video_id}}',
            '{{%comment}}',
            'video_id'
        );

        // add foreign key for table `{{%video}}`
        $this->addForeignKey(
            '{{%fk-comment-video_id}}',
            '{{%comment}}',
            'video_id',
            '{{%video}}',
            'video_id',
            'CASCADE'
        );

        // creates index for column `parent_id`
        $this->createIndex(
            '{{%idx-comment-parent_id}}',
            '{{%comment}}',
            'parent_id'
        );

        // add foreign key for table `{{%comment}}`
        $this->addForeignKey(
            '{{%fk-comment-parent_id}}',
            '{{%comment}}',
            'parent_id',
            '{{%comment}}',
            'id',
            'CASCADE'
        );

        // creates index for column `created_by`
        $this->createIndex(
            '{{%idx-comment-created_by}}',
            '{{%comment}}',
            'created_by'
        );

        // add foreign key for table `{{%user}}`
        $this->addForeignKey(
            '{{%fk-comment-created_by}}',
            '{{%comment}}',
            'created_by',
            '{{%user}}',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        // drops foreign key for table `{{%video}}`
        $this->dropForeignKey(
            '{{%fk-comment-video_id}}',
            '{{%comment}}'
        );

        // drops index for column `video_id`
        $this->dropIndex(
            '{{%idx-comment-video_id}}',
            '{{%comment}}'
        );

        // drops foreign key for table `{{%comment}}`
        $this->dropForeignKey(
            '{{%fk-comment-parent_id}}',
            '{{%comment}}'
        );

        // drops index for column `parent_id`
        $this->dropIndex(
            '{{%idx-comment-parent_id}}',
            '{{%comment}}'
        );

        // drops foreign key for table `{{%user}}`
        $this->dropForeignKey(
            '{{%fk-comment-created_by}}',
            '{{%comment}}'
        );

        // drops index for column `created_by`
        $this->dropIndex(
            '{{%idx-comment-created_by}}',
            '{{%comment}}'
        );

        $this->dropTable('{{%comment}}');
    }
}


================================================
FILE: console/migrations/m201115_124738_add_mention_column_to_comment_table.php
================================================
<?php

use yii\db\Migration;

/**
 * Handles adding columns to table `{{%comment}}`.
 */
class m201115_124738_add_mention_column_to_comment_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->addColumn('{{%comment}}', 'mention', $this->string(255)->after('pinned'));
    }

    /**
     * {@inheritdoc}
     */
    public function safeDown()
    {
        $this->dropColumn('{{%comment}}', 'mention');
    }
}


================================================
FILE: console/models/.gitkeep
================================================
*


================================================
FILE: console/runtime/.gitignore
================================================
*
!.gitignore

================================================
FILE: docker-compose.yml
================================================
version: '3.2'

services:

  frontend:
    build: frontend
    ports:
      - 20080:80
    volumes:
      # Re-use local composer cache via host-volume
      - ~/.composer-docker/cache:/root/.composer/cache:delegated
      # Mount source-code for development
      - ./:/app

  backend:
    build: backend
    ports:
      - 21080:80
    volumes:
      # Re-use local composer cache via host-volume
      - ~/.composer-docker/cache:/root/.composer/cache:delegated
      # Mount source-code for development
      - ./:/app

  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=verysecret
      - MYSQL_DATABASE=yii2advanced
      - MYSQL_USER=yii2advanced
      - MYSQL_PASSWORD=secret

  #pgsql:
  #  image: postgres:9.5
  #  environment:
  #    - POSTGRES_DB=yii2advanced
  #    - POSTGRES_USER=yii2advanced
  #    - POSTGRES_PASSWORD=secret

================================================
FILE: environments/dev/backend/config/codeception-local.php
================================================
<?php

return yii\helpers\ArrayHelper::merge(
    require dirname(dirname(__DIR__)) . '/common/config/codeception-local.php',
    require __DIR__ . '/main.php',
    require __DIR__ . '/main-local.php',
    require __DIR__ . '/test.php',
    require __DIR__ . '/test-local.php',
    [
    ]
);


================================================
FILE: environments/dev/backend/config/main-local.php
================================================
<?php

$config = [
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => '',
        ],
    ],
];

if (!YII_ENV_TEST) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = [
        'class' => 'yii\debug\Module',
    ];

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
    ];
}

return $config;


================================================
FILE: environments/dev/backend/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/dev/backend/config/test-local.php
================================================
<?php
return [
];


================================================
FILE: environments/dev/backend/web/index-test.php
================================================
<?php

// NOTE: Make sure this file is not accessible when deployed to production
if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
    die('You are not allowed to access this file.');
}

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../../common/config/test.php',
    require __DIR__ . '/../../common/config/test-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php',
    require __DIR__ . '/../config/test.php',
    require __DIR__ . '/../config/test-local.php'
);

(new yii\web\Application($config))->run();


================================================
FILE: environments/dev/backend/web/index.php
================================================
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php'
);

(new yii\web\Application($config))->run();


================================================
FILE: environments/dev/backend/web/robots.txt
================================================
User-agent: *
Disallow: /

================================================
FILE: environments/dev/common/config/codeception-local.php
================================================
<?php

return yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/main.php',
    require __DIR__ . '/main-local.php',
    require __DIR__ . '/test.php',
    require __DIR__ . '/test-local.php',
    [
        'components' => [
            'request' => [
                // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
                'cookieValidationKey' => '',
            ],
        ],
    ]
);


================================================
FILE: environments/dev/common/config/main-local.php
================================================
<?php
return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=yii2advanced',
            'username' => 'root',
            'password' => '',
            'charset' => 'utf8',
        ],
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'viewPath' => '@common/mail',
            // send all mails to a file by default. You have to set
            // 'useFileTransport' to false and configure a transport
            // for the mailer to send real emails.
            'useFileTransport' => true,
        ],
    ],
];


================================================
FILE: environments/dev/common/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/dev/common/config/test-local.php
================================================
<?php
return [
    'components' => [
        'db' => [
            'dsn' => 'mysql:host=localhost;dbname=yii2advanced_test',
        ],
    ],
];


================================================
FILE: environments/dev/console/config/main-local.php
================================================
<?php
return [
    'bootstrap' => ['gii'],
    'modules' => [
        'gii' => 'yii\gii\Module',
    ],
];


================================================
FILE: environments/dev/console/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/dev/console/config/test-local.php
================================================
<?php
return [
];

================================================
FILE: environments/dev/frontend/config/codeception-local.php
================================================
<?php

return yii\helpers\ArrayHelper::merge(
    require dirname(dirname(__DIR__)) . '/common/config/codeception-local.php',
    require __DIR__ . '/main.php',
    require __DIR__ . '/main-local.php',
    require __DIR__ . '/test.php',
    require __DIR__ . '/test-local.php',
    [
    ]
);


================================================
FILE: environments/dev/frontend/config/main-local.php
================================================
<?php

$config = [
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => '',
        ],
    ],
];

if (!YII_ENV_TEST) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = [
        'class' => 'yii\debug\Module',
    ];

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = [
        'class' => 'yii\gii\Module',
    ];
}

return $config;


================================================
FILE: environments/dev/frontend/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/dev/frontend/config/test-local.php
================================================
<?php
return [
];


================================================
FILE: environments/dev/frontend/web/index-test.php
================================================
<?php

// NOTE: Make sure this file is not accessible when deployed to production
if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
    die('You are not allowed to access this file.');
}

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';


$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../../common/config/test.php',
    require __DIR__ . '/../../common/config/test-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php',
    require __DIR__ . '/../config/test.php',
    require __DIR__ . '/../config/test-local.php'
);

(new yii\web\Application($config))->run();


================================================
FILE: environments/dev/frontend/web/index.php
================================================
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php'
);

(new yii\web\Application($config))->run();


================================================
FILE: environments/dev/frontend/web/robots.txt
================================================
User-agent: *
Disallow: /

================================================
FILE: environments/dev/yii
================================================
#!/usr/bin/env php
<?php
/**
 * Yii console bootstrap file.
 */

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/common/config/bootstrap.php';
require __DIR__ . '/console/config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/common/config/main.php',
    require __DIR__ . '/common/config/main-local.php',
    require __DIR__ . '/console/config/main.php',
    require __DIR__ . '/console/config/main-local.php'
);

$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);


================================================
FILE: environments/dev/yii_test
================================================
#!/usr/bin/env php
<?php
/**
 * Yii console bootstrap file.
 */

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'test');

require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/common/config/bootstrap.php';
require __DIR__ . '/console/config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/common/config/main.php',
    require __DIR__ . '/common/config/main-local.php',
    require __DIR__ . '/common/config/test.php',
    require __DIR__ . '/common/config/test-local.php',
    require __DIR__ . '/console/config/main.php',
    require __DIR__ . '/console/config/main-local.php',
    require __DIR__ . '/console/config/test.php',
    require __DIR__ . '/console/config/test-local.php'
);

$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);


================================================
FILE: environments/dev/yii_test.bat
================================================
@echo off

rem -------------------------------------------------------------
rem  Yii command line bootstrap script for Windows.
rem -------------------------------------------------------------

@setlocal

set YII_PATH=%~dp0

if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe

"%PHP_COMMAND%" "%YII_PATH%yii_test" %*

@endlocal


================================================
FILE: environments/index.php
================================================
<?php
/**
 * The manifest of files that are local to specific environment.
 * This file returns a list of environments that the application
 * may be installed under. The returned data must be in the following
 * format:
 *
 * ```php
 * return [
 *     'environment name' => [
 *         'path' => 'directory storing the local files',
 *         'skipFiles'  => [
 *             // list of files that should only copied once and skipped if they already exist
 *         ],
 *         'setWritable' => [
 *             // list of directories that should be set writable
 *         ],
 *         'setExecutable' => [
 *             // list of files that should be set executable
 *         ],
 *         'setCookieValidationKey' => [
 *             // list of config files that need to be inserted with automatically generated cookie validation keys
 *         ],
 *         'createSymlink' => [
 *             // list of symlinks to be created. Keys are symlinks, and values are the targets.
 *         ],
 *     ],
 * ];
 * ```
 */
return [
    'Development' => [
        'path' => 'dev',
        'setWritable' => [
            'backend/runtime',
            'backend/web/assets',
            'console/runtime',
            'frontend/runtime',
            'frontend/web/assets',
        ],
        'setExecutable' => [
            'yii',
            'yii_test',
        ],
        'setCookieValidationKey' => [
            'backend/config/main-local.php',
            'common/config/codeception-local.php',
            'frontend/config/main-local.php',
        ],
    ],
    'Production' => [
        'path' => 'prod',
        'setWritable' => [
            'backend/runtime',
            'backend/web/assets',
            'console/runtime',
            'frontend/runtime',
            'frontend/web/assets',
        ],
        'setExecutable' => [
            'yii',
        ],
        'setCookieValidationKey' => [
            'backend/config/main-local.php',
            'frontend/config/main-local.php',
        ],
    ],
];


================================================
FILE: environments/prod/backend/config/main-local.php
================================================
<?php
return [
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => '',
        ],
    ],
];


================================================
FILE: environments/prod/backend/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/prod/backend/web/index.php
================================================
<?php
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php'
);

(new yii\web\Application($config))->run();


================================================
FILE: environments/prod/backend/web/robots.txt
================================================
User-agent: *
Disallow: /

================================================
FILE: environments/prod/common/config/main-local.php
================================================
<?php
return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=yii2advanced',
            'username' => 'root',
            'password' => '',
            'charset' => 'utf8',
        ],
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'viewPath' => '@common/mail',
        ],
    ],
];


================================================
FILE: environments/prod/common/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/prod/console/config/main-local.php
================================================
<?php
return [
];


================================================
FILE: environments/prod/console/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/prod/frontend/config/main-local.php
================================================
<?php
return [
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => '',
        ],
    ],
];


================================================
FILE: environments/prod/frontend/config/params-local.php
================================================
<?php
return [
];


================================================
FILE: environments/prod/frontend/web/index.php
================================================
<?php
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');

require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/../../common/config/main.php',
    require __DIR__ . '/../../common/config/main-local.php',
    require __DIR__ . '/../config/main.php',
    require __DIR__ . '/../config/main-local.php'
);

(new yii\web\Application($config))->run();


================================================
FILE: environments/prod/frontend/web/robots.txt
================================================
User-agent: *
Allow: /

================================================
FILE: environments/prod/yii
================================================
#!/usr/bin/env php
<?php
/**
 * Yii console bootstrap file.
 */

defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');

require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/common/config/bootstrap.php';
require __DIR__ . '/console/config/bootstrap.php';

$config = yii\helpers\ArrayHelper::merge(
    require __DIR__ . '/common/config/main.php',
    require __DIR__ . '/common/config/main-local.php',
    require __DIR__ . '/console/config/main.php',
    require __DIR__ . '/console/config/main-local.php'
);

$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);


================================================
FILE: frontend/Dockerfile
================================================
FROM yiisoftware/yii2-php:7.2-apache

# Change document root for Apache
RUN sed -i -e 's|/app/web|/app/frontend/web|g' /etc/apache2/sites-available/000-default.conf

================================================
FILE: frontend/assets/AppAsset.php
================================================
<?php

namespace frontend\assets;

use yii\web\AssetBundle;

/**
 * Main frontend application asset bundle.
 */
class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
    ];
    public $js = [
        'js/app.js'
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap4\BootstrapAsset',
    ];
}


================================================
FILE: frontend/codeception.yml
================================================
namespace: frontend\tests
actor_suffix: Tester
paths:
    tests: tests
    output: tests/_output
    data: tests/_data
    support: tests/_support
bootstrap: _bootstrap.php
settings:
    colors: true
    memory_limit: 1024M
modules:
    config:
        Yii2:
            configFile: 'config/codeception-local.php'


================================================
FILE: frontend/config/.gitignore
================================================
codeception-local.php
main-local.php
params-local.php
test-local.php


================================================
FILE: frontend/config/bootstrap.php
================================================
<?php


================================================
FILE: frontend/config/main.php
================================================
<?php
$params = array_merge(
    require __DIR__ . '/../../common/config/params.php',
    require __DIR__ . '/../../common/config/params-local.php',
    require __DIR__ . '/params.php',
    require __DIR__ . '/params-local.php'
);

return [
    'id' => 'app-frontend',
    'name' => 'FreeCodeTube',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'defaultRoute' => '/video/index',
    'layout' => 'auth',
    'controllerNamespace' => 'frontend\controllers',
    'components' => [
        'request' => [
            'csrfParam' => '_csrf-frontend',
        ],
        'user' => [
            'identityClass' => 'common\models\User',
            'enableAutoLogin' => true,
            'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true],
        ],
        'session' => [
            // this is the name of the session cookie used for login on the frontend
            'name' => 'advanced-frontend',
        ],
        'log' => [
            'traceLevel' => YII_DEBUG ? 3 : 0,
            'targets' => [
                [
                    'class' => 'yii\log\FileTarget',
                    'levels' => ['error', 'warning'],
                ],
            ],
        ],
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],

        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
                '/c/<username>' => '/channel/view',
                '/v/<id>' => '/video/view',
            ],
        ],

        'assetManager' => [
            'appendTimestamp' => true
        ]

    ],
    'params' => $params,
];


================================================
FILE: frontend/config/params.php
================================================
<?php
return [
    'adminEmail' => 'admin@example.com',
];


================================================
FILE: frontend/config/test.php
================================================
<?php
return [
    'id' => 'app-frontend-tests',
    'components' => [
        'assetManager' => [
            'basePath' => __DIR__ . '/../web/assets',
        ],
        'urlManager' => [
            'showScriptName' => true,
        ],
        'request' => [
            'cookieValidationKey' => 'test',
        ],
    ],
];


================================================
FILE: frontend/controllers/ChannelController.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/18/2020
 * Time: 9:48 AM
 */

namespace frontend\controllers;


use common\models\Subscriber;
use common\models\User;
use common\models\Video;
use yii\data\ActiveDataProvider;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\NotFoundHttpException;

/**
 * Class ChannelController
 *
 * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>
 * @package frontend\controllers
 */
class ChannelController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'only' => ['subscribe'],
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@']
                    ]
                ]
            ],
        ];
    }

    public function actionView($username)
    {
        $channel = $this->findChannel($username);

        $dataProvider = new ActiveDataProvider([
            'query' => Video::find()->creator($channel->id)->published()
        ]);

        return $this->render('view', [
            'channel' => $channel,
            'dataProvider' => $dataProvider
        ]);
    }

    public function actionSubscribe($username)
    {
        $channel = $this->findChannel($username);

        $userId = \Yii::$app->user->id;
        $subscriber = $channel->isSubscribed($userId);
        if (!$subscriber) {
            $subscriber = new Subscriber();
            $subscriber->channel_id = $channel->id;
            $subscriber->user_id = $userId;
            $subscriber->created_at = time();
            $subscriber->save();
            \Yii::$app->mailer->compose([
                'html' => 'subscriber-html', 'text' => 'subscriber-text'
            ], [
                'channel' => $channel,
                'user' => \Yii::$app->user->identity
            ])
                ->setFrom(\Yii::$app->params['senderEmail'])
                ->setTo($channel->email)
                ->setSubject('You have new subscriber')
                ->send();

        } else {
            $subscriber->delete();
        }

        return $this->renderAjax('_subscribe', [
            'channel' => $channel
        ]);
    }

    /**
     * @param $username
     * @return \common\models\User|null
     * @throws \yii\web\NotFoundHttpException
     * @author Zura Sekhniashvili <zurasekhniashvili@gmail.com>
     */
    protected function findChannel($username)
    {
        $channel = User::findByUsername($username);
        if (!$channel) {
            throw new NotFoundHttpException("Channel does not exist");
        }

        return $channel;
    }
}

================================================
FILE: frontend/controllers/CommentController.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 11/12/2020
 * Time: 8:58 AM
 */

namespace frontend\controllers;


use common\models\Comment;
use common\models\User;
use yii\filters\AccessControl;
use yii\filters\ContentNegotiator;
use yii\filters\VerbFilter;
use yii\web\Controller;
use yii\web\ForbiddenHttpException;
use yii\web\NotFoundHttpException;
use yii\web\Response;

/**
 * Class CommentController
 *
 * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>
 * @package frontend\controllers
 */
class CommentController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'only' => ['create'],
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@']
                    ]
                ]
            ],
            'content' => [
                'class' => ContentNegotiator::class,
                'only' => ['create', 'update', 'delete', 'reply', 'by-parent', 'pin'],
                'formats' => [
                    'application/json' => Response::FORMAT_JSON
                ]
            ],
            'verb' => [
                'class' => VerbFilter::class,
                'actions' => [
                    'delete' => ['POST']
                ]
            ]
        ];
    }

    public function actionCreate($id)
    {
        $comment = new Comment();
        $comment->video_id = $id;
        if ($comment->load(\Yii::$app->request->post(), '') && $comment->save()) {
            return [
                'success' => true,
                'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [
                    'model' => $comment,
                ])
            ];
        }

        return [
            'success' => false,
            'errors' => $comment->errors
        ];
    }

    public function actionDelete($id)
    {
        $comment = $this->findModel($id);
        if ($comment->belongsTo(\Yii::$app->user->id) || $comment->video->belongsTo(\Yii::$app->user->id)) {
            $comment->delete();

            return ['success' => true];
        }
        throw new ForbiddenHttpException();
    }

    public function actionUpdate($id)
    {
        $comment = $this->findModel($id);
        if ($comment->belongsTo(\Yii::$app->user->id)) {
            $commentText = \Yii::$app->request->post('comment');
            $comment->comment = $commentText;
            if ($comment->save()) {
                return [
                    'success' => true,
                    'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [
                        'model' => $comment,
                    ])
                ];
            }

            return [
                'success' => false,
                'errors' => $comment->errors
            ];
        }
        throw new ForbiddenHttpException();
    }

    public function actionReply()
    {
        $parentId = \Yii::$app->request->post('parent_id');
        $parentComment = $this->findModel($parentId);

        $commentText = \Yii::$app->request->post('comment');
        $mentionUsername = \Yii::$app->request->post('mention');
        if (strpos($commentText, '@' . $mentionUsername) !== 0) {
            $mentionUsername = null;
        } else {
            $commentText = trim(str_replace('@' . $mentionUsername, '', $commentText));
        }

        if ($mentionUsername) {
            $mentionUser = User::findByUsername($mentionUsername);
            if (!$mentionUser) {
                $mentionUsername = null;
            } else {
                $currentUser = \Yii::$app->user->identity;
                \Yii::$app->mailer->compose([
                    'html' => 'mention-html', 'text' => 'mention-text'
                ], [
                    'comment' => $commentText,
                    'channel' => $mentionUser,
                    'user' => $currentUser
                ])
                    ->setFrom(\Yii::$app->params['senderEmail'])
                    ->setTo($mentionUser->email)
                    ->setSubject('User '.$currentUser->username.' mention you in a comment')
                    ->send();
            }
        }

        $comment = new Comment();
        $comment->comment = $commentText;
        $comment->mention = $mentionUsername;
        $comment->video_id = $parentComment->video_id;

        if ($parentComment->parent_id) {
            $comment->parent_id = $parentComment->parent_id;
        } else {
            $comment->parent_id = $parentId;
        }


        if ($comment->save()) {
            return [
                'success' => true,
                'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [
                    'model' => $comment,
                ])
            ];
        }

        return [
            'success' => false,
            'errors' => $comment->errors
        ];
    }

    public function actionByParent($id)
    {
        $parentComment = $this->findModel($id);

        $finalContent = "";
        foreach ($parentComment->comments as $comment) {
            $finalContent .= $this->renderPartial('@frontend/views/video/_comment_item', [
                'model' => $comment,
            ]);
        }

        return [
            'success' => true,
            'comments' => $finalContent
        ];
    }

    public function actionPin($id)
    {
        $comment = $this->findModel($id);
        if ($comment->video->belongsTo(\Yii::$app->user->id)) {
            if ($comment->pinned) {
                $comment->pinned = 0;
            } else {
                Comment::updateAll(['pinned' => 0], ['video_id' => $comment->video]);
                $comment->pinned = 1;
            }
            if ($comment->save()) {
                return [
                    'success' => true,
                    'comment' => $this->renderPartial('@frontend/views/video/_comment_item', [
                        'model' => $comment,
                    ])
                ];
            }

            return [
                'success' => false,
                'errors' => $comment->errors
            ];
        }

        throw new ForbiddenHttpException();
    }

    protected function findModel($id)
    {
        $comment = Comment::findOne($id);
        if (!$comment) {
            throw new NotFoundHttpException;
        }

        return $comment;
    }
}

================================================
FILE: frontend/controllers/SiteController.php
================================================
<?php
namespace frontend\controllers;

use frontend\models\ResendVerificationEmailForm;
use frontend\models\VerifyEmailForm;
use Yii;
use yii\base\InvalidArgumentException;
use yii\web\BadRequestHttpException;
use yii\web\Controller;
use yii\filters\VerbFilter;
use yii\filters\AccessControl;
use common\models\LoginForm;
use frontend\models\PasswordResetRequestForm;
use frontend\models\ResetPasswordForm;
use frontend\models\SignupForm;
use frontend\models\ContactForm;

/**
 * Site controller
 */
class SiteController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'only' => ['logout', 'signup'],
                'rules' => [
                    [
                        'actions' => ['signup'],
                        'allow' => true,
                        'roles' => ['?'],
                    ],
                    [
                        'actions' => ['logout'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'logout' => ['post'],
                ],
            ],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
            'captcha' => [
                'class' => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
            ],
        ];
    }

    /**
     * Displays homepage.
     *
     * @return mixed
     */
    public function actionIndex()
    {
        return $this->render('index');
    }

    /**
     * Logs in a user.
     *
     * @return mixed
     */
    public function actionLogin()
    {
        if (!Yii::$app->user->isGuest) {
            return $this->goHome();
        }

        $model = new LoginForm();
        if ($model->load(Yii::$app->request->post()) && $model->login()) {
            return $this->goBack();
        } else {
            $model->password = '';

            return $this->render('login', [
                'model' => $model,
            ]);
        }
    }

    /**
     * Logs out the current user.
     *
     * @return mixed
     */
    public function actionLogout()
    {
        Yii::$app->user->logout();

        return $this->goHome();
    }

    /**
     * Signs user up.
     *
     * @return mixed
     */
    public function actionSignup()
    {
        $model = new SignupForm();
        if ($model->load(Yii::$app->request->post()) && $model->signup()) {
            Yii::$app->session->setFlash('success', 'Thank you for registration. Please check your inbox for verification email.');
            return $this->goHome();
        }

        return $this->render('signup', [
            'model' => $model,
        ]);
    }

    /**
     * Requests password reset.
     *
     * @return mixed
     */
    public function actionRequestPasswordReset()
    {
        $model = new PasswordResetRequestForm();
        if ($model->load(Yii::$app->request->post()) && $model->validate()) {
            if ($model->sendEmail()) {
                Yii::$app->session->setFlash('success', 'Check your email for further instructions.');

                return $this->goHome();
            } else {
                Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for the provided email address.');
            }
        }

        return $this->render('requestPasswordResetToken', [
            'model' => $model,
        ]);
    }

    /**
     * Resets password.
     *
     * @param string $token
     * @return mixed
     * @throws BadRequestHttpException
     */
    public function actionResetPassword($token)
    {
        try {
            $model = new ResetPasswordForm($token);
        } catch (InvalidArgumentException $e) {
            throw new BadRequestHttpException($e->getMessage());
        }

        if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) {
            Yii::$app->session->setFlash('success', 'New password saved.');

            return $this->goHome();
        }

        return $this->render('resetPassword', [
            'model' => $model,
        ]);
    }

    /**
     * Verify email address
     *
     * @param string $token
     * @throws BadRequestHttpException
     * @return yii\web\Response
     */
    public function actionVerifyEmail($token)
    {
        try {
            $model = new VerifyEmailForm($token);
        } catch (InvalidArgumentException $e) {
            throw new BadRequestHttpException($e->getMessage());
        }
        if ($user = $model->verifyEmail()) {
            if (Yii::$app->user->login($user)) {
                Yii::$app->session->setFlash('success', 'Your email has been confirmed!');
                return $this->goHome();
            }
        }

        Yii::$app->session->setFlash('error', 'Sorry, we are unable to verify your account with provided token.');
        return $this->goHome();
    }

    /**
     * Resend verification email
     *
     * @return mixed
     */
    public function actionResendVerificationEmail()
    {
        $model = new ResendVerificationEmailForm();
        if ($model->load(Yii::$app->request->post()) && $model->validate()) {
            if ($model->sendEmail()) {
                Yii::$app->session->setFlash('success', 'Check your email for further instructions.');
                return $this->goHome();
            }
            Yii::$app->session->setFlash('error', 'Sorry, we are unable to resend verification email for the provided email address.');
        }

        return $this->render('resendVerificationEmail', [
            'model' => $model
        ]);
    }
}


================================================
FILE: frontend/controllers/VideoController.php
================================================
<?php
/**
 * User: TheCodeholic
 * Date: 4/17/2020
 * Time: 11:56 AM
 */

namespace frontend\controllers;


use common\models\Comment;
use common\models\Video;
use common\models\VideoLike;
use common\models\VideoView;
use yii\data\ActiveDataProvider;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\web\Controller;
use yii\web\NotFoundHttpException;

/**
 * Class VideoController
 *
 * @author  Zura Sekhniashvili <zurasekhniashvili@gmail.com>
 * @package frontend\controllers
 */
class VideoController extends Controller
{

    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'only' => ['like', 'dislike', 'history'],
                'rules' => [
                    [
                        'allow' => true,
                        'roles' => ['@']
                    ]
                ]
            ],
            'verb' => [
                'class' => VerbFilter::class,
                'actions' => [
                    'like' => ['post'],
                    'dislike' => ['post'],
                ]
            ]
        ];
    }

    public function actionIndex()
    {
        $this->layout = 'main';
        $dataProvider = new ActiveDataProvider([
            'query' => Video::find()->with('createdBy')->published()->latest(),
        ]);

        return $this->render('index', [
            'dataProvider' => $dataProvider
        ]);
    }

    public function actionView($id)
    {
        $this->layout = 'auth';
        $video = $this->findVideo($id);

        $videoView = new VideoView();
        $videoView->video_id = $id;
        $videoView->user_id = \Yii::$app->user->id;
        $videoView->created_at = time();
        $videoView->save();

        $similarVideos = Video::find()
            ->published()
            ->byKeyword($video->title)
            ->andWhere(['NOT', ['video_id' => $id]])
            ->limit(10)
            ->all();

        $comments = $video
            ->getComments()
            ->with(['createdBy'])
            ->parent()
            ->latest()
            ->all();

        return $this->render('view', [
            'model' => $video,
            'comments' => $comments,
            'similarVideos' => $similarVideos
        ]);
    }

    public function actionLike($id)
    {
        $video = $this->findVideo($id);
        $userId = \Yii::$app->user->id;

        $videoLikeDislike = VideoLike::find()
            ->userIdVideoId($userId, $id)
            ->one();
        if (!$videoLikeDislike) {
            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_LIKE);
        } else if ($videoLikeDislike->type == VideoLike::TYPE_LIKE) {
            $videoLikeDislike->delete();
        } else {
            $videoLikeDislike->delete();
            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_LIKE);
        }

        return $this->renderAjax('_buttons', [
            'model' => $video
        ]);
    }

    public function actionDislike($id)
    {
        $video = $this->findVideo($id);
        $userId = \Yii::$app->user->id;

        $videoLikeDislike = VideoLike::find()
            ->userIdVideoId($userId, $id)
            ->one();
        if (!$videoLikeDislike) {
            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_DISLIKE);
        } else if ($videoLikeDislike->type == VideoLike::TYPE_DISLIKE) {
            $videoLikeDislike->delete();
        } else {
            $videoLikeDislike->delete();
            $this->saveLikeDislike($id, $userId, VideoLike::TYPE_DISLIKE);
        }

        return $this->renderAjax('_buttons', [
            'model' => $video
        ]);
    }

    public function actionSearch($keyword)
    {
        $this->layout = 'main';
        $query = Video::find()
            ->with('createdBy')
            ->published()
            ->latest();
        if ($keyword) {
            $query->byKeyword($keyword);
        }
        $dataProvider = new ActiveDataProvider([
            'query' => $query
        ]);

        return $this->render('search', [
            'dataProvider' => $dataProvider
        ]);
    }

    public function actionHistory()
    {
        $this->layout = 'main';
        $query = Video::find()
            ->alias('v')
            ->innerJoin("(SELECT video_id, MAX(created_at) as max_date FROM video_view
                    WHERE user_id = :userId
                    GROUP BY video_id) vv", 'vv.video_id = v.video_id', [
                'userId' => \Yii::$app->user->id
            ])
            ->orderBy("vv.max_date DESC");

        $dataProvider = new ActiveDataProvider([
            'query' => $query
        ]);

        return $this->render('history', [
            'dataProvider' => $dataProvider
        ]);
    }

    protected function findVideo($id)
    {
        $video = Video::findOne($id);
        if (!$video) {
            throw new NotFoundHttpException("Video does not exit");
        }

        return $video;
    }

    protected function saveLikeDislike($videoId, $userId, $type)
    {
        $videoLikeDislike = new VideoLike();
        $videoLikeDislike->video_id = $videoId;
        $videoLikeDislike->user_id = $userId;
        $videoLikeDislike->type = $type;
        $videoLikeDislike->created_at = time();
        $videoLikeDislike->save();
    }
}

================================================
FILE: frontend/models/PasswordResetRequestForm.php
================================================
<?php
namespace frontend\models;

use Yii;
use yii\base\Model;
use common\models\User;

/**
 * Password reset request form
 */
class PasswordResetRequestForm extends Model
{
    public $email;


    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'exist',
                'targetClass' => '\common\models\User',
                'filter' => ['status' => User::STATUS_ACTIVE],
                'message' => 'There is no user with this email address.'
            ],
        ];
    }

    /**
     * Sends an email with a link, for resetting the password.
     *
     * @return bool whether the email was send
     */
    public function sendEmail()
    {
        /* @var $user User */
        $user = User::findOne([
            'status' => User::STATUS_ACTIVE,
            'email' => $this->email,
        ]);

        if (!$user) {
            return false;
        }
        
        if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
            $user->generatePasswordResetToken();
            if (!$user->save()) {
                return false;
            }
        }

        return Yii::$app
            ->mailer
            ->compose(
                ['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text'],
                ['user' => $user]
            )
            ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])
            ->setTo($this->email)
            ->setSubject('Password reset for ' . Yii::$app->name)
            ->send();
    }
}


================================================
FILE: frontend/models/ResendVerificationEmailForm.php
================================================
<?php


namespace frontend\models;

use Yii;
use common\models\User;
use yii\base\Model;

class ResendVerificationEmailForm extends Model
{
    /**
     * @var string
     */
    public $email;


    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'exist',
                'targetClass' => '\common\models\User',
                'filter' => ['status' => User::STATUS_INACTIVE],
                'message' => 'There is no user with this email address.'
            ],
        ];
    }

    /**
     * Sends confirmation email to user
     *
     * @return bool whether the email was sent
     */
    public function sendEmail()
    {
        $user = User::findOne([
            'email' => $this->email,
            'status' => User::STATUS_INACTIVE
        ]);

        if ($user === null) {
            return false;
        }

        return Yii::$app
            ->mailer
            ->compose(
                ['html' => 'emailVerify-html', 'text' => 'emailVerify-text'],
                ['user' => $user]
            )
            ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])
            ->setTo($this->email)
            ->setSubject('Account registration at ' . Yii::$app->name)
            ->send();
    }
}


================================================
FILE: frontend/models/ResetPasswordForm.php
================================================
<?php
namespace frontend\models;

use yii\base\InvalidArgumentException;
use yii\base\Model;
use common\models\User;

/**
 * Password reset form
 */
class ResetPasswordForm extends Model
{
    public $password;

    /**
     * @var \common\models\User
     */
    private $_user;


    /**
     * Creates a form model given a token.
     *
     * @param string $token
     * @param array $config name-value pairs that will be used to initialize the object properties
     * @throws InvalidArgumentException if token is empty or not valid
     */
    public function __construct($token, $config = [])
    {
        if (empty($token) || !is_string($token)) {
            throw new InvalidArgumentException('Password reset token cannot be blank.');
        }
        $this->_user = User::findByPasswordResetToken($token);
        if (!$this->_user) {
            throw new InvalidArgumentException('Wrong password reset token.');
        }
        parent::__construct($config);
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['password', 'required'],
            ['password', 'string', 'min' => 6],
        ];
    }

    /**
     * Resets password.
     *
     * @return bool if password was reset.
     */
    public function resetPassword()
    {
        $user = $this->_user;
        $user->setPassword($this->password);
        $user->removePasswordResetToken();

        return $user->save(false);
    }
}


================================================
FILE: frontend/models/SignupForm.php
================================================
<?php
namespace frontend\models;

use Yii;
use yii\base\Model;
use common\models\User;

/**
 * Signup form
 */
class SignupForm extends Model
{
    public $username;
    public $email;
    public $password;


    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['username', 'trim'],
            ['username', 'required'],
            ['username', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This username has already been taken.'],
            ['username', 'string', 'min' => 2, 'max' => 255],

            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'string', 'max' => 255],
            ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'],

            ['password', 'required'],
            ['password', 'string', 'min' => 6],
        ];
    }

    /**
     * Signs user up.
     *
     * @return bool whether the creating new account was successful and email was sent
     */
    public function signup()
    {
        if (!$this->validate()) {
            return null;
        }
        
        $user = new User();
        $user->username = $this->username;
        $user->email = $this->email;
        $user->setPassword($this->password);
        $user->generateAuthKey();
        $user->generateEmailVerificationToken();
        return $user->save() && $this->sendEmail($user);

    }

    /**
     * Sends confirmation email to user
     * @param User $user user model to with email should be send
     * @return bool whether the email was sent
     */
    protected function sendEmail($user)
    {
        return Yii::$app
            ->mailer
            ->compose(
                ['html' => 'emailVerify-html', 'text' => 'emailVerify-text'],
                ['user' => $user]
            )
            ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])
            ->setTo($this->email)
            ->setSubject('Account registration at ' . Yii::$app->name)
            ->send();
    }
}


================================================
FILE: frontend/models/VerifyEmailForm.php
================================================
<?php

namespace frontend\models;

use common\models\User;
use yii\base\InvalidArgumentException;
u
Download .txt
gitextract_etck1_vw/

├── .bowerrc
├── .gitignore
├── LICENSE.md
├── README.md
├── Vagrantfile
├── backend/
│   ├── Dockerfile
│   ├── assets/
│   │   ├── AppAsset.php
│   │   └── TagsInputAsset.php
│   ├── codeception.yml
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── controllers/
│   │   ├── CommentController.php
│   │   ├── SiteController.php
│   │   └── VideoController.php
│   ├── models/
│   │   ├── .gitkeep
│   │   └── CommentSearch.php
│   ├── runtime/
│   │   └── .gitignore
│   ├── tests/
│   │   ├── _bootstrap.php
│   │   ├── _data/
│   │   │   ├── .gitignore
│   │   │   └── login_data.php
│   │   ├── _output/
│   │   │   └── .gitignore
│   │   ├── _support/
│   │   │   ├── .gitignore
│   │   │   ├── FunctionalTester.php
│   │   │   └── UnitTester.php
│   │   ├── functional/
│   │   │   ├── LoginCest.php
│   │   │   └── _bootstrap.php
│   │   ├── functional.suite.yml
│   │   ├── unit/
│   │   │   └── _bootstrap.php
│   │   └── unit.suite.yml
│   ├── views/
│   │   ├── comment/
│   │   │   ├── _comment_item.php
│   │   │   ├── _form.php
│   │   │   ├── _item.php
│   │   │   ├── _search.php
│   │   │   ├── create.php
│   │   │   ├── index.php
│   │   │   ├── update.php
│   │   │   └── view.php
│   │   ├── layouts/
│   │   │   ├── _header.php
│   │   │   ├── _sidebar.php
│   │   │   ├── auth.php
│   │   │   ├── base.php
│   │   │   └── main.php
│   │   ├── site/
│   │   │   ├── error.php
│   │   │   ├── index.php
│   │   │   └── login.php
│   │   └── video/
│   │       ├── _form.php
│   │       ├── _video_item.php
│   │       ├── create.php
│   │       ├── index.php
│   │       └── update.php
│   └── web/
│       ├── app.js
│       ├── assets/
│       │   └── .gitignore
│       ├── css/
│       │   └── site.css
│       └── tagsinput/
│           ├── tagsinput.css
│           └── tagsinput.js
├── codeception.yml
├── common/
│   ├── codeception.yml
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── fixtures/
│   │   └── UserFixture.php
│   ├── helpers/
│   │   └── Html.php
│   ├── mail/
│   │   ├── emailVerify-html.php
│   │   ├── emailVerify-text.php
│   │   ├── layouts/
│   │   │   ├── html.php
│   │   │   └── text.php
│   │   ├── mention-html.php
│   │   ├── mention-text.php
│   │   ├── passwordResetToken-html.php
│   │   ├── passwordResetToken-text.php
│   │   ├── subscriber-html.php
│   │   └── subscriber-text.php
│   ├── models/
│   │   ├── Comment.php
│   │   ├── LoginForm.php
│   │   ├── Subscriber.php
│   │   ├── User.php
│   │   ├── Video.php
│   │   ├── VideoLike.php
│   │   ├── VideoView.php
│   │   └── query/
│   │       ├── CommentQuery.php
│   │       ├── SubscriberQuery.php
│   │       ├── VideoLikeQuery.php
│   │       ├── VideoQuery.php
│   │       └── VideoViewQuery.php
│   ├── tests/
│   │   ├── _bootstrap.php
│   │   ├── _data/
│   │   │   └── user.php
│   │   ├── _output/
│   │   │   └── .gitignore
│   │   ├── _support/
│   │   │   ├── .gitignore
│   │   │   └── UnitTester.php
│   │   ├── unit/
│   │   │   └── models/
│   │   │       └── LoginFormTest.php
│   │   └── unit.suite.yml
│   └── widgets/
│       └── Alert.php
├── composer.json
├── console/
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── controllers/
│   │   └── .gitkeep
│   ├── migrations/
│   │   ├── m130524_201442_init.php
│   │   ├── m190124_110200_add_verification_token_column_to_user_table.php
│   │   ├── m200417_054237_create_videos_table.php
│   │   ├── m200418_050048_create_video_view_table.php
│   │   ├── m200418_051244_create_video_like_table.php
│   │   ├── m200418_060320_create_subscriber_table.php
│   │   ├── m200418_064142_create_fulltext_index_on_video.php
│   │   ├── m201112_042619_create_comment_table.php
│   │   └── m201115_124738_add_mention_column_to_comment_table.php
│   ├── models/
│   │   └── .gitkeep
│   └── runtime/
│       └── .gitignore
├── docker-compose.yml
├── environments/
│   ├── dev/
│   │   ├── backend/
│   │   │   ├── config/
│   │   │   │   ├── codeception-local.php
│   │   │   │   ├── main-local.php
│   │   │   │   ├── params-local.php
│   │   │   │   └── test-local.php
│   │   │   └── web/
│   │   │       ├── index-test.php
│   │   │       ├── index.php
│   │   │       └── robots.txt
│   │   ├── common/
│   │   │   └── config/
│   │   │       ├── codeception-local.php
│   │   │       ├── main-local.php
│   │   │       ├── params-local.php
│   │   │       └── test-local.php
│   │   ├── console/
│   │   │   └── config/
│   │   │       ├── main-local.php
│   │   │       ├── params-local.php
│   │   │       └── test-local.php
│   │   ├── frontend/
│   │   │   ├── config/
│   │   │   │   ├── codeception-local.php
│   │   │   │   ├── main-local.php
│   │   │   │   ├── params-local.php
│   │   │   │   └── test-local.php
│   │   │   └── web/
│   │   │       ├── index-test.php
│   │   │       ├── index.php
│   │   │       └── robots.txt
│   │   ├── yii
│   │   ├── yii_test
│   │   └── yii_test.bat
│   ├── index.php
│   └── prod/
│       ├── backend/
│       │   ├── config/
│       │   │   ├── main-local.php
│       │   │   └── params-local.php
│       │   └── web/
│       │       ├── index.php
│       │       └── robots.txt
│       ├── common/
│       │   └── config/
│       │       ├── main-local.php
│       │       └── params-local.php
│       ├── console/
│       │   └── config/
│       │       ├── main-local.php
│       │       └── params-local.php
│       ├── frontend/
│       │   ├── config/
│       │   │   ├── main-local.php
│       │   │   └── params-local.php
│       │   └── web/
│       │       ├── index.php
│       │       └── robots.txt
│       └── yii
├── frontend/
│   ├── Dockerfile
│   ├── assets/
│   │   └── AppAsset.php
│   ├── codeception.yml
│   ├── config/
│   │   ├── .gitignore
│   │   ├── bootstrap.php
│   │   ├── main.php
│   │   ├── params.php
│   │   └── test.php
│   ├── controllers/
│   │   ├── ChannelController.php
│   │   ├── CommentController.php
│   │   ├── SiteController.php
│   │   └── VideoController.php
│   ├── models/
│   │   ├── PasswordResetRequestForm.php
│   │   ├── ResendVerificationEmailForm.php
│   │   ├── ResetPasswordForm.php
│   │   ├── SignupForm.php
│   │   └── VerifyEmailForm.php
│   ├── runtime/
│   │   └── .gitignore
│   ├── tests/
│   │   ├── _bootstrap.php
│   │   ├── _data/
│   │   │   ├── login_data.php
│   │   │   └── user.php
│   │   ├── _output/
│   │   │   └── .gitignore
│   │   ├── _support/
│   │   │   ├── .gitignore
│   │   │   ├── FunctionalTester.php
│   │   │   └── UnitTester.php
│   │   ├── acceptance/
│   │   │   ├── HomeCest.php
│   │   │   └── _bootstrap.php
│   │   ├── acceptance.suite.yml.example
│   │   ├── functional/
│   │   │   ├── AboutCest.php
│   │   │   ├── ContactCest.php
│   │   │   ├── HomeCest.php
│   │   │   ├── LoginCest.php
│   │   │   ├── ResendVerificationEmailCest.php
│   │   │   ├── SignupCest.php
│   │   │   ├── VerifyEmailCest.php
│   │   │   └── _bootstrap.php
│   │   ├── functional.suite.yml
│   │   ├── unit/
│   │   │   ├── _bootstrap.php
│   │   │   └── models/
│   │   │       ├── ContactFormTest.php
│   │   │       ├── PasswordResetRequestFormTest.php
│   │   │       ├── ResendVerificationEmailFormTest.php
│   │   │       ├── ResetPasswordFormTest.php
│   │   │       ├── SignupFormTest.php
│   │   │       └── VerifyEmailFormTest.php
│   │   └── unit.suite.yml
│   ├── views/
│   │   ├── channel/
│   │   │   ├── _subscribe.php
│   │   │   └── view.php
│   │   ├── layouts/
│   │   │   ├── _header.php
│   │   │   ├── _sidebar.php
│   │   │   ├── auth.php
│   │   │   ├── base.php
│   │   │   └── main.php
│   │   ├── site/
│   │   │   ├── error.php
│   │   │   ├── index.php
│   │   │   ├── login.php
│   │   │   ├── requestPasswordResetToken.php
│   │   │   ├── resendVerificationEmail.php
│   │   │   ├── resetPassword.php
│   │   │   └── signup.php
│   │   └── video/
│   │       ├── _buttons.php
│   │       ├── _comment_item.php
│   │       ├── _video_item.php
│   │       ├── history.php
│   │       ├── index.php
│   │       ├── search.php
│   │       └── view.php
│   └── web/
│       ├── assets/
│       │   └── .gitignore
│       ├── css/
│       │   └── site.css
│       ├── js/
│       │   └── app.js
│       └── storage/
│           └── .gitignore
├── init
├── init.bat
├── requirements.php
├── sample.php
├── vagrant/
│   ├── config/
│   │   ├── .gitignore
│   │   └── vagrant-local.example.yml
│   ├── nginx/
│   │   ├── app.conf
│   │   └── log/
│   │       └── .gitignore
│   └── provision/
│       ├── always-as-root.sh
│       ├── common.sh
│       ├── once-as-root.sh
│       └── once-as-vagrant.sh
└── yii.bat
Download .txt
SYMBOL INDEX (310 symbols across 63 files)

FILE: backend/assets/AppAsset.php
  class AppAsset (line 10) | class AppAsset extends AssetBundle

FILE: backend/assets/TagsInputAsset.php
  class TagsInputAsset (line 20) | class TagsInputAsset extends AssetBundle

FILE: backend/controllers/CommentController.php
  class CommentController (line 16) | class CommentController extends Controller
    method behaviors (line 21) | public function behaviors()
    method actionIndex (line 46) | public function actionIndex()
    method actionView (line 63) | public function actionView($id)
    method actionCreate (line 75) | public function actionCreate()
    method actionUpdate (line 95) | public function actionUpdate($id)
    method actionDelete (line 115) | public function actionDelete($id)
    method findModel (line 129) | protected function findModel($id)

FILE: backend/controllers/SiteController.php
  class SiteController (line 17) | class SiteController extends Controller
    method behaviors (line 22) | public function behaviors()
    method actions (line 51) | public function actions()
    method actionIndex (line 65) | public function actionIndex()
    method actionLogin (line 108) | public function actionLogin()
    method actionLogout (line 132) | public function actionLogout()

FILE: backend/controllers/VideoController.php
  class VideoController (line 17) | class VideoController extends Controller
    method behaviors (line 22) | public function behaviors()
    method actionIndex (line 48) | public function actionIndex()
    method actionCreate (line 67) | public function actionCreate()
    method actionUpdate (line 89) | public function actionUpdate($id)
    method actionDelete (line 113) | public function actionDelete($id)
    method findModel (line 128) | protected function findModel($id)

FILE: backend/models/CommentSearch.php
  class CommentSearch (line 12) | class CommentSearch extends Comment
    method rules (line 17) | public function rules()
    method scenarios (line 28) | public function scenarios()
    method search (line 41) | public function search($params, $userId)

FILE: backend/tests/_support/FunctionalTester.php
  class FunctionalTester (line 19) | class FunctionalTester extends \Codeception\Actor

FILE: backend/tests/_support/UnitTester.php
  class UnitTester (line 19) | class UnitTester extends \Codeception\Actor

FILE: backend/tests/functional/LoginCest.php
  class LoginCest (line 11) | class LoginCest
    method _fixtures (line 20) | public function _fixtures()
    method loginUser (line 33) | public function loginUser(FunctionalTester $I)

FILE: backend/web/tagsinput/tagsinput.js
  function TagsInput (line 43) | function TagsInput(element, options) {
  function processItems (line 307) | function processItems(items) {
  function makeOptionItemFunction (line 614) | function makeOptionItemFunction(options, key) {
  function makeOptionFunction (line 620) | function makeOptionFunction(options, key) {
  function htmlEncode (line 630) | function htmlEncode(value) {
  function doGetCaretPosition (line 642) | function doGetCaretPosition(oField) {
  function keyCombinationInList (line 662) | function keyCombinationInList(keyPressEvent, lookupList) {

FILE: common/fixtures/UserFixture.php
  class UserFixture (line 6) | class UserFixture extends ActiveFixture

FILE: common/helpers/Html.php
  class Html (line 19) | class Html
    method channelLink (line 21) | public static function channelLink($user, $schema = false)

FILE: common/models/Comment.php
  class Comment (line 27) | class Comment extends \yii\db\ActiveRecord
    method tableName (line 32) | public static function tableName()
    method behaviors (line 37) | public function behaviors()
    method rules (line 51) | public function rules()
    method attributeLabels (line 68) | public function attributeLabels()
    method getCreatedBy (line 88) | public function getCreatedBy()
    method getParent (line 98) | public function getParent()
    method getComments (line 108) | public function getComments()
    method getVideo (line 118) | public function getVideo()
    method find (line 127) | public static function find()
    method belongsTo (line 132) | public function belongsTo($userId)

FILE: common/models/LoginForm.php
  class LoginForm (line 10) | class LoginForm extends Model
    method rules (line 22) | public function rules()
    method validatePassword (line 41) | public function validatePassword($attribute, $params)
    method login (line 56) | public function login()
    method getUser (line 70) | protected function getUser()

FILE: common/models/Subscriber.php
  class Subscriber (line 18) | class Subscriber extends \yii\db\ActiveRecord
    method tableName (line 23) | public static function tableName()
    method rules (line 31) | public function rules()
    method attributeLabels (line 43) | public function attributeLabels()
    method getChannel (line 58) | public function getChannel()
    method getUser (line 68) | public function getUser()
    method find (line 77) | public static function find()

FILE: common/models/User.php
  class User (line 25) | class User extends ActiveRecord implements IdentityInterface
    method tableName (line 35) | public static function tableName()
    method behaviors (line 43) | public function behaviors()
    method rules (line 53) | public function rules()
    method getSubscribers (line 66) | public function getSubscribers()
    method findIdentity (line 75) | public static function findIdentity($id)
    method findIdentityByAccessToken (line 83) | public static function findIdentityByAccessToken($token, $type = null)
    method findByUsername (line 94) | public static function findByUsername($username)
    method findByPasswordResetToken (line 105) | public static function findByPasswordResetToken($token)
    method findByVerificationToken (line 123) | public static function findByVerificationToken($token) {
    method isPasswordResetTokenValid (line 136) | public static function isPasswordResetTokenValid($token)
    method getId (line 150) | public function getId()
    method getAuthKey (line 158) | public function getAuthKey()
    method validateAuthKey (line 166) | public function validateAuthKey($authKey)
    method validatePassword (line 177) | public function validatePassword($password)
    method setPassword (line 187) | public function setPassword($password)
    method generateAuthKey (line 195) | public function generateAuthKey()
    method generatePasswordResetToken (line 203) | public function generatePasswordResetToken()
    method generateEmailVerificationToken (line 211) | public function generateEmailVerificationToken()
    method removePasswordResetToken (line 219) | public function removePasswordResetToken()
    method isSubscribed (line 224) | public function isSubscribed($userId)

FILE: common/models/Video.php
  class Video (line 30) | class Video extends \yii\db\ActiveRecord
    method tableName (line 48) | public static function tableName()
    method behaviors (line 53) | public function behaviors()
    method rules (line 67) | public function rules()
    method attributeLabels (line 87) | public function attributeLabels()
    method getStatusLabels (line 104) | public function getStatusLabels()
    method getCreatedBy (line 117) | public function getCreatedBy()
    method getViews (line 125) | public function getViews()
    method getLikes (line 133) | public function getLikes()
    method getDislikes (line 142) | public function getDislikes()
    method getComments (line 151) | public function getComments()
    method find (line 160) | public static function find()
    method save (line 165) | public function save($runValidation = true, $attributeNames = null)
    method getVideoLink (line 202) | public function getVideoLink()
    method getThumbnailLink (line 207) | public function getThumbnailLink()
    method afterDelete (line 214) | public function afterDelete()
    method isLikedBy (line 226) | public function isLikedBy($userId)
    method isDislikedBy (line 234) | public function isDislikedBy($userId)
    method belongsTo (line 242) | public function belongsTo($userId)

FILE: common/models/VideoLike.php
  class VideoLike (line 19) | class VideoLike extends \yii\db\ActiveRecord
    method tableName (line 27) | public static function tableName()
    method rules (line 35) | public function rules()
    method attributeLabels (line 49) | public function attributeLabels()
    method getUser (line 65) | public function getUser()
    method getVideo (line 75) | public function getVideo()
    method find (line 84) | public static function find()

FILE: common/models/VideoView.php
  class VideoView (line 18) | class VideoView extends \yii\db\ActiveRecord
    method tableName (line 23) | public static function tableName()
    method rules (line 31) | public function rules()
    method attributeLabels (line 45) | public function attributeLabels()
    method getUser (line 60) | public function getUser()
    method getVideo (line 70) | public function getVideo()
    method find (line 79) | public static function find()

FILE: common/models/query/CommentQuery.php
  class CommentQuery (line 13) | class CommentQuery extends \yii\db\ActiveQuery
    method all (line 24) | public function all($db = null)
    method one (line 33) | public function one($db = null)
    method videoId (line 38) | public function videoId($videoId)
    method parent (line 43) | public function parent()
    method latest (line 48) | public function latest()
    method byChannel (line 53) | public function byChannel($userId)

FILE: common/models/query/SubscriberQuery.php
  class SubscriberQuery (line 10) | class SubscriberQuery extends \yii\db\ActiveQuery
    method all (line 21) | public function all($db = null)
    method one (line 30) | public function one($db = null)

FILE: common/models/query/VideoLikeQuery.php
  class VideoLikeQuery (line 12) | class VideoLikeQuery extends \yii\db\ActiveQuery
    method all (line 23) | public function all($db = null)
    method one (line 32) | public function one($db = null)
    method userIdVideoId (line 37) | public function userIdVideoId($userId, $videoId)
    method liked (line 45) | public function liked()
    method disliked (line 50) | public function disliked()

FILE: common/models/query/VideoQuery.php
  class VideoQuery (line 12) | class VideoQuery extends \yii\db\ActiveQuery
    method all (line 23) | public function all($db = null)
    method one (line 32) | public function one($db = null)
    method creator (line 37) | public function creator($userId)
    method latest (line 42) | public function latest()
    method published (line 47) | public function published()
    method byKeyword (line 52) | public function byKeyword($keyword)

FILE: common/models/query/VideoViewQuery.php
  class VideoViewQuery (line 10) | class VideoViewQuery extends \yii\db\ActiveQuery
    method all (line 21) | public function all($db = null)
    method one (line 30) | public function one($db = null)

FILE: common/tests/_support/UnitTester.php
  class UnitTester (line 19) | class UnitTester extends \Codeception\Actor

FILE: common/tests/unit/models/LoginFormTest.php
  class LoginFormTest (line 12) | class LoginFormTest extends \Codeception\Test\Unit
    method _fixtures (line 23) | public function _fixtures()
    method testLoginNoUser (line 33) | public function testLoginNoUser()
    method testLoginWrongPassword (line 44) | public function testLoginWrongPassword()
    method testLoginCorrect (line 56) | public function testLoginCorrect()

FILE: common/widgets/Alert.php
  class Alert (line 25) | class Alert extends \yii\bootstrap4\Widget
    method run (line 50) | public function run()

FILE: console/migrations/m130524_201442_init.php
  class m130524_201442_init (line 5) | class m130524_201442_init extends Migration
    method up (line 7) | public function up()
    method down (line 29) | public function down()

FILE: console/migrations/m190124_110200_add_verification_token_column_to_user_table.php
  class m190124_110200_add_verification_token_column_to_user_table (line 5) | class m190124_110200_add_verification_token_column_to_user_table extends...
    method up (line 7) | public function up()
    method down (line 12) | public function down()

FILE: console/migrations/m200417_054237_create_videos_table.php
  class m200417_054237_create_videos_table (line 11) | class m200417_054237_create_videos_table extends Migration
    method safeUp (line 16) | public function safeUp()
    method safeDown (line 54) | public function safeDown()

FILE: console/migrations/m200418_050048_create_video_view_table.php
  class m200418_050048_create_video_view_table (line 12) | class m200418_050048_create_video_view_table extends Migration
    method safeUp (line 17) | public function safeUp()
    method safeDown (line 64) | public function safeDown()

FILE: console/migrations/m200418_051244_create_video_like_table.php
  class m200418_051244_create_video_like_table (line 12) | class m200418_051244_create_video_like_table extends Migration
    method safeUp (line 17) | public function safeUp()
    method safeDown (line 65) | public function safeDown()

FILE: console/migrations/m200418_060320_create_subscriber_table.php
  class m200418_060320_create_subscriber_table (line 12) | class m200418_060320_create_subscriber_table extends Migration
    method safeUp (line 17) | public function safeUp()
    method safeDown (line 64) | public function safeDown()

FILE: console/migrations/m200418_064142_create_fulltext_index_on_video.php
  class m200418_064142_create_fulltext_index_on_video (line 8) | class m200418_064142_create_fulltext_index_on_video extends Migration
    method safeUp (line 13) | public function safeUp()
    method safeDown (line 21) | public function safeDown()

FILE: console/migrations/m201112_042619_create_comment_table.php
  class m201112_042619_create_comment_table (line 13) | class m201112_042619_create_comment_table extends Migration
    method safeUp (line 18) | public function safeUp()
    method safeDown (line 86) | public function safeDown()

FILE: console/migrations/m201115_124738_add_mention_column_to_comment_table.php
  class m201115_124738_add_mention_column_to_comment_table (line 8) | class m201115_124738_add_mention_column_to_comment_table extends Migration
    method safeUp (line 13) | public function safeUp()
    method safeDown (line 21) | public function safeDown()

FILE: frontend/assets/AppAsset.php
  class AppAsset (line 10) | class AppAsset extends AssetBundle

FILE: frontend/controllers/ChannelController.php
  class ChannelController (line 25) | class ChannelController extends Controller
    method behaviors (line 27) | public function behaviors()
    method actionView (line 43) | public function actionView($username)
    method actionSubscribe (line 57) | public function actionSubscribe($username)
    method findChannel (line 95) | protected function findChannel($username)

FILE: frontend/controllers/CommentController.php
  class CommentController (line 27) | class CommentController extends Controller
    method behaviors (line 29) | public function behaviors()
    method actionCreate (line 58) | public function actionCreate($id)
    method actionDelete (line 77) | public function actionDelete($id)
    method actionUpdate (line 88) | public function actionUpdate($id)
    method actionReply (line 111) | public function actionReply()
    method actionByParent (line 171) | public function actionByParent($id)
    method actionPin (line 188) | public function actionPin($id)
    method findModel (line 216) | protected function findModel($id)

FILE: frontend/controllers/SiteController.php
  class SiteController (line 21) | class SiteController extends Controller
    method behaviors (line 26) | public function behaviors()
    method actions (line 57) | public function actions()
    method actionIndex (line 75) | public function actionIndex()
    method actionLogin (line 85) | public function actionLogin()
    method actionLogout (line 108) | public function actionLogout()
    method actionSignup (line 120) | public function actionSignup()
    method actionRequestPasswordReset (line 138) | public function actionRequestPasswordReset()
    method actionResetPassword (line 163) | public function actionResetPassword($token)
    method actionVerifyEmail (line 189) | public function actionVerifyEmail($token)
    method actionResendVerificationEmail (line 212) | public function actionResendVerificationEmail()

FILE: frontend/controllers/VideoController.php
  class VideoController (line 27) | class VideoController extends Controller
    method behaviors (line 30) | public function behaviors()
    method actionIndex (line 53) | public function actionIndex()
    method actionView (line 65) | public function actionView($id)
    method actionLike (line 97) | public function actionLike($id)
    method actionDislike (line 119) | public function actionDislike($id)
    method actionSearch (line 141) | public function actionSearch($keyword)
    method actionHistory (line 160) | public function actionHistory()
    method findVideo (line 181) | protected function findVideo($id)
    method saveLikeDislike (line 191) | protected function saveLikeDislike($videoId, $userId, $type)

FILE: frontend/models/PasswordResetRequestForm.php
  class PasswordResetRequestForm (line 11) | class PasswordResetRequestForm extends Model
    method rules (line 19) | public function rules()
    method sendEmail (line 38) | public function sendEmail()

FILE: frontend/models/ResendVerificationEmailForm.php
  class ResendVerificationEmailForm (line 10) | class ResendVerificationEmailForm extends Model
    method rules (line 21) | public function rules()
    method sendEmail (line 40) | public function sendEmail()

FILE: frontend/models/ResetPasswordForm.php
  class ResetPasswordForm (line 11) | class ResetPasswordForm extends Model
    method __construct (line 28) | public function __construct($token, $config = [])
    method rules (line 43) | public function rules()
    method resetPassword (line 56) | public function resetPassword()

FILE: frontend/models/SignupForm.php
  class SignupForm (line 11) | class SignupForm extends Model
    method rules (line 21) | public function rules()
    method signup (line 45) | public function signup()
    method sendEmail (line 66) | protected function sendEmail($user)

FILE: frontend/models/VerifyEmailForm.php
  class VerifyEmailForm (line 9) | class VerifyEmailForm extends Model
    method __construct (line 29) | public function __construct($token, array $config = [])
    method verifyEmail (line 46) | public function verifyEmail()

FILE: frontend/tests/_support/FunctionalTester.php
  class FunctionalTester (line 19) | class FunctionalTester extends \Codeception\Actor
    method seeValidationError (line 24) | public function seeValidationError($message)
    method dontSeeValidationError (line 29) | public function dontSeeValidationError($message)

FILE: frontend/tests/_support/UnitTester.php
  class UnitTester (line 19) | class UnitTester extends \Codeception\Actor

FILE: frontend/tests/acceptance/HomeCest.php
  class HomeCest (line 7) | class HomeCest
    method checkHome (line 9) | public function checkHome(AcceptanceTester $I)

FILE: frontend/tests/functional/AboutCest.php
  class AboutCest (line 6) | class AboutCest
    method checkAbout (line 8) | public function checkAbout(FunctionalTester $I)

FILE: frontend/tests/functional/ContactCest.php
  class ContactCest (line 8) | class ContactCest
    method _before (line 10) | public function _before(FunctionalTester $I)
    method checkContact (line 15) | public function checkContact(FunctionalTester $I)
    method checkContactSubmitNoData (line 20) | public function checkContactSubmitNoData(FunctionalTester $I)
    method checkContactSubmitNotCorrectEmail (line 31) | public function checkContactSubmitNotCorrectEmail(FunctionalTester $I)
    method checkContactSubmitCorrectData (line 47) | public function checkContactSubmitCorrectData(FunctionalTester $I)

FILE: frontend/tests/functional/HomeCest.php
  class HomeCest (line 7) | class HomeCest
    method checkOpen (line 9) | public function checkOpen(FunctionalTester $I)

FILE: frontend/tests/functional/LoginCest.php
  class LoginCest (line 8) | class LoginCest
    method _fixtures (line 17) | public function _fixtures()
    method _before (line 27) | public function _before(FunctionalTester $I)
    method formParams (line 32) | protected function formParams($login, $password)
    method checkEmpty (line 40) | public function checkEmpty(FunctionalTester $I)
    method checkWrongPassword (line 47) | public function checkWrongPassword(FunctionalTester $I)
    method checkInactiveAccount (line 53) | public function checkInactiveAccount(FunctionalTester $I)
    method checkValidLogin (line 59) | public function checkValidLogin(FunctionalTester $I)

FILE: frontend/tests/functional/ResendVerificationEmailCest.php
  class ResendVerificationEmailCest (line 8) | class ResendVerificationEmailCest
    method _fixtures (line 20) | public function _fixtures()
    method _before (line 30) | public function _before(FunctionalTester $I)
    method formParams (line 35) | protected function formParams($email)
    method checkPage (line 42) | public function checkPage(FunctionalTester $I)
    method checkEmptyField (line 48) | public function checkEmptyField(FunctionalTester $I)
    method checkWrongEmailFormat (line 54) | public function checkWrongEmailFormat(FunctionalTester $I)
    method checkWrongEmail (line 60) | public function checkWrongEmail(FunctionalTester $I)
    method checkAlreadyVerifiedEmail (line 66) | public function checkAlreadyVerifiedEmail(FunctionalTester $I)
    method checkSendSuccessfully (line 72) | public function checkSendSuccessfully(FunctionalTester $I)

FILE: frontend/tests/functional/SignupCest.php
  class SignupCest (line 7) | class SignupCest
    method _before (line 12) | public function _before(FunctionalTester $I)
    method signupWithEmptyFields (line 17) | public function signupWithEmptyFields(FunctionalTester $I)
    method signupWithWrongEmail (line 28) | public function signupWithWrongEmail(FunctionalTester $I)
    method signupSuccessfully (line 42) | public function signupSuccessfully(FunctionalTester $I)

FILE: frontend/tests/functional/VerifyEmailCest.php
  class VerifyEmailCest (line 8) | class VerifyEmailCest
    method _fixtures (line 17) | public function _fixtures()
    method checkEmptyToken (line 27) | public function checkEmptyToken(FunctionalTester $I)
    method checkInvalidToken (line 34) | public function checkInvalidToken(FunctionalTester $I)
    method checkNoToken (line 41) | public function checkNoToken(FunctionalTester $I)
    method checkAlreadyActivatedToken (line 48) | public function checkAlreadyActivatedToken(FunctionalTester $I)
    method checkSuccessVerification (line 55) | public function checkSuccessVerification(FunctionalTester $I)

FILE: frontend/tests/unit/models/ContactFormTest.php
  class ContactFormTest (line 7) | class ContactFormTest extends \Codeception\Test\Unit
    method testSendEmail (line 9) | public function testSendEmail()

FILE: frontend/tests/unit/models/PasswordResetRequestFormTest.php
  class PasswordResetRequestFormTest (line 10) | class PasswordResetRequestFormTest extends \Codeception\Test\Unit
    method _before (line 18) | public function _before()
    method testSendMessageWithWrongEmailAddress (line 28) | public function testSendMessageWithWrongEmailAddress()
    method testNotSendEmailsToInactiveUser (line 35) | public function testNotSendEmailsToInactiveUser()
    method testSendEmailSuccessfully (line 43) | public function testSendEmailSuccessfully()

FILE: frontend/tests/unit/models/ResendVerificationEmailFormTest.php
  class ResendVerificationEmailFormTest (line 10) | class ResendVerificationEmailFormTest extends Unit
    method _before (line 18) | public function _before()
    method testWrongEmailAddress (line 28) | public function testWrongEmailAddress()
    method testEmptyEmailAddress (line 40) | public function testEmptyEmailAddress()
    method testResendToActiveUser (line 52) | public function testResendToActiveUser()
    method testSuccessfullyResend (line 64) | public function testSuccessfullyResend()

FILE: frontend/tests/unit/models/ResetPasswordFormTest.php
  class ResetPasswordFormTest (line 8) | class ResetPasswordFormTest extends \Codeception\Test\Unit
    method _before (line 16) | public function _before()
    method testResetWrongToken (line 26) | public function testResetWrongToken()
    method testResetCorrectToken (line 37) | public function testResetCorrectToken()

FILE: frontend/tests/unit/models/SignupFormTest.php
  class SignupFormTest (line 7) | class SignupFormTest extends \Codeception\Test\Unit
    method _before (line 15) | public function _before()
    method testCorrectSignup (line 25) | public function testCorrectSignup()
    method testNotCorrectSignup (line 54) | public function testNotCorrectSignup()

FILE: frontend/tests/unit/models/VerifyEmailFormTest.php
  class VerifyEmailFormTest (line 8) | class VerifyEmailFormTest extends \Codeception\Test\Unit
    method _before (line 16) | public function _before()
    method testVerifyWrongToken (line 26) | public function testVerifyWrongToken()
    method testAlreadyActivatedToken (line 37) | public function testAlreadyActivatedToken()
    method testVerifyCorrectToken (line 44) | public function testVerifyCorrectToken()

FILE: frontend/web/js/app.js
  function resetForm (line 33) | function resetForm() {
  function initCommentForm (line 41) | function initCommentForm($form,
  function initComment (line 78) | function initComment($comment) {
  function initComments (line 268) | function initComments() {
  function onDeleteClick (line 276) | function onDeleteClick(ev) {
Condensed preview — 237 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (288K chars).
[
  {
    "path": ".bowerrc",
    "chars": 43,
    "preview": "{\n    \"directory\" : \"vendor/bower-asset\"\n}\n"
  },
  {
    "path": ".gitignore",
    "chars": 627,
    "preview": "# 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"
  },
  {
    "path": "LICENSE.md",
    "chars": 1523,
    "preview": "Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)\nAll rights reserved.\n\nRedistribution and use in source and"
  },
  {
    "path": "README.md",
    "chars": 2482,
    "preview": "<p align=\"center\">\n    <a href=\"#\" target=\"_blank\">\n        <img src=\"https://avatars0.githubusercontent.com/u/993323\" h"
  },
  {
    "path": "Vagrantfile",
    "chars": 2624,
    "preview": "require 'yaml'\nrequire 'fileutils'\n\nrequired_plugins = %w( vagrant-hostmanager vagrant-vbguest )\nrequired_plugins.each d"
  },
  {
    "path": "backend/Dockerfile",
    "chars": 163,
    "preview": "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' /e"
  },
  {
    "path": "backend/assets/AppAsset.php",
    "chars": 408,
    "preview": "<?php\n\nnamespace backend\\assets;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * Main backend application asset bundle.\n */\nclass AppA"
  },
  {
    "path": "backend/assets/TagsInputAsset.php",
    "chars": 565,
    "preview": "<?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\\AssetB"
  },
  {
    "path": "backend/codeception.yml",
    "chars": 313,
    "preview": "namespace: backend\\tests\nactor_suffix: Tester\npaths:\n    tests: tests\n    output: tests/_output\n    data: tests/_data\n  "
  },
  {
    "path": "backend/config/.gitignore",
    "chars": 69,
    "preview": "codeception-local.php\nmain-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "backend/config/bootstrap.php",
    "chars": 6,
    "preview": "<?php\n"
  },
  {
    "path": "backend/config/main.php",
    "chars": 1604,
    "preview": "<?php\n$params = array_merge(\n    require __DIR__ . '/../../common/config/params.php',\n    require __DIR__ . '/../../comm"
  },
  {
    "path": "backend/config/params.php",
    "chars": 59,
    "preview": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n];\n"
  },
  {
    "path": "backend/config/test.php",
    "chars": 327,
    "preview": "<?php\nreturn [\n    'id' => 'app-backend-tests',\n    'components' => [\n        'assetManager' => [\n            'basePath'"
  },
  {
    "path": "backend/controllers/CommentController.php",
    "chars": 3579,
    "preview": "<?php\n\nnamespace backend\\controllers;\n\nuse Yii;\nuse common\\models\\Comment;\nuse backend\\models\\CommentSearch;\nuse yii\\fil"
  },
  {
    "path": "backend/controllers/SiteController.php",
    "chars": 3287,
    "preview": "<?php\n\nnamespace backend\\controllers;\n\nuse common\\models\\Subscriber;\nuse common\\models\\Video;\nuse common\\models\\VideoVie"
  },
  {
    "path": "backend/controllers/VideoController.php",
    "chars": 3506,
    "preview": "<?php\n\nnamespace backend\\controllers;\n\nuse Yii;\nuse common\\models\\Video;\nuse yii\\data\\ActiveDataProvider;\nuse yii\\filter"
  },
  {
    "path": "backend/models/.gitkeep",
    "chars": 2,
    "preview": "*\n"
  },
  {
    "path": "backend/models/CommentSearch.php",
    "chars": 1951,
    "preview": "<?php\n\nnamespace backend\\models;\n\nuse yii\\base\\Model;\nuse yii\\data\\ActiveDataProvider;\nuse common\\models\\Comment;\n\n/**\n "
  },
  {
    "path": "backend/runtime/.gitignore",
    "chars": 13,
    "preview": "*\n!.gitignore"
  },
  {
    "path": "backend/tests/_bootstrap.php",
    "chars": 425,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\ndefined('YII_A"
  },
  {
    "path": "backend/tests/_data/.gitignore",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "backend/tests/_data/login_data.php",
    "chars": 433,
    "preview": "<?php\nreturn [\n    [\n        'username' => 'erau',\n        'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',\n        // "
  },
  {
    "path": "backend/tests/_output/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "backend/tests/_support/.gitignore",
    "chars": 11,
    "preview": "_generated\n"
  },
  {
    "path": "backend/tests/_support/FunctionalTester.php",
    "chars": 634,
    "preview": "<?php\nnamespace backend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)"
  },
  {
    "path": "backend/tests/_support/UnitTester.php",
    "chars": 622,
    "preview": "<?php\nnamespace backend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)"
  },
  {
    "path": "backend/tests/functional/LoginCest.php",
    "chars": 1021,
    "preview": "<?php\n\nnamespace backend\\tests\\functional;\n\nuse backend\\tests\\FunctionalTester;\nuse common\\fixtures\\UserFixture;\n\n/**\n *"
  },
  {
    "path": "backend/tests/functional/_bootstrap.php",
    "chars": 334,
    "preview": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and"
  },
  {
    "path": "backend/tests/functional.suite.yml",
    "chars": 103,
    "preview": "suite_namespace: backend\\tests\\functional\nactor: FunctionalTester\nmodules:\n    enabled:\n        - Yii2\n"
  },
  {
    "path": "backend/tests/unit/_bootstrap.php",
    "chars": 335,
    "preview": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and"
  },
  {
    "path": "backend/tests/unit.suite.yml",
    "chars": 54,
    "preview": "suite_namespace: backend\\tests\\unit\nactor: UnitTester\n"
  },
  {
    "path": "backend/views/comment/_comment_item.php",
    "chars": 2422,
    "preview": "<?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 $mod"
  },
  {
    "path": "backend/views/comment/_form.php",
    "chars": 841,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Com"
  },
  {
    "path": "backend/views/comment/_item.php",
    "chars": 493,
    "preview": "<?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 $mode"
  },
  {
    "path": "backend/views/comment/_search.php",
    "chars": 946,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\ActiveForm;\n\n/* @var $this yii\\web\\View */\n/* @var $model backend\\models\\Co"
  },
  {
    "path": "backend/views/comment/create.php",
    "chars": 418,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Comment */\n\n$this->title = 'Cre"
  },
  {
    "path": "backend/views/comment/index.php",
    "chars": 700,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\grid\\GridView;\n\n/* @var $this yii\\web\\View */\n/* @var $searchModel backend\\models\\C"
  },
  {
    "path": "backend/views/comment/update.php",
    "chars": 526,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Comment */\n\n$this->title = 'Upd"
  },
  {
    "path": "backend/views/comment/view.php",
    "chars": 1087,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\widgets\\DetailView;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Com"
  },
  {
    "path": "backend/views/layouts/_header.php",
    "chars": 889,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 9:20 AM\n */\n\nuse yii\\bootstrap4\\Nav;\nuse yii\\bootstrap4\\NavB"
  },
  {
    "path": "backend/views/layouts/_sidebar.php",
    "chars": 540,
    "preview": "<?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\\"
  },
  {
    "path": "backend/views/layouts/auth.php",
    "chars": 473,
    "preview": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse backend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\nus"
  },
  {
    "path": "backend/views/layouts/base.php",
    "chars": 1072,
    "preview": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse backend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\nus"
  },
  {
    "path": "backend/views/layouts/main.php",
    "chars": 518,
    "preview": "<?php\n\n/* @var $this \\yii\\web\\View */\n\n/* @var $content string */\n\nuse backend\\assets\\AppAsset;\nuse yii\\helpers\\Html;\nus"
  },
  {
    "path": "backend/views/site/error.php",
    "chars": 536,
    "preview": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $name string */\n/* @var $message string */\n/* @var $exception Exception */\n"
  },
  {
    "path": "backend/views/site/index.php",
    "chars": 2341,
    "preview": "<?php\n\n/* @var $this yii\\web\\View */\n/** @var $latestVideo \\common\\models\\Video */\n/** @var $numberOfView integer */\n/**"
  },
  {
    "path": "backend/views/site/login.php",
    "chars": 997,
    "preview": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $form yii\\bootstrap4\\ActiveForm */\n/* @var $model \\common\\models\\LoginForm "
  },
  {
    "path": "backend/views/video/_form.php",
    "chars": 2241,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\bootstrap4\\ActiveForm;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\"
  },
  {
    "path": "backend/views/video/_video_item.php",
    "chars": 768,
    "preview": "<?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 \\"
  },
  {
    "path": "backend/views/video/create.php",
    "chars": 1084,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Video */\n\n$this->title = 'Creat"
  },
  {
    "path": "backend/views/video/index.php",
    "chars": 1543,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\nuse yii\\grid\\GridView;\n\n/* @var $this yii\\web\\View */\n/* @var $dataProvider yii\\data\\Active"
  },
  {
    "path": "backend/views/video/update.php",
    "chars": 530,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $model common\\models\\Video */\n\n$this->title = 'Updat"
  },
  {
    "path": "backend/web/app.js",
    "chars": 174,
    "preview": "/**\n * Created by TheCodeholic on 4/17/2020.\n */\n$(function () {\n  'use strict';\n  $('#videoFile').change(ev => {\n    $("
  },
  {
    "path": "backend/web/assets/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "backend/web/css/site.css",
    "chars": 907,
    "preview": "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}\n"
  },
  {
    "path": "backend/web/tagsinput/tagsinput.css",
    "chars": 2233,
    "preview": "/*\n * bootstrap-tagsinput v0.8.0\n * \n */\n\n.bootstrap-tagsinput {\n  background-color: #fff;\n  border: 1px solid #ccc;\n  b"
  },
  {
    "path": "backend/web/tagsinput/tagsinput.js",
    "chars": 22654,
    "preview": "/*\n * bootstrap-tagsinput v0.8.0\n * \n */\n\n(function ($) {\n  \"use strict\";\n\n  var defaultOptions = {\n    tagClass: functi"
  },
  {
    "path": "codeception.yml",
    "chars": 167,
    "preview": "# global codeception file to run tests from all apps\ninclude:\n    - common\n    - frontend\n    - backend\npaths:\n    log: "
  },
  {
    "path": "common/codeception.yml",
    "chars": 312,
    "preview": "namespace: common\\tests\nactor_suffix: Tester\npaths:\n    tests: tests\n    output: tests/_output\n    data: tests/_data\n   "
  },
  {
    "path": "common/config/.gitignore",
    "chars": 69,
    "preview": "codeception-local.php\nmain-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "common/config/bootstrap.php",
    "chars": 253,
    "preview": "<?php\nYii::setAlias('@common', dirname(__DIR__));\nYii::setAlias('@frontend', dirname(dirname(__DIR__)) . '/frontend');\nY"
  },
  {
    "path": "common/config/main.php",
    "chars": 326,
    "preview": "<?php\nreturn [\n    'name' => 'FreeCodeTube',\n    'aliases' => [\n        '@bower' => '@vendor/bower-asset',\n        '@npm"
  },
  {
    "path": "common/config/params.php",
    "chars": 235,
    "preview": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n    'supportEmail' => 'support@example.com',\n    'senderEmail' ="
  },
  {
    "path": "common/config/test.php",
    "chars": 238,
    "preview": "<?php\nreturn [\n    'id' => 'app-common-tests',\n    'basePath' => dirname(__DIR__),\n    'components' => [\n        'user' "
  },
  {
    "path": "common/fixtures/UserFixture.php",
    "chars": 153,
    "preview": "<?php\nnamespace common\\fixtures;\n\nuse yii\\test\\ActiveFixture;\n\nclass UserFixture extends ActiveFixture\n{\n    public $mod"
  },
  {
    "path": "common/helpers/Html.php",
    "chars": 511,
    "preview": "<?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\\Ur"
  },
  {
    "path": "common/mail/emailVerify-html.php",
    "chars": 428,
    "preview": "<?php\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$verifyLink = Yii::$app-"
  },
  {
    "path": "common/mail/emailVerify-text.php",
    "chars": 289,
    "preview": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$verifyLink = Yii::$app->urlManager->createAbs"
  },
  {
    "path": "common/mail/layouts/html.php",
    "chars": 714,
    "preview": "<?php\nuse yii\\helpers\\Html;\n\n/* @var $this \\yii\\web\\View view component instance */\n/* @var $message \\yii\\mail\\MessageIn"
  },
  {
    "path": "common/mail/layouts/text.php",
    "chars": 341,
    "preview": "<?php\n\nuse yii\\helpers\\Html;\n\n/** @var \\yii\\web\\View $this view component instance */\n/** @var \\yii\\mail\\MessageInterfac"
  },
  {
    "path": "common/mail/mention-html.php",
    "chars": 429,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/15/2020\n * Time: 4:52 PM\n */\n/** @var $channel \\common\\models\\User */\n/** @v"
  },
  {
    "path": "common/mail/mention-text.php",
    "chars": 454,
    "preview": "<?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/"
  },
  {
    "path": "common/mail/passwordResetToken-html.php",
    "chars": 433,
    "preview": "<?php\nuse yii\\helpers\\Html;\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$resetLink = Yii::$app->"
  },
  {
    "path": "common/mail/passwordResetToken-text.php",
    "chars": 293,
    "preview": "<?php\n\n/* @var $this yii\\web\\View */\n/* @var $user common\\models\\User */\n\n$resetLink = Yii::$app->urlManager->createAbso"
  },
  {
    "path": "common/mail/subscriber-html.php",
    "chars": 328,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 10:27 AM\n */\n/** @var $channel \\common\\models\\User */\n/** @v"
  },
  {
    "path": "common/mail/subscriber-text.php",
    "chars": 272,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 10:27 AM\n */\n/** @var $channel \\common\\models\\User */\n/** @v"
  },
  {
    "path": "common/models/Comment.php",
    "chars": 3586,
    "preview": "<?php\n\nnamespace common\\models;\n\nuse Yii;\nuse yii\\behaviors\\BlameableBehavior;\nuse yii\\behaviors\\TimestampBehavior;\n\n/**"
  },
  {
    "path": "common/models/LoginForm.php",
    "chars": 1847,
    "preview": "<?php\nnamespace common\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n\n/**\n * Login form\n */\nclass LoginForm extends Model\n{\n    "
  },
  {
    "path": "common/models/Subscriber.php",
    "chars": 1900,
    "preview": "<?php\n\nnamespace common\\models;\n\nuse Yii;\n\n/**\n * This is the model class for table \"{{%subscriber}}\".\n *\n * @property i"
  },
  {
    "path": "common/models/User.php",
    "chars": 5480,
    "preview": "<?php\nnamespace common\\models;\n\nuse Yii;\nuse yii\\base\\NotSupportedException;\nuse yii\\behaviors\\TimestampBehavior;\nuse yi"
  },
  {
    "path": "common/models/Video.php",
    "chars": 6726,
    "preview": "<?php\n\nnamespace common\\models;\n\nuse Imagine\\Image\\Box;\nuse Yii;\nuse yii\\behaviors\\BlameableBehavior;\nuse yii\\behaviors\\"
  },
  {
    "path": "common/models/VideoLike.php",
    "chars": 2096,
    "preview": "<?php\n\nnamespace common\\models;\n\nuse Yii;\n\n/**\n * This is the model class for table \"{{%video_like}}\".\n *\n * @property i"
  },
  {
    "path": "common/models/VideoView.php",
    "chars": 1970,
    "preview": "<?php\n\nnamespace common\\models;\n\nuse Yii;\n\n/**\n * This is the model class for table \"{{%video_view}}\".\n *\n * @property i"
  },
  {
    "path": "common/models/query/CommentQuery.php",
    "chars": 1205,
    "preview": "<?php\n\nnamespace common\\models\\query;\n\nuse common\\models\\Comment;\nuse common\\models\\Video;\n\n/**\n * This is the ActiveQue"
  },
  {
    "path": "common/models/query/SubscriberQuery.php",
    "chars": 638,
    "preview": "<?php\n\nnamespace common\\models\\query;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\Subscriber]].\n *\n * @se"
  },
  {
    "path": "common/models/query/VideoLikeQuery.php",
    "chars": 1063,
    "preview": "<?php\n\nnamespace common\\models\\query;\n\nuse common\\models\\VideoLike;\n\n/**\n * This is the ActiveQuery class for [[\\common\\"
  },
  {
    "path": "common/models/query/VideoQuery.php",
    "chars": 1138,
    "preview": "<?php\n\nnamespace common\\models\\query;\n\nuse common\\models\\Video;\n\n/**\n * This is the ActiveQuery class for [[\\common\\mode"
  },
  {
    "path": "common/models/query/VideoViewQuery.php",
    "chars": 633,
    "preview": "<?php\n\nnamespace common\\models\\query;\n\n/**\n * This is the ActiveQuery class for [[\\common\\models\\VideoView]].\n *\n * @see"
  },
  {
    "path": "common/tests/_bootstrap.php",
    "chars": 350,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\ndefined('YII_A"
  },
  {
    "path": "common/tests/_data/user.php",
    "chars": 446,
    "preview": "<?php\n\nreturn [\n    [\n        'username' => 'bayer.hudson',\n        'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR',\n  "
  },
  {
    "path": "common/tests/_output/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "common/tests/_support/.gitignore",
    "chars": 11,
    "preview": "_generated\n"
  },
  {
    "path": "common/tests/_support/UnitTester.php",
    "chars": 621,
    "preview": "<?php\nnamespace common\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text)\n"
  },
  {
    "path": "common/tests/unit/models/LoginFormTest.php",
    "chars": 1737,
    "preview": "<?php\n\nnamespace common\\tests\\unit\\models;\n\nuse Yii;\nuse common\\models\\LoginForm;\nuse common\\fixtures\\UserFixture;\n\n/**\n"
  },
  {
    "path": "common/tests/unit.suite.yml",
    "chars": 135,
    "preview": "suite_namespace: common\\tests\\unit\nactor: UnitTester\nbootstrap: false\nmodules:\n    enabled:\n        - Yii2:\n            "
  },
  {
    "path": "common/widgets/Alert.php",
    "chars": 2326,
    "preview": "<?php\nnamespace common\\widgets;\n\nuse Yii;\n\n/**\n * Alert widget renders a message from session flash. All flash messages "
  },
  {
    "path": "composer.json",
    "chars": 1524,
    "preview": "{\n    \"name\": \"yiisoft/yii2-app-advanced\",\n    \"description\": \"Yii 2 Advanced Project Template\",\n    \"keywords\": [\"yii2\""
  },
  {
    "path": "console/config/.gitignore",
    "chars": 47,
    "preview": "main-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "console/config/bootstrap.php",
    "chars": 6,
    "preview": "<?php\n"
  },
  {
    "path": "console/config/main.php",
    "chars": 949,
    "preview": "<?php\n$params = array_merge(\n    require __DIR__ . '/../../common/config/params.php',\n    require __DIR__ . '/../../comm"
  },
  {
    "path": "console/config/params.php",
    "chars": 59,
    "preview": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n];\n"
  },
  {
    "path": "console/config/test.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n\n];"
  },
  {
    "path": "console/controllers/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "console/migrations/m130524_201442_init.php",
    "chars": 1100,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\nclass m130524_201442_init extends Migration\n{\n    public function up()\n    {\n        $tabl"
  },
  {
    "path": "console/migrations/m190124_110200_add_verification_token_column_to_user_table.php",
    "chars": 355,
    "preview": "<?php\n\nuse \\yii\\db\\Migration;\n\nclass m190124_110200_add_verification_token_column_to_user_table extends Migration\n{\n    "
  },
  {
    "path": "console/migrations/m200417_054237_create_videos_table.php",
    "chars": 1759,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%video}}`.\n * Has foreign keys to the tables:\n *\n "
  },
  {
    "path": "console/migrations/m200418_050048_create_video_view_table.php",
    "chars": 2211,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%video_view}}`.\n * Has foreign keys to the tables:"
  },
  {
    "path": "console/migrations/m200418_051244_create_video_like_table.php",
    "chars": 2263,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%video_like}}`.\n * Has foreign keys to the tables:"
  },
  {
    "path": "console/migrations/m200418_060320_create_subscriber_table.php",
    "chars": 2209,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%subscriber}}`.\n * Has foreign keys to the tables:"
  },
  {
    "path": "console/migrations/m200418_064142_create_fulltext_index_on_video.php",
    "chars": 790,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Class m200418_064142_create_fulltext_index_on_video\n */\nclass m200418_064142_create"
  },
  {
    "path": "console/migrations/m201112_042619_create_comment_table.php",
    "chars": 3163,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles the creation of table `{{%comment}}`.\n * Has foreign keys to the tables:\n *"
  },
  {
    "path": "console/migrations/m201115_124738_add_mention_column_to_comment_table.php",
    "chars": 471,
    "preview": "<?php\n\nuse yii\\db\\Migration;\n\n/**\n * Handles adding columns to table `{{%comment}}`.\n */\nclass m201115_124738_add_mentio"
  },
  {
    "path": "console/models/.gitkeep",
    "chars": 2,
    "preview": "*\n"
  },
  {
    "path": "console/runtime/.gitignore",
    "chars": 13,
    "preview": "*\n!.gitignore"
  },
  {
    "path": "docker-compose.yml",
    "chars": 864,
    "preview": "version: '3.2'\n\nservices:\n\n  frontend:\n    build: frontend\n    ports:\n      - 20080:80\n    volumes:\n      # Re-use local"
  },
  {
    "path": "environments/dev/backend/config/codeception-local.php",
    "chars": 293,
    "preview": "<?php\n\nreturn yii\\helpers\\ArrayHelper::merge(\n    require dirname(dirname(__DIR__)) . '/common/config/codeception-local."
  },
  {
    "path": "environments/dev/backend/config/main-local.php",
    "chars": 570,
    "preview": "<?php\n\n$config = [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following "
  },
  {
    "path": "environments/dev/backend/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/backend/config/test-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/backend/web/index-test.php",
    "chars": 1023,
    "preview": "<?php\n\n// NOTE: Make sure this file is not accessible when deployed to production\nif (!in_array(@$_SERVER['REMOTE_ADDR']"
  },
  {
    "path": "environments/dev/backend/web/index.php",
    "chars": 613,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR_"
  },
  {
    "path": "environments/dev/backend/web/robots.txt",
    "chars": 25,
    "preview": "User-agent: *\nDisallow: /"
  },
  {
    "path": "environments/dev/common/config/codeception-local.php",
    "chars": 454,
    "preview": "<?php\n\nreturn yii\\helpers\\ArrayHelper::merge(\n    require __DIR__ . '/main.php',\n    require __DIR__ . '/main-local.php'"
  },
  {
    "path": "environments/dev/common/config/main-local.php",
    "chars": 634,
    "preview": "<?php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => "
  },
  {
    "path": "environments/dev/common/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/common/config/test-local.php",
    "chars": 146,
    "preview": "<?php\nreturn [\n    'components' => [\n        'db' => [\n            'dsn' => 'mysql:host=localhost;dbname=yii2advanced_te"
  },
  {
    "path": "environments/dev/console/config/main-local.php",
    "chars": 107,
    "preview": "<?php\nreturn [\n    'bootstrap' => ['gii'],\n    'modules' => [\n        'gii' => 'yii\\gii\\Module',\n    ],\n];\n"
  },
  {
    "path": "environments/dev/console/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/console/config/test-local.php",
    "chars": 17,
    "preview": "<?php\nreturn [\n];"
  },
  {
    "path": "environments/dev/frontend/config/codeception-local.php",
    "chars": 293,
    "preview": "<?php\n\nreturn yii\\helpers\\ArrayHelper::merge(\n    require dirname(dirname(__DIR__)) . '/common/config/codeception-local."
  },
  {
    "path": "environments/dev/frontend/config/main-local.php",
    "chars": 570,
    "preview": "<?php\n\n$config = [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following "
  },
  {
    "path": "environments/dev/frontend/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/frontend/config/test-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/dev/frontend/web/index-test.php",
    "chars": 1024,
    "preview": "<?php\n\n// NOTE: Make sure this file is not accessible when deployed to production\nif (!in_array(@$_SERVER['REMOTE_ADDR']"
  },
  {
    "path": "environments/dev/frontend/web/index.php",
    "chars": 613,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'dev');\n\nrequire __DIR_"
  },
  {
    "path": "environments/dev/frontend/web/robots.txt",
    "chars": 25,
    "preview": "User-agent: *\nDisallow: /"
  },
  {
    "path": "environments/dev/yii",
    "chars": 717,
    "preview": "#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefi"
  },
  {
    "path": "environments/dev/yii_test",
    "chars": 928,
    "preview": "#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefi"
  },
  {
    "path": "environments/dev/yii_test.bat",
    "chars": 328,
    "preview": "@echo off\n\nrem -------------------------------------------------------------\nrem  Yii command line bootstrap script for "
  },
  {
    "path": "environments/index.php",
    "chars": 2029,
    "preview": "<?php\n/**\n * The manifest of files that are local to specific environment.\n * This file returns a list of environments t"
  },
  {
    "path": "environments/prod/backend/config/main-local.php",
    "chars": 235,
    "preview": "<?php\nreturn [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following (if "
  },
  {
    "path": "environments/prod/backend/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/backend/web/index.php",
    "chars": 615,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\ndefined('YII_ENV') or define('YII_ENV', 'prod');\n\nrequire __DI"
  },
  {
    "path": "environments/prod/backend/web/robots.txt",
    "chars": 25,
    "preview": "User-agent: *\nDisallow: /"
  },
  {
    "path": "environments/prod/common/config/main-local.php",
    "chars": 406,
    "preview": "<?php\nreturn [\n    'components' => [\n        'db' => [\n            'class' => 'yii\\db\\Connection',\n            'dsn' => "
  },
  {
    "path": "environments/prod/common/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/console/config/main-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/console/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/frontend/config/main-local.php",
    "chars": 235,
    "preview": "<?php\nreturn [\n    'components' => [\n        'request' => [\n            // !!! insert a secret key in the following (if "
  },
  {
    "path": "environments/prod/frontend/config/params-local.php",
    "chars": 18,
    "preview": "<?php\nreturn [\n];\n"
  },
  {
    "path": "environments/prod/frontend/web/index.php",
    "chars": 615,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\ndefined('YII_ENV') or define('YII_ENV', 'prod');\n\nrequire __DI"
  },
  {
    "path": "environments/prod/frontend/web/robots.txt",
    "chars": 22,
    "preview": "User-agent: *\nAllow: /"
  },
  {
    "path": "environments/prod/yii",
    "chars": 719,
    "preview": "#!/usr/bin/env php\n<?php\n/**\n * Yii console bootstrap file.\n */\n\ndefined('YII_DEBUG') or define('YII_DEBUG', false);\ndef"
  },
  {
    "path": "frontend/Dockerfile",
    "chars": 164,
    "preview": "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' /"
  },
  {
    "path": "frontend/assets/AppAsset.php",
    "chars": 413,
    "preview": "<?php\n\nnamespace frontend\\assets;\n\nuse yii\\web\\AssetBundle;\n\n/**\n * Main frontend application asset bundle.\n */\nclass Ap"
  },
  {
    "path": "frontend/codeception.yml",
    "chars": 314,
    "preview": "namespace: frontend\\tests\nactor_suffix: Tester\npaths:\n    tests: tests\n    output: tests/_output\n    data: tests/_data\n "
  },
  {
    "path": "frontend/config/.gitignore",
    "chars": 69,
    "preview": "codeception-local.php\nmain-local.php\nparams-local.php\ntest-local.php\n"
  },
  {
    "path": "frontend/config/bootstrap.php",
    "chars": 6,
    "preview": "<?php\n"
  },
  {
    "path": "frontend/config/main.php",
    "chars": 1660,
    "preview": "<?php\n$params = array_merge(\n    require __DIR__ . '/../../common/config/params.php',\n    require __DIR__ . '/../../comm"
  },
  {
    "path": "frontend/config/params.php",
    "chars": 59,
    "preview": "<?php\nreturn [\n    'adminEmail' => 'admin@example.com',\n];\n"
  },
  {
    "path": "frontend/config/test.php",
    "chars": 328,
    "preview": "<?php\nreturn [\n    'id' => 'app-frontend-tests',\n    'components' => [\n        'assetManager' => [\n            'basePath"
  },
  {
    "path": "frontend/controllers/ChannelController.php",
    "chars": 2703,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/18/2020\n * Time: 9:48 AM\n */\n\nnamespace frontend\\controllers;\n\n\nuse common\\mo"
  },
  {
    "path": "frontend/controllers/CommentController.php",
    "chars": 6517,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 11/12/2020\n * Time: 8:58 AM\n */\n\nnamespace frontend\\controllers;\n\n\nuse common\\m"
  },
  {
    "path": "frontend/controllers/SiteController.php",
    "chars": 6031,
    "preview": "<?php\nnamespace frontend\\controllers;\n\nuse frontend\\models\\ResendVerificationEmailForm;\nuse frontend\\models\\VerifyEmailF"
  },
  {
    "path": "frontend/controllers/VideoController.php",
    "chars": 5358,
    "preview": "<?php\n/**\n * User: TheCodeholic\n * Date: 4/17/2020\n * Time: 11:56 AM\n */\n\nnamespace frontend\\controllers;\n\n\nuse common\\m"
  },
  {
    "path": "frontend/models/PasswordResetRequestForm.php",
    "chars": 1675,
    "preview": "<?php\nnamespace frontend\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse common\\models\\User;\n\n/**\n * Password reset request fo"
  },
  {
    "path": "frontend/models/ResendVerificationEmailForm.php",
    "chars": 1402,
    "preview": "<?php\n\n\nnamespace frontend\\models;\n\nuse Yii;\nuse common\\models\\User;\nuse yii\\base\\Model;\n\nclass ResendVerificationEmailF"
  },
  {
    "path": "frontend/models/ResetPasswordForm.php",
    "chars": 1468,
    "preview": "<?php\nnamespace frontend\\models;\n\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\nuse common\\models\\User;\n\n/*"
  },
  {
    "path": "frontend/models/SignupForm.php",
    "chars": 2123,
    "preview": "<?php\nnamespace frontend\\models;\n\nuse Yii;\nuse yii\\base\\Model;\nuse common\\models\\User;\n\n/**\n * Signup form\n */\nclass Sig"
  },
  {
    "path": "frontend/models/VerifyEmailForm.php",
    "chars": 1252,
    "preview": "<?php\n\nnamespace frontend\\models;\n\nuse common\\models\\User;\nuse yii\\base\\InvalidArgumentException;\nuse yii\\base\\Model;\n\nc"
  },
  {
    "path": "frontend/runtime/.gitignore",
    "chars": 13,
    "preview": "*\n!.gitignore"
  },
  {
    "path": "frontend/tests/_bootstrap.php",
    "chars": 425,
    "preview": "<?php\ndefined('YII_DEBUG') or define('YII_DEBUG', true);\ndefined('YII_ENV') or define('YII_ENV', 'test');\ndefined('YII_A"
  },
  {
    "path": "frontend/tests/_data/login_data.php",
    "chars": 838,
    "preview": "<?php\nreturn [\n    [\n        'username' => 'erau',\n        'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',\n        // "
  },
  {
    "path": "frontend/tests/_data/user.php",
    "chars": 1700,
    "preview": "<?php\n\nreturn [\n    [\n        'username' => 'okirlin',\n        'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv',\n       "
  },
  {
    "path": "frontend/tests/_output/.gitignore",
    "chars": 14,
    "preview": "*\n!.gitignore\n"
  },
  {
    "path": "frontend/tests/_support/.gitignore",
    "chars": 11,
    "preview": "_generated\n"
  },
  {
    "path": "frontend/tests/_support/FunctionalTester.php",
    "chars": 811,
    "preview": "<?php\nnamespace frontend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text"
  },
  {
    "path": "frontend/tests/_support/UnitTester.php",
    "chars": 623,
    "preview": "<?php\nnamespace frontend\\tests;\n\n/**\n * Inherited Methods\n * @method void wantToTest($text)\n * @method void wantTo($text"
  },
  {
    "path": "frontend/tests/acceptance/HomeCest.php",
    "chars": 426,
    "preview": "<?php\nnamespace frontend\\tests\\acceptance;\n\nuse frontend\\tests\\AcceptanceTester;\nuse yii\\helpers\\Url;\n\nclass HomeCest\n{\n"
  },
  {
    "path": "frontend/tests/acceptance/_bootstrap.php",
    "chars": 333,
    "preview": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and"
  },
  {
    "path": "frontend/tests/acceptance.suite.yml.example",
    "chars": 217,
    "preview": "suite_namespace: frontend\\tests\\acceptance\nactor: AcceptanceTester\nmodules:\n    enabled:\n        - WebDriver:\n          "
  },
  {
    "path": "frontend/tests/functional/AboutCest.php",
    "chars": 235,
    "preview": "<?php\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\nclass AboutCest\n{\n    public function "
  },
  {
    "path": "frontend/tests/functional/ContactCest.php",
    "chars": 2049,
    "preview": "<?php\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\n/* @var $scenario \\Codeception\\Scenari"
  },
  {
    "path": "frontend/tests/functional/HomeCest.php",
    "chars": 344,
    "preview": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\nclass HomeCest\n{\n    public function "
  },
  {
    "path": "frontend/tests/functional/LoginCest.php",
    "chars": 1844,
    "preview": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\nuse common\\fixtures\\UserFixture;\n\nclas"
  },
  {
    "path": "frontend/tests/functional/ResendVerificationEmailCest.php",
    "chars": 2433,
    "preview": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\tests\\FunctionalTester;\n\nclas"
  },
  {
    "path": "frontend/tests/functional/SignupCest.php",
    "chars": 1778,
    "preview": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse frontend\\tests\\FunctionalTester;\n\nclass SignupCest\n{\n    protected $for"
  },
  {
    "path": "frontend/tests/functional/VerifyEmailCest.php",
    "chars": 2064,
    "preview": "<?php\n\nnamespace frontend\\tests\\functional;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\tests\\FunctionalTester;\n\nclas"
  },
  {
    "path": "frontend/tests/functional/_bootstrap.php",
    "chars": 334,
    "preview": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and"
  },
  {
    "path": "frontend/tests/functional.suite.yml",
    "chars": 125,
    "preview": "suite_namespace: frontend\\tests\\functional\nactor: FunctionalTester\nmodules:\n    enabled:\n        - Filesystem\n        - "
  },
  {
    "path": "frontend/tests/unit/_bootstrap.php",
    "chars": 335,
    "preview": "<?php\n/**\n * Here you can initialize variables via \\Codeception\\Util\\Fixtures class\n * to store data in global array and"
  },
  {
    "path": "frontend/tests/unit/models/ContactFormTest.php",
    "chars": 1248,
    "preview": "<?php\nnamespace frontend\\tests\\unit\\models;\n\nuse frontend\\models\\ContactForm;\nuse yii\\mail\\MessageInterface;\n\nclass Cont"
  },
  {
    "path": "frontend/tests/unit/models/PasswordResetRequestFormTest.php",
    "chars": 1766,
    "preview": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\nuse Yii;\nuse frontend\\models\\PasswordResetRequestForm;\nuse common\\fixtures"
  },
  {
    "path": "frontend/tests/unit/models/ResendVerificationEmailFormTest.php",
    "chars": 2494,
    "preview": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\n\nuse Codeception\\Test\\Unit;\nuse common\\fixtures\\UserFixture;\nuse frontend\\"
  },
  {
    "path": "frontend/tests/unit/models/ResetPasswordFormTest.php",
    "chars": 1102,
    "preview": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\ResetPasswordForm;\n\nc"
  },
  {
    "path": "frontend/tests/unit/models/SignupFormTest.php",
    "chars": 2161,
    "preview": "<?php\nnamespace frontend\\tests\\unit\\models;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\SignupForm;\n\nclass Sig"
  },
  {
    "path": "frontend/tests/unit/models/VerifyEmailFormTest.php",
    "chars": 1582,
    "preview": "<?php\n\nnamespace frontend\\tests\\unit\\models;\n\nuse common\\fixtures\\UserFixture;\nuse frontend\\models\\VerifyEmailForm;\n\ncla"
  },
  {
    "path": "frontend/tests/unit.suite.yml",
    "chars": 152,
    "preview": "suite_namespace: frontend\\tests\\unit\nactor: UnitTester\nmodules:\n    enabled:\n        - Yii2:\n            part: [orm, ema"
  },
  {
    "path": "frontend/views/channel/_subscribe.php",
    "chars": 473,
    "preview": "<?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"
  }
]

// ... and 37 more files (download for full content)

About this extraction

This page contains the full source code of the thecodeholic/Yii2-Youtube-Clone GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 237 files (254.7 KB), approximately 71.3k tokens, and a symbol index with 310 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!