commit fdf3e1e602b80c232c50845479d5a968d5a46ea6 Author: denis Date: Sat May 6 20:40:02 2023 +0300 first commit diff --git a/.bowerrc b/.bowerrc new file mode 100755 index 0000000..a39b5b0 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory" : "vendor/bower-asset" +} diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..3cafba1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# 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 diff --git a/LICENSE.md b/LICENSE.md new file mode 100755 index 0000000..ee872b9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +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. diff --git a/README.md b/README.md new file mode 100755 index 0000000..68d0841 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +

+ + + +

Yii 2 Advanced Project Template

+
+

+ +Yii 2 Advanced Project Template is a skeleton [Yii 2](http://www.yiiframework.com/) application best for +developing complex Web applications with multiple tiers. + +The template includes three tiers: front end, back end, and console, each of which +is a separate Yii application. + +The template is designed to work in a team development environment. It supports +deploying the application in different environments. + +Documentation is at [docs/guide/README.md](docs/guide/README.md). + +[![Latest Stable Version](https://img.shields.io/packagist/v/yiisoft/yii2-app-advanced.svg)](https://packagist.org/packages/yiisoft/yii2-app-advanced) +[![Total Downloads](https://img.shields.io/packagist/dt/yiisoft/yii2-app-advanced.svg)](https://packagist.org/packages/yiisoft/yii2-app-advanced) +[![build](https://github.com/yiisoft/yii2-app-advanced/workflows/build/badge.svg)](https://github.com/yiisoft/yii2-app-advanced/actions?query=workflow%3Abuild) + +DIRECTORY STRUCTURE +------------------- + +``` +common + config/ contains shared configurations + mail/ contains view files for e-mails + models/ contains model classes used in both backend and frontend + tests/ contains tests for common classes +console + config/ contains console configurations + controllers/ contains console controllers (commands) + migrations/ contains database migrations + models/ contains console-specific model classes + runtime/ contains files generated during runtime +backend + assets/ contains application assets such as JavaScript and CSS + config/ contains backend configurations + controllers/ contains Web controller classes + models/ contains backend-specific model classes + runtime/ contains files generated during runtime + tests/ contains tests for backend application + views/ contains view files for the Web application + web/ contains the entry script and Web resources +frontend + assets/ contains application assets such as JavaScript and CSS + config/ contains frontend configurations + controllers/ contains Web controller classes + models/ contains frontend-specific model classes + runtime/ contains files generated during runtime + tests/ contains tests for frontend application + views/ contains view files for the Web application + web/ contains the entry script and Web resources + widgets/ contains frontend widgets +vendor/ contains dependent 3rd-party packages +environments/ contains environment-based overrides +``` diff --git a/Vagrantfile b/Vagrantfile new file mode 100755 index 0000000..3940bfb --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,88 @@ +require 'yaml' +require 'fileutils' + +required_plugins_installed = nil +required_plugins = %w( vagrant-hostmanager vagrant-vbguest ) +required_plugins.each do |plugin| + unless Vagrant.has_plugin? plugin + system "vagrant plugin install #{plugin}" + required_plugins_installed = true + end +end + +# IF plugin[s] was just installed - restart required +if required_plugins_installed + # Get CLI command[s] and call again + system 'vagrant' + ARGV.to_s.gsub(/\[\"|\", \"|\"\]/, ' ') + exit +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-18.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'], options['ip']] + 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 diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100755 index 0000000..0515d35 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,4 @@ +FROM yiisoftware/yii2-php:8.1-apache + +# Change document root for Apache +RUN sed -i -e 's|/app/web|/app/backend/web|g' /etc/apache2/sites-available/000-default.conf diff --git a/backend/assets/AppAsset.php b/backend/assets/AppAsset.php new file mode 100755 index 0000000..7494859 --- /dev/null +++ b/backend/assets/AppAsset.php @@ -0,0 +1,23 @@ + '/secure', + 'id' => 'app-backend', + 'basePath' => dirname(__DIR__), + 'controllerNamespace' => 'backend\controllers', + 'bootstrap' => ['log'], + 'modules' => [ + 'news' => [ + 'class' => 'backend\modules\news\News', + ], + + 'profile' => [ + 'class' => 'app\modules\profile\Profile', + ], + ], + 'components' => [ + 'request' => [ + 'csrfParam' => '_csrf-backend', + 'baseUrl' => '/secure', + ], + 'user' => [ + 'identityClass' => 'common\models\User', + 'enableAutoLogin' => true, + 'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true], + ], + 'session' => [ + // this is the name of the session cookie used for login on the backend + 'name' => 'advanced-backend', + ], + 'log' => [ + 'traceLevel' => YII_DEBUG ? 3 : 0, + 'targets' => [ + [ + 'class' => \yii\log\FileTarget::class, + 'levels' => ['error', 'warning'], + ], + ], + ], + 'errorHandler' => [ + 'errorAction' => 'site/error', + ], + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'rules' => [ + ], + ], + ], + 'params' => $params, +]; diff --git a/backend/config/params.php b/backend/config/params.php new file mode 100755 index 0000000..7f754b9 --- /dev/null +++ b/backend/config/params.php @@ -0,0 +1,4 @@ + 'admin@example.com', +]; diff --git a/backend/config/test.php b/backend/config/test.php new file mode 100755 index 0000000..411b05f --- /dev/null +++ b/backend/config/test.php @@ -0,0 +1,15 @@ + 'app-backend-tests', + 'components' => [ + 'assetManager' => [ + 'basePath' => __DIR__ . '/../web/assets', + ], + 'urlManager' => [ + 'showScriptName' => true, + ], + 'request' => [ + 'cookieValidationKey' => 'test', + ], + ], +]; diff --git a/backend/controllers/SiteController.php b/backend/controllers/SiteController.php new file mode 100755 index 0000000..d7c7b68 --- /dev/null +++ b/backend/controllers/SiteController.php @@ -0,0 +1,105 @@ + [ + 'class' => AccessControl::class, + 'rules' => [ + [ + 'actions' => ['login', 'error'], + 'allow' => true, + ], + [ + 'actions' => ['logout', 'index'], + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'logout' => ['post'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function actions() + { + return [ + 'error' => [ + 'class' => \yii\web\ErrorAction::class, + ], + ]; + } + + /** + * Displays homepage. + * + * @return string + */ + public function actionIndex() + { + return $this->render('index'); + } + + /** + * Login action. + * + * @return string|Response + */ + public function actionLogin() + { + if (!Yii::$app->user->isGuest) { + return $this->goHome(); + } + + $this->layout = 'blank'; + + $model = new LoginForm(); + if ($model->load(Yii::$app->request->post()) && $model->login()) { + return $this->goBack(); + } + + $model->password = ''; + + return $this->render('login', [ + 'model' => $model, + ]); + } + + /** + * Logout action. + * + * @return Response + */ + public function actionLogout() + { + Yii::$app->user->logout(); + + return $this->goHome(); + } +} diff --git a/backend/models/.gitkeep b/backend/models/.gitkeep new file mode 100755 index 0000000..72e8ffc --- /dev/null +++ b/backend/models/.gitkeep @@ -0,0 +1 @@ +* diff --git a/backend/modules/news/News.php b/backend/modules/news/News.php new file mode 100755 index 0000000..89127ff --- /dev/null +++ b/backend/modules/news/News.php @@ -0,0 +1,24 @@ + [ + 'class' => AccessControl::class, + 'rules' => [ + [ + 'actions' => ['login', 'error'], + 'allow' => true, + ], + [ + 'actions' => ['logout', 'index'], + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ] + ); + } + /** + * Renders the index view for the module + * @return string + */ + public function actionIndex() + { + return $this->render('index'); + } +} diff --git a/backend/modules/news/controllers/NewsController.php b/backend/modules/news/controllers/NewsController.php new file mode 100755 index 0000000..b064ca2 --- /dev/null +++ b/backend/modules/news/controllers/NewsController.php @@ -0,0 +1,148 @@ + [ + 'class' => AccessControl::class, + 'rules' => [ + [ + 'actions' => ['login', 'error'], + 'allow' => true, + ], + [ + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ] + ); + } + + /** + * Lists all News models. + * + * @return string + */ + public function actionIndex() + { + $searchModel = new NewsSearch(); + $dataProvider = $searchModel->search($this->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single News model. + * @param int $id ID + * @return string + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new News model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return string|\yii\web\Response + */ + public function actionCreate() + { + $model = new News(); + + if ($this->request->isPost) { + if ($model->load($this->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + } else { + $model->loadDefaultValues(); + } + + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing News model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param int $id ID + * @return string|\yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing News model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param int $id ID + * @return \yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the News model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param int $id ID + * @return News the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = News::findOne(['id' => $id])) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested page does not exist.'); + } +} diff --git a/backend/modules/news/models/NewsSearch.php b/backend/modules/news/models/NewsSearch.php new file mode 100755 index 0000000..164254c --- /dev/null +++ b/backend/modules/news/models/NewsSearch.php @@ -0,0 +1,70 @@ + $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, + ]); + + $query->andFilterWhere(['like', 'title', $this->title]) + ->andFilterWhere(['like', 'text', $this->text]) + ->andFilterWhere(['like', 'slug', $this->slug]); + + return $dataProvider; + } +} diff --git a/backend/modules/news/views/default/index.php b/backend/modules/news/views/default/index.php new file mode 100755 index 0000000..b498126 --- /dev/null +++ b/backend/modules/news/views/default/index.php @@ -0,0 +1,12 @@ +
+

context->action->uniqueId ?>

+

+ This is the view content for action "context->action->id ?>". + The action belongs to the controller "context) ?>" + in the "context->module->id ?>" module. +

+

+ You may customize this page by editing the following file:
+ +

+
diff --git a/backend/modules/news/views/news/_form.php b/backend/modules/news/views/news/_form.php new file mode 100755 index 0000000..1b27add --- /dev/null +++ b/backend/modules/news/views/news/_form.php @@ -0,0 +1,31 @@ + + +
+ + ['enctype' => 'multipart/form-data']]); ?> + + field($model, 'title')->textInput(['maxlength' => true]) ?> + + field($model, 'text')->widget(\asmoday74\ckeditor5\EditorClassic::class, [ + 'clientOptions' => [ + 'language' => 'en', + ] + ]) ?> + + field($model, 'slug')->textInput(['maxlength' => true]) ?> + +
+ 'btn btn-success']) ?> +
+ + + +
diff --git a/backend/modules/news/views/news/_search.php b/backend/modules/news/views/news/_search.php new file mode 100755 index 0000000..78a9848 --- /dev/null +++ b/backend/modules/news/views/news/_search.php @@ -0,0 +1,33 @@ + + + diff --git a/backend/modules/news/views/news/create.php b/backend/modules/news/views/news/create.php new file mode 100755 index 0000000..05b9d01 --- /dev/null +++ b/backend/modules/news/views/news/create.php @@ -0,0 +1,20 @@ +title = 'Create News'; +$this->params['breadcrumbs'][] = ['label' => 'News', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/news/views/news/index.php b/backend/modules/news/views/news/index.php new file mode 100755 index 0000000..d9562af --- /dev/null +++ b/backend/modules/news/views/news/index.php @@ -0,0 +1,46 @@ +title = 'News'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ +

+ 'btn btn-success']) ?> +

+ + render('_search', ['model' => $searchModel]); ?> + + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + ['class' => 'yii\grid\SerialColumn'], + + 'id', + 'title', + 'text:ntext', + 'slug', + [ + 'class' => ActionColumn::className(), + 'urlCreator' => function ($action, News $model, $key, $index, $column) { + return Url::toRoute([$action, 'id' => $model->id]); + } + ], + ], + ]); ?> + + +
diff --git a/backend/modules/news/views/news/update.php b/backend/modules/news/views/news/update.php new file mode 100755 index 0000000..5ec79e8 --- /dev/null +++ b/backend/modules/news/views/news/update.php @@ -0,0 +1,21 @@ +title = 'Update News: ' . $model->title; +$this->params['breadcrumbs'][] = ['label' => 'News', 'url' => ['index']]; +$this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]]; +$this->params['breadcrumbs'][] = 'Update'; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/news/views/news/view.php b/backend/modules/news/views/news/view.php new file mode 100755 index 0000000..2a592ec --- /dev/null +++ b/backend/modules/news/views/news/view.php @@ -0,0 +1,39 @@ +title = $model->title; +$this->params['breadcrumbs'][] = ['label' => 'News', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +\yii\web\YiiAsset::register($this); +?> +
+ +

title) ?>

+ +

+ $model->id], ['class' => 'btn btn-primary']) ?> + $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Are you sure you want to delete this item?', + 'method' => 'post', + ], + ]) ?> +

+ + $model, + 'attributes' => [ + 'id', + 'title', + 'text:ntext', + 'slug', + ], + ]) ?> + +
diff --git a/backend/modules/profile/Profile.php b/backend/modules/profile/Profile.php new file mode 100755 index 0000000..28c30f9 --- /dev/null +++ b/backend/modules/profile/Profile.php @@ -0,0 +1,24 @@ +render('index'); + } +} diff --git a/backend/modules/profile/controllers/ProfileController.php b/backend/modules/profile/controllers/ProfileController.php new file mode 100755 index 0000000..5c4be09 --- /dev/null +++ b/backend/modules/profile/controllers/ProfileController.php @@ -0,0 +1,166 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + 'access' => [ + 'class' => AccessControl::class, + 'rules' => [ + [ + 'actions' => ['login', 'error'], + 'allow' => true, + ], + [ + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + ] + ); + } + + /** + * Lists all Profile models. + * + * @return string + */ + public function actionIndex() + { + $searchModel = new ProfileSearch(); + $dataProvider = $searchModel->search($this->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single Profile model. + * @param int $id ID + * @return string + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Profile model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return string|\yii\web\Response + */ + public function actionCreate() + { + $model = new Profile(); + $service = UserService::run(); + $texts = $service->newTexts(); + $users = $service->getAllUsers(); + if ($this->request->isPost) { + if ($service->saveProfile($this->request->post(), $model)) { + $service->saveTexts($this->request->post('Text'), $model->id, $texts); + + return $this->redirect(['view', 'id' => $model->id]); + } + } + + return $this->render('create', [ + 'model' => $model, + 'users' => $users, + 'texts' => $texts + ]); + } + + /** + * Updates an existing Profile model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param int $id ID + * @return string|\yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + $service = UserService::run(); + $texts = $service->getTexts($id); + $users = $service->getAllUsers(); + + if ($this->request->isPost) { + if ($service->saveProfile($this->request->post(), $model)) { + $service->saveTexts($this->request->post('Text'), $model->id, $texts); + + return $this->redirect(['view', 'id' => $model->id]); + } + } + + return $this->render('update', [ + 'model' => $model, + 'users' => $users, + 'texts' => $texts + ]); + } + + /** + * Deletes an existing Profile model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param int $id ID + * @return \yii\web\Response + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $model = $this->findModel($id); + foreach ($model->texts as $text) { + $text->delete(); + } + $model->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Profile model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param int $id ID + * @return Profile the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Profile::findOne(['id' => $id])) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested page does not exist.'); + } +} diff --git a/backend/modules/profile/models/ProfileSearch.php b/backend/modules/profile/models/ProfileSearch.php new file mode 100755 index 0000000..c7996ee --- /dev/null +++ b/backend/modules/profile/models/ProfileSearch.php @@ -0,0 +1,71 @@ + $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, + 'user_id' => $this->user_id, + 'created_at' => $this->created_at, + 'activity' => $this->activity, + ]); + + $query->andFilterWhere(['like', 'image', $this->image]); + + return $dataProvider; + } +} diff --git a/backend/modules/profile/views/default/index.php b/backend/modules/profile/views/default/index.php new file mode 100755 index 0000000..b80c3ad --- /dev/null +++ b/backend/modules/profile/views/default/index.php @@ -0,0 +1,12 @@ +
+

context->action->uniqueId ?>

+

+ This is the view content for action "context->action->id ?>". + The action belongs to the controller "context) ?>" + in the "context->module->id ?>" module. +

+

+ You may customize this page by editing the following file:
+ +

+
diff --git a/backend/modules/profile/views/profile/_form.php b/backend/modules/profile/views/profile/_form.php new file mode 100755 index 0000000..4d54ee4 --- /dev/null +++ b/backend/modules/profile/views/profile/_form.php @@ -0,0 +1,40 @@ + + +
+ + + + field($model, 'user_id')->dropDownList(ArrayHelper::map($users, 'id', 'username')) ?> + + field($model, 'file')->fileInput() ?> + + field($model, 'activity')->checkbox() ?> + + $text): ?> + + field($text, 'title[' . $text['language'] . ']')->textInput(['maxlength' => true, 'value' => $text->title])->label($text['language'] . ' заголовок') ?> + + field($text, 'text[' . $text['language'] . ']')->textarea(['value' => $text->title])->label($text['language'] . ' текст') ?> + + + +
+ 'btn btn-success']) ?> +
+ + + +
diff --git a/backend/modules/profile/views/profile/_search.php b/backend/modules/profile/views/profile/_search.php new file mode 100755 index 0000000..8d290c7 --- /dev/null +++ b/backend/modules/profile/views/profile/_search.php @@ -0,0 +1,35 @@ + + + diff --git a/backend/modules/profile/views/profile/create.php b/backend/modules/profile/views/profile/create.php new file mode 100755 index 0000000..f64f472 --- /dev/null +++ b/backend/modules/profile/views/profile/create.php @@ -0,0 +1,24 @@ +title = 'Create Profile'; +$this->params['breadcrumbs'][] = ['label' => 'Profiles', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + 'users' => $users, + 'texts' => $texts + ]) ?> + +
diff --git a/backend/modules/profile/views/profile/index.php b/backend/modules/profile/views/profile/index.php new file mode 100755 index 0000000..7c8264f --- /dev/null +++ b/backend/modules/profile/views/profile/index.php @@ -0,0 +1,57 @@ +title = 'Profiles'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

title) ?>

+ +

+ 'btn btn-success']) ?> +

+ + render('_search', ['model' => $searchModel]); ?> + + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + ['class' => 'yii\grid\SerialColumn'], + + 'id', + 'created_at', + [ + 'attribute' => 'user_id', + 'value' => function($model){return $model->user->username;}, + ], + [ + 'attribute' => 'image', + 'value' => function($model){return '/uploads/' . $model->image;}, + 'format' => ['image', ['width' => 100, 'height' => 100]], + ], + [ + 'attribute' => 'activity', + 'value' => function($model){return $model->getStatusLabel();}, + ], + [ + 'class' => ActionColumn::className(), + 'urlCreator' => function ($action, Profile $model, $key, $index, $column) { + return Url::toRoute([$action, 'id' => $model->id]); + } + ], + ], + ]); ?> + + +
diff --git a/backend/modules/profile/views/profile/update.php b/backend/modules/profile/views/profile/update.php new file mode 100755 index 0000000..d95586e --- /dev/null +++ b/backend/modules/profile/views/profile/update.php @@ -0,0 +1,25 @@ +title = 'Update Profile: ' . $model->id; +$this->params['breadcrumbs'][] = ['label' => 'Profiles', 'url' => ['index']]; +$this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]]; +$this->params['breadcrumbs'][] = 'Update'; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + 'users' => $users, + 'texts' => $texts + ]) ?> + +
diff --git a/backend/modules/profile/views/profile/view.php b/backend/modules/profile/views/profile/view.php new file mode 100755 index 0000000..7caa439 --- /dev/null +++ b/backend/modules/profile/views/profile/view.php @@ -0,0 +1,50 @@ +title = $model->id; +$this->params['breadcrumbs'][] = ['label' => 'Profiles', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +\yii\web\YiiAsset::register($this); +?> +
+ +

title) ?>

+ +

+ $model->id], ['class' => 'btn btn-primary']) ?> + $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Are you sure you want to delete this item?', + 'method' => 'post', + ], + ]) ?> +

+ + $model, + 'attributes' => [ + 'id', + [ + 'attribute' => 'user_id', + 'value' => function($model){return $model->user->username;}, + ], + 'created_at', + [ + 'attribute' => 'image', + 'value' => function($model){return '/uploads/' . $model->image;}, + 'format' => ['image', ['width' => 100, 'height' => 100]], + ], + [ + 'attribute' => 'activity', + 'value' => function($model){return $model->getStatusLabel();}, + ], + ], + ]) ?> + +
diff --git a/backend/runtime/.gitignore b/backend/runtime/.gitignore new file mode 100755 index 0000000..c96a04f --- /dev/null +++ b/backend/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/backend/tests/_bootstrap.php b/backend/tests/_bootstrap.php new file mode 100755 index 0000000..637ce14 --- /dev/null +++ b/backend/tests/_bootstrap.php @@ -0,0 +1,10 @@ + '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', + ], +]; diff --git a/backend/tests/_output/.gitignore b/backend/tests/_output/.gitignore new file mode 100755 index 0000000..d6b7ef3 --- /dev/null +++ b/backend/tests/_output/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/backend/tests/_support/.gitignore b/backend/tests/_support/.gitignore new file mode 100755 index 0000000..36e264c --- /dev/null +++ b/backend/tests/_support/.gitignore @@ -0,0 +1 @@ +_generated diff --git a/backend/tests/_support/FunctionalTester.php b/backend/tests/_support/FunctionalTester.php new file mode 100755 index 0000000..a2bb8cb --- /dev/null +++ b/backend/tests/_support/FunctionalTester.php @@ -0,0 +1,26 @@ + [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'login_data.php' + ] + ]; + } + + /** + * @param FunctionalTester $I + */ + public function loginUser(FunctionalTester $I) + { + $I->amOnRoute('/site/login'); + $I->fillField('Username', 'erau'); + $I->fillField('Password', 'password_0'); + $I->click('login-button'); + + $I->see('Logout (erau)', 'form button[type=submit]'); + $I->dontSeeLink('Login'); + $I->dontSeeLink('Signup'); + } +} diff --git a/backend/tests/functional/_bootstrap.php b/backend/tests/functional/_bootstrap.php new file mode 100755 index 0000000..30ed54b --- /dev/null +++ b/backend/tests/functional/_bootstrap.php @@ -0,0 +1,16 @@ + 'davert']); + * ``` + * + * In Cests + * + * ```php + * \Codeception\Util\Fixtures::get('user1'); + * ``` + */ \ No newline at end of file diff --git a/backend/tests/unit.suite.yml b/backend/tests/unit.suite.yml new file mode 100755 index 0000000..f90030f --- /dev/null +++ b/backend/tests/unit.suite.yml @@ -0,0 +1,2 @@ +suite_namespace: backend\tests\unit +actor: UnitTester diff --git a/backend/tests/unit/_bootstrap.php b/backend/tests/unit/_bootstrap.php new file mode 100755 index 0000000..e432ce5 --- /dev/null +++ b/backend/tests/unit/_bootstrap.php @@ -0,0 +1,16 @@ + 'davert']); + * ``` + * + * In Tests + * + * ```php + * \Codeception\Util\Fixtures::get('user1'); + * ``` + */ diff --git a/backend/views/layouts/blank.php b/backend/views/layouts/blank.php new file mode 100755 index 0000000..c843320 --- /dev/null +++ b/backend/views/layouts/blank.php @@ -0,0 +1,33 @@ + +beginPage() ?> + + + + + + registerCsrfMetaTags() ?> + <?= Html::encode($this->title) ?> + head() ?> + + +beginBody() ?> + +
+
+ +
+
+ +endBody() ?> + + +endPage(); diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php new file mode 100755 index 0000000..a8f51a7 --- /dev/null +++ b/backend/views/layouts/main.php @@ -0,0 +1,83 @@ + +beginPage() ?> + + + + + + registerCsrfMetaTags() ?> + <?= Html::encode($this->title) ?> + head() ?> + + +beginBody() ?> + +
+ Yii::$app->name, + 'brandUrl' => Yii::$app->homeUrl, + 'options' => [ + 'class' => 'navbar navbar-expand-md navbar-dark bg-dark fixed-top', + ], + ]); + $menuItems = [ + ['label' => 'Home', 'url' => ['/site/index']], + ['label' => 'Круд новостей', 'url' => ['/news/news']], + ['label' => 'Круд профилей', 'url' => ['/profile/profile']], + ]; + if (Yii::$app->user->isGuest) { + $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']]; + } + echo Nav::widget([ + 'options' => ['class' => 'navbar-nav me-auto mb-2 mb-md-0'], + 'items' => $menuItems, + ]); + if (Yii::$app->user->isGuest) { + echo Html::tag('div',Html::a('Login',['/site/login'],['class' => ['btn btn-link login text-decoration-none']]),['class' => ['d-flex']]); + } else { + echo Html::beginForm(['/site/logout'], 'post', ['class' => 'd-flex']) + . Html::submitButton( + 'Logout (' . Yii::$app->user->identity->username . ')', + ['class' => 'btn btn-link logout text-decoration-none'] + ) + . Html::endForm(); + } + NavBar::end(); + ?> +
+ +
+
+ isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], + ]) ?> + + +
+
+ + + +endBody() ?> + + +endPage(); diff --git a/backend/views/site/error.php b/backend/views/site/error.php new file mode 100755 index 0000000..e0f8b65 --- /dev/null +++ b/backend/views/site/error.php @@ -0,0 +1,27 @@ +title = $name; +?> +
+ +

title) ?>

+ +
+ +
+ +

+ The above error occurred while the Web server was processing your request. +

+

+ Please contact us if you think this is a server error. Thank you. +

+ +
diff --git a/backend/views/site/index.php b/backend/views/site/index.php new file mode 100755 index 0000000..a981f91 --- /dev/null +++ b/backend/views/site/index.php @@ -0,0 +1,53 @@ +title = 'My Yii Application'; +?> +
+ +
+

Congratulations!

+ +

You have successfully created your Yii-powered application.

+ +

Get started with Yii

+
+ +
+ +
+
+

Heading

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur.

+ +

Yii Documentation »

+
+
+

Heading

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur.

+ +

Yii Forum »

+
+
+

Heading

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur.

+ +

Yii Extensions »

+
+
+ +
+
diff --git a/backend/views/site/login.php b/backend/views/site/login.php new file mode 100755 index 0000000..d4b5a3b --- /dev/null +++ b/backend/views/site/login.php @@ -0,0 +1,32 @@ +title = 'Login'; +?> +
+
+

title) ?>

+ +

Please fill out the following fields to login:

+ + 'login-form']); ?> + + field($model, 'username')->textInput(['autofocus' => true]) ?> + + field($model, 'password')->passwordInput() ?> + + field($model, 'rememberMe')->checkbox() ?> + +
+ 'btn btn-primary btn-block', 'name' => 'login-button']) ?> +
+ + +
+
diff --git a/backend/web/assets/.gitignore b/backend/web/assets/.gitignore new file mode 100755 index 0000000..d6b7ef3 --- /dev/null +++ b/backend/web/assets/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/backend/web/css/site.css b/backend/web/css/site.css new file mode 100755 index 0000000..34c35c6 --- /dev/null +++ b/backend/web/css/site.css @@ -0,0 +1,90 @@ +main > .container { + padding: 70px 15px 20px; +} + +.footer { + background-color: #f5f5f5; + font-size: .9em; + height: 60px; +} + +.footer > .container { + padding-right: 15px; + padding-left: 15px; +} + +.not-set { + color: #c55; + font-style: italic; +} + +/* add sorting icons to gridview sort links */ +a.asc:after, a.desc:after { + content: ''; + left: 3px; + display: inline-block; + width: 0; + height: 0; + border: solid 5px transparent; + margin: 4px 4px 2px 4px; + background: transparent; +} + +a.asc:after { + border-bottom: solid 7px #212529; + border-top-width: 0; +} + +a.desc:after { + border-top: solid 7px #212529; + border-bottom-width: 0; +} + +.grid-view th, +.grid-view td:last-child { + white-space: nowrap; +} + +.grid-view .filters input, +.grid-view .filters select { + min-width: 50px; +} + +.hint-block { + display: block; + margin-top: 5px; + color: #999; +} + +.error-summary { + color: #a94442; + background: #fdf7f7; + border-left: 3px solid #eed3d7; + padding: 10px 20px; + margin: 0 0 15px 0; +} + +/* align the logout "link" (button in form) of the navbar */ +.nav li > form > button.logout { + padding-top: 7px; + color: rgba(255, 255, 255, 0.5); +} + +@media(max-width:767px) { + .nav li > form > button.logout { + display:block; + text-align: left; + width: 100%; + padding: 10px 0; + } +} + +.nav > li > form > button.logout:focus, +.nav > li > form > button.logout:hover { + text-decoration: none; + color: rgba(255, 255, 255, 0.75); +} + +.nav > li > form > button.logout:focus { + outline: none; +} diff --git a/backend/web/favicon.ico b/backend/web/favicon.ico new file mode 100755 index 0000000..580ed73 Binary files /dev/null and b/backend/web/favicon.ico differ diff --git a/codeception.yml b/codeception.yml new file mode 100755 index 0000000..c6f0143 --- /dev/null +++ b/codeception.yml @@ -0,0 +1,9 @@ +# global codeception file to run tests from all apps +include: + - common + - frontend + - backend +paths: + output: console/runtime/output +settings: + colors: true \ No newline at end of file diff --git a/common/codeception.yml b/common/codeception.yml new file mode 100755 index 0000000..1dfaf97 --- /dev/null +++ b/common/codeception.yml @@ -0,0 +1,15 @@ +namespace: common\tests +actor_suffix: Tester +paths: + tests: tests + output: tests/_output + data: tests/_data + support: tests/_support +bootstrap: _bootstrap.php +settings: + colors: true + memory_limit: 1024M +modules: + config: + Yii2: + configFile: 'config/codeception-local.php' diff --git a/common/config/.gitignore b/common/config/.gitignore new file mode 100755 index 0000000..7c090d2 --- /dev/null +++ b/common/config/.gitignore @@ -0,0 +1,4 @@ +codeception-local.php +main-local.php +params-local.php +test-local.php diff --git a/common/config/__autocomplete.php b/common/config/__autocomplete.php new file mode 100755 index 0000000..d99dea5 --- /dev/null +++ b/common/config/__autocomplete.php @@ -0,0 +1,33 @@ + [ + '@bower' => '@vendor/bower-asset', + '@npm' => '@vendor/npm-asset', + ], + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', + 'components' => [ + 'cache' => [ + 'class' => \yii\caching\FileCache::class, + ], + ], +]; diff --git a/common/config/params.php b/common/config/params.php new file mode 100755 index 0000000..9361512 --- /dev/null +++ b/common/config/params.php @@ -0,0 +1,9 @@ + 'admin@example.com', + 'supportEmail' => 'support@example.com', + 'senderEmail' => 'noreply@example.com', + 'senderName' => 'Example.com mailer', + 'user.passwordResetTokenExpire' => 3600, + 'user.passwordMinLength' => 8, +]; diff --git a/common/config/test.php b/common/config/test.php new file mode 100755 index 0000000..c50955f --- /dev/null +++ b/common/config/test.php @@ -0,0 +1,11 @@ + 'app-common-tests', + 'basePath' => dirname(__DIR__), + 'components' => [ + 'user' => [ + 'class' => \yii\web\User::class, + 'identityClass' => 'common\models\User', + ], + ], +]; diff --git a/common/fixtures/UserFixture.php b/common/fixtures/UserFixture.php new file mode 100755 index 0000000..6df7a98 --- /dev/null +++ b/common/fixtures/UserFixture.php @@ -0,0 +1,10 @@ +urlManager->createAbsoluteUrl(['site/verify-email', 'token' => $user->verification_token]); +?> +
+

Hello username) ?>,

+ +

Follow the link below to verify your email:

+ +

+
diff --git a/common/mail/emailVerify-text.php b/common/mail/emailVerify-text.php new file mode 100755 index 0000000..48a68fc --- /dev/null +++ b/common/mail/emailVerify-text.php @@ -0,0 +1,12 @@ +urlManager->createAbsoluteUrl(['site/verify-email', 'token' => $user->verification_token]); +?> +Hello username ?>, + +Follow the link below to verify your email: + + diff --git a/common/mail/layouts/html.php b/common/mail/layouts/html.php new file mode 100755 index 0000000..8560d47 --- /dev/null +++ b/common/mail/layouts/html.php @@ -0,0 +1,24 @@ + +beginPage() ?> + + + + + <?= Html::encode($this->title) ?> + head() ?> + + + beginBody() ?> + + endBody() ?> + + +endPage(); diff --git a/common/mail/layouts/text.php b/common/mail/layouts/text.php new file mode 100755 index 0000000..9b4c548 --- /dev/null +++ b/common/mail/layouts/text.php @@ -0,0 +1,12 @@ + +beginPage() ?> +beginBody() ?> + +endBody() ?> +endPage() ?> diff --git a/common/mail/passwordResetToken-html.php b/common/mail/passwordResetToken-html.php new file mode 100755 index 0000000..9f6e470 --- /dev/null +++ b/common/mail/passwordResetToken-html.php @@ -0,0 +1,16 @@ +urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); +?> +
+

Hello username) ?>,

+ +

Follow the link below to reset your password:

+ +

+
diff --git a/common/mail/passwordResetToken-text.php b/common/mail/passwordResetToken-text.php new file mode 100755 index 0000000..6a5120f --- /dev/null +++ b/common/mail/passwordResetToken-text.php @@ -0,0 +1,12 @@ +urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); +?> +Hello username ?>, + +Follow the link below to reset your password: + + diff --git a/common/models/LoginForm.php b/common/models/LoginForm.php new file mode 100755 index 0000000..1f937a8 --- /dev/null +++ b/common/models/LoginForm.php @@ -0,0 +1,79 @@ +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; + } +} diff --git a/common/models/News.php b/common/models/News.php new file mode 100755 index 0000000..6dc8ca5 --- /dev/null +++ b/common/models/News.php @@ -0,0 +1,47 @@ + 255], + [['slug'], 'unique'], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'title' => 'Title', + 'text' => 'Text', + 'slug' => 'Slug', + ]; + } +} diff --git a/common/models/Profile.php b/common/models/Profile.php new file mode 100755 index 0000000..8b9496e --- /dev/null +++ b/common/models/Profile.php @@ -0,0 +1,95 @@ + 255], + [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['user_id' => 'id']], + [['file'], 'file', 'extensions' => 'jpg, png'], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'user_id' => 'Пользователь', + 'created_at' => 'Когда создан', + 'image' => 'Картинка', + 'activity' => 'Активность', + ]; + } + + /** + * Gets query for [[Texts]]. + * + * @return \yii\db\ActiveQuery + */ + public function getTexts() + { + return $this->hasMany(Text::class, ['profile_id' => 'id']); + } + + /** + * Gets query for [[User]]. + * + * @return \yii\db\ActiveQuery + */ + public function getUser() + { + return $this->hasOne(User::class, ['id' => 'user_id']); + } + + public function getStatusLabel() + { + if ($this->activity == self::STATUS_ACTIVE) + return 'Активен'; + else + return 'Не активен'; + } + + /** + * @param string $lang + * @return \yii\db\ActiveQuery + */ + public function getTextByLanguage(string $lang) + { + return $this->hasMany(Text::class, ['profile_id' => 'id', 'language' => $lang]); + } +} diff --git a/common/models/Text.php b/common/models/Text.php new file mode 100755 index 0000000..b418fcf --- /dev/null +++ b/common/models/Text.php @@ -0,0 +1,78 @@ + 255], + [['profile_id'], 'exist', 'skipOnError' => true, 'targetClass' => Profile::class, 'targetAttribute' => ['profile_id' => 'id']], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'profile_id' => 'Profile ID', + 'title' => 'Title', + 'text' => 'Text', + 'language' => 'Language', + ]; + } + + public function extraFields() + { + return ['text[]', 'title[]']; + } + + /** + * Gets query for [[Profile]]. + * + * @return \yii\db\ActiveQuery + */ + public function getProfile() + { + return $this->hasOne(Profile::class, ['id' => 'profile_id']); + } + + public static function getLanguages() + { + return [self::LANG_RU, self::LANG_IT, self::LANG_FR, self::LANG_EN, self::LANG_ES]; + } +} diff --git a/common/models/User.php b/common/models/User.php new file mode 100755 index 0000000..53b6e1a --- /dev/null +++ b/common/models/User.php @@ -0,0 +1,213 @@ + self::STATUS_INACTIVE], + ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]], + ]; + } + + /** + * {@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; + } +} diff --git a/common/services/UserService.php b/common/services/UserService.php new file mode 100644 index 0000000..72bb5d3 --- /dev/null +++ b/common/services/UserService.php @@ -0,0 +1,76 @@ +text = $post['text'][$text->language]; + $text->title = $post['title'][$text->language]; + $text->profile_id = $profile_id; + $text->save(); + } + } + + public function saveProfile($post, $model) + { + $model->load($post); + $model->created_at = date('Y-d-m h:i:s'); + $model->file = UploadedFile::getInstance($model, 'file'); + if ($model->file) { + $model->image = $model->file->baseName . '.' . $model->file->extension; + $model->file->saveAs('@frontend/web/uploads/' . $model->file->baseName . '.' . $model->file->extension); + $model->file = null; + } + if ($model->save()) { + + return true; + } else + + return false; + } + + public function getAllUsers() + { + return $users = User::find()->all(); + } + + public function newTexts() + { + $texts = []; + foreach (Text::getLanguages() as $lang) { + $text = new Text(); + $text->language = $lang; + $texts[] = $text; + } + + return $texts; + } + + public function getTexts($id) + { + $textModels = Text::find()->where(['profile_id' => $id])->all(); +// foreach ($textModels as $text) +// { +// $text->title[$text->language] = $text->title; +// $text->text[$text->language] = $text->text; +// } + return $textModels; + } + + public function getTextByLanguage($id, $slug) + { + return Text::find()->where(['language' => $slug, 'profile_id' => $id])->all(); + } + + public static function run() + { + return new self(); + } +} \ No newline at end of file diff --git a/common/tests/_bootstrap.php b/common/tests/_bootstrap.php new file mode 100755 index 0000000..9915cc3 --- /dev/null +++ b/common/tests/_bootstrap.php @@ -0,0 +1,9 @@ + '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', + ], +]; diff --git a/common/tests/_output/.gitignore b/common/tests/_output/.gitignore new file mode 100755 index 0000000..d6b7ef3 --- /dev/null +++ b/common/tests/_output/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/common/tests/_support/.gitignore b/common/tests/_support/.gitignore new file mode 100755 index 0000000..36e264c --- /dev/null +++ b/common/tests/_support/.gitignore @@ -0,0 +1 @@ +_generated diff --git a/common/tests/_support/UnitTester.php b/common/tests/_support/UnitTester.php new file mode 100755 index 0000000..a0cc7a7 --- /dev/null +++ b/common/tests/_support/UnitTester.php @@ -0,0 +1,26 @@ + [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php' + ] + ]; + } + + public function testLoginNoUser() + { + $model = new LoginForm([ + 'username' => 'not_existing_username', + 'password' => 'not_existing_password', + ]); + + verify($model->login())->false(); + verify(Yii::$app->user->isGuest)->true(); + } + + public function testLoginWrongPassword() + { + $model = new LoginForm([ + 'username' => 'bayer.hudson', + 'password' => 'wrong_password', + ]); + + verify($model->login())->false(); + verify( $model->errors)->arrayHasKey('password'); + verify(Yii::$app->user->isGuest)->true(); + } + + public function testLoginCorrect() + { + $model = new LoginForm([ + 'username' => 'bayer.hudson', + 'password' => 'password_0', + ]); + + verify($model->login())->true(); + verify($model->errors)->arrayHasNotKey('password'); + verify(Yii::$app->user->isGuest)->false(); + } +} diff --git a/common/widgets/Alert.php b/common/widgets/Alert.php new file mode 100755 index 0000000..4ac3d15 --- /dev/null +++ b/common/widgets/Alert.php @@ -0,0 +1,76 @@ +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 + * @author Alexander Makarov + */ +class Alert extends \yii\bootstrap5\Widget +{ + /** + * @var array the alert types configuration for the flash messages. + * This array is setup as $key => $value, where: + * - key: the name of the session flash variable + * - value: the bootstrap alert type (i.e. danger, success, info, warning) + */ + public $alertTypes = [ + 'error' => 'alert-danger', + 'danger' => 'alert-danger', + 'success' => 'alert-success', + 'info' => 'alert-info', + 'warning' => 'alert-warning' + ]; + /** + * @var array the options for rendering the close button tag. + * Array will be passed to [[\yii\bootstrap\Alert::closeButton]]. + */ + public $closeButton = []; + + + /** + * {@inheritdoc} + */ + public function run() + { + $session = Yii::$app->session; + $flashes = $session->getAllFlashes(); + $appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : ''; + + foreach ($flashes as $type => $flash) { + if (!isset($this->alertTypes[$type])) { + continue; + } + + foreach ((array) $flash as $i => $message) { + echo \yii\bootstrap5\Alert::widget([ + 'body' => $message, + 'closeButton' => $this->closeButton, + 'options' => array_merge($this->options, [ + 'id' => $this->getId() . '-' . $type . '-' . $i, + 'class' => $this->alertTypes[$type] . $appendClass, + ]), + ]); + } + + $session->removeFlash($type); + } + } +} diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..e121f05 --- /dev/null +++ b/composer.json @@ -0,0 +1,56 @@ +{ + "name": "yiisoft/yii2-app-advanced", + "description": "Yii 2 Advanced Project Template", + "keywords": ["yii2", "framework", "advanced", "project template"], + "homepage": "https://www.yiiframework.com/", + "type": "project", + "license": "BSD-3-Clause", + "support": { + "issues": "https://github.com/yiisoft/yii2/issues?state=open", + "forum": "https://www.yiiframework.com/forum/", + "wiki": "https://www.yiiframework.com/wiki/", + "irc": "irc://irc.freenode.net/yii", + "source": "https://github.com/yiisoft/yii2" + }, + "minimum-stability": "stable", + "require-dev": { + "yiisoft/yii2-debug": "~2.1.0", + "yiisoft/yii2-gii": "~2.2.0", + "yiisoft/yii2-faker": "~2.0.0", + "phpunit/phpunit": "~9.5.0", + "codeception/codeception": "^5.0.0 || ^4.0", + "codeception/lib-innerbrowser": "^3.0 || ^1.1", + "codeception/module-asserts": "^3.0 || ^1.1", + "codeception/module-yii2": "^1.1", + "codeception/module-filesystem": "^2.0 || ^1.1", + "codeception/verify": "^2.2", + "symfony/browser-kit": "^6.0 || >=2.7 <=4.2.4" + }, + "autoload-dev": { + "psr-4": { + "common\\tests\\": ["common/tests/", "common/tests/_support"], + "backend\\tests\\": ["backend/tests/", "backend/tests/_support"], + "frontend\\tests\\": ["frontend/tests/", "frontend/tests/_support"] + } + }, + "config": { + "allow-plugins": { + "yiisoft/yii2-composer" : true + }, + "process-timeout": 1800, + "fxp-asset": { + "enabled": false + } + }, + "repositories": [ + { + "type": "composer", + "url": "https://asset-packagist.org" + } + ], + "require": { + "yiisoft/yii2-bootstrap5": "@dev", + "kartik-v/yii2-widget-fileinput": "@dev", + "asmoday74/yii2-ckeditor5": "*" + } +} diff --git a/composer.lock b/composer.lock new file mode 100755 index 0000000..f35858c --- /dev/null +++ b/composer.lock @@ -0,0 +1,5183 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "92027d1ec778c40df9b904c18932a569", + "packages": [ + { + "name": "asmoday74/yii2-ckeditor5", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/asmoday74/yii2-ckeditor5.git", + "reference": "d5cb38abfc6afd9f0a8729662897ecefc83b18a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/asmoday74/yii2-ckeditor5/zipball/d5cb38abfc6afd9f0a8729662897ecefc83b18a6", + "reference": "d5cb38abfc6afd9f0a8729662897ecefc83b18a6", + "shasum": "" + }, + "require": { + "yiisoft/yii2": "~2.0.0" + }, + "type": "yii2-extension", + "autoload": { + "psr-4": { + "asmoday74\\ckeditor5\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maksim Klimov" + } + ], + "description": "CKEditor 5 WYSIWYG widget for Yii2", + "keywords": [ + "ckeditor 5", + "extension", + "yii2" + ], + "support": { + "issues": "https://github.com/asmoday74/yii2-ckeditor5/issues", + "source": "https://github.com/asmoday74/yii2-ckeditor5/tree/1.0.2" + }, + "time": "2019-09-24T20:23:27+00:00" + }, + { + "name": "bower-asset/bootstrap", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/twbs/bootstrap.git", + "reference": "cb021439c683d9805e2864c58095b92d405e9b11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twbs/bootstrap/zipball/cb021439c683d9805e2864c58095b92d405e9b11", + "reference": "cb021439c683d9805e2864c58095b92d405e9b11" + }, + "type": "bower-asset" + }, + { + "name": "bower-asset/inputmask", + "version": "3.3.11", + "source": { + "type": "git", + "url": "https://github.com/RobinHerbots/Inputmask.git", + "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/RobinHerbots/Inputmask/zipball/5e670ad62f50c738388d4dcec78d2888505ad77b", + "reference": "5e670ad62f50c738388d4dcec78d2888505ad77b" + }, + "require": { + "bower-asset/jquery": ">=1.7" + }, + "type": "bower-asset", + "license": [ + "http://opensource.org/licenses/mit-license.php" + ] + }, + { + "name": "bower-asset/jquery", + "version": "3.6.4", + "source": { + "type": "git", + "url": "https://github.com/jquery/jquery-dist.git", + "reference": "91ef2d8836342875f2519b5815197ea0f23613cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/91ef2d8836342875f2519b5815197ea0f23613cf", + "reference": "91ef2d8836342875f2519b5815197ea0f23613cf" + }, + "type": "bower-asset", + "license": [ + "MIT" + ] + }, + { + "name": "bower-asset/punycode", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/mathiasbynens/punycode.js.git", + "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mathiasbynens/punycode.js/zipball/38c8d3131a82567bfef18da09f7f4db68c84f8a3", + "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" + }, + "type": "bower-asset" + }, + { + "name": "bower-asset/yii2-pjax", + "version": "2.0.8", + "source": { + "type": "git", + "url": "git@github.com:yiisoft/jquery-pjax.git", + "reference": "a9298d57da63d14a950f1b94366a864bc62264fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/a9298d57da63d14a950f1b94366a864bc62264fb", + "reference": "a9298d57da63d14a950f1b94366a864bc62264fb" + }, + "require": { + "bower-asset/jquery": ">=1.8" + }, + "type": "bower-asset", + "license": [ + "MIT" + ] + }, + { + "name": "cebe/markdown", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/cebe/markdown.git", + "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cebe/markdown/zipball/9bac5e971dd391e2802dca5400bbeacbaea9eb86", + "reference": "9bac5e971dd391e2802dca5400bbeacbaea9eb86", + "shasum": "" + }, + "require": { + "lib-pcre": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "cebe/indent": "*", + "facebook/xhprof": "*@dev", + "phpunit/phpunit": "4.1.*" + }, + "bin": [ + "bin/markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "cebe\\markdown\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Creator" + } + ], + "description": "A super fast, highly extensible markdown parser for PHP", + "homepage": "https://github.com/cebe/markdown#readme", + "keywords": [ + "extensible", + "fast", + "gfm", + "markdown", + "markdown-extra" + ], + "support": { + "issues": "https://github.com/cebe/markdown/issues", + "source": "https://github.com/cebe/markdown" + }, + "time": "2018-03-26T11:24:36+00:00" + }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.16.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8", + "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0" + }, + "time": "2022-09-18T07:06:19+00:00" + }, + { + "name": "kartik-v/bootstrap-fileinput", + "version": "v5.5.2", + "source": { + "type": "git", + "url": "https://github.com/kartik-v/bootstrap-fileinput.git", + "reference": "35c29cd9fd5bc47eae5238feaabee95b2d7efe3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kartik-v/bootstrap-fileinput/zipball/35c29cd9fd5bc47eae5238feaabee95b2d7efe3c", + "reference": "35c29cd9fd5bc47eae5238feaabee95b2d7efe3c", + "shasum": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "kartik\\plugins\\fileinput\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kartik Visweswaran", + "email": "kartikv2@gmail.com", + "homepage": "http://www.krajee.com/" + } + ], + "description": "An enhanced HTML 5 file input for Bootstrap 5.x, 4.x, and 3.x with features for file preview for many file types, multiple selection, ajax uploads, and more.", + "homepage": "https://github.com/kartik-v/bootstrap-fileinput", + "keywords": [ + "ajax", + "bootstrap", + "delete", + "file", + "image", + "input", + "jquery", + "multiple", + "preview", + "progress", + "upload" + ], + "support": { + "issues": "https://github.com/kartik-v/bootstrap-fileinput/issues", + "source": "https://github.com/kartik-v/bootstrap-fileinput/tree/v5.5.2" + }, + "funding": [ + { + "url": "https://opencollective.com/bootstrap-fileinput", + "type": "open_collective" + } + ], + "time": "2022-10-13T06:16:39+00:00" + }, + { + "name": "kartik-v/yii2-krajee-base", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/kartik-v/yii2-krajee-base.git", + "reference": "5c095126d1be47e0bb1f92779b7dc099f6feae31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kartik-v/yii2-krajee-base/zipball/5c095126d1be47e0bb1f92779b7dc099f6feae31", + "reference": "5c095126d1be47e0bb1f92779b7dc099f6feae31", + "shasum": "" + }, + "suggest": { + "yiisoft/yii2-bootstrap": "for Krajee extensions to work with Bootstrap 3.x version", + "yiisoft/yii2-bootstrap4": "for Krajee extensions to work with Bootstrap 4.x version", + "yiisoft/yii2-bootstrap5": "for Krajee extensions to work with Bootstrap 5.x version" + }, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "kartik\\base\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kartik Visweswaran", + "email": "kartikv2@gmail.com", + "homepage": "http://www.krajee.com/" + } + ], + "description": "Base library and foundation components for all Yii2 Krajee extensions.", + "homepage": "https://github.com/kartik-v/yii2-krajee-base", + "keywords": [ + "base", + "extension", + "foundation", + "krajee", + "widget", + "yii2" + ], + "support": { + "issues": "https://github.com/kartik-v/yii2-krajee-base/issues", + "source": "https://github.com/kartik-v/yii2-krajee-base/tree/v3.0.5" + }, + "time": "2022-06-01T14:05:39+00:00" + }, + { + "name": "kartik-v/yii2-widget-fileinput", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/kartik-v/yii2-widget-fileinput.git", + "reference": "b5500b6855526837154694c2afab8dbc3afc8abd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kartik-v/yii2-widget-fileinput/zipball/b5500b6855526837154694c2afab8dbc3afc8abd", + "reference": "b5500b6855526837154694c2afab8dbc3afc8abd", + "shasum": "" + }, + "require": { + "kartik-v/bootstrap-fileinput": ">=5.5.0", + "kartik-v/yii2-krajee-base": ">=3.0.5" + }, + "default-branch": true, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "kartik\\file\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kartik Visweswaran", + "email": "kartikv2@gmail.com", + "homepage": "http://www.krajee.com/" + } + ], + "description": "An enhanced FileInput widget for Bootstrap 3.x, 4.x & 5.x with file preview, multiple selection, and more features (sub repo split from yii2-widgets)", + "homepage": "https://github.com/kartik-v/yii2-widget-fileinput", + "keywords": [ + "extension", + "file", + "form", + "input", + "jquery", + "plugin", + "upload", + "widget", + "yii2" + ], + "support": { + "issues": "https://github.com/kartik-v/yii2-widget-fileinput/issues", + "source": "https://github.com/kartik-v/yii2-widget-fileinput/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://opencollective.com/yii2-widget-fileinput", + "type": "open_collective" + } + ], + "time": "2022-06-28T04:31:04+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "yiisoft/yii2", + "version": "2.0.47", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "8ecf57895d9c4b29cf9658ffe57af5f3d0e25254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/8ecf57895d9c4b29cf9658ffe57af5f3d0e25254", + "reference": "8ecf57895d9c4b29cf9658ffe57af5f3d0e25254", + "shasum": "" + }, + "require": { + "bower-asset/inputmask": "~3.2.2 | ~3.3.5", + "bower-asset/jquery": "3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", + "bower-asset/punycode": "1.3.*", + "bower-asset/yii2-pjax": "~2.0.1", + "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", + "ext-ctype": "*", + "ext-mbstring": "*", + "ezyang/htmlpurifier": "~4.6", + "lib-pcre": "*", + "paragonie/random_compat": ">=1", + "php": ">=5.4.0", + "yiisoft/yii2-composer": "~2.0.4" + }, + "bin": [ + "yii" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "yii\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "https://www.yiiframework.com/", + "role": "Founder and project lead" + }, + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru", + "homepage": "https://rmcreative.ru/", + "role": "Core framework development" + }, + { + "name": "Maurizio Domba", + "homepage": "http://mdomba.info/", + "role": "Core framework development" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "https://www.cebe.cc/", + "role": "Core framework development" + }, + { + "name": "Timur Ruziev", + "email": "resurtm@gmail.com", + "homepage": "http://resurtm.com/", + "role": "Core framework development" + }, + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com", + "role": "Core framework development" + }, + { + "name": "Dmitry Naumenko", + "email": "d.naumenko.a@gmail.com", + "role": "Core framework development" + }, + { + "name": "Boudewijn Vahrmeijer", + "email": "info@dynasource.eu", + "homepage": "http://dynasource.eu", + "role": "Core framework development" + } + ], + "description": "Yii PHP Framework Version 2", + "homepage": "https://www.yiiframework.com/", + "keywords": [ + "framework", + "yii2" + ], + "support": { + "forum": "https://forum.yiiframework.com/", + "irc": "ircs://irc.libera.chat:6697/yii", + "issues": "https://github.com/yiisoft/yii2/issues?state=open", + "source": "https://github.com/yiisoft/yii2", + "wiki": "https://www.yiiframework.com/wiki" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2", + "type": "tidelift" + } + ], + "time": "2022-11-18T16:21:58+00:00" + }, + { + "name": "yiisoft/yii2-bootstrap5", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-bootstrap5.git", + "reference": "5ea4bb6dc7f729835b5ace59721932f66e1b05de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-bootstrap5/zipball/5ea4bb6dc7f729835b5ace59721932f66e1b05de", + "reference": "5ea4bb6dc7f729835b5ace59721932f66e1b05de", + "shasum": "" + }, + "require": { + "bower-asset/bootstrap": "^5.1.0", + "ext-json": "*", + "php": ">=7.0", + "yiisoft/yii2": "^2.0.42" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14", + "twbs/bootstrap-icons": "^1.7.2", + "yiisoft/yii2-coding-standards": "~2.0" + }, + "suggest": { + "twbs/bootstrap-icons": "Add this package to the `require` section of your `composer.json` if you'd like to use the bootstrap icon asset." + }, + "default-branch": true, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + }, + "bootstrap": "yii\\bootstrap5\\i18n\\TranslationBootstrap" + }, + "autoload": { + "psr-4": { + "yii\\bootstrap5\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sergey Zhukovskiy", + "email": "mylistryx@gmail.com", + "homepage": "https://net23.ru/" + }, + { + "name": "Simon Karlen", + "email": "simi.albi@outlook.com" + } + ], + "description": "The Twitter Bootstrap v5 extension for the Yii framework", + "keywords": [ + "bootstrap", + "bootstrap5", + "yii2" + ], + "support": { + "issues": "https://github.com/yiisoft/yii2-bootstrap5/issues", + "source": "https://github.com/yiisoft/yii2-bootstrap5" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-bootstrap5", + "type": "tidelift" + } + ], + "time": "2023-04-25T07:51:59+00:00" + }, + { + "name": "yiisoft/yii2-composer", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-composer.git", + "reference": "94bb3f66e779e2774f8776d6e1bdeab402940510" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/94bb3f66e779e2774f8776d6e1bdeab402940510", + "reference": "94bb3f66e779e2774f8776d6e1bdeab402940510", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 | ^2.0" + }, + "require-dev": { + "composer/composer": "^1.0 | ^2.0@dev", + "phpunit/phpunit": "<7" + }, + "type": "composer-plugin", + "extra": { + "class": "yii\\composer\\Plugin", + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "yii\\composer\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc" + } + ], + "description": "The composer plugin for Yii extension installer", + "keywords": [ + "composer", + "extension installer", + "yii2" + ], + "support": { + "forum": "http://www.yiiframework.com/forum/", + "irc": "irc://irc.freenode.net/yii", + "issues": "https://github.com/yiisoft/yii2-composer/issues", + "source": "https://github.com/yiisoft/yii2-composer", + "wiki": "http://www.yiiframework.com/wiki/" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-composer", + "type": "tidelift" + } + ], + "time": "2020-06-24T00:04:01+00:00" + } + ], + "packages-dev": [ + { + "name": "behat/gherkin", + "version": "v4.9.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Gherkin.git", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "shasum": "" + }, + "require": { + "php": "~7.2|~8.0" + }, + "require-dev": { + "cucumber/cucumber": "dev-gherkin-22.0.0", + "phpunit/phpunit": "~8|~9", + "symfony/yaml": "~3|~4|~5" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Gherkin": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + }, + "time": "2021-10-12T13:05:09+00:00" + }, + { + "name": "codeception/codeception", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Codeception.git", + "reference": "b88014f3348c93f3df99dc6d0967b0dbfa804474" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/b88014f3348c93f3df99dc6d0967b0dbfa804474", + "reference": "b88014f3348c93f3df99dc6d0967b0dbfa804474", + "shasum": "" + }, + "require": { + "behat/gherkin": "^4.4.0", + "codeception/lib-asserts": "^1.0 | 2.0.*@dev", + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", + "codeception/stub": "^2.0 | ^3.0 | ^4.0", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/psr7": "^1.4 | ^2.0", + "php": ">=5.6.0 <9.0", + "symfony/console": ">=2.7 <6.0", + "symfony/css-selector": ">=2.7 <6.0", + "symfony/event-dispatcher": ">=2.7 <6.0", + "symfony/finder": ">=2.7 <6.0", + "symfony/yaml": ">=2.7 <6.0" + }, + "require-dev": { + "codeception/module-asserts": "^1.0 | 2.0.*@dev", + "codeception/module-cli": "^1.0 | 2.0.*@dev", + "codeception/module-db": "^1.0 | 2.0.*@dev", + "codeception/module-filesystem": "^1.0 | 2.0.*@dev", + "codeception/module-phpbrowser": "^1.0 | 2.0.*@dev", + "codeception/specify": "~0.3", + "codeception/util-universalframework": "*@dev", + "monolog/monolog": "~1.8", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <6.0", + "vlucas/phpdotenv": "^2.0 | ^3.0 | ^4.0 | ^5.0" + }, + "suggest": { + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "hoa/console": "For interactive console functionality", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" + }, + "bin": [ + "codecept" + ], + "type": "library", + "extra": { + "branch-alias": [] + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Codeception\\": "src/Codeception", + "Codeception\\Extension\\": "ext" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "https://codegyre.com" + } + ], + "description": "BDD-style testing framework", + "homepage": "https://codeception.com/", + "keywords": [ + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" + ], + "support": { + "issues": "https://github.com/Codeception/Codeception/issues", + "source": "https://github.com/Codeception/Codeception/tree/4.2.2" + }, + "funding": [ + { + "url": "https://opencollective.com/codeception", + "type": "open_collective" + } + ], + "time": "2022-08-13T13:28:25+00:00" + }, + { + "name": "codeception/lib-asserts", + "version": "1.13.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/lib-asserts.git", + "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/184231d5eab66bc69afd6b9429344d80c67a33b6", + "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6", + "shasum": "" + }, + "require": { + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3 | ^9.0", + "ext-dom": "*", + "php": ">=5.6.0 <9.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + }, + { + "name": "Gintautas Miselis" + }, + { + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" + } + ], + "description": "Assertion methods used by Codeception core and Asserts module", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/lib-asserts/issues", + "source": "https://github.com/Codeception/lib-asserts/tree/1.13.2" + }, + "time": "2020-10-21T16:26:20+00:00" + }, + { + "name": "codeception/lib-innerbrowser", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/lib-innerbrowser.git", + "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/lib-innerbrowser/zipball/31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", + "reference": "31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2", + "shasum": "" + }, + "require": { + "codeception/codeception": "4.*@dev", + "ext-dom": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.6.0 <9.0", + "symfony/browser-kit": ">=2.7 <6.0", + "symfony/dom-crawler": ">=2.7 <6.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "require-dev": { + "codeception/util-universalframework": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + }, + { + "name": "Gintautas Miselis" + } + ], + "description": "Parent library for all Codeception framework modules and PhpBrowser", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/lib-innerbrowser/issues", + "source": "https://github.com/Codeception/lib-innerbrowser/tree/1.5.1" + }, + "time": "2021-08-30T15:21:42+00:00" + }, + { + "name": "codeception/module-asserts", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-asserts.git", + "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-asserts/zipball/59374f2fef0cabb9e8ddb53277e85cdca74328de", + "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de", + "shasum": "" + }, + "require": { + "codeception/codeception": "*@dev", + "codeception/lib-asserts": "^1.13.1", + "php": ">=5.6.0 <9.0" + }, + "conflict": { + "codeception/codeception": "<4.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + }, + { + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" + } + ], + "description": "Codeception module containing various assertions", + "homepage": "https://codeception.com/", + "keywords": [ + "assertions", + "asserts", + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/module-asserts/issues", + "source": "https://github.com/Codeception/module-asserts/tree/1.3.1" + }, + "time": "2020-10-21T16:48:15+00:00" + }, + { + "name": "codeception/module-filesystem", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-filesystem.git", + "reference": "ae1fa5f13ba00bdb8a83d4258ef577c5114ddef9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-filesystem/zipball/ae1fa5f13ba00bdb8a83d4258ef577c5114ddef9", + "reference": "ae1fa5f13ba00bdb8a83d4258ef577c5114ddef9", + "shasum": "" + }, + "require": { + "codeception/codeception": "^4.1 | *@dev", + "php": "^7.4 | ^8.0", + "symfony/finder": "^4.4 | ^5.4 | ^6.0" + }, + "conflict": { + "codeception/codeception": "<4.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk" + }, + { + "name": "Gintautas Miselis" + } + ], + "description": "Codeception module for testing local filesystem", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception", + "filesystem" + ], + "support": { + "issues": "https://github.com/Codeception/module-filesystem/issues", + "source": "https://github.com/Codeception/module-filesystem/tree/2.0.2" + }, + "time": "2021-12-18T14:11:30+00:00" + }, + { + "name": "codeception/module-yii2", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-yii2.git", + "reference": "14269d059b8eaedf3d414a673907bd874cd4ed04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-yii2/zipball/14269d059b8eaedf3d414a673907bd874cd4ed04", + "reference": "14269d059b8eaedf3d414a673907bd874cd4ed04", + "shasum": "" + }, + "require": { + "codeception/codeception": "^4.0", + "codeception/lib-innerbrowser": "^1.0", + "php": ">=5.6.0 <=8.1 | ~8.1.0" + }, + "require-dev": { + "codeception/module-asserts": "^1.3", + "codeception/module-filesystem": "^1.0", + "codeception/verify": "<2", + "codemix/yii2-localeurls": "^1.7", + "yiisoft/yii2": "dev-master", + "yiisoft/yii2-app-advanced": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander Makarov" + }, + { + "name": "Sam Mouse" + }, + { + "name": "Michael Bodnarchuk" + } + ], + "description": "Codeception module for Yii2 framework", + "homepage": "http://codeception.com/", + "keywords": [ + "codeception", + "yii2" + ], + "support": { + "issues": "https://github.com/Codeception/module-yii2/issues", + "source": "https://github.com/Codeception/module-yii2/tree/1.1.5" + }, + "time": "2021-12-30T09:16:01+00:00" + }, + { + "name": "codeception/phpunit-wrapper", + "version": "9.0.9", + "source": { + "type": "git", + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "7439a53ae367986e9c22b2ac00f9d7376bb2f8cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7439a53ae367986e9c22b2ac00f9d7376bb2f8cf", + "reference": "7439a53ae367986e9c22b2ac00f9d7376bb2f8cf", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "phpunit/phpunit": "^9.0" + }, + "require-dev": { + "codeception/specify": "*", + "consolidation/robo": "^3.0.0-alpha3", + "vlucas/phpdotenv": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + }, + { + "name": "Naktibalda" + } + ], + "description": "PHPUnit classes used by Codeception", + "support": { + "issues": "https://github.com/Codeception/phpunit-wrapper/issues", + "source": "https://github.com/Codeception/phpunit-wrapper/tree/9.0.9" + }, + "time": "2022-05-23T06:24:11+00:00" + }, + { + "name": "codeception/stub", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Stub.git", + "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/18a148dacd293fc7b044042f5aa63a82b08bff5d", + "reference": "18a148dacd293fc7b044042f5aa63a82b08bff5d", + "shasum": "" + }, + "require": { + "php": "^7.4 | ^8.0", + "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | 10.0.x-dev" + }, + "require-dev": { + "consolidation/robo": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "support": { + "issues": "https://github.com/Codeception/Stub/issues", + "source": "https://github.com/Codeception/Stub/tree/4.0.2" + }, + "time": "2022-01-31T19:25:15+00:00" + }, + { + "name": "codeception/verify", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Verify.git", + "reference": "2f8f5ce6ea1be5c011f77eb7f1ca0c9f4d551513" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Verify/zipball/2f8f5ce6ea1be5c011f77eb7f1ca0c9f4d551513", + "reference": "2f8f5ce6ea1be5c011f77eb7f1ca0c9f4d551513", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^7.4 || ^8.0", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/Codeception/bootstrap.php" + ], + "psr-4": { + "Codeception\\": "src\\Codeception" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@codeception.com" + }, + { + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" + } + ], + "description": "BDD assertion library for PHPUnit", + "support": { + "issues": "https://github.com/Codeception/Verify/issues", + "source": "https://github.com/Codeception/Verify/tree/2.3.0" + }, + "time": "2023-02-09T07:21:39+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.21.0", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", + "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "v1.21-dev" + } + }, + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + }, + "time": "2022-12-13T13:54:32+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "b635f279edd83fc275f822a1188157ffea568ff6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6", + "reference": "b635f279edd83fc275f822a1188157ffea568ff6", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-04-17T16:11:26+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.15.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + }, + "time": "2023-03-05T19:49:14+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpspec/php-diff", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/phpspec/php-diff.git", + "reference": "fc1156187f9f6c8395886fe85ed88a0a245d72e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/php-diff/zipball/fc1156187f9f6c8395886fe85ed88a0a245d72e9", + "reference": "fc1156187f9f6c8395886fe85ed88a0a245d72e9", + "shasum": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Diff": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Chris Boulton", + "homepage": "http://github.com/chrisboulton" + } + ], + "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", + "support": { + "source": "https://github.com/phpspec/php-diff/tree/v1.1.3" + }, + "time": "2020-09-18T13:47:07+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.26", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-03-06T12:58:08+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.28", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/954ca3113a03bf780d22f07bf055d883ee04b65e", + "reference": "954ca3113a03bf780d22f07bf055d883ee04b65e", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.28" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-01-14T12:32:24+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "e616d01114759c4c489f93b099585439f795fe35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + }, + "time": "2023-04-10T20:10:41+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-02-14T08:28:10+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c", + "reference": "9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/dom-crawler": "~2.8|~3.0|~4.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.23", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "90f21e27d0d88ce38720556dd164d4a1e4c3934c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/90f21e27d0d88ce38720556dd164d4a1e4c3934c", + "reference": "90f21e27d0d88ce38720556dd164d4a1e4c3934c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.23" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-24T18:47:29+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v5.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "95f3c7468db1da8cc360b24fa2a26e7cefcb355d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/95f3c7468db1da8cc360b24fa2a26e7cefcb355d", + "reference": "95f3c7468db1da8cc360b24fa2a26e7cefcb355d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v5.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-14T08:03:56+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-01T10:25:55+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v4.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "4b8daf6c56801e6d664224261cb100b73edc78a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b8daf6c56801e6d664224261cb100b73edc78a5", + "reference": "4b8daf6c56801e6d664224261cb100b73edc78a5", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "masterminds/html5": "<2.6" + }, + "require-dev": { + "masterminds/html5": "^2.6", + "symfony/css-selector": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v4.4.45" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-08-03T12:57:57+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v5.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "1df20e45d56da29a4b1d8259dd6e950acbf1b13f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1df20e45d56da29a4b1d8259dd6e950acbf1b13f", + "reference": "1df20e45d56da29a4b1d8259dd6e950acbf1b13f", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.22" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-17T11:31:58+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", + "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-01T10:32:47+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "078e9a5e1871fcfe6a5ce421b539344c21afef19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/078e9a5e1871fcfe6a5ce421b539344c21afef19", + "reference": "078e9a5e1871fcfe6a5ce421b539344c21afef19", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-16T09:33:00+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-01T10:32:47+00:00" + }, + { + "name": "symfony/string", + "version": "v6.2.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.2.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-20T16:06:02+00:00" + }, + { + "name": "symfony/yaml", + "version": "v5.4.23", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "4cd2e3ea301aadd76a4172756296fe552fb45b0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4cd2e3ea301aadd76a4172756296fe552fb45b0b", + "reference": "4cd2e3ea301aadd76a4172756296fe552fb45b0b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.3" + }, + "require-dev": { + "symfony/console": "^5.3|^6.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v5.4.23" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-23T19:33:36+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "yiisoft/yii2-debug", + "version": "2.1.22", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-debug.git", + "reference": "c0fa388c56b64edfb92987fdcc37d7a0243170d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-debug/zipball/c0fa388c56b64edfb92987fdcc37d7a0243170d7", + "reference": "c0fa388c56b64edfb92987fdcc37d7a0243170d7", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.4", + "yiisoft/yii2": "~2.0.13" + }, + "require-dev": { + "cweagans/composer-patches": "^1.7", + "phpunit/phpunit": "4.8.34", + "yiisoft/yii2-coding-standards": "~2.0", + "yiisoft/yii2-swiftmailer": "*" + }, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + }, + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php7.patch", + "Fix PHP 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php8.patch", + "Fix PHP 8.1 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php81.patch" + } + } + }, + "autoload": { + "psr-4": { + "yii\\debug\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + }, + { + "name": "Simon Karlen", + "email": "simi.albi@outlook.com" + } + ], + "description": "The debugger extension for the Yii framework", + "keywords": [ + "debug", + "debugger", + "yii2" + ], + "support": { + "forum": "http://www.yiiframework.com/forum/", + "irc": "irc://irc.freenode.net/yii", + "issues": "https://github.com/yiisoft/yii2-debug/issues", + "source": "https://github.com/yiisoft/yii2-debug", + "wiki": "http://www.yiiframework.com/wiki/" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-debug", + "type": "tidelift" + } + ], + "time": "2022-11-18T17:29:27+00:00" + }, + { + "name": "yiisoft/yii2-faker", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-faker.git", + "reference": "8c361657143bfaea58ff7dcc9bf51f1991a46f5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-faker/zipball/8c361657143bfaea58ff7dcc9bf51f1991a46f5d", + "reference": "8c361657143bfaea58ff7dcc9bf51f1991a46f5d", + "shasum": "" + }, + "require": { + "fakerphp/faker": "~1.9|~1.10", + "yiisoft/yii2": "~2.0.0" + }, + "require-dev": { + "cweagans/composer-patches": "^1.7", + "phpunit/phpunit": "4.8.34" + }, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + }, + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php7.patch", + "Fix PHP 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php8.patch" + } + } + }, + "autoload": { + "psr-4": { + "yii\\faker\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Mark Jebri", + "email": "mark.github@yandex.ru" + } + ], + "description": "Fixture generator. The Faker integration for the Yii framework.", + "keywords": [ + "Fixture", + "faker", + "yii2" + ], + "support": { + "forum": "http://www.yiiframework.com/forum/", + "irc": "irc://irc.freenode.net/yii", + "issues": "https://github.com/yiisoft/yii2-faker/issues", + "source": "https://github.com/yiisoft/yii2-faker", + "wiki": "http://www.yiiframework.com/wiki/" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-faker", + "type": "tidelift" + } + ], + "time": "2020-11-10T12:27:35+00:00" + }, + { + "name": "yiisoft/yii2-gii", + "version": "2.2.5", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-gii.git", + "reference": "e2f2dcf0f16713e678df6ba70362c99a215a8f72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-gii/zipball/e2f2dcf0f16713e678df6ba70362c99a215a8f72", + "reference": "e2f2dcf0f16713e678df6ba70362c99a215a8f72", + "shasum": "" + }, + "require": { + "phpspec/php-diff": "^1.1.0", + "yiisoft/yii2": "~2.0.14" + }, + "require-dev": { + "cweagans/composer-patches": "^1.7", + "phpunit/phpunit": "4.8.34", + "yiisoft/yii2-coding-standards": "~2.0" + }, + "type": "yii2-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + }, + "composer-exit-on-patch-failure": true, + "patches": { + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" + }, + "phpunit/phpunit": { + "Fix PHP 7 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php7.patch", + "Fix PHP 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php8.patch" + } + } + }, + "autoload": { + "psr-4": { + "yii\\gii\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + } + ], + "description": "The Gii extension for the Yii framework", + "keywords": [ + "code generator", + "gii", + "yii2" + ], + "support": { + "forum": "http://www.yiiframework.com/forum/", + "irc": "irc://irc.freenode.net/yii", + "issues": "https://github.com/yiisoft/yii2-gii/issues", + "source": "https://github.com/yiisoft/yii2-gii", + "wiki": "http://www.yiiframework.com/wiki/" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/yiisoft/yii2-gii", + "type": "tidelift" + } + ], + "time": "2022-09-04T10:00:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "yiisoft/yii2-bootstrap5": 20, + "kartik-v/yii2-widget-fileinput": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/console/config/.gitignore b/console/config/.gitignore new file mode 100755 index 0000000..42799dd --- /dev/null +++ b/console/config/.gitignore @@ -0,0 +1,3 @@ +main-local.php +params-local.php +test-local.php diff --git a/console/config/bootstrap.php b/console/config/bootstrap.php new file mode 100755 index 0000000..b3d9bbc --- /dev/null +++ b/console/config/bootstrap.php @@ -0,0 +1 @@ + '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::class, + 'namespace' => 'common\fixtures', + ], + ], + 'components' => [ + 'log' => [ + 'targets' => [ + [ + 'class' => \yii\log\FileTarget::class, + 'levels' => ['error', 'warning'], + ], + ], + ], + ], + 'params' => $params, +]; diff --git a/console/config/params.php b/console/config/params.php new file mode 100755 index 0000000..6ebf279 --- /dev/null +++ b/console/config/params.php @@ -0,0 +1,5 @@ + 'admin@example.com', +]; diff --git a/console/config/test.php b/console/config/test.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/console/config/test.php @@ -0,0 +1,4 @@ +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}}'); + } +} diff --git a/console/migrations/m190124_110200_add_verification_token_column_to_user_table.php b/console/migrations/m190124_110200_add_verification_token_column_to_user_table.php new file mode 100755 index 0000000..4a20dc7 --- /dev/null +++ b/console/migrations/m190124_110200_add_verification_token_column_to_user_table.php @@ -0,0 +1,16 @@ +addColumn('{{%user}}', 'verification_token', $this->string()->defaultValue(null)); + } + + public function down() + { + $this->dropColumn('{{%user}}', 'verification_token'); + } +} diff --git a/console/migrations/m230503_111312_create_news_table.php b/console/migrations/m230503_111312_create_news_table.php new file mode 100755 index 0000000..148fcbf --- /dev/null +++ b/console/migrations/m230503_111312_create_news_table.php @@ -0,0 +1,30 @@ +createTable('{{%news}}', [ + 'id' => $this->primaryKey(), + 'title' => $this->string(), + 'text' => $this->text(), + 'slug' => $this->string()->unique() + ]); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('{{%news}}'); + } +} diff --git a/console/migrations/m230503_151339_create_profile_table.php b/console/migrations/m230503_151339_create_profile_table.php new file mode 100755 index 0000000..2d7d4c7 --- /dev/null +++ b/console/migrations/m230503_151339_create_profile_table.php @@ -0,0 +1,33 @@ +createTable('{{%profile}}', [ + 'id' => $this->primaryKey(), + 'user_id' => $this->integer(), + 'created_at' => $this->timestamp(), + 'image' => $this->string(), + 'activity' => $this->tinyInteger() + ]); + $this->addForeignKey('user_foreign', 'profile', 'user_id', 'user', 'id'); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropForeignKey('user_foreign', 'profile'); + $this->dropTable('{{%profile}}'); + } +} diff --git a/console/migrations/m230503_151354_create_text_table.php b/console/migrations/m230503_151354_create_text_table.php new file mode 100755 index 0000000..e57edd2 --- /dev/null +++ b/console/migrations/m230503_151354_create_text_table.php @@ -0,0 +1,33 @@ +createTable('{{%text}}', [ + 'id' => $this->primaryKey(), + 'profile_id' => $this->integer(), + 'title' => $this->string(), + 'text' => $this->text(), + 'language' => $this->string() + ]); + $this->addForeignKey('profile_foreign', 'text', 'profile_id', 'profile', 'id'); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropForeignKey('profile_foreign', 'text'); + $this->dropTable('{{%text}}'); + } +} diff --git a/console/models/.gitkeep b/console/models/.gitkeep new file mode 100755 index 0000000..72e8ffc --- /dev/null +++ b/console/models/.gitkeep @@ -0,0 +1 @@ +* diff --git a/console/runtime/.gitignore b/console/runtime/.gitignore new file mode 100755 index 0000000..c96a04f --- /dev/null +++ b/console/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100755 index 0000000..c8ad750 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +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 \ No newline at end of file diff --git a/environments/dev/backend/config/codeception-local.php b/environments/dev/backend/config/codeception-local.php new file mode 100755 index 0000000..2d875dd --- /dev/null +++ b/environments/dev/backend/config/codeception-local.php @@ -0,0 +1,11 @@ + [ + 'request' => [ + // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation + 'cookieValidationKey' => '', + ], + ], +]; + +if (!YII_ENV_TEST) { + // configuration adjustments for 'dev' environment + $config['bootstrap'][] = 'debug'; + $config['modules']['debug'] = [ + 'class' => \yii\debug\Module::class, + ]; + + $config['bootstrap'][] = 'gii'; + $config['modules']['gii'] = [ + 'class' => \yii\gii\Module::class, + ]; +} + +return $config; diff --git a/environments/dev/backend/config/params-local.php b/environments/dev/backend/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/dev/backend/config/params-local.php @@ -0,0 +1,4 @@ +run(); diff --git a/environments/dev/backend/web/index.php b/environments/dev/backend/web/index.php new file mode 100755 index 0000000..d0b6601 --- /dev/null +++ b/environments/dev/backend/web/index.php @@ -0,0 +1,18 @@ +run(); diff --git a/environments/dev/backend/web/robots.txt b/environments/dev/backend/web/robots.txt new file mode 100755 index 0000000..77470cb --- /dev/null +++ b/environments/dev/backend/web/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/environments/dev/common/config/codeception-local.php b/environments/dev/common/config/codeception-local.php new file mode 100755 index 0000000..654f801 --- /dev/null +++ b/environments/dev/common/config/codeception-local.php @@ -0,0 +1,16 @@ + [ + 'request' => [ + // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation + 'cookieValidationKey' => '', + ], + ], + ] +); diff --git a/environments/dev/common/config/main-local.php b/environments/dev/common/config/main-local.php new file mode 100755 index 0000000..d00db3e --- /dev/null +++ b/environments/dev/common/config/main-local.php @@ -0,0 +1,43 @@ + [ + 'db' => [ + 'class' => \yii\db\Connection::class, + 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + ], + 'mailer' => [ + 'class' => \yii\symfonymailer\Mailer::class, + 'viewPath' => '@common/mail', + // send all mails to a file by default. + 'useFileTransport' => true, + // You have to set + // + // 'useFileTransport' => false, + // + // and configure a transport for the mailer to send real emails. + // + // SMTP server example: + // 'transport' => [ + // 'scheme' => 'smtps', + // 'host' => '', + // 'username' => '', + // 'password' => '', + // 'port' => 465, + // 'dsn' => 'native://default', + // ], + // + // DSN example: + // 'transport' => [ + // 'dsn' => 'smtp://user:pass@smtp.example.com:25', + // ], + // + // See: https://symfony.com/doc/current/mailer.html#using-built-in-transports + // Or if you use a 3rd party service, see: + // https://symfony.com/doc/current/mailer.html#using-a-3rd-party-transport + ], + ], +]; diff --git a/environments/dev/common/config/params-local.php b/environments/dev/common/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/dev/common/config/params-local.php @@ -0,0 +1,4 @@ + [ + 'db' => [ + 'dsn' => 'mysql:host=localhost;dbname=yii2advanced_test', + ], + ], +]; diff --git a/environments/dev/console/config/main-local.php b/environments/dev/console/config/main-local.php new file mode 100755 index 0000000..a3246e0 --- /dev/null +++ b/environments/dev/console/config/main-local.php @@ -0,0 +1,8 @@ + ['gii'], + 'modules' => [ + 'gii' => 'yii\gii\Module', + ], +]; diff --git a/environments/dev/console/config/params-local.php b/environments/dev/console/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/dev/console/config/params-local.php @@ -0,0 +1,4 @@ + [ + 'request' => [ + // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation + 'cookieValidationKey' => '', + ], + ], +]; + +if (!YII_ENV_TEST) { + // configuration adjustments for 'dev' environment + $config['bootstrap'][] = 'debug'; + $config['modules']['debug'] = [ + 'class' => \yii\debug\Module::class, + ]; + + $config['bootstrap'][] = 'gii'; + $config['modules']['gii'] = [ + 'class' => \yii\gii\Module::class, + ]; +} + +return $config; diff --git a/environments/dev/frontend/config/params-local.php b/environments/dev/frontend/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/dev/frontend/config/params-local.php @@ -0,0 +1,4 @@ +run(); diff --git a/environments/dev/frontend/web/index.php b/environments/dev/frontend/web/index.php new file mode 100755 index 0000000..d0b6601 --- /dev/null +++ b/environments/dev/frontend/web/index.php @@ -0,0 +1,18 @@ +run(); diff --git a/environments/dev/frontend/web/robots.txt b/environments/dev/frontend/web/robots.txt new file mode 100755 index 0000000..77470cb --- /dev/null +++ b/environments/dev/frontend/web/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/environments/dev/yii b/environments/dev/yii new file mode 100755 index 0000000..b93b5cf --- /dev/null +++ b/environments/dev/yii @@ -0,0 +1,24 @@ +#!/usr/bin/env php +run(); +exit($exitCode); diff --git a/environments/dev/yii_test b/environments/dev/yii_test new file mode 100755 index 0000000..63a4e7a --- /dev/null +++ b/environments/dev/yii_test @@ -0,0 +1,28 @@ +#!/usr/bin/env php +run(); +exit($exitCode); diff --git a/environments/dev/yii_test.bat b/environments/dev/yii_test.bat new file mode 100755 index 0000000..29fbbea --- /dev/null +++ b/environments/dev/yii_test.bat @@ -0,0 +1,15 @@ +@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 diff --git a/environments/index.php b/environments/index.php new file mode 100755 index 0000000..1083c32 --- /dev/null +++ b/environments/index.php @@ -0,0 +1,68 @@ + [ + * 'path' => 'directory storing the local files', + * 'skipFiles' => [ + * // list of files that should only be 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', + ], + ], +]; diff --git a/environments/prod/backend/config/main-local.php b/environments/prod/backend/config/main-local.php new file mode 100755 index 0000000..babe4a4 --- /dev/null +++ b/environments/prod/backend/config/main-local.php @@ -0,0 +1,10 @@ + [ + 'request' => [ + // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation + 'cookieValidationKey' => '', + ], + ], +]; diff --git a/environments/prod/backend/config/params-local.php b/environments/prod/backend/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/prod/backend/config/params-local.php @@ -0,0 +1,4 @@ +run(); diff --git a/environments/prod/backend/web/robots.txt b/environments/prod/backend/web/robots.txt new file mode 100755 index 0000000..77470cb --- /dev/null +++ b/environments/prod/backend/web/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/environments/prod/common/config/main-local.php b/environments/prod/common/config/main-local.php new file mode 100755 index 0000000..da5a6d3 --- /dev/null +++ b/environments/prod/common/config/main-local.php @@ -0,0 +1,17 @@ + [ + 'db' => [ + 'class' => \yii\db\Connection::class, + 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + ], + 'mailer' => [ + 'class' => \yii\symfonymailer\Mailer::class, + 'viewPath' => '@common/mail', + ], + ], +]; diff --git a/environments/prod/common/config/params-local.php b/environments/prod/common/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/prod/common/config/params-local.php @@ -0,0 +1,4 @@ + [ + 'request' => [ + // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation + 'cookieValidationKey' => '', + ], + ], +]; diff --git a/environments/prod/frontend/config/params-local.php b/environments/prod/frontend/config/params-local.php new file mode 100755 index 0000000..b625128 --- /dev/null +++ b/environments/prod/frontend/config/params-local.php @@ -0,0 +1,4 @@ +run(); diff --git a/environments/prod/frontend/web/robots.txt b/environments/prod/frontend/web/robots.txt new file mode 100755 index 0000000..14267e9 --- /dev/null +++ b/environments/prod/frontend/web/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / \ No newline at end of file diff --git a/environments/prod/yii b/environments/prod/yii new file mode 100755 index 0000000..5b0d890 --- /dev/null +++ b/environments/prod/yii @@ -0,0 +1,24 @@ +#!/usr/bin/env php +run(); +exit($exitCode); diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100755 index 0000000..a0487d2 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,4 @@ +FROM yiisoftware/yii2-php:8.1-apache + +# Change document root for Apache +RUN sed -i -e 's|/app/web|/app/frontend/web|g' /etc/apache2/sites-available/000-default.conf diff --git a/frontend/assets/AppAsset.php b/frontend/assets/AppAsset.php new file mode 100755 index 0000000..f8478f6 --- /dev/null +++ b/frontend/assets/AppAsset.php @@ -0,0 +1,23 @@ + 'app-frontend', + 'basePath' => dirname(__DIR__), + 'bootstrap' => ['log'], + 'controllerNamespace' => 'frontend\controllers', + 'components' => [ + 'request' => [ + 'baseUrl' => '', + '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::class, + 'levels' => ['error', 'warning'], + ], + ], + ], + 'errorHandler' => [ + 'errorAction' => 'site/error', + ], + 'urlManager' => [ + 'enablePrettyUrl' => true, + 'showScriptName' => false, + 'rules' => [ + '/new/' => 'news/get-one', + '/text//' => 'news/text', + ], + ], + ], + 'params' => $params, +]; diff --git a/frontend/config/params.php b/frontend/config/params.php new file mode 100755 index 0000000..7f754b9 --- /dev/null +++ b/frontend/config/params.php @@ -0,0 +1,4 @@ + 'admin@example.com', +]; diff --git a/frontend/config/test.php b/frontend/config/test.php new file mode 100755 index 0000000..55d820c --- /dev/null +++ b/frontend/config/test.php @@ -0,0 +1,18 @@ + 'app-frontend-tests', + 'components' => [ + 'assetManager' => [ + 'basePath' => __DIR__ . '/../web/assets', + ], + 'urlManager' => [ + 'showScriptName' => true, + ], + 'request' => [ + 'cookieValidationKey' => 'test', + ], + 'mailer' => [ + 'messageClass' => \yii\symfonymailer\Message::class + ] + ], +]; diff --git a/frontend/controllers/NewsController.php b/frontend/controllers/NewsController.php new file mode 100755 index 0000000..3fcc4f3 --- /dev/null +++ b/frontend/controllers/NewsController.php @@ -0,0 +1,117 @@ + [ + 'class' => AccessControl::class, + 'only' => ['logout', 'signup'], + 'rules' => [ + [ + 'actions' => ['signup'], + 'allow' => true, + 'roles' => ['?'], + ], + [ + 'actions' => ['logout'], + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'logout' => ['post'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function actions() + { + return [ + 'error' => [ + 'class' => \yii\web\ErrorAction::class, + ], + 'captcha' => [ + 'class' => \yii\captcha\CaptchaAction::class, + 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, + ], + ]; + } + + /** + * Displays homepage. + * + * @return mixed + */ + public function actionIndex() + { + $news = News::find()->all(); + + return $this->render('index', [ + 'news' => $news + ]); + } + + /** + * @param null $slug + * @return mixed + */ + public function actionGetOne($slug = null) + { + $new = News::find()->where(['slug' => $slug])->one(); + return $this->render('one', [ + 'new' => $new + ]); + } + + public function actionData() + { + $model = new DataForm(); + + if (Yii::$app->request->isPost) { + if ($model->load(Yii::$app->request->post()) && $model->validate()) { + Yii::$app->session->setFlash('success', 'Валидация прошла успешно'); + } else { + Yii::$app->session->setFlash('error', 'Что-то не так'); + } + return $this->refresh(); + } + + return $this->render('data', [ + 'model' => $model, + ]); + } + + public function actionText($id, $slug) + { + $texts = UserService::run()->getTextByLanguage($id, $slug); + + return $this->render('text', [ + 'texts' => $texts + ]); + } +} \ No newline at end of file diff --git a/frontend/controllers/SiteController.php b/frontend/controllers/SiteController.php new file mode 100755 index 0000000..6de678a --- /dev/null +++ b/frontend/controllers/SiteController.php @@ -0,0 +1,259 @@ + [ + 'class' => AccessControl::class, + 'only' => ['logout', 'signup'], + 'rules' => [ + [ + 'actions' => ['signup'], + 'allow' => true, + 'roles' => ['?'], + ], + [ + 'actions' => ['logout'], + 'allow' => true, + 'roles' => ['@'], + ], + ], + ], + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'logout' => ['post'], + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function actions() + { + return [ + 'error' => [ + 'class' => \yii\web\ErrorAction::class, + ], + 'captcha' => [ + 'class' => \yii\captcha\CaptchaAction::class, + '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(); + } + + $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(); + } + + /** + * Displays contact page. + * + * @return mixed + */ + public function actionContact() + { + $model = new ContactForm(); + if ($model->load(Yii::$app->request->post()) && $model->validate()) { + if ($model->sendEmail(Yii::$app->params['adminEmail'])) { + Yii::$app->session->setFlash('success', 'Thank you for contacting us. We will respond to you as soon as possible.'); + } else { + Yii::$app->session->setFlash('error', 'There was an error sending your message.'); + } + + return $this->refresh(); + } + + return $this->render('contact', [ + 'model' => $model, + ]); + } + + /** + * Displays about page. + * + * @return mixed + */ + public function actionAbout() + { + return $this->render('about'); + } + + /** + * 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(); + } + + 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()) && 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 + ]); + } +} diff --git a/frontend/models/ContactForm.php b/frontend/models/ContactForm.php new file mode 100755 index 0000000..1dd419c --- /dev/null +++ b/frontend/models/ContactForm.php @@ -0,0 +1,61 @@ + 'Verification Code', + ]; + } + + /** + * Sends an email to the specified email address using the information collected by this model. + * + * @param string $email the target email address + * @return bool whether the email was sent + */ + public function sendEmail($email) + { + return Yii::$app->mailer->compose() + ->setTo($email) + ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']]) + ->setReplyTo([$this->email => $this->name]) + ->setSubject($this->subject) + ->setTextBody($this->body) + ->send(); + } +} diff --git a/frontend/models/DataForm.php b/frontend/models/DataForm.php new file mode 100755 index 0000000..aae9f9e --- /dev/null +++ b/frontend/models/DataForm.php @@ -0,0 +1,40 @@ + 'Электронная почта', + 'fio' => 'ФИО', + 'phone' => 'Номер телефона', + ]; + } +} diff --git a/frontend/models/PasswordResetRequestForm.php b/frontend/models/PasswordResetRequestForm.php new file mode 100755 index 0000000..db963e3 --- /dev/null +++ b/frontend/models/PasswordResetRequestForm.php @@ -0,0 +1,69 @@ + '\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(); + } +} diff --git a/frontend/models/ResendVerificationEmailForm.php b/frontend/models/ResendVerificationEmailForm.php new file mode 100755 index 0000000..8fb265b --- /dev/null +++ b/frontend/models/ResendVerificationEmailForm.php @@ -0,0 +1,61 @@ + '\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(); + } +} diff --git a/frontend/models/ResetPasswordForm.php b/frontend/models/ResetPasswordForm.php new file mode 100755 index 0000000..31c786f --- /dev/null +++ b/frontend/models/ResetPasswordForm.php @@ -0,0 +1,67 @@ +_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' => Yii::$app->params['user.passwordMinLength']], + ]; + } + + /** + * Resets password. + * + * @return bool if password was reset. + */ + public function resetPassword() + { + $user = $this->_user; + $user->setPassword($this->password); + $user->removePasswordResetToken(); + $user->generateAuthKey(); + + return $user->save(false); + } +} diff --git a/frontend/models/SignupForm.php b/frontend/models/SignupForm.php new file mode 100755 index 0000000..e42a8ca --- /dev/null +++ b/frontend/models/SignupForm.php @@ -0,0 +1,80 @@ + '\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' => Yii::$app->params['user.passwordMinLength']], + ]; + } + + /** + * 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(); + } +} diff --git a/frontend/models/VerifyEmailForm.php b/frontend/models/VerifyEmailForm.php new file mode 100755 index 0000000..0740746 --- /dev/null +++ b/frontend/models/VerifyEmailForm.php @@ -0,0 +1,52 @@ +_user = User::findByVerificationToken($token); + if (!$this->_user) { + throw new InvalidArgumentException('Wrong verify email token.'); + } + parent::__construct($config); + } + + /** + * Verify email + * + * @return User|null the saved model or null if saving fails + */ + public function verifyEmail() + { + $user = $this->_user; + $user->status = User::STATUS_ACTIVE; + return $user->save(false) ? $user : null; + } +} diff --git a/frontend/runtime/.gitignore b/frontend/runtime/.gitignore new file mode 100755 index 0000000..c96a04f --- /dev/null +++ b/frontend/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/frontend/tests/_bootstrap.php b/frontend/tests/_bootstrap.php new file mode 100755 index 0000000..637ce14 --- /dev/null +++ b/frontend/tests/_bootstrap.php @@ -0,0 +1,10 @@ + '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', + ], + [ + 'username' => 'test.test', + 'auth_key' => 'O87GkY3_UfmMHYkyezZ7QLfmkKNsllzT', + // Test1234 + 'password_hash' => 'O87GkY3_UfmMHYkyezZ7QLfmkKNsllzT', + 'email' => 'test@mail.com', + 'status' => '9', + 'created_at' => '1548675330', + 'updated_at' => '1548675330', + 'verification_token' => '4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330', + ], +]; diff --git a/frontend/tests/_data/user.php b/frontend/tests/_data/user.php new file mode 100755 index 0000000..0b94332 --- /dev/null +++ b/frontend/tests/_data/user.php @@ -0,0 +1,45 @@ + 'okirlin', + 'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv', + 'password_hash' => '$2y$13$CXT0Rkle1EMJ/c1l5bylL.EylfmQ39O5JlHJVFpNn618OUS1HwaIi', + 'password_reset_token' => 't5GU9NwpuGYSfb7FEZMAxqtuz2PkEvv_' . time(), + 'created_at' => '1391885313', + 'updated_at' => '1391885313', + 'email' => 'brady.renner@rutherford.com', + ], + [ + 'username' => 'troy.becker', + 'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS3Lp', + 'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2', + 'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_' . time(), + 'created_at' => '1391885313', + 'updated_at' => '1391885313', + 'email' => 'nicolas.dianna@hotmail.com', + 'status' => '0', + ], + [ + 'username' => 'test.test', + 'auth_key' => 'O87GkY3_UfmMHYkyezZ7QLfmkKNsllzT', + //Test1234 + 'password_hash' => '$2y$13$d17z0w/wKC4LFwtzBcmx6up4jErQuandJqhzKGKczfWuiEhLBtQBK', + 'email' => 'test@mail.com', + 'status' => '9', + 'created_at' => '1548675330', + 'updated_at' => '1548675330', + 'verification_token' => '4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330', + ], + [ + 'username' => 'test2.test', + 'auth_key' => '4XXdVqi3rDpa_a6JH6zqVreFxUPcUPvJ', + //Test1234 + 'password_hash' => '$2y$13$d17z0w/wKC4LFwtzBcmx6up4jErQuandJqhzKGKczfWuiEhLBtQBK', + 'email' => 'test2@mail.com', + 'status' => '10', + 'created_at' => '1548675330', + 'updated_at' => '1548675330', + 'verification_token' => 'already_used_token_1548675330', + ], +]; diff --git a/frontend/tests/_output/.gitignore b/frontend/tests/_output/.gitignore new file mode 100755 index 0000000..d6b7ef3 --- /dev/null +++ b/frontend/tests/_output/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/frontend/tests/_support/.gitignore b/frontend/tests/_support/.gitignore new file mode 100755 index 0000000..36e264c --- /dev/null +++ b/frontend/tests/_support/.gitignore @@ -0,0 +1 @@ +_generated diff --git a/frontend/tests/_support/FunctionalTester.php b/frontend/tests/_support/FunctionalTester.php new file mode 100755 index 0000000..77ad9c5 --- /dev/null +++ b/frontend/tests/_support/FunctionalTester.php @@ -0,0 +1,34 @@ +see($message, '.invalid-feedback'); + } + + public function dontSeeValidationError($message) + { + $this->dontSee($message, '.invalid-feedback'); + } +} diff --git a/frontend/tests/_support/UnitTester.php b/frontend/tests/_support/UnitTester.php new file mode 100755 index 0000000..91557de --- /dev/null +++ b/frontend/tests/_support/UnitTester.php @@ -0,0 +1,26 @@ +amOnRoute(Url::toRoute('/site/index')); + $I->see('My Application'); + + $I->seeLink('About'); + $I->click('About'); + $I->wait(2); // wait for page to be opened + + $I->see('This is the About page.'); + } +} diff --git a/frontend/tests/acceptance/_bootstrap.php b/frontend/tests/acceptance/_bootstrap.php new file mode 100755 index 0000000..47716f0 --- /dev/null +++ b/frontend/tests/acceptance/_bootstrap.php @@ -0,0 +1,16 @@ + 'davert']); + * ``` + * + * In Cept + * + * ```php + * \Codeception\Util\Fixtures::get('user1'); + * ``` + */ \ No newline at end of file diff --git a/frontend/tests/functional.suite.yml b/frontend/tests/functional.suite.yml new file mode 100755 index 0000000..d66644d --- /dev/null +++ b/frontend/tests/functional.suite.yml @@ -0,0 +1,7 @@ +suite_namespace: frontend\tests\functional +actor: FunctionalTester +modules: + enabled: + - Filesystem + - Yii2 + - Asserts diff --git a/frontend/tests/functional/AboutCest.php b/frontend/tests/functional/AboutCest.php new file mode 100755 index 0000000..1bcce2b --- /dev/null +++ b/frontend/tests/functional/AboutCest.php @@ -0,0 +1,14 @@ +amOnRoute('site/about'); + $I->see('About', 'h1'); + } +} diff --git a/frontend/tests/functional/ContactCest.php b/frontend/tests/functional/ContactCest.php new file mode 100755 index 0000000..9b5f90b --- /dev/null +++ b/frontend/tests/functional/ContactCest.php @@ -0,0 +1,60 @@ +amOnRoute('site/contact'); + } + + public function checkContact(FunctionalTester $I) + { + $I->see('Contact', 'h1'); + } + + public function checkContactSubmitNoData(FunctionalTester $I) + { + $I->submitForm('#contact-form', []); + $I->see('Contact', 'h1'); + $I->seeValidationError('Name cannot be blank'); + $I->seeValidationError('Email cannot be blank'); + $I->seeValidationError('Subject cannot be blank'); + $I->seeValidationError('Body cannot be blank'); + $I->seeValidationError('The verification code is incorrect'); + } + + public function checkContactSubmitNotCorrectEmail(FunctionalTester $I) + { + $I->submitForm('#contact-form', [ + 'ContactForm[name]' => 'tester', + 'ContactForm[email]' => 'tester.email', + 'ContactForm[subject]' => 'test subject', + 'ContactForm[body]' => 'test content', + 'ContactForm[verifyCode]' => 'testme', + ]); + $I->seeValidationError('Email is not a valid email address.'); + $I->dontSeeValidationError('Name cannot be blank'); + $I->dontSeeValidationError('Subject cannot be blank'); + $I->dontSeeValidationError('Body cannot be blank'); + $I->dontSeeValidationError('The verification code is incorrect'); + } + + public function checkContactSubmitCorrectData(FunctionalTester $I) + { + $I->submitForm('#contact-form', [ + 'ContactForm[name]' => 'tester', + 'ContactForm[email]' => 'tester@example.com', + 'ContactForm[subject]' => 'test subject', + 'ContactForm[body]' => 'test content', + 'ContactForm[verifyCode]' => 'testme', + ]); + $I->seeEmailIsSent(); + $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); + } +} diff --git a/frontend/tests/functional/HomeCest.php b/frontend/tests/functional/HomeCest.php new file mode 100755 index 0000000..99048af --- /dev/null +++ b/frontend/tests/functional/HomeCest.php @@ -0,0 +1,17 @@ +amOnRoute(\Yii::$app->homeUrl); + $I->see('My Application'); + $I->seeLink('About'); + $I->click('About'); + $I->see('This is the About page.'); + } +} \ No newline at end of file diff --git a/frontend/tests/functional/LoginCest.php b/frontend/tests/functional/LoginCest.php new file mode 100755 index 0000000..ee160b6 --- /dev/null +++ b/frontend/tests/functional/LoginCest.php @@ -0,0 +1,66 @@ + [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'login_data.php', + ], + ]; + } + + public function _before(FunctionalTester $I) + { + $I->amOnRoute('site/login'); + } + + protected function formParams($login, $password) + { + return [ + 'LoginForm[username]' => $login, + 'LoginForm[password]' => $password, + ]; + } + + public function checkEmpty(FunctionalTester $I) + { + $I->submitForm('#login-form', $this->formParams('', '')); + $I->seeValidationError('Username cannot be blank.'); + $I->seeValidationError('Password cannot be blank.'); + } + + public function checkWrongPassword(FunctionalTester $I) + { + $I->submitForm('#login-form', $this->formParams('admin', 'wrong')); + $I->seeValidationError('Incorrect username or password.'); + } + + public function checkInactiveAccount(FunctionalTester $I) + { + $I->submitForm('#login-form', $this->formParams('test.test', 'Test1234')); + $I->seeValidationError('Incorrect username or password'); + } + + public function checkValidLogin(FunctionalTester $I) + { + $I->submitForm('#login-form', $this->formParams('erau', 'password_0')); + $I->see('Logout (erau)', 'form button[type=submit]'); + $I->dontSeeLink('Login'); + $I->dontSeeLink('Signup'); + } +} diff --git a/frontend/tests/functional/ResendVerificationEmailCest.php b/frontend/tests/functional/ResendVerificationEmailCest.php new file mode 100755 index 0000000..8ecaeac --- /dev/null +++ b/frontend/tests/functional/ResendVerificationEmailCest.php @@ -0,0 +1,83 @@ + [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php', + ], + ]; + } + + public function _before(FunctionalTester $I) + { + $I->amOnRoute('/site/resend-verification-email'); + } + + protected function formParams($email) + { + return [ + 'ResendVerificationEmailForm[email]' => $email + ]; + } + + public function checkPage(FunctionalTester $I) + { + $I->see('Resend verification email', 'h1'); + $I->see('Please fill out your email. A verification email will be sent there.'); + } + + public function checkEmptyField(FunctionalTester $I) + { + $I->submitForm($this->formId, $this->formParams('')); + $I->seeValidationError('Email cannot be blank.'); + } + + public function checkWrongEmailFormat(FunctionalTester $I) + { + $I->submitForm($this->formId, $this->formParams('abcd.com')); + $I->seeValidationError('Email is not a valid email address.'); + } + + public function checkWrongEmail(FunctionalTester $I) + { + $I->submitForm($this->formId, $this->formParams('wrong@email.com')); + $I->seeValidationError('There is no user with this email address.'); + } + + public function checkAlreadyVerifiedEmail(FunctionalTester $I) + { + $I->submitForm($this->formId, $this->formParams('test2@mail.com')); + $I->seeValidationError('There is no user with this email address.'); + } + + public function checkSendSuccessfully(FunctionalTester $I) + { + $I->submitForm($this->formId, $this->formParams('test@mail.com')); + $I->canSeeEmailIsSent(); + $I->seeRecord('common\models\User', [ + 'email' => 'test@mail.com', + 'username' => 'test.test', + 'status' => \common\models\User::STATUS_INACTIVE + ]); + $I->see('Check your email for further instructions.'); + } +} diff --git a/frontend/tests/functional/SignupCest.php b/frontend/tests/functional/SignupCest.php new file mode 100755 index 0000000..b83b795 --- /dev/null +++ b/frontend/tests/functional/SignupCest.php @@ -0,0 +1,59 @@ +amOnRoute('site/signup'); + } + + public function signupWithEmptyFields(FunctionalTester $I) + { + $I->see('Signup', 'h1'); + $I->see('Please fill out the following fields to signup:'); + $I->submitForm($this->formId, []); + $I->seeValidationError('Username cannot be blank.'); + $I->seeValidationError('Email cannot be blank.'); + $I->seeValidationError('Password cannot be blank.'); + + } + + public function signupWithWrongEmail(FunctionalTester $I) + { + $I->submitForm( + $this->formId, [ + 'SignupForm[username]' => 'tester', + 'SignupForm[email]' => 'ttttt', + 'SignupForm[password]' => 'tester_password', + ] + ); + $I->dontSee('Username cannot be blank.', '.invalid-feedback'); + $I->dontSee('Password cannot be blank.', '.invalid-feedback'); + $I->see('Email is not a valid email address.', '.invalid-feedback'); + } + + public function signupSuccessfully(FunctionalTester $I) + { + $I->submitForm($this->formId, [ + 'SignupForm[username]' => 'tester', + 'SignupForm[email]' => 'tester.email@example.com', + 'SignupForm[password]' => 'tester_password', + ]); + + $I->seeRecord('common\models\User', [ + 'username' => 'tester', + 'email' => 'tester.email@example.com', + 'status' => \common\models\User::STATUS_INACTIVE + ]); + + $I->seeEmailIsSent(); + $I->see('Thank you for registration. Please check your inbox for verification email.'); + } +} diff --git a/frontend/tests/functional/VerifyEmailCest.php b/frontend/tests/functional/VerifyEmailCest.php new file mode 100755 index 0000000..b227426 --- /dev/null +++ b/frontend/tests/functional/VerifyEmailCest.php @@ -0,0 +1,68 @@ + [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php', + ], + ]; + } + + public function checkEmptyToken(FunctionalTester $I) + { + $I->amOnRoute('site/verify-email', ['token' => '']); + $I->canSee('Bad Request', 'h1'); + $I->canSee('Verify email token cannot be blank.'); + } + + public function checkInvalidToken(FunctionalTester $I) + { + $I->amOnRoute('site/verify-email', ['token' => 'wrong_token']); + $I->canSee('Bad Request', 'h1'); + $I->canSee('Wrong verify email token.'); + } + + public function checkNoToken(FunctionalTester $I) + { + $I->amOnRoute('site/verify-email'); + $I->canSee('Bad Request', 'h1'); + $I->canSee('Missing required parameters: token'); + } + + public function checkAlreadyActivatedToken(FunctionalTester $I) + { + $I->amOnRoute('site/verify-email', ['token' => 'already_used_token_1548675330']); + $I->canSee('Bad Request', 'h1'); + $I->canSee('Wrong verify email token.'); + } + + public function checkSuccessVerification(FunctionalTester $I) + { + $I->amOnRoute('site/verify-email', ['token' => '4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330']); + $I->canSee('Your email has been confirmed!'); + $I->canSee('Congratulations!', 'h1'); + $I->see('Logout (test.test)', 'form button[type=submit]'); + + $I->seeRecord('common\models\User', [ + 'username' => 'test.test', + 'email' => 'test@mail.com', + 'status' => \common\models\User::STATUS_ACTIVE + ]); + } +} diff --git a/frontend/tests/functional/_bootstrap.php b/frontend/tests/functional/_bootstrap.php new file mode 100755 index 0000000..30ed54b --- /dev/null +++ b/frontend/tests/functional/_bootstrap.php @@ -0,0 +1,16 @@ + 'davert']); + * ``` + * + * In Cests + * + * ```php + * \Codeception\Util\Fixtures::get('user1'); + * ``` + */ \ No newline at end of file diff --git a/frontend/tests/unit.suite.yml b/frontend/tests/unit.suite.yml new file mode 100755 index 0000000..23ea63b --- /dev/null +++ b/frontend/tests/unit.suite.yml @@ -0,0 +1,7 @@ +suite_namespace: frontend\tests\unit +actor: UnitTester +modules: + enabled: + - Yii2: + part: [orm, email, fixtures] + - Asserts diff --git a/frontend/tests/unit/_bootstrap.php b/frontend/tests/unit/_bootstrap.php new file mode 100755 index 0000000..e432ce5 --- /dev/null +++ b/frontend/tests/unit/_bootstrap.php @@ -0,0 +1,16 @@ + 'davert']); + * ``` + * + * In Tests + * + * ```php + * \Codeception\Util\Fixtures::get('user1'); + * ``` + */ diff --git a/frontend/tests/unit/models/ContactFormTest.php b/frontend/tests/unit/models/ContactFormTest.php new file mode 100755 index 0000000..1fb4525 --- /dev/null +++ b/frontend/tests/unit/models/ContactFormTest.php @@ -0,0 +1,35 @@ +attributes = [ + 'name' => 'Tester', + 'email' => 'tester@example.com', + 'subject' => 'very important letter subject', + 'body' => 'body of current message', + ]; + + verify($model->sendEmail('admin@example.com'))->notEmpty(); + + // using Yii2 module actions to check email was sent + $this->tester->seeEmailIsSent(); + + /** @var MessageInterface $emailMessage */ + $emailMessage = $this->tester->grabLastSentEmail(); + verify($emailMessage)->instanceOf('yii\mail\MessageInterface'); + verify($emailMessage->getTo())->arrayHasKey('admin@example.com'); + verify($emailMessage->getFrom())->arrayHasKey('noreply@example.com'); + verify($emailMessage->getReplyTo())->arrayHasKey('tester@example.com'); + verify($emailMessage->getSubject())->equals('very important letter subject'); + verify($emailMessage->toString())->stringContainsString('body of current message'); + } +} diff --git a/frontend/tests/unit/models/PasswordResetRequestFormTest.php b/frontend/tests/unit/models/PasswordResetRequestFormTest.php new file mode 100755 index 0000000..8492fba --- /dev/null +++ b/frontend/tests/unit/models/PasswordResetRequestFormTest.php @@ -0,0 +1,59 @@ +tester->haveFixtures([ + 'user' => [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php' + ] + ]); + } + + public function testSendMessageWithWrongEmailAddress() + { + $model = new PasswordResetRequestForm(); + $model->email = 'not-existing-email@example.com'; + verify($model->sendEmail())->false(); + } + + public function testNotSendEmailsToInactiveUser() + { + $user = $this->tester->grabFixture('user', 1); + $model = new PasswordResetRequestForm(); + $model->email = $user['email']; + verify($model->sendEmail())->false(); + } + + public function testSendEmailSuccessfully() + { + $userFixture = $this->tester->grabFixture('user', 0); + + $model = new PasswordResetRequestForm(); + $model->email = $userFixture['email']; + $user = User::findOne(['password_reset_token' => $userFixture['password_reset_token']]); + + verify($model->sendEmail())->notEmpty(); + verify($user->password_reset_token)->notEmpty(); + + $emailMessage = $this->tester->grabLastSentEmail(); + verify($emailMessage)->instanceOf('yii\mail\MessageInterface'); + verify($emailMessage->getTo())->arrayHasKey($model->email); + verify($emailMessage->getFrom())->arrayHasKey(Yii::$app->params['supportEmail']); + } +} diff --git a/frontend/tests/unit/models/ResendVerificationEmailFormTest.php b/frontend/tests/unit/models/ResendVerificationEmailFormTest.php new file mode 100755 index 0000000..af3548a --- /dev/null +++ b/frontend/tests/unit/models/ResendVerificationEmailFormTest.php @@ -0,0 +1,85 @@ +tester->haveFixtures([ + 'user' => [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php' + ] + ]); + } + + public function testWrongEmailAddress() + { + $model = new ResendVerificationEmailForm(); + $model->attributes = [ + 'email' => 'aaa@bbb.cc' + ]; + + verify($model->validate())->false(); + verify($model->hasErrors())->true(); + verify($model->getFirstError('email'))->equals('There is no user with this email address.'); + } + + public function testEmptyEmailAddress() + { + $model = new ResendVerificationEmailForm(); + $model->attributes = [ + 'email' => '' + ]; + + verify($model->validate())->false(); + verify($model->hasErrors())->true(); + verify($model->getFirstError('email'))->equals('Email cannot be blank.'); + } + + public function testResendToActiveUser() + { + $model = new ResendVerificationEmailForm(); + $model->attributes = [ + 'email' => 'test2@mail.com' + ]; + + verify($model->validate())->false(); + verify($model->hasErrors())->true(); + verify($model->getFirstError('email'))->equals('There is no user with this email address.'); + } + + public function testSuccessfullyResend() + { + $model = new ResendVerificationEmailForm(); + $model->attributes = [ + 'email' => 'test@mail.com' + ]; + + verify($model->validate())->true(); + verify($model->hasErrors())->false(); + + verify($model->sendEmail())->true(); + $this->tester->seeEmailIsSent(); + + $mail = $this->tester->grabLastSentEmail(); + + verify($mail)->instanceOf('yii\mail\MessageInterface'); + verify($mail->getTo())->arrayHasKey('test@mail.com'); + verify($mail->getFrom())->arrayHasKey(\Yii::$app->params['supportEmail']); + verify($mail->getSubject())->equals('Account registration at ' . \Yii::$app->name); + verify($mail->toString())->stringContainsString('4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330'); + } +} diff --git a/frontend/tests/unit/models/ResetPasswordFormTest.php b/frontend/tests/unit/models/ResetPasswordFormTest.php new file mode 100755 index 0000000..8d969a6 --- /dev/null +++ b/frontend/tests/unit/models/ResetPasswordFormTest.php @@ -0,0 +1,44 @@ +tester->haveFixtures([ + 'user' => [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php' + ], + ]); + } + + public function testResetWrongToken() + { + $this->tester->expectThrowable('\yii\base\InvalidArgumentException', function() { + new ResetPasswordForm(''); + }); + + $this->tester->expectThrowable('\yii\base\InvalidArgumentException', function() { + new ResetPasswordForm('notexistingtoken_1391882543'); + }); + } + + public function testResetCorrectToken() + { + $user = $this->tester->grabFixture('user', 0); + $form = new ResetPasswordForm($user['password_reset_token']); + verify($form->resetPassword())->notEmpty(); + } + +} diff --git a/frontend/tests/unit/models/SignupFormTest.php b/frontend/tests/unit/models/SignupFormTest.php new file mode 100755 index 0000000..332ba6b --- /dev/null +++ b/frontend/tests/unit/models/SignupFormTest.php @@ -0,0 +1,72 @@ +tester->haveFixtures([ + 'user' => [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php' + ] + ]); + } + + public function testCorrectSignup() + { + $model = new SignupForm([ + 'username' => 'some_username', + 'email' => 'some_email@example.com', + 'password' => 'some_password', + ]); + + $user = $model->signup(); + verify($user)->notEmpty(); + + /** @var \common\models\User $user */ + $user = $this->tester->grabRecord('common\models\User', [ + 'username' => 'some_username', + 'email' => 'some_email@example.com', + 'status' => \common\models\User::STATUS_INACTIVE + ]); + + $this->tester->seeEmailIsSent(); + + $mail = $this->tester->grabLastSentEmail(); + + verify($mail)->instanceOf('yii\mail\MessageInterface'); + verify($mail->getTo())->arrayHasKey('some_email@example.com'); + verify($mail->getFrom())->arrayHasKey(\Yii::$app->params['supportEmail']); + verify($mail->getSubject())->equals('Account registration at ' . \Yii::$app->name); + verify($mail->toString())->stringContainsString($user->verification_token); + } + + public function testNotCorrectSignup() + { + $model = new SignupForm([ + 'username' => 'troy.becker', + 'email' => 'nicolas.dianna@hotmail.com', + 'password' => 'some_password', + ]); + + verify($model->signup())->empty(); + verify($model->getErrors('username'))->notEmpty(); + verify($model->getErrors('email'))->notEmpty(); + + verify($model->getFirstError('username')) + ->equals('This username has already been taken.'); + verify($model->getFirstError('email')) + ->equals('This email address has already been taken.'); + } +} diff --git a/frontend/tests/unit/models/VerifyEmailFormTest.php b/frontend/tests/unit/models/VerifyEmailFormTest.php new file mode 100755 index 0000000..e0fe2e4 --- /dev/null +++ b/frontend/tests/unit/models/VerifyEmailFormTest.php @@ -0,0 +1,55 @@ +tester->haveFixtures([ + 'user' => [ + 'class' => UserFixture::class, + 'dataFile' => codecept_data_dir() . 'user.php' + ] + ]); + } + + public function testVerifyWrongToken() + { + $this->tester->expectThrowable('\yii\base\InvalidArgumentException', function() { + new VerifyEmailForm(''); + }); + + $this->tester->expectThrowable('\yii\base\InvalidArgumentException', function() { + new VerifyEmailForm('notexistingtoken_1391882543'); + }); + } + + public function testAlreadyActivatedToken() + { + $this->tester->expectThrowable('\yii\base\InvalidArgumentException', function() { + new VerifyEmailForm('already_used_token_1548675330'); + }); + } + + public function testVerifyCorrectToken() + { + $model = new VerifyEmailForm('4ch0qbfhvWwkcuWqjN8SWRq72SOw1KYT_1548675330'); + $user = $model->verifyEmail(); + verify($user)->instanceOf('common\models\User'); + + verify($user->username)->equals('test.test'); + verify($user->email)->equals('test@mail.com'); + verify($user->status)->equals(\common\models\User::STATUS_ACTIVE); + verify($user->validatePassword('Test1234'))->true(); + } +} diff --git a/frontend/views/layouts/main.php b/frontend/views/layouts/main.php new file mode 100755 index 0000000..493730a --- /dev/null +++ b/frontend/views/layouts/main.php @@ -0,0 +1,86 @@ + +beginPage() ?> + + + + + + registerCsrfMetaTags() ?> + <?= Html::encode($this->title) ?> + head() ?> + + +beginBody() ?> + +
+ Yii::$app->name, + 'brandUrl' => Yii::$app->homeUrl, + 'options' => [ + 'class' => 'navbar navbar-expand-md navbar-dark bg-dark fixed-top', + ], + ]); + $menuItems = [ + ['label' => 'Home', 'url' => ['/site/index']], + ['label' => 'About', 'url' => ['/site/about']], + ['label' => 'Contact', 'url' => ['/site/contact']], + ['label' => 'Новости', 'url' => ['/news']], + ['label' => 'Форма', 'url' => ['/news/data']], + ]; + if (Yii::$app->user->isGuest) { + $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']]; + } + + echo Nav::widget([ + 'options' => ['class' => 'navbar-nav me-auto mb-2 mb-md-0'], + 'items' => $menuItems, + ]); + if (Yii::$app->user->isGuest) { + echo Html::tag('div',Html::a('Login',['/site/login'],['class' => ['btn btn-link login text-decoration-none']]),['class' => ['d-flex']]); + } else { + echo Html::beginForm(['/site/logout'], 'post', ['class' => 'd-flex']) + . Html::submitButton( + 'Logout (' . Yii::$app->user->identity->username . ')', + ['class' => 'btn btn-link logout text-decoration-none'] + ) + . Html::endForm(); + } + NavBar::end(); + ?> +
+ +
+
+ isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], + ]) ?> + + +
+
+ +
+
+

© name) ?>

+

+
+
+ +endBody() ?> + + +endPage(); diff --git a/frontend/views/news/data.php b/frontend/views/news/data.php new file mode 100755 index 0000000..d3ec7b4 --- /dev/null +++ b/frontend/views/news/data.php @@ -0,0 +1,32 @@ + + +
+
+ 'contact-form']); ?> + + field($model, 'fio')->textInput(['autofocus' => true]) ?> + + field($model, 'email') ?> + + field($model, 'phone')->widget(\yii\widgets\MaskedInput::class, + ['mask' => '+7 (999) 999-99-99'] + )->textInput(['placeholder' => '+7 (999) 999-99-99', 'class' => ''])->label('Ваш Телефон'); ?> + +
+ 'btn btn-primary', 'name' => 'contact-button']) ?> +
+ + +
+
diff --git a/frontend/views/news/index.php b/frontend/views/news/index.php new file mode 100755 index 0000000..0a7062d --- /dev/null +++ b/frontend/views/news/index.php @@ -0,0 +1,20 @@ + +

Новости

+ + +
+ title ?> +

text ?>

+
+ +

Новостей нет

+ diff --git a/frontend/views/news/one.php b/frontend/views/news/one.php new file mode 100755 index 0000000..e5922c1 --- /dev/null +++ b/frontend/views/news/one.php @@ -0,0 +1,18 @@ + + + +
+

title ?>

+ Назад +

text ?>

+
+ +

Новость удалена

+ \ No newline at end of file diff --git a/frontend/views/news/text.php b/frontend/views/news/text.php new file mode 100644 index 0000000..a1d858c --- /dev/null +++ b/frontend/views/news/text.php @@ -0,0 +1,20 @@ + +

Тексты

+ + +
+

title ?>

+

text ?>

+ + +

текстов нет

+ diff --git a/frontend/views/site/about.php b/frontend/views/site/about.php new file mode 100755 index 0000000..b40ea44 --- /dev/null +++ b/frontend/views/site/about.php @@ -0,0 +1,16 @@ +title = 'About'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ +

This is the About page. You may modify the following file to customize its content:

+ + +
diff --git a/frontend/views/site/contact.php b/frontend/views/site/contact.php new file mode 100755 index 0000000..6e6b8ac --- /dev/null +++ b/frontend/views/site/contact.php @@ -0,0 +1,45 @@ +title = 'Contact'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ +

+ If you have business inquiries or other questions, please fill out the following form to contact us. Thank you. +

+ +
+
+ 'contact-form']); ?> + + field($model, 'name')->textInput(['autofocus' => true]) ?> + + field($model, 'email') ?> + + field($model, 'subject') ?> + + field($model, 'body')->textarea(['rows' => 6]) ?> + + field($model, 'verifyCode')->widget(Captcha::class, [ + 'template' => '
{image}
{input}
', + ]) ?> + +
+ 'btn btn-primary', 'name' => 'contact-button']) ?> +
+ + +
+
+ +
diff --git a/frontend/views/site/error.php b/frontend/views/site/error.php new file mode 100755 index 0000000..1768747 --- /dev/null +++ b/frontend/views/site/error.php @@ -0,0 +1,27 @@ +title = $name; +?> +
+ +

title) ?>

+ +
+ +
+ +

+ The above error occurred while the Web server was processing your request. +

+

+ Please contact us if you think this is a server error. Thank you. +

+ +
diff --git a/frontend/views/site/index.php b/frontend/views/site/index.php new file mode 100755 index 0000000..b489c8e --- /dev/null +++ b/frontend/views/site/index.php @@ -0,0 +1,52 @@ +title = 'My Yii Application'; +?> +
+
+
+

Congratulations!

+

You have successfully created your Yii-powered application.

+

Get started with Yii

+
+
+ +
+ +
+
+

Heading

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur.

+ +

Yii Documentation »

+
+
+

Heading

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur.

+ +

Yii Forum »

+
+
+

Heading

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur.

+ +

Yii Extensions »

+
+
+ +
+
diff --git a/frontend/views/site/login.php b/frontend/views/site/login.php new file mode 100755 index 0000000..6685b8c --- /dev/null +++ b/frontend/views/site/login.php @@ -0,0 +1,41 @@ +title = 'Login'; +$this->params['breadcrumbs'][] = $this->title; +?> + diff --git a/frontend/views/site/requestPasswordResetToken.php b/frontend/views/site/requestPasswordResetToken.php new file mode 100755 index 0000000..72f1693 --- /dev/null +++ b/frontend/views/site/requestPasswordResetToken.php @@ -0,0 +1,31 @@ +title = 'Request password reset'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ +

Please fill out your email. A link to reset password will be sent there.

+ +
+
+ 'request-password-reset-form']); ?> + + field($model, 'email')->textInput(['autofocus' => true]) ?> + +
+ 'btn btn-primary']) ?> +
+ + +
+
+
diff --git a/frontend/views/site/resendVerificationEmail.php b/frontend/views/site/resendVerificationEmail.php new file mode 100755 index 0000000..23f5666 --- /dev/null +++ b/frontend/views/site/resendVerificationEmail.php @@ -0,0 +1,31 @@ +title = 'Resend verification email'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ +

Please fill out your email. A verification email will be sent there.

+ +
+
+ 'resend-verification-email-form']); ?> + + field($model, 'email')->textInput(['autofocus' => true]) ?> + +
+ 'btn btn-primary']) ?> +
+ + +
+
+
diff --git a/frontend/views/site/resetPassword.php b/frontend/views/site/resetPassword.php new file mode 100755 index 0000000..61e15b4 --- /dev/null +++ b/frontend/views/site/resetPassword.php @@ -0,0 +1,31 @@ +title = 'Reset password'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+

title) ?>

+ +

Please choose your new password:

+ +
+
+ 'reset-password-form']); ?> + + field($model, 'password')->passwordInput(['autofocus' => true]) ?> + +
+ 'btn btn-primary']) ?> +
+ + +
+
+
diff --git a/frontend/views/site/signup.php b/frontend/views/site/signup.php new file mode 100755 index 0000000..3b005eb --- /dev/null +++ b/frontend/views/site/signup.php @@ -0,0 +1,35 @@ +title = 'Signup'; +$this->params['breadcrumbs'][] = $this->title; +?> + diff --git a/frontend/web/assets/.gitignore b/frontend/web/assets/.gitignore new file mode 100755 index 0000000..d6b7ef3 --- /dev/null +++ b/frontend/web/assets/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/frontend/web/css/site.css b/frontend/web/css/site.css new file mode 100755 index 0000000..4a65ca7 --- /dev/null +++ b/frontend/web/css/site.css @@ -0,0 +1,103 @@ +main > .container, main > .container-fluid +{ + padding: 70px 15px 20px; +} + +.footer { + background-color: #f5f5f5; + font-size: .9em; + height: 60px; +} + +.footer > .container, .footer > .container-fluid { + padding-right: 15px; + padding-left: 15px; +} + +.not-set { + color: #c55; + font-style: italic; +} + +/* add sorting icons to gridview sort links */ +a.asc:after, a.desc:after { + content: ''; + left: 3px; + display: inline-block; + width: 0; + height: 0; + border: solid 5px transparent; + margin: 4px 4px 2px 4px; + background: transparent; +} + +a.asc:after { + border-bottom: solid 7px #212529; + border-top-width: 0; +} + +a.desc:after { + border-top: solid 7px #212529; + border-bottom-width: 0; +} + +.grid-view th, +.grid-view td:last-child { + white-space: nowrap; +} + +.grid-view .filters input, +.grid-view .filters select { + min-width: 50px; +} + +.hint-block { + display: block; + margin-top: 5px; + color: #999; +} + +.error-summary { + color: #a94442; + background: #fdf7f7; + border-left: 3px solid #eed3d7; + padding: 10px 20px; + margin: 0 0 15px 0; +} + +/* align the logout "link" (button in form) of the navbar */ +.navbar form > button.logout { + padding-top: 7px; + color: rgba(255, 255, 255, 0.5); +} + +@media(max-width:767px) { + .navbar form > button.logout { + display:block; + text-align: left; + width: 100%; + padding: 10px 0; + } +} + +.navbar form > button.logout:focus, +.navbar form > button.logout:hover { + text-decoration: none; + color: rgba(255, 255, 255, 0.75); +} + +.navbar form > button.logout:focus { + outline: none; +} + +/* style breadcrumb widget as in previous bootstrap versions */ +.breadcrumb { + background-color: var(--bs-gray-200); + border-radius: .25rem; + padding: .75rem 1rem; +} + +.breadcrumb-item > a +{ + text-decoration: none; +} \ No newline at end of file diff --git a/frontend/web/favicon.ico b/frontend/web/favicon.ico new file mode 100755 index 0000000..580ed73 Binary files /dev/null and b/frontend/web/favicon.ico differ diff --git a/frontend/web/uploads/image_2022-11-19_16-48-34.png b/frontend/web/uploads/image_2022-11-19_16-48-34.png new file mode 100644 index 0000000..f31664a Binary files /dev/null and b/frontend/web/uploads/image_2022-11-19_16-48-34.png differ diff --git a/frontend/web/uploads/image_2022-12-22_17-22-19.png b/frontend/web/uploads/image_2022-12-22_17-22-19.png new file mode 100644 index 0000000..b181549 Binary files /dev/null and b/frontend/web/uploads/image_2022-12-22_17-22-19.png differ diff --git a/frontend/web/uploads/image_2023-01-19_19-48-33.png b/frontend/web/uploads/image_2023-01-19_19-48-33.png new file mode 100755 index 0000000..b9226ef Binary files /dev/null and b/frontend/web/uploads/image_2023-01-19_19-48-33.png differ diff --git a/frontend/web/uploads/image_2023-01-23_21-38-25.png b/frontend/web/uploads/image_2023-01-23_21-38-25.png new file mode 100755 index 0000000..d5c0ca7 Binary files /dev/null and b/frontend/web/uploads/image_2023-01-23_21-38-25.png differ diff --git a/frontend/web/uploads/Снимок экрана от 2023-03-01 16-59-46.png b/frontend/web/uploads/Снимок экрана от 2023-03-01 16-59-46.png new file mode 100755 index 0000000..a45ccc6 Binary files /dev/null and b/frontend/web/uploads/Снимок экрана от 2023-03-01 16-59-46.png differ diff --git a/init b/init new file mode 100755 index 0000000..cf472c9 --- /dev/null +++ b/init @@ -0,0 +1,356 @@ +#!/usr/bin/env php + $name) { + echo " [$i] $name\n"; + } + echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; + $answer = trim(fgets(STDIN)); + + if (!ctype_digit($answer) || !in_array($answer, range(0, count($envs) - 1))) { + echo "\n Quit initialization.\n"; + exit(0); + } + + if (isset($envNames[$answer])) { + $envName = $envNames[$answer]; + } +} else { + $envName = $params['env']; +} + +if (!in_array($envName, $envNames, true)) { + $envsList = implode(', ', $envNames); + echo "\n $envName is not a valid environment. Try one of the following: $envsList. \n"; + exit(2); +} + +$env = $envs[$envName]; + +if (empty($params['env'])) { + echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] "; + $answer = trim(fgets(STDIN)); + if (strncasecmp($answer, 'y', 1)) { + echo "\n Quit initialization.\n"; + exit(0); + } +} + +$rootPath = "$root/environments/{$env['path']}"; +if (!is_dir($rootPath)) { + printError("$rootPath directory does not exist. Check path in $envName environment."); + exit(3); +} + +echo "\n Start initialization ...\n\n"; + +$files = getFileList($rootPath); +if (isset($env['skipFiles'])) { + $skipFiles = $env['skipFiles']; + array_walk($skipFiles, function(&$value) use($env, $root) { $value = "$root/$value"; }); + $files = array_diff($files, array_intersect_key($env['skipFiles'], array_filter($skipFiles, 'file_exists'))); +} +$all = false; +foreach ($files as $file) { + if (!copyFile($root, "environments/{$env['path']}/$file", $file, $all, $params)) { + break; + } +} + +$filesToRemove = []; +$skipFiles = !empty($env['skipFiles']) ? $env['skipFiles'] : []; +foreach(array_column($envs, 'path') as $envPath) { + if ($env['path'] === $envPath) continue; + + $filesToRemove = + array_merge( + $filesToRemove, + array_diff(getFileList("$root/environments/{$envPath}"), $files, $filesToRemove, $skipFiles) + ); +} +$filesToRemove = array_filter($filesToRemove, 'file_exists'); +if ($filesToRemove) { + echo "\n Remove files from other environments ...\n\n"; + + $all = false; + foreach ($filesToRemove as $file) { + if (!removeFile($root, $file, $all, $params)) { + break; + } + } + echo "\n"; +} + +$callbacks = ['setCookieValidationKey', 'setWritable', 'setExecutable', 'createSymlink']; +foreach ($callbacks as $callback) { + if (!empty($env[$callback])) { + $callback($root, $env[$callback]); + } +} + +echo "\n ... initialization completed.\n\n"; + +function getFileList($root, $basePath = '') +{ + $files = []; + $handle = opendir($root); + while (($path = readdir($handle)) !== false) { + if ($path === '.git' || $path === '.svn' || $path === '.' || $path === '..') { + continue; + } + $fullPath = "$root/$path"; + $relativePath = $basePath === '' ? $path : "$basePath/$path"; + if (is_dir($fullPath)) { + $files = array_merge($files, getFileList($fullPath, $relativePath)); + } else { + $files[] = $relativePath; + } + } + closedir($handle); + return $files; +} + +function copyFile($root, $source, $target, &$all, $params) +{ + if (!is_file($root . '/' . $source)) { + echo " skip $target ($source not exist)\n"; + return true; + } + if (is_file($root . '/' . $target)) { + if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { + echo " unchanged $target\n"; + return true; + } + if ($all) { + echo " overwrite $target\n"; + } else { + echo " exist $target\n"; + echo " ...overwrite? [Yes|No|All|Quit] "; + + + $answer = !empty($params['overwrite']) ? $params['overwrite'] : trim(fgets(STDIN)); + if (!strncasecmp($answer, 'q', 1)) { + return false; + } else { + if (!strncasecmp($answer, 'y', 1)) { + echo " overwrite $target\n"; + } else { + if (!strncasecmp($answer, 'a', 1)) { + echo " overwrite $target\n"; + $all = true; + } else { + echo " skip $target\n"; + return true; + } + } + } + } + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; + } + echo " generate $target\n"; + @mkdir(dirname($root . '/' . $target), 0777, true); + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; +} + +function removeFile($root, $target, &$all, $params) +{ + if (is_file($root . '/' . $target)) { + if ($all) { + echo " delete $target\n"; + } else { + echo " delete $target\n"; + echo " ...confirm? [Yes|No|All|Quit] "; + + $answer = !empty($params['delete']) ? $params['delete'] : trim(fgets(STDIN)); + if (!strncasecmp($answer, 'q', 1)) { + return false; + } else { + if (!strncasecmp($answer, 'y', 1)) { + echo " delete $target\n"; + } else { + if (!strncasecmp($answer, 'a', 1)) { + echo " delete $target\n"; + $all = true; + } else { + echo " skip $target\n"; + return true; + } + } + } + } + return unlink($root . '/' . $target); + } + + return true; +} + +function getParams() +{ + $rawParams = []; + if (isset($_SERVER['argv'])) { + $rawParams = $_SERVER['argv']; + array_shift($rawParams); + } + + $params = []; + foreach ($rawParams as $param) { + if (preg_match('/^--([\w-]*\w)(=(.*))?$/', $param, $matches)) { + $name = $matches[1]; + $params[$name] = isset($matches[3]) ? $matches[3] : true; + } else { + $params[] = $param; + } + } + return $params; +} + +function setWritable($root, $paths) +{ + foreach ($paths as $writable) { + if (is_dir("$root/$writable")) { + if (@chmod("$root/$writable", 0777)) { + echo " chmod 0777 $writable\n"; + } else { + printError("Operation chmod not permitted for directory $writable."); + } + } else { + printError("Directory $writable does not exist."); + } + } +} + +function setExecutable($root, $paths) +{ + foreach ($paths as $executable) { + if (file_exists("$root/$executable")) { + if (@chmod("$root/$executable", 0755)) { + echo " chmod 0755 $executable\n"; + } else { + printError("Operation chmod not permitted for $executable."); + } + } else { + printError("$executable does not exist."); + } + } +} + +function setCookieValidationKey($root, $paths) +{ + foreach ($paths as $file) { + echo " generate cookie validation key in $file\n"; + $file = $root . '/' . $file; + $length = 32; + $bytes = openssl_random_pseudo_bytes($length); + $key = strtr(substr(base64_encode($bytes), 0, $length), '+/=', '_-.'); + $content = preg_replace('/(("|\')cookieValidationKey("|\')\s*=>\s*)(""|\'\')/', "\\1'$key'", file_get_contents($file)); + file_put_contents($file, $content); + } +} + +function createSymlink($root, $links) +{ + foreach ($links as $link => $target) { + //first removing folders to avoid errors if the folder already exists + @rmdir($root . "/" . $link); + //next removing existing symlink in order to update the target + if (is_link($root . "/" . $link)) { + @unlink($root . "/" . $link); + } + if (@symlink($root . "/" . $target, $root . "/" . $link)) { + echo " symlink $root/$target $root/$link\n"; + } else { + printError("Cannot create symlink $root/$target $root/$link."); + } + } +} + +/** + * Prints error message. + * @param string $message message + */ +function printError($message) +{ + echo "\n " . formatMessage("Error. $message", ['fg-red']) . " \n"; +} + +/** + * Returns true if the stream supports colorization. ANSI colors are disabled if not supported by the stream. + * + * - windows without ansicon + * - not tty consoles + * + * @return boolean true if the stream supports ANSI colors, otherwise false. + */ +function ansiColorsSupported() +{ + return DIRECTORY_SEPARATOR === '\\' + ? getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON' + : function_exists('posix_isatty') && @posix_isatty(STDOUT); +} + +/** + * Get ANSI code of style. + * @param string $name style name + * @return integer ANSI code of style. + */ +function getStyleCode($name) +{ + $styles = [ + 'bold' => 1, + 'fg-black' => 30, + 'fg-red' => 31, + 'fg-green' => 32, + 'fg-yellow' => 33, + 'fg-blue' => 34, + 'fg-magenta' => 35, + 'fg-cyan' => 36, + 'fg-white' => 37, + 'bg-black' => 40, + 'bg-red' => 41, + 'bg-green' => 42, + 'bg-yellow' => 43, + 'bg-blue' => 44, + 'bg-magenta' => 45, + 'bg-cyan' => 46, + 'bg-white' => 47, + ]; + return $styles[$name]; +} + +/** + * Formats message using styles if STDOUT supports it. + * @param string $message message + * @param string[] $styles styles + * @return string formatted message. + */ +function formatMessage($message, $styles) +{ + if (empty($styles) || !ansiColorsSupported()) { + return $message; + } + + return sprintf("\x1b[%sm", implode(';', array_map('getStyleCode', $styles))) . $message . "\x1b[0m"; +} diff --git a/init.bat b/init.bat new file mode 100755 index 0000000..1b92c19 --- /dev/null +++ b/init.bat @@ -0,0 +1,15 @@ +@echo off + +rem ------------------------------------------------------------- +rem Yii command line init script for Windows. +rem ------------------------------------------------------------- + +@setlocal + +set YII_PATH=%~dp0 + +if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe + +"%PHP_COMMAND%" "%YII_PATH%init" %* + +@endlocal diff --git a/requirements.php b/requirements.php new file mode 100755 index 0000000..67b344b --- /dev/null +++ b/requirements.php @@ -0,0 +1,155 @@ +Error\n\n" + . "

The path to yii framework seems to be incorrect.

\n" + . '

You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . ".

\n" + . '

Please refer to the README on how to install Yii.

\n"; + if (!empty($_SERVER['argv'])) { + // do not print HTML when used in console mode + echo strip_tags($message); + } else { + echo $message; + } + exit(1); +} + +require_once $frameworkPath . '/requirements/YiiRequirementChecker.php'; +$requirementsChecker = new YiiRequirementChecker(); + +$gdMemo = $imagickMemo = 'Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required for image CAPTCHA.'; +$gdOK = $imagickOK = false; + +if (extension_loaded('imagick')) { + $imagick = new Imagick(); + $imagickFormats = $imagick->queryFormats('PNG'); + if (in_array('PNG', $imagickFormats)) { + $imagickOK = true; + } else { + $imagickMemo = 'Imagick extension should be installed with PNG support in order to be used for image CAPTCHA.'; + } +} + +if (extension_loaded('gd')) { + $gdInfo = gd_info(); + if (!empty($gdInfo['FreeType Support'])) { + $gdOK = true; + } else { + $gdMemo = 'GD extension should be installed with FreeType support in order to be used for image CAPTCHA.'; + } +} + +/** + * Adjust requirements according to your application specifics. + */ +$requirements = array( + // Database : + array( + 'name' => 'PDO extension', + 'mandatory' => true, + 'condition' => extension_loaded('pdo'), + 'by' => 'All DB-related classes', + ), + array( + 'name' => 'PDO SQLite extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_sqlite'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for SQLite database.', + ), + array( + 'name' => 'PDO MySQL extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_mysql'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for MySQL database.', + ), + array( + 'name' => 'PDO PostgreSQL extension', + 'mandatory' => false, + 'condition' => extension_loaded('pdo_pgsql'), + 'by' => 'All DB-related classes', + 'memo' => 'Required for PostgreSQL database.', + ), + // Cache : + array( + 'name' => 'Memcache extension', + 'mandatory' => false, + 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), + 'by' => 'MemCache', + 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : '' + ), + array( + 'name' => 'APC extension', + 'mandatory' => false, + 'condition' => extension_loaded('apc'), + 'by' => 'ApcCache', + ), + // CAPTCHA: + array( + 'name' => 'GD PHP extension with FreeType support', + 'mandatory' => false, + 'condition' => $gdOK, + 'by' => 'Captcha', + 'memo' => $gdMemo, + ), + array( + 'name' => 'ImageMagick PHP extension with PNG support', + 'mandatory' => false, + 'condition' => $imagickOK, + 'by' => 'Captcha', + 'memo' => $imagickMemo, + ), + // PHP ini : + 'phpExposePhp' => array( + 'name' => 'Expose PHP', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), + 'by' => 'Security reasons', + 'memo' => '"expose_php" should be disabled at php.ini', + ), + 'phpAllowUrlInclude' => array( + 'name' => 'PHP allow url include', + 'mandatory' => false, + 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), + 'by' => 'Security reasons', + 'memo' => '"allow_url_include" should be disabled at php.ini', + ), + 'phpSmtp' => array( + 'name' => 'PHP mail SMTP', + 'mandatory' => false, + 'condition' => strlen(ini_get('SMTP')) > 0, + 'by' => 'Email sending', + 'memo' => 'PHP mail SMTP server required', + ), +); + +$result = $requirementsChecker->checkYii()->check($requirements)->getResult(); +$requirementsChecker->render(); + +exit($result['summary']['errors'] === 0 ? 0 : 1); diff --git a/vagrant/config/.gitignore b/vagrant/config/.gitignore new file mode 100755 index 0000000..0685a56 --- /dev/null +++ b/vagrant/config/.gitignore @@ -0,0 +1,2 @@ +# local configuration +vagrant-local.yml \ No newline at end of file diff --git a/vagrant/config/vagrant-local.example.yml b/vagrant/config/vagrant-local.example.yml new file mode 100755 index 0000000..7b36400 --- /dev/null +++ b/vagrant/config/vagrant-local.example.yml @@ -0,0 +1,22 @@ +# Your personal GitHub token +github_token: +# Read more: https://github.com/blog/1509-personal-api-tokens +# You can generate it here: https://github.com/settings/tokens + +# Guest OS timezone +timezone: Europe/London + +# Are we need check box updates for every 'vagrant up'? +box_check_update: false + +# Virtual machine name +machine_name: y2aa + +# Virtual machine IP +ip: 192.168.83.137 + +# Virtual machine CPU cores number +cpus: 1 + +# Virtual machine RAM +memory: 1024 diff --git a/vagrant/nginx/app.conf b/vagrant/nginx/app.conf new file mode 100755 index 0000000..0462830 --- /dev/null +++ b/vagrant/nginx/app.conf @@ -0,0 +1,77 @@ +server { + charset utf-8; + client_max_body_size 128M; + sendfile off; + + listen 80; ## listen for ipv4 + #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 + + server_name y2aa-frontend.test; + root /app/frontend/web/; + index index.php; + + access_log /app/vagrant/nginx/log/frontend-access.log; + error_log /app/vagrant/nginx/log/frontend-error.log; + + location / { + # Redirect everything that isn't a real file to index.php + try_files $uri $uri/ /index.php$is_args$args; + } + + # uncomment to avoid processing of calls to non-existing static files by Yii + #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { + # try_files $uri =404; + #} + #error_page 404 /404.html; + + location ~ \.php$ { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + #fastcgi_pass 127.0.0.1:9000; + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + try_files $uri =404; + } + + location ~ /\.(ht|svn|git) { + deny all; + } +} + +server { + charset utf-8; + client_max_body_size 128M; + sendfile off; + + listen 80; ## listen for ipv4 + #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 + + server_name y2aa-backend.test; + root /app/backend/web/; + index index.php; + + access_log /app/vagrant/nginx/log/backend-access.log; + error_log /app/vagrant/nginx/log/backend-error.log; + + location / { + # Redirect everything that isn't a real file to index.php + try_files $uri $uri/ /index.php$is_args$args; + } + + # uncomment to avoid processing of calls to non-existing static files by Yii + #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { + # try_files $uri =404; + #} + #error_page 404 /404.html; + + location ~ \.php$ { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + #fastcgi_pass 127.0.0.1:9000; + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + try_files $uri =404; + } + + location ~ /\.(ht|svn|git) { + deny all; + } +} diff --git a/vagrant/nginx/log/.gitignore b/vagrant/nginx/log/.gitignore new file mode 100755 index 0000000..c15cedd --- /dev/null +++ b/vagrant/nginx/log/.gitignore @@ -0,0 +1,5 @@ +# nginx logs +backend-access.log +backend-error.log +frontend-access.log +frontend-error.log \ No newline at end of file diff --git a/vagrant/provision/always-as-root.sh b/vagrant/provision/always-as-root.sh new file mode 100755 index 0000000..cca9cfb --- /dev/null +++ b/vagrant/provision/always-as-root.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +source /app/vagrant/provision/common.sh + +#== Provision script == + +info "Provision-script user: `whoami`" + +info "Restart web-stack" +service php7.4-fpm restart +service nginx restart +service mysql restart \ No newline at end of file diff --git a/vagrant/provision/common.sh b/vagrant/provision/common.sh new file mode 100755 index 0000000..ab5e1e0 --- /dev/null +++ b/vagrant/provision/common.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +#== Bash helpers == + +function info { + echo " " + echo "--> $1" + echo " " +} diff --git a/vagrant/provision/once-as-root.sh b/vagrant/provision/once-as-root.sh new file mode 100755 index 0000000..4542a43 --- /dev/null +++ b/vagrant/provision/once-as-root.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +source /app/vagrant/provision/common.sh + +#== Import script args == + +timezone=$(echo "$1") +readonly IP=$2 + +#== Provision script == + +info "Provision-script user: `whoami`" + +export DEBIAN_FRONTEND=noninteractive + +info "Configure timezone" +timedatectl set-timezone ${timezone} --no-ask-password + +info "AWK initial replacement work" +awk -v ip=$IP -f /app/vagrant/provision/provision.awk /app/environments/dev/*end/config/main-local.php + +info "Prepare root password for MySQL" +debconf-set-selections <<< "mysql-community-server mysql-community-server/root-pass password \"''\"" +debconf-set-selections <<< "mysql-community-server mysql-community-server/re-root-pass password \"''\"" +echo "Done!" + +info "Update OS software" +apt-get update +apt-get upgrade -y + +info "Add ppa:ondrej/php" +apt-get install -y python-software-properties +apt-get update && apt-get upgrade -y +add-apt-repository -y ppa:ondrej/php + +info "Install additional software" +apt-get install -y php7.4-curl php7.4-cli php7.4-intl php7.4-mysqlnd php7.4-gd php7.4-fpm php7.4-mbstring php7.4-xml unzip nginx mysql-server-5.7 php7.4-xdebug + +info "Configure MySQL" +sed -i "s/.*bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/mysql.conf.d/mysqld.cnf +mysql -uroot <<< "CREATE USER 'root'@'%' IDENTIFIED BY ''" +mysql -uroot <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'" +mysql -uroot <<< "DROP USER 'root'@'localhost'" +mysql -uroot <<< "FLUSH PRIVILEGES" +echo "Done!" + +info "Configure PHP-FPM" +sed -i 's/user = www-data/user = vagrant/g' /etc/php/7.4/fpm/pool.d/www.conf +sed -i 's/group = www-data/group = vagrant/g' /etc/php/7.4/fpm/pool.d/www.conf +sed -i 's/owner = www-data/owner = vagrant/g' /etc/php/7.4/fpm/pool.d/www.conf +cat << EOF > /etc/php/7.4/mods-available/xdebug.ini +zend_extension=xdebug.so +xdebug.remote_enable=1 +xdebug.remote_connect_back=1 +xdebug.remote_port=9000 +xdebug.remote_autostart=1 +EOF +echo "Done!" + +info "Configure NGINX" +sed -i 's/user www-data/user vagrant/g' /etc/nginx/nginx.conf +echo "Done!" + +info "Enabling site configuration" +ln -s /app/vagrant/nginx/app.conf /etc/nginx/sites-enabled/app.conf +echo "Done!" + +info "Initailize databases for MySQL" +mysql -uroot <<< "CREATE DATABASE yii2advanced" +mysql -uroot <<< "CREATE DATABASE yii2advanced_test" +echo "Done!" + +info "Install composer" +curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ No newline at end of file diff --git a/vagrant/provision/once-as-vagrant.sh b/vagrant/provision/once-as-vagrant.sh new file mode 100755 index 0000000..ffaa898 --- /dev/null +++ b/vagrant/provision/once-as-vagrant.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +source /app/vagrant/provision/common.sh + +#== Import script args == + +github_token=$(echo "$1") + +#== Provision script == + +info "Provision-script user: `whoami`" + +info "Configure composer" +composer config --global github-oauth.github.com ${github_token} +echo "Done!" + +info "Install project dependencies" +cd /app +composer --no-progress --prefer-dist install + +info "Init project" +./init --env=Development --overwrite=y + +info "Apply migrations" +./yii migrate --interactive=0 +./yii_test migrate --interactive=0 + +info "Create bash-alias 'app' for vagrant user" +echo 'alias app="cd /app"' | tee /home/vagrant/.bash_aliases + +info "Enabling colorized prompt for guest console" +sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/" /home/vagrant/.bashrc diff --git a/vagrant/provision/provision.awk b/vagrant/provision/provision.awk new file mode 100755 index 0000000..65e9bde --- /dev/null +++ b/vagrant/provision/provision.awk @@ -0,0 +1,70 @@ +### +# Modifying Yii2's files for initialize Vagrant VM +# +# @author HA3IK +# @version 1.0.0 + +BEGIN { + print "AWK BEGINs its work:" + IGNORECASE = 1 + # Correct IP - wildcard last octet + match(ip, /(([0-9]+\.)+)/, arr) + ip = arr[1] "*" +} +BEGINFILE { + msg = "- Work with: " FILENAME + # Define array index for the file + switch (FILENAME) { + case /environments\/dev\/(back|front)end\/config\/main\-local\.php$/: + isFile["IsMainLocConf"] = 1 + msg = msg " - allow VM IP for Gii and debug toolbar" + break + } + # Print the final message + print msg +} +# BODY +{ + # IF environments/dev/(back|front)end/config/main-local.php + if (isFile["IsMainLocConf"]) { + # IF the line[s] after yii\(debug|gii)\Module + if (FNR == nextLine["nubmer"]) { + # Prepare for next line + ++nextLine["nubmer"] + # IF line has "allowedIPs" + if (index($0, "allowedIPs")) { + # IF our IP is not there + if (!index($0, ip)) { + # Add it + match($0, /([^\]]+)(.+)/, arr) + $0 = sprintf("%s, '%s'%s", arr[1], ip, arr[2]) + } + # Delete next line + delete nextLine + # IF "allowedIPs" are not set - search for the end of an array structure + } else if ($0 ~ /\];$/) { + # Rewrite line + $0 = nextLine["indent"] "'allowedIPs' => ['127.0.0.1', '::1', '" ip "'],\n" $0 + delete nextLine + } + # IF line is done + if (!length(nextLine)) { + printf " Line %d: Allowed IP: %s\n", FNR, ip + } + # Search for yii\(debug|gii)\Module + } else if (match($0, /^(\s+).+yii\\(debug|gii)\\Module/, arr)) { + # Save next line and indent + nextLine["nubmer"] = FNR + 1 + nextLine["indent"] = arr[1] + } + # Rewrite the file + print $0 > FILENAME + } +} +ENDFILE { + delete isFile + close(FILENAME) +} +END { + print "AWK ENDs its work." +} diff --git a/yii.bat b/yii.bat new file mode 100755 index 0000000..3a68942 --- /dev/null +++ b/yii.bat @@ -0,0 +1,15 @@ +@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" %* + +@endlocal